bridge: Avoid unnecessary clone on forward path
authorHerbert Xu <herbert@gondor.apana.org.au>
Sat, 27 Feb 2010 19:41:41 +0000 (19:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 28 Feb 2010 08:48:43 +0000 (00:48 -0800)
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>
net/bridge/br_forward.c
net/bridge/br_input.c
net/bridge/br_private.h

index bc1704ac6cd9f68f2b73e36443a3c8162b5f5fe1..6cd50c6e57cf1936db03fbab001018fe16cdeb62 100644 (file)
@@ -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);
 }
index be5ab8df66614478cfec21c25b35aae6b0cf658c..edfdaef44296184e82390ae82fdfef01da626eda 100644 (file)
@@ -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)
index 16513793156e4a713cb0cec3ecd7f3a775889cf2..fad5a2669d3460e76df10908d4ff096c100848ae 100644 (file)
@@ -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);