diff options
-rw-r--r-- | include/linux/netdevice.h | 1 | ||||
-rw-r--r-- | net/bridge/br_if.c | 1 | ||||
-rw-r--r-- | net/core/dev.c | 24 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 21 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 6 |
5 files changed, 48 insertions, 5 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 45dce2b58d4c..1304ad2d7105 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -886,6 +886,7 @@ extern struct net_device *__dev_get_by_name(struct net *net, const char *name); | |||
886 | extern int dev_alloc_name(struct net_device *dev, const char *name); | 886 | extern int dev_alloc_name(struct net_device *dev, const char *name); |
887 | extern int dev_open(struct net_device *dev); | 887 | extern int dev_open(struct net_device *dev); |
888 | extern int dev_close(struct net_device *dev); | 888 | extern int dev_close(struct net_device *dev); |
889 | extern void dev_disable_lro(struct net_device *dev); | ||
889 | extern int dev_queue_xmit(struct sk_buff *skb); | 890 | extern int dev_queue_xmit(struct sk_buff *skb); |
890 | extern int register_netdevice(struct net_device *dev); | 891 | extern int register_netdevice(struct net_device *dev); |
891 | extern void unregister_netdevice(struct net_device *dev); | 892 | extern void unregister_netdevice(struct net_device *dev); |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 143c954681b8..832a561500d9 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
@@ -387,6 +387,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
387 | goto err2; | 387 | goto err2; |
388 | 388 | ||
389 | rcu_assign_pointer(dev->br_port, p); | 389 | rcu_assign_pointer(dev->br_port, p); |
390 | dev_disable_lro(dev); | ||
390 | dev_set_promiscuity(dev, 1); | 391 | dev_set_promiscuity(dev, 1); |
391 | 392 | ||
392 | list_add_rcu(&p->list, &br->port_list); | 393 | list_add_rcu(&p->list, &br->port_list); |
diff --git a/net/core/dev.c b/net/core/dev.c index a495f712d38c..f6944ecd5b2e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -90,6 +90,7 @@ | |||
90 | #include <linux/if_ether.h> | 90 | #include <linux/if_ether.h> |
91 | #include <linux/netdevice.h> | 91 | #include <linux/netdevice.h> |
92 | #include <linux/etherdevice.h> | 92 | #include <linux/etherdevice.h> |
93 | #include <linux/ethtool.h> | ||
93 | #include <linux/notifier.h> | 94 | #include <linux/notifier.h> |
94 | #include <linux/skbuff.h> | 95 | #include <linux/skbuff.h> |
95 | #include <net/net_namespace.h> | 96 | #include <net/net_namespace.h> |
@@ -1123,6 +1124,29 @@ int dev_close(struct net_device *dev) | |||
1123 | } | 1124 | } |
1124 | 1125 | ||
1125 | 1126 | ||
1127 | /** | ||
1128 | * dev_disable_lro - disable Large Receive Offload on a device | ||
1129 | * @dev: device | ||
1130 | * | ||
1131 | * Disable Large Receive Offload (LRO) on a net device. Must be | ||
1132 | * called under RTNL. This is needed if received packets may be | ||
1133 | * forwarded to another interface. | ||
1134 | */ | ||
1135 | void dev_disable_lro(struct net_device *dev) | ||
1136 | { | ||
1137 | if (dev->ethtool_ops && dev->ethtool_ops->get_flags && | ||
1138 | dev->ethtool_ops->set_flags) { | ||
1139 | u32 flags = dev->ethtool_ops->get_flags(dev); | ||
1140 | if (flags & ETH_FLAG_LRO) { | ||
1141 | flags &= ~ETH_FLAG_LRO; | ||
1142 | dev->ethtool_ops->set_flags(dev, flags); | ||
1143 | } | ||
1144 | } | ||
1145 | WARN_ON(dev->features & NETIF_F_LRO); | ||
1146 | } | ||
1147 | EXPORT_SYMBOL(dev_disable_lro); | ||
1148 | |||
1149 | |||
1126 | static int dev_boot_phase = 1; | 1150 | static int dev_boot_phase = 1; |
1127 | 1151 | ||
1128 | /* | 1152 | /* |
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f8c0b0aea93a..9de2514946ca 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -168,6 +168,8 @@ static struct in_device *inetdev_init(struct net_device *dev) | |||
168 | in_dev->dev = dev; | 168 | in_dev->dev = dev; |
169 | if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) | 169 | if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) |
170 | goto out_kfree; | 170 | goto out_kfree; |
171 | if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) | ||
172 | dev_disable_lro(dev); | ||
171 | /* Reference in_dev->dev */ | 173 | /* Reference in_dev->dev */ |
172 | dev_hold(dev); | 174 | dev_hold(dev); |
173 | /* Account for reference dev->ip_ptr (below) */ | 175 | /* Account for reference dev->ip_ptr (below) */ |
@@ -1241,6 +1243,8 @@ static void inet_forward_change(struct net *net) | |||
1241 | read_lock(&dev_base_lock); | 1243 | read_lock(&dev_base_lock); |
1242 | for_each_netdev(net, dev) { | 1244 | for_each_netdev(net, dev) { |
1243 | struct in_device *in_dev; | 1245 | struct in_device *in_dev; |
1246 | if (on) | ||
1247 | dev_disable_lro(dev); | ||
1244 | rcu_read_lock(); | 1248 | rcu_read_lock(); |
1245 | in_dev = __in_dev_get_rcu(dev); | 1249 | in_dev = __in_dev_get_rcu(dev); |
1246 | if (in_dev) | 1250 | if (in_dev) |
@@ -1248,8 +1252,6 @@ static void inet_forward_change(struct net *net) | |||
1248 | rcu_read_unlock(); | 1252 | rcu_read_unlock(); |
1249 | } | 1253 | } |
1250 | read_unlock(&dev_base_lock); | 1254 | read_unlock(&dev_base_lock); |
1251 | |||
1252 | rt_cache_flush(0); | ||
1253 | } | 1255 | } |
1254 | 1256 | ||
1255 | static int devinet_conf_proc(ctl_table *ctl, int write, | 1257 | static int devinet_conf_proc(ctl_table *ctl, int write, |
@@ -1335,10 +1337,19 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, | |||
1335 | if (write && *valp != val) { | 1337 | if (write && *valp != val) { |
1336 | struct net *net = ctl->extra2; | 1338 | struct net *net = ctl->extra2; |
1337 | 1339 | ||
1338 | if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) | 1340 | if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { |
1339 | inet_forward_change(net); | 1341 | rtnl_lock(); |
1340 | else if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) | 1342 | if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { |
1343 | inet_forward_change(net); | ||
1344 | } else if (*valp) { | ||
1345 | struct ipv4_devconf *cnf = ctl->extra1; | ||
1346 | struct in_device *idev = | ||
1347 | container_of(cnf, struct in_device, cnf); | ||
1348 | dev_disable_lro(idev->dev); | ||
1349 | } | ||
1350 | rtnl_unlock(); | ||
1341 | rt_cache_flush(0); | 1351 | rt_cache_flush(0); |
1352 | } | ||
1342 | } | 1353 | } |
1343 | 1354 | ||
1344 | return ret; | 1355 | return ret; |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9be6be3a7ff3..84127d854cfc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -348,6 +348,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) | |||
348 | kfree(ndev); | 348 | kfree(ndev); |
349 | return NULL; | 349 | return NULL; |
350 | } | 350 | } |
351 | if (ndev->cnf.forwarding) | ||
352 | dev_disable_lro(dev); | ||
351 | /* We refer to the device */ | 353 | /* We refer to the device */ |
352 | dev_hold(dev); | 354 | dev_hold(dev); |
353 | 355 | ||
@@ -442,6 +444,8 @@ static void dev_forward_change(struct inet6_dev *idev) | |||
442 | if (!idev) | 444 | if (!idev) |
443 | return; | 445 | return; |
444 | dev = idev->dev; | 446 | dev = idev->dev; |
447 | if (idev->cnf.forwarding) | ||
448 | dev_disable_lro(dev); | ||
445 | if (dev && (dev->flags & IFF_MULTICAST)) { | 449 | if (dev && (dev->flags & IFF_MULTICAST)) { |
446 | if (idev->cnf.forwarding) | 450 | if (idev->cnf.forwarding) |
447 | ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); | 451 | ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); |
@@ -487,12 +491,14 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) | |||
487 | if (p == &net->ipv6.devconf_dflt->forwarding) | 491 | if (p == &net->ipv6.devconf_dflt->forwarding) |
488 | return; | 492 | return; |
489 | 493 | ||
494 | rtnl_lock(); | ||
490 | if (p == &net->ipv6.devconf_all->forwarding) { | 495 | if (p == &net->ipv6.devconf_all->forwarding) { |
491 | __s32 newf = net->ipv6.devconf_all->forwarding; | 496 | __s32 newf = net->ipv6.devconf_all->forwarding; |
492 | net->ipv6.devconf_dflt->forwarding = newf; | 497 | net->ipv6.devconf_dflt->forwarding = newf; |
493 | addrconf_forward_change(net, newf); | 498 | addrconf_forward_change(net, newf); |
494 | } else if ((!*p) ^ (!old)) | 499 | } else if ((!*p) ^ (!old)) |
495 | dev_forward_change((struct inet6_dev *)table->extra1); | 500 | dev_forward_change((struct inet6_dev *)table->extra1); |
501 | rtnl_unlock(); | ||
496 | 502 | ||
497 | if (*p) | 503 | if (*p) |
498 | rt6_purge_dflt_routers(net); | 504 | rt6_purge_dflt_routers(net); |