]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
vlan: allow nested vlan_do_receive()
authorMaxim Uvarov <maxim.uvarov@oracle.com>
Fri, 30 Mar 2012 23:52:48 +0000 (16:52 -0700)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Fri, 30 Mar 2012 23:52:48 +0000 (16:52 -0700)
commit 2425717b27eb (net: allow vlan traffic to be received under bond)
broke ARP processing on vlan on top of bonding.

       +-------+
eth0 --| bond0 |---bond0.103
eth1 --|       |
       +-------+

52870.115435: skb_gro_reset_offset <-napi_gro_receive
52870.115435: dev_gro_receive <-napi_gro_receive
52870.115435: napi_skb_finish <-napi_gro_receive
52870.115435: netif_receive_skb <-napi_skb_finish
52870.115435: get_rps_cpu <-netif_receive_skb
52870.115435: __netif_receive_skb <-netif_receive_skb
52870.115436: vlan_do_receive <-__netif_receive_skb
52870.115436: bond_handle_frame <-__netif_receive_skb
52870.115436: vlan_do_receive <-__netif_receive_skb
52870.115436: arp_rcv <-__netif_receive_skb
52870.115436: kfree_skb <-arp_rcv

Packet is dropped in arp_rcv() because its pkt_type was set to
PACKET_OTHERHOST in the first vlan_do_receive() call, since no eth0.103
exists.

We really need to change pkt_type only if no more rx_handler is about to
be called for the packet.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Reviewed-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Conflicts:

include/linux/if_vlan.h

include/linux/if_vlan.h
net/8021q/vlan_core.c
net/core/dev.c

index affa27380b72e6096a060bf59c8f5261dfb03b1c..e57c19fe0f8bb7f887aae96c4ac838e1d7471928 100644 (file)
@@ -136,7 +136,7 @@ extern u16 vlan_dev_vlan_id(const struct net_device *dev);
 
 extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
                             u16 vlan_tci, int polling);
-extern bool vlan_do_receive(struct sk_buff **skb);
+extern bool vlan_do_receive(struct sk_buff **skb, bool last_handler);
 extern struct sk_buff *vlan_untag(struct sk_buff *skb);
 extern gro_result_t
 vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp,
@@ -164,6 +164,7 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev)
        return 0;
 }
 
+<<<<<<< HEAD
 static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
                                    u16 vlan_tci, int polling)
 {
@@ -172,8 +173,11 @@ static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
 }
 
 static inline bool vlan_do_receive(struct sk_buff **skb)
+=======
+static inline bool vlan_do_receive(struct sk_buff **skb, bool last_handler)
+>>>>>>> 6a32e4f... vlan: allow nested vlan_do_receive()
 {
-       if ((*skb)->vlan_tci & VLAN_VID_MASK)
+       if (((*skb)->vlan_tci & VLAN_VID_MASK) && last_handler)
                (*skb)->pkt_type = PACKET_OTHERHOST;
        return false;
 }
index 27263fb15642d884622c0567557aea45bc292020..6a07ce74e6cf9495525261a4a22f6bba798cb988 100644 (file)
@@ -4,7 +4,7 @@
 #include <linux/netpoll.h>
 #include "vlan.h"
 
-bool vlan_do_receive(struct sk_buff **skbp)
+bool vlan_do_receive(struct sk_buff **skbp, bool last_handler)
 {
        struct sk_buff *skb = *skbp;
        u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK;
@@ -13,7 +13,10 @@ bool vlan_do_receive(struct sk_buff **skbp)
 
        vlan_dev = vlan_find_dev(skb->dev, vlan_id);
        if (!vlan_dev) {
-               if (vlan_id)
+               /* Only the last call to vlan_do_receive() should change
+                * pkt_type to PACKET_OTHERHOST
+                */
+               if (vlan_id && last_handler)
                        skb->pkt_type = PACKET_OTHERHOST;
                return false;
        }
index fdab9a1174cbf91b0979c484d75be4f591b8e2e4..48f3aa7a4473ffcf8f6c4f1b3d0ab48ac73aac3c 100644 (file)
@@ -3152,18 +3152,18 @@ another_round:
 ncls:
 #endif
 
+       rx_handler = rcu_dereference(skb->dev->rx_handler);
        if (vlan_tx_tag_present(skb)) {
                if (pt_prev) {
                        ret = deliver_skb(skb, pt_prev, orig_dev);
                        pt_prev = NULL;
                }
-               if (vlan_do_receive(&skb))
+               if (vlan_do_receive(&skb, !rx_handler))
                        goto another_round;
                else if (unlikely(!skb))
                        goto out;
        }
 
-       rx_handler = rcu_dereference(skb->dev->rx_handler);
        if (rx_handler) {
                if (pt_prev) {
                        ret = deliver_skb(skb, pt_prev, orig_dev);