diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2008-06-19 19:15:47 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-06-19 19:15:47 -0400 |
commit | 0187bdfb05674147774ca79a79942537f3ad54bd (patch) | |
tree | 43980261ce9e2b6ef76356dbbbc7efe3dbb60a02 /net/ipv4 | |
parent | 2e3216cd54b142ba605e87522e15f42e0c4e3996 (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>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/devinet.c | 21 |
1 files changed, 16 insertions, 5 deletions
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; |