net: generic dev_disable_lro() stacked device handling
authorMichal Kubeček <mkubecek@suse.cz>
Thu, 13 Nov 2014 06:54:50 +0000 (07:54 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 13 Nov 2014 19:48:56 +0000 (14:48 -0500)
Large receive offloading is known to cause problems if received packets
are passed to other host. Therefore the kernel disables it by calling
dev_disable_lro() whenever a network device is enslaved in a bridge or
forwarding is enabled for it (or globally). For virtual devices we need
to disable LRO on the underlying physical device (which is actually
receiving the packets).

Current dev_disable_lro() code handles this  propagation for a vlan
(including 802.1ad nested vlan), macvlan or a vlan on top of a macvlan.
It doesn't handle other stacked devices and their combinations, in
particular propagation from a bond to its slaves which often causes
problems in virtualization setups.

As we now have generic data structures describing the upper-lower device
relationship, dev_disable_lro() can be generalized to disable LRO also
for all lower devices (if any) once it is disabled for the device
itself.

For bonding and teaming devices, it is necessary to disable LRO not only
on current slaves at the moment when dev_disable_lro() is called but
also on any slave (port) added later.

v2: use lower device links for all devices (including vlan and macvlan)

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Acked-by: Veaceslav Falico <vfalico@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/bonding/bond_main.c
drivers/net/team/team.c
net/core/dev.c

index b9b34566b9b85d54b0b6b1eb2dfcead6c1205983..8575fee8b359713a40de48d1b163c1f4783425af 100644 (file)
@@ -1526,6 +1526,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 #endif
 
+       if (!(bond_dev->features & NETIF_F_LRO))
+               dev_disable_lro(slave_dev);
+
        res = netdev_rx_handler_register(slave_dev, bond_handle_frame,
                                         new_slave);
        if (res) {
index 2368395d8ae5acb957e8a819f71dc08480c31ed1..93e224217e24b36b089102be11ada5921f62d83b 100644 (file)
@@ -1179,6 +1179,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
                goto err_enable_netpoll;
        }
 
+       if (!(dev->features & NETIF_F_LRO))
+               dev_disable_lro(port_dev);
+
        err = netdev_rx_handler_register(port_dev, team_handle_frame,
                                         port);
        if (err) {
index bb09b0364619fd577acc45c067211a33d8f0b142..1ab168e0fdf72a251c4ce990dd5aae67cc02bac2 100644 (file)
@@ -1437,22 +1437,17 @@ EXPORT_SYMBOL(dev_close);
  */
 void dev_disable_lro(struct net_device *dev)
 {
-       /*
-        * If we're trying to disable lro on a vlan device
-        * use the underlying physical device instead
-        */
-       if (is_vlan_dev(dev))
-               dev = vlan_dev_real_dev(dev);
-
-       /* the same for macvlan devices */
-       if (netif_is_macvlan(dev))
-               dev = macvlan_dev_real_dev(dev);
+       struct net_device *lower_dev;
+       struct list_head *iter;
 
        dev->wanted_features &= ~NETIF_F_LRO;
        netdev_update_features(dev);
 
        if (unlikely(dev->features & NETIF_F_LRO))
                netdev_WARN(dev, "failed to disable LRO!\n");
+
+       netdev_for_each_lower_dev(dev, lower_dev, iter)
+               dev_disable_lro(lower_dev);
 }
 EXPORT_SYMBOL(dev_disable_lro);