aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2008-06-19 19:15:47 -0400
committerDavid S. Miller <davem@davemloft.net>2008-06-19 19:15:47 -0400
commit0187bdfb05674147774ca79a79942537f3ad54bd (patch)
tree43980261ce9e2b6ef76356dbbbc7efe3dbb60a02
parent2e3216cd54b142ba605e87522e15f42e0c4e3996 (diff)
net: Disable LRO on devices that are forwarding
Large Receive Offload (LRO) is only appropriate for packets that are destined for the host, and should be disabled if received packets may be forwarded. It can also confuse the GSO on output. Add dev_disable_lro() function which uses the appropriate ethtool ops to disable LRO if enabled. Add calls to dev_disable_lro() in br_add_if() and functions that enable IPv4 and IPv6 forwarding. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-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);