Commit 7f353bf2 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[NET]: Share correct feature code between bridging and bonding

http://bugzilla.kernel.org/show_bug.cgi?id=8797 shows that the
bonding driver may produce bogus combinations of the checksum
flags and SG/TSO.

For example, if you bond devices with NETIF_F_HW_CSUM and
NETIF_F_IP_CSUM you'll end up with a bonding device that
has neither flag set.  If both have TSO then this produces
an illegal combination.

The bridge device on the other hand has the correct code to
deal with this.

In fact, the same code can be used for both.  So this patch
moves that logic into net/core/dev.c and uses it for both
bonding and bridging.

In the process I've made small adjustments such as only
setting GSO_ROBUST if at least one constituent device
supports it.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f7141761
...@@ -1202,43 +1202,35 @@ static int bond_sethwaddr(struct net_device *bond_dev, ...@@ -1202,43 +1202,35 @@ static int bond_sethwaddr(struct net_device *bond_dev,
return 0; return 0;
} }
#define BOND_INTERSECT_FEATURES \ #define BOND_VLAN_FEATURES \
(NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_TSO | NETIF_F_UFO) (NETIF_F_VLAN_CHALLENGED | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX | \
NETIF_F_HW_VLAN_FILTER)
/* /*
* Compute the common dev->feature set available to all slaves. Some * Compute the common dev->feature set available to all slaves. Some
* feature bits are managed elsewhere, so preserve feature bits set on * feature bits are managed elsewhere, so preserve those feature bits
* master device that are not part of the examined set. * on the master device.
*/ */
static int bond_compute_features(struct bonding *bond) static int bond_compute_features(struct bonding *bond)
{ {
unsigned long features = BOND_INTERSECT_FEATURES;
struct slave *slave; struct slave *slave;
struct net_device *bond_dev = bond->dev; struct net_device *bond_dev = bond->dev;
unsigned long features = bond_dev->features;
unsigned short max_hard_header_len = ETH_HLEN; unsigned short max_hard_header_len = ETH_HLEN;
int i; int i;
features &= ~(NETIF_F_ALL_CSUM | BOND_VLAN_FEATURES);
features |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM;
bond_for_each_slave(bond, slave, i) { bond_for_each_slave(bond, slave, i) {
features &= (slave->dev->features & BOND_INTERSECT_FEATURES); features = netdev_compute_features(features,
slave->dev->features);
if (slave->dev->hard_header_len > max_hard_header_len) if (slave->dev->hard_header_len > max_hard_header_len)
max_hard_header_len = slave->dev->hard_header_len; max_hard_header_len = slave->dev->hard_header_len;
} }
if ((features & NETIF_F_SG) && features |= (bond_dev->features & BOND_VLAN_FEATURES);
!(features & NETIF_F_ALL_CSUM))
features &= ~NETIF_F_SG;
/*
* features will include NETIF_F_TSO (NETIF_F_UFO) iff all
* slave devices support NETIF_F_TSO (NETIF_F_UFO), which
* implies that all slaves also support scatter-gather
* (NETIF_F_SG), which implies that features also includes
* NETIF_F_SG. So no need to check whether we have an
* illegal combination of NETIF_F_{TSO,UFO} and
* !NETIF_F_SG
*/
features |= (bond_dev->features & ~BOND_INTERSECT_FEATURES);
bond_dev->features = features; bond_dev->features = features;
bond_dev->hard_header_len = max_hard_header_len; bond_dev->hard_header_len = max_hard_header_len;
......
...@@ -1131,6 +1131,8 @@ extern void dev_seq_stop(struct seq_file *seq, void *v); ...@@ -1131,6 +1131,8 @@ extern void dev_seq_stop(struct seq_file *seq, void *v);
extern void linkwatch_run_queue(void); extern void linkwatch_run_queue(void);
extern int netdev_compute_features(unsigned long all, unsigned long one);
static inline int net_gso_ok(int features, int gso_type) static inline int net_gso_ok(int features, int gso_type)
{ {
int feature = gso_type << NETIF_F_GSO_SHIFT; int feature = gso_type << NETIF_F_GSO_SHIFT;
......
...@@ -179,5 +179,5 @@ void br_dev_setup(struct net_device *dev) ...@@ -179,5 +179,5 @@ void br_dev_setup(struct net_device *dev)
dev->priv_flags = IFF_EBRIDGE; dev->priv_flags = IFF_EBRIDGE;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST; NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX;
} }
...@@ -349,43 +349,15 @@ int br_min_mtu(const struct net_bridge *br) ...@@ -349,43 +349,15 @@ int br_min_mtu(const struct net_bridge *br)
void br_features_recompute(struct net_bridge *br) void br_features_recompute(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
unsigned long features, checksum; unsigned long features;
checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0; features = br->feature_mask;
features = br->feature_mask & ~NETIF_F_ALL_CSUM;
list_for_each_entry(p, &br->port_list, list) { list_for_each_entry(p, &br->port_list, list) {
unsigned long feature = p->dev->features; features = netdev_compute_features(features, p->dev->features);
/* if device needs checksumming, downgrade to hw checksumming */
if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM))
checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
/* if device can't do all checksum, downgrade to ipv4/ipv6 */
if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM))
checksum ^= NETIF_F_HW_CSUM
| NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
if (checksum & NETIF_F_IPV6_CSUM && !(feature & NETIF_F_IPV6_CSUM))
checksum &= ~NETIF_F_IPV6_CSUM;
if (!(feature & NETIF_F_IP_CSUM))
checksum = 0;
if (feature & NETIF_F_GSO)
feature |= NETIF_F_GSO_SOFTWARE;
feature |= NETIF_F_GSO;
features &= feature;
} }
if (!(checksum & NETIF_F_ALL_CSUM)) br->dev->features = features;
features &= ~NETIF_F_SG;
if (!(features & NETIF_F_SG))
features &= ~NETIF_F_GSO_MASK;
br->dev->features = features | checksum | NETIF_F_LLTX |
NETIF_F_GSO_ROBUST;
} }
/* called with RTNL */ /* called with RTNL */
......
...@@ -3993,6 +3993,45 @@ static int __init netdev_dma_register(void) ...@@ -3993,6 +3993,45 @@ static int __init netdev_dma_register(void)
static int __init netdev_dma_register(void) { return -ENODEV; } static int __init netdev_dma_register(void) { return -ENODEV; }
#endif /* CONFIG_NET_DMA */ #endif /* CONFIG_NET_DMA */
/**
* netdev_compute_feature - compute conjunction of two feature sets
* @all: first feature set
* @one: second feature set
*
* Computes a new feature set after adding a device with feature set
* @one to the master device with current feature set @all. Returns
* the new feature set.
*/
int netdev_compute_features(unsigned long all, unsigned long one)
{
/* if device needs checksumming, downgrade to hw checksumming */
if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM))
all ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
/* if device can't do all checksum, downgrade to ipv4/ipv6 */
if (all & NETIF_F_HW_CSUM && !(one & NETIF_F_HW_CSUM))
all ^= NETIF_F_HW_CSUM
| NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
if (one & NETIF_F_GSO)
one |= NETIF_F_GSO_SOFTWARE;
one |= NETIF_F_GSO;
/* If even one device supports robust GSO, enable it for all. */
if (one & NETIF_F_GSO_ROBUST)
all |= NETIF_F_GSO_ROBUST;
all &= one | NETIF_F_LLTX;
if (!(all & NETIF_F_ALL_CSUM))
all &= ~NETIF_F_SG;
if (!(all & NETIF_F_SG))
all &= ~NETIF_F_GSO_MASK;
return all;
}
EXPORT_SYMBOL(netdev_compute_features);
/* /*
* Initialize the DEV module. At boot time this walks the device list and * Initialize the DEV module. At boot time this walks the device list and
* unhooks any devices that fail to initialise (normally hardware not * unhooks any devices that fail to initialise (normally hardware not
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment