aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--net/bridge/br_if.c1
-rw-r--r--net/core/dev.c24
-rw-r--r--net/ipv4/devinet.c21
-rw-r--r--net/ipv6/addrconf.c6
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);
886extern int dev_alloc_name(struct net_device *dev, const char *name); 886extern int dev_alloc_name(struct net_device *dev, const char *name);
887extern int dev_open(struct net_device *dev); 887extern int dev_open(struct net_device *dev);
888extern int dev_close(struct net_device *dev); 888extern int dev_close(struct net_device *dev);
889extern void dev_disable_lro(struct net_device *dev);
889extern int dev_queue_xmit(struct sk_buff *skb); 890extern int dev_queue_xmit(struct sk_buff *skb);
890extern int register_netdevice(struct net_device *dev); 891extern int register_netdevice(struct net_device *dev);
891extern void unregister_netdevice(struct net_device *dev); 892extern 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 */
1135void 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}
1147EXPORT_SYMBOL(dev_disable_lro);
1148
1149
1126static int dev_boot_phase = 1; 1150static 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
1255static int devinet_conf_proc(ctl_table *ctl, int write, 1257static 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);