summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-02-27 22:41:41 +0300
committerDavid S. Miller <davem@davemloft.net>2010-02-28 11:48:43 +0300
commitb33084be192ee1e347d98bb5c9e38a53d98d35e2 (patch)
tree940c62309437ca5fa087275bc64b4ffd677c79c3
parent68b7c895be336b19f4c38d7cb500132fabba0afd (diff)
downloadlinux-b33084be192ee1e347d98bb5c9e38a53d98d35e2.tar.xz
bridge: Avoid unnecessary clone on forward path
When the packet is delivered to the local bridge device we may end up cloning it unnecessarily if no bridge port can receive the packet in br_flood. This patch avoids this by moving the skb_clone into br_flood. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/br_forward.c33
-rw-r--r--net/bridge/br_input.c5
-rw-r--r--net/bridge/br_private.h3
3 files changed, 25 insertions, 16 deletions
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index bc1704ac6cd9..6cd50c6e57cf 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -105,8 +105,9 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
/* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb,
- void (*__packet_hook)(const struct net_bridge_port *p,
- struct sk_buff *skb))
+ struct sk_buff *skb0,
+ void (*__packet_hook)(const struct net_bridge_port *p,
+ struct sk_buff *skb))
{
struct net_bridge_port *p;
struct net_bridge_port *prev;
@@ -120,8 +121,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
br->dev->stats.tx_dropped++;
- kfree_skb(skb);
- return;
+ goto out;
}
__packet_hook(prev, skb2);
@@ -131,23 +131,34 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
}
}
- if (prev != NULL) {
- __packet_hook(prev, skb);
- return;
+ if (!prev)
+ goto out;
+
+ if (skb0) {
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (!skb) {
+ br->dev->stats.tx_dropped++;
+ goto out;
+ }
}
+ __packet_hook(prev, skb);
+ return;
- kfree_skb(skb);
+out:
+ if (!skb0)
+ kfree_skb(skb);
}
/* called with rcu_read_lock */
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
{
- br_flood(br, skb, __br_deliver);
+ br_flood(br, skb, NULL, __br_deliver);
}
/* called under bridge lock */
-void br_flood_forward(struct net_bridge *br, struct sk_buff *skb)
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
+ struct sk_buff *skb2)
{
- br_flood(br, skb, __br_forward);
+ br_flood(br, skb, skb2, __br_forward);
}
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index be5ab8df6661..edfdaef44296 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -72,14 +72,11 @@ int br_handle_frame_finish(struct sk_buff *skb)
skb = NULL;
}
- if (skb2 == skb)
- skb2 = skb_clone(skb, GFP_ATOMIC);
-
if (skb) {
if (dst)
br_forward(dst->dst, skb);
else
- br_flood_forward(br, skb);
+ br_flood_forward(br, skb, skb2);
}
if (skb2)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 16513793156e..fad5a2669d34 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -180,7 +180,8 @@ extern void br_forward(const struct net_bridge_port *to,
struct sk_buff *skb);
extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
-extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb);
+extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
+ struct sk_buff *skb2);
/* br_if.c */
extern void br_port_carrier_check(struct net_bridge_port *p);