diff options
author | David S. Miller <davem@davemloft.net> | 2015-03-29 16:33:32 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 16:33:32 -0400 |
commit | afb0bc972b5269a2046a1194c475d929c62fc1a3 (patch) | |
tree | 1c51d0aad72f03283affc7cc91e8106f3e67d672 | |
parent | 608cd71a9c7c9db76e78a792c5a4101e12fea43f (diff) | |
parent | 1abbc98a8ac5c6cc016e672e5888b6416ae243f6 (diff) |
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>
-rw-r--r-- | drivers/net/bonding/bond_main.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_main.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 1 | ||||
-rw-r--r-- | drivers/net/team/team.c | 1 | ||||
-rw-r--r-- | include/linux/if_vlan.h | 67 | ||||
-rw-r--r-- | include/linux/netdevice.h | 3 | ||||
-rw-r--r-- | net/8021q/vlan_dev.c | 1 | ||||
-rw-r--r-- | net/core/dev.c | 41 |
10 files changed, 96 insertions, 22 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c026ce9cd7b6..7b4684ccdb3f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -4038,6 +4038,7 @@ static const struct net_device_ops bond_netdev_ops = { | |||
4038 | .ndo_fix_features = bond_fix_features, | 4038 | .ndo_fix_features = bond_fix_features, |
4039 | .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, | 4039 | .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, |
4040 | .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, | 4040 | .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, |
4041 | .ndo_features_check = passthru_features_check, | ||
4041 | }; | 4042 | }; |
4042 | 4043 | ||
4043 | static const struct device_type bond_type = { | 4044 | static const struct device_type bond_type = { |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 9677431c582a..039b0c1f480e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | |||
@@ -12557,6 +12557,7 @@ static netdev_features_t bnx2x_features_check(struct sk_buff *skb, | |||
12557 | struct net_device *dev, | 12557 | struct net_device *dev, |
12558 | netdev_features_t features) | 12558 | netdev_features_t features) |
12559 | { | 12559 | { |
12560 | features = vlan_features_check(skb, features); | ||
12560 | return vxlan_features_check(skb, features); | 12561 | return vxlan_features_check(skb, features); |
12561 | } | 12562 | } |
12562 | 12563 | ||
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 0e07545ccc97..8457d0306e3a 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c | |||
@@ -2093,6 +2093,7 @@ static const struct net_device_ops igb_netdev_ops = { | |||
2093 | #endif | 2093 | #endif |
2094 | .ndo_fix_features = igb_fix_features, | 2094 | .ndo_fix_features = igb_fix_features, |
2095 | .ndo_set_features = igb_set_features, | 2095 | .ndo_set_features = igb_set_features, |
2096 | .ndo_features_check = passthru_features_check, | ||
2096 | }; | 2097 | }; |
2097 | 2098 | ||
2098 | /** | 2099 | /** |
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index a8339e98ad24..ebc93a101c93 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c | |||
@@ -2373,6 +2373,7 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, | |||
2373 | struct net_device *dev, | 2373 | struct net_device *dev, |
2374 | netdev_features_t features) | 2374 | netdev_features_t features) |
2375 | { | 2375 | { |
2376 | features = vlan_features_check(skb, features); | ||
2376 | return vxlan_features_check(skb, features); | 2377 | return vxlan_features_check(skb, features); |
2377 | } | 2378 | } |
2378 | #endif | 2379 | #endif |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index a430a34a4434..367f3976df56 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | |||
@@ -507,6 +507,7 @@ static netdev_features_t qlcnic_features_check(struct sk_buff *skb, | |||
507 | struct net_device *dev, | 507 | struct net_device *dev, |
508 | netdev_features_t features) | 508 | netdev_features_t features) |
509 | { | 509 | { |
510 | features = vlan_features_check(skb, features); | ||
510 | return vxlan_features_check(skb, features); | 511 | return vxlan_features_check(skb, features); |
511 | } | 512 | } |
512 | #endif | 513 | #endif |
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index a23319fc78ca..6928448f6b7f 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -1979,6 +1979,7 @@ static const struct net_device_ops team_netdev_ops = { | |||
1979 | .ndo_change_carrier = team_change_carrier, | 1979 | .ndo_change_carrier = team_change_carrier, |
1980 | .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, | 1980 | .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, |
1981 | .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, | 1981 | .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, |
1982 | .ndo_features_check = passthru_features_check, | ||
1982 | }; | 1983 | }; |
1983 | 1984 | ||
1984 | /*********************** | 1985 | /*********************** |
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index b11b28a30b9e..920e4457ce6e 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -561,4 +561,71 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb, | |||
561 | skb->protocol = htons(ETH_P_802_2); | 561 | skb->protocol = htons(ETH_P_802_2); |
562 | } | 562 | } |
563 | 563 | ||
564 | /** | ||
565 | * skb_vlan_tagged - check if skb is vlan tagged. | ||
566 | * @skb: skbuff to query | ||
567 | * | ||
568 | * Returns true if the skb is tagged, regardless of whether it is hardware | ||
569 | * accelerated or not. | ||
570 | */ | ||
571 | static inline bool skb_vlan_tagged(const struct sk_buff *skb) | ||
572 | { | ||
573 | if (!skb_vlan_tag_present(skb) && | ||
574 | likely(skb->protocol != htons(ETH_P_8021Q) && | ||
575 | skb->protocol != htons(ETH_P_8021AD))) | ||
576 | return false; | ||
577 | |||
578 | return true; | ||
579 | } | ||
580 | |||
581 | /** | ||
582 | * skb_vlan_tagged_multi - check if skb is vlan tagged with multiple headers. | ||
583 | * @skb: skbuff to query | ||
584 | * | ||
585 | * Returns true if the skb is tagged with multiple vlan headers, regardless | ||
586 | * of whether it is hardware accelerated or not. | ||
587 | */ | ||
588 | static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb) | ||
589 | { | ||
590 | __be16 protocol = skb->protocol; | ||
591 | |||
592 | if (!skb_vlan_tag_present(skb)) { | ||
593 | struct vlan_ethhdr *veh; | ||
594 | |||
595 | if (likely(protocol != htons(ETH_P_8021Q) && | ||
596 | protocol != htons(ETH_P_8021AD))) | ||
597 | return false; | ||
598 | |||
599 | veh = (struct vlan_ethhdr *)skb->data; | ||
600 | protocol = veh->h_vlan_encapsulated_proto; | ||
601 | } | ||
602 | |||
603 | if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) | ||
604 | return false; | ||
605 | |||
606 | return true; | ||
607 | } | ||
608 | |||
609 | /** | ||
610 | * vlan_features_check - drop unsafe features for skb with multiple tags. | ||
611 | * @skb: skbuff to query | ||
612 | * @features: features to be checked | ||
613 | * | ||
614 | * Returns features without unsafe ones if the skb has multiple tags. | ||
615 | */ | ||
616 | static inline netdev_features_t vlan_features_check(const struct sk_buff *skb, | ||
617 | netdev_features_t features) | ||
618 | { | ||
619 | if (skb_vlan_tagged_multi(skb)) | ||
620 | features = netdev_intersect_features(features, | ||
621 | NETIF_F_SG | | ||
622 | NETIF_F_HIGHDMA | | ||
623 | NETIF_F_FRAGLIST | | ||
624 | NETIF_F_GEN_CSUM | | ||
625 | NETIF_F_HW_VLAN_CTAG_TX | | ||
626 | NETIF_F_HW_VLAN_STAG_TX); | ||
627 | |||
628 | return features; | ||
629 | } | ||
630 | |||
564 | #endif /* !(_LINUX_IF_VLAN_H_) */ | 631 | #endif /* !(_LINUX_IF_VLAN_H_) */ |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 08c4ab37189f..967bb4c8caf1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -3657,6 +3657,9 @@ void netdev_change_features(struct net_device *dev); | |||
3657 | void netif_stacked_transfer_operstate(const struct net_device *rootdev, | 3657 | void netif_stacked_transfer_operstate(const struct net_device *rootdev, |
3658 | struct net_device *dev); | 3658 | struct net_device *dev); |
3659 | 3659 | ||
3660 | netdev_features_t passthru_features_check(struct sk_buff *skb, | ||
3661 | struct net_device *dev, | ||
3662 | netdev_features_t features); | ||
3660 | netdev_features_t netif_skb_features(struct sk_buff *skb); | 3663 | netdev_features_t netif_skb_features(struct sk_buff *skb); |
3661 | 3664 | ||
3662 | static inline bool net_gso_ok(netdev_features_t features, int gso_type) | 3665 | static inline bool net_gso_ok(netdev_features_t features, int gso_type) |
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index f196552ec3c4..8b5ab9033b41 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c | |||
@@ -554,6 +554,7 @@ static int vlan_dev_init(struct net_device *dev) | |||
554 | if (dev->features & NETIF_F_VLAN_FEATURES) | 554 | if (dev->features & NETIF_F_VLAN_FEATURES) |
555 | netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n"); | 555 | netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n"); |
556 | 556 | ||
557 | dev->vlan_features = real_dev->vlan_features & ~NETIF_F_ALL_FCOE; | ||
557 | 558 | ||
558 | /* ipv6 shared card related stuff */ | 559 | /* ipv6 shared card related stuff */ |
559 | dev->dev_id = real_dev->dev_id; | 560 | dev->dev_id = real_dev->dev_id; |
diff --git a/net/core/dev.c b/net/core/dev.c index a0408d497dae..3a06003ecafd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -2562,12 +2562,26 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, | |||
2562 | return features; | 2562 | return features; |
2563 | } | 2563 | } |
2564 | 2564 | ||
2565 | netdev_features_t passthru_features_check(struct sk_buff *skb, | ||
2566 | struct net_device *dev, | ||
2567 | netdev_features_t features) | ||
2568 | { | ||
2569 | return features; | ||
2570 | } | ||
2571 | EXPORT_SYMBOL(passthru_features_check); | ||
2572 | |||
2573 | static netdev_features_t dflt_features_check(const struct sk_buff *skb, | ||
2574 | struct net_device *dev, | ||
2575 | netdev_features_t features) | ||
2576 | { | ||
2577 | return vlan_features_check(skb, features); | ||
2578 | } | ||
2579 | |||
2565 | netdev_features_t netif_skb_features(struct sk_buff *skb) | 2580 | netdev_features_t netif_skb_features(struct sk_buff *skb) |
2566 | { | 2581 | { |
2567 | struct net_device *dev = skb->dev; | 2582 | struct net_device *dev = skb->dev; |
2568 | netdev_features_t features = dev->features; | 2583 | netdev_features_t features = dev->features; |
2569 | u16 gso_segs = skb_shinfo(skb)->gso_segs; | 2584 | u16 gso_segs = skb_shinfo(skb)->gso_segs; |
2570 | __be16 protocol = skb->protocol; | ||
2571 | 2585 | ||
2572 | if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) | 2586 | if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) |
2573 | features &= ~NETIF_F_GSO_MASK; | 2587 | features &= ~NETIF_F_GSO_MASK; |
@@ -2579,34 +2593,17 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) | |||
2579 | if (skb->encapsulation) | 2593 | if (skb->encapsulation) |
2580 | features &= dev->hw_enc_features; | 2594 | features &= dev->hw_enc_features; |
2581 | 2595 | ||
2582 | if (!skb_vlan_tag_present(skb)) { | 2596 | if (skb_vlan_tagged(skb)) |
2583 | if (unlikely(protocol == htons(ETH_P_8021Q) || | ||
2584 | protocol == htons(ETH_P_8021AD))) { | ||
2585 | struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; | ||
2586 | protocol = veh->h_vlan_encapsulated_proto; | ||
2587 | } else { | ||
2588 | goto finalize; | ||
2589 | } | ||
2590 | } | ||
2591 | |||
2592 | features = netdev_intersect_features(features, | ||
2593 | dev->vlan_features | | ||
2594 | NETIF_F_HW_VLAN_CTAG_TX | | ||
2595 | NETIF_F_HW_VLAN_STAG_TX); | ||
2596 | |||
2597 | if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) | ||
2598 | features = netdev_intersect_features(features, | 2597 | features = netdev_intersect_features(features, |
2599 | NETIF_F_SG | | 2598 | dev->vlan_features | |
2600 | NETIF_F_HIGHDMA | | ||
2601 | NETIF_F_FRAGLIST | | ||
2602 | NETIF_F_GEN_CSUM | | ||
2603 | NETIF_F_HW_VLAN_CTAG_TX | | 2599 | NETIF_F_HW_VLAN_CTAG_TX | |
2604 | NETIF_F_HW_VLAN_STAG_TX); | 2600 | NETIF_F_HW_VLAN_STAG_TX); |
2605 | 2601 | ||
2606 | finalize: | ||
2607 | if (dev->netdev_ops->ndo_features_check) | 2602 | if (dev->netdev_ops->ndo_features_check) |
2608 | features &= dev->netdev_ops->ndo_features_check(skb, dev, | 2603 | features &= dev->netdev_ops->ndo_features_check(skb, dev, |
2609 | features); | 2604 | features); |
2605 | else | ||
2606 | features &= dflt_features_check(skb, dev, features); | ||
2610 | 2607 | ||
2611 | return harmonize_features(skb, features); | 2608 | return harmonize_features(skb, features); |
2612 | } | 2609 | } |