Merge branch 'stacked_vlan_tso'

Toshiaki Makita says:

====================
Stacked vlan TSO

On the basis of Netdev 0.1 discussion[1], I made a patch set to enable
TSO for packets with multiple vlans.

Currently, packets with multiple vlans are always segmented by software,
which is caused by that netif_skb_features() drops most feature flags
for multiple tagged packets.

To allow NICs to segment them, we need to get rid of that check from core.
Fortunately, recently introduced ndo_features_check() can be used to
move the check to each driver, and this patch set is based on the idea.

For the initial patch set, I chose 3 drivers, bonding, team, and igb, as
candidates to enable TSO. I tested them and confirmed they works fine
with this change.

Here are samples of performance test results. As I expected, %sys gets
pretty lower than before.

* TEST1: vlan (.1Q) on vlan (.1ad) on igb (I350)

- before

$ netperf -t TCP_STREAM -H 192.168.10.1 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.02     933.72

Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all      0.13      0.00     11.28      0.01      0.00     88.58

- after

$ netperf -t TCP_STREAM -H 192.168.10.1 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.01     936.13

Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all      0.24      0.00      4.17      0.01      0.00     95.58

* TEST2: vlan (.1Q) on bridge (.1ad vlan filtering) on team on igb (I350)

- before

$ netperf -t TCP_STREAM -H 192.168.10.1 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.01     936.28

Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all      0.41      0.00     11.57      0.01      0.00     88.01

- after

$ netperf -t TCP_STREAM -H 192.168.10.1 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.02     935.72

Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all      0.14      0.00      7.66      0.01      0.00     92.19

In addition to above, I tested these configurations:
- vlan (.1Q) on vlan (1.ad) on bonding on igb (I350)
- vlan (.1Q) on vlan (1.Q) on igb (I350)
- vlan (.1Q) on vlan (1.Q) on team on igb (I350)
And didn't find any problem.

[1] https://netdev01.org/sessions/18
    https://netdev01.org/docs/netdev01_bof_8021ad_makita_150212.pdf
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2015-03-29 13:33:32 -07:00
commit afb0bc972b
10 changed files with 96 additions and 22 deletions

View File

@ -4038,6 +4038,7 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_fix_features = bond_fix_features, .ndo_fix_features = bond_fix_features,
.ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink,
.ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink,
.ndo_features_check = passthru_features_check,
}; };
static const struct device_type bond_type = { static const struct device_type bond_type = {

View File

@ -12557,6 +12557,7 @@ static netdev_features_t bnx2x_features_check(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
netdev_features_t features) netdev_features_t features)
{ {
features = vlan_features_check(skb, features);
return vxlan_features_check(skb, features); return vxlan_features_check(skb, features);
} }

View File

@ -2093,6 +2093,7 @@ static const struct net_device_ops igb_netdev_ops = {
#endif #endif
.ndo_fix_features = igb_fix_features, .ndo_fix_features = igb_fix_features,
.ndo_set_features = igb_set_features, .ndo_set_features = igb_set_features,
.ndo_features_check = passthru_features_check,
}; };
/** /**

View File

@ -2373,6 +2373,7 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
netdev_features_t features) netdev_features_t features)
{ {
features = vlan_features_check(skb, features);
return vxlan_features_check(skb, features); return vxlan_features_check(skb, features);
} }
#endif #endif

View File

@ -507,6 +507,7 @@ static netdev_features_t qlcnic_features_check(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
netdev_features_t features) netdev_features_t features)
{ {
features = vlan_features_check(skb, features);
return vxlan_features_check(skb, features); return vxlan_features_check(skb, features);
} }
#endif #endif

View File

@ -1979,6 +1979,7 @@ static const struct net_device_ops team_netdev_ops = {
.ndo_change_carrier = team_change_carrier, .ndo_change_carrier = team_change_carrier,
.ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink,
.ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink,
.ndo_features_check = passthru_features_check,
}; };
/*********************** /***********************

View File

@ -561,4 +561,71 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb,
skb->protocol = htons(ETH_P_802_2); skb->protocol = htons(ETH_P_802_2);
} }
/**
* skb_vlan_tagged - check if skb is vlan tagged.
* @skb: skbuff to query
*
* Returns true if the skb is tagged, regardless of whether it is hardware
* accelerated or not.
*/
static inline bool skb_vlan_tagged(const struct sk_buff *skb)
{
if (!skb_vlan_tag_present(skb) &&
likely(skb->protocol != htons(ETH_P_8021Q) &&
skb->protocol != htons(ETH_P_8021AD)))
return false;
return true;
}
/**
* skb_vlan_tagged_multi - check if skb is vlan tagged with multiple headers.
* @skb: skbuff to query
*
* Returns true if the skb is tagged with multiple vlan headers, regardless
* of whether it is hardware accelerated or not.
*/
static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb)
{
__be16 protocol = skb->protocol;
if (!skb_vlan_tag_present(skb)) {
struct vlan_ethhdr *veh;
if (likely(protocol != htons(ETH_P_8021Q) &&
protocol != htons(ETH_P_8021AD)))
return false;
veh = (struct vlan_ethhdr *)skb->data;
protocol = veh->h_vlan_encapsulated_proto;
}
if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD))
return false;
return true;
}
/**
* vlan_features_check - drop unsafe features for skb with multiple tags.
* @skb: skbuff to query
* @features: features to be checked
*
* Returns features without unsafe ones if the skb has multiple tags.
*/
static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
netdev_features_t features)
{
if (skb_vlan_tagged_multi(skb))
features = netdev_intersect_features(features,
NETIF_F_SG |
NETIF_F_HIGHDMA |
NETIF_F_FRAGLIST |
NETIF_F_GEN_CSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
return features;
}
#endif /* !(_LINUX_IF_VLAN_H_) */ #endif /* !(_LINUX_IF_VLAN_H_) */

View File

@ -3657,6 +3657,9 @@ void netdev_change_features(struct net_device *dev);
void netif_stacked_transfer_operstate(const struct net_device *rootdev, void netif_stacked_transfer_operstate(const struct net_device *rootdev,
struct net_device *dev); struct net_device *dev);
netdev_features_t passthru_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
netdev_features_t netif_skb_features(struct sk_buff *skb); netdev_features_t netif_skb_features(struct sk_buff *skb);
static inline bool net_gso_ok(netdev_features_t features, int gso_type) static inline bool net_gso_ok(netdev_features_t features, int gso_type)

View File

@ -554,6 +554,7 @@ static int vlan_dev_init(struct net_device *dev)
if (dev->features & NETIF_F_VLAN_FEATURES) if (dev->features & NETIF_F_VLAN_FEATURES)
netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n"); netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n");
dev->vlan_features = real_dev->vlan_features & ~NETIF_F_ALL_FCOE;
/* ipv6 shared card related stuff */ /* ipv6 shared card related stuff */
dev->dev_id = real_dev->dev_id; dev->dev_id = real_dev->dev_id;

View File

@ -2562,12 +2562,26 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
return features; return features;
} }
netdev_features_t passthru_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
{
return features;
}
EXPORT_SYMBOL(passthru_features_check);
static netdev_features_t dflt_features_check(const struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
{
return vlan_features_check(skb, features);
}
netdev_features_t netif_skb_features(struct sk_buff *skb) netdev_features_t netif_skb_features(struct sk_buff *skb)
{ {
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
netdev_features_t features = dev->features; netdev_features_t features = dev->features;
u16 gso_segs = skb_shinfo(skb)->gso_segs; u16 gso_segs = skb_shinfo(skb)->gso_segs;
__be16 protocol = skb->protocol;
if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs)
features &= ~NETIF_F_GSO_MASK; features &= ~NETIF_F_GSO_MASK;
@ -2579,34 +2593,17 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
if (skb->encapsulation) if (skb->encapsulation)
features &= dev->hw_enc_features; features &= dev->hw_enc_features;
if (!skb_vlan_tag_present(skb)) { if (skb_vlan_tagged(skb))
if (unlikely(protocol == htons(ETH_P_8021Q) ||
protocol == htons(ETH_P_8021AD))) {
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
protocol = veh->h_vlan_encapsulated_proto;
} else {
goto finalize;
}
}
features = netdev_intersect_features(features,
dev->vlan_features |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD))
features = netdev_intersect_features(features, features = netdev_intersect_features(features,
NETIF_F_SG | dev->vlan_features |
NETIF_F_HIGHDMA |
NETIF_F_FRAGLIST |
NETIF_F_GEN_CSUM |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX); NETIF_F_HW_VLAN_STAG_TX);
finalize:
if (dev->netdev_ops->ndo_features_check) if (dev->netdev_ops->ndo_features_check)
features &= dev->netdev_ops->ndo_features_check(skb, dev, features &= dev->netdev_ops->ndo_features_check(skb, dev,
features); features);
else
features &= dflt_features_check(skb, dev, features);
return harmonize_features(skb, features); return harmonize_features(skb, features);
} }