<div dir="ltr">I have a question regarding this, and the current maximum number of htb leaf classes and/or qdiscs per interface.<br>I recently integrated Jesper's <a href="https://github.com/xdp-project/xdp-cpumap-tc">xdp-cpumap-tc</a> code into <a href="https://github.com/rchac/LibreQoS" target="_blank">LibreQoS</a>, which increased throughput to 10Gbps on tests.<br>I suspect somewhere between 10Gbps and 40Gbps throughput is now possible if you throw enough cores at it. Asking our local university to help us test this.<br>Xdp-cpumap-tc uses xdp's cpumap-redirect feature to filter packets into the appropriate CPU / queue using eBPF hash maps, rather than linux tc filters / u32.<br><br>Question) Since LibreQoS would not depend on tc filters, would the current 32-bit or 64-bit feature limit impose a practical client limit on LibreQoS?<br><div>The average user's throughput is around 3.5Mbps at peak hours, so I'm thinking ~5800 qdiscs and ~5800 htb leaf classes would be required for each interface at 20Gbps throughput for example.</div><div>There may be some more immediate limitations I'm not understanding. Just curious about the practical limitations there.</div><div><br></div><div>Thanks!<br></div>Robert<br><br>On Sat, Jul 10, 2021 at 9:33 AM Dave Taht <<a href="mailto:dave.taht@gmail.com" target="_blank">dave.taht@gmail.com</a>> wrote:<br>><br>> One thing somewhat related to this was finally expanding the space<br>> available for the tc and iptables functionality for<br>> things like hashing and actions etc from 16 bits to 32. That is<br>> something of a fork lift upgrade, but... 64k queues is not<br>> enough in some cases, nor is 64k possible users in libreqos. thoughts<br>><br>> ---------- Forwarded message ---------<br>> From: Jian Shen <<a href="mailto:shenjian15@huawei.com" target="_blank">shenjian15@huawei.com</a>><br>> Date: Sat, Jul 10, 2021 at 2:47 AM<br>> Subject: [RFC net-next] net: extend netdev features<br>> To: <<a href="mailto:davem@davemloft.net" target="_blank">davem@davemloft.net</a>>, <<a href="mailto:kuba@kernel.org" target="_blank">kuba@kernel.org</a>><br>> Cc: <<a href="mailto:netdev@vger.kernel.org" target="_blank">netdev@vger.kernel.org</a>>, <<a href="mailto:linuxarm@openeuler.org" target="_blank">linuxarm@openeuler.org</a>><br>><br>><br>> For the prototype of netdev_features_t is u64, and the number<br>> of netdevice feature bits is 64 now. So there is no space to<br>> introduce new feature bit.<br>><br>> I did a small change for this. Keep the prototype of<br>> netdev_feature_t, and extend the feature members in struct<br>> net_device to an array of netdev_features_t. So more features<br>> bits can be used.<br>><br>> As this change, some functions which use netdev_features_t as<br>> parameter or returen value will be affected.<br>> I did below changes:<br>> a. parameter: "netdev_features_t" to "netdev_features_t *"<br>> b. return value: "netdev_feature_t" to "void", and add<br>> "netdev_feature_t *" as output parameter.<br>><br>> I kept some functions no change, which are surely useing the<br>> first 64 bit of net device features now, such as function<br>> nedev_add_tso_features(). In order to minimize to changes.<br>><br>> For the features are array now, so it's unable to do logical<br>> operation directly. I introduce a inline function set for<br>> them, including "netdev_features_and/andnot/or/xor/equal/empty".<br>><br>> For NETDEV_FEATURE_COUNT may be more than 64, so the shift<br>> operation for NETDEV_FEATURE_COUNT is illegal. I changed some<br>> macroes and functions, which does shift opertion with it.<br>><br>> I haven't finished all the changes, for it affected all the<br>> drivers which use the feature, need more time and test. I<br>> sent this RFC patch, want to know whether this change is<br>> acceptable, and how to improve it.<br>><br>> Any comments will be helpful.<br>><br>> Signed-off-by: Jian Shen <<a href="mailto:shenjian15@huawei.com" target="_blank">shenjian15@huawei.com</a>><br>> ---<br>>  drivers/net/ethernet/hisilicon/hns/hns_enet.c   |  34 +--<br>>  drivers/net/ethernet/hisilicon/hns3/hns3_enet.c |  97 ++++-----<br>>  drivers/net/ethernet/huawei/hinic/hinic_main.c  |  71 +++---<br>>  drivers/net/ethernet/huawei/hinic/hinic_rx.c    |   4 +-<br>>  include/linux/if_vlan.h                         |   2 +-<br>>  include/linux/netdev_features.h                 | 105 ++++++++-<br>>  include/linux/netdevice.h                       |  31 +--<br>>  net/8021q/vlan.c                                |   4 +-<br>>  net/8021q/vlan.h                                |   2 +-<br>>  net/8021q/vlan_dev.c                            |  49 +++--<br>>  net/core/dev.c                                  | 276 ++++++++++++------------<br>>  net/core/netpoll.c                              |   6 +-<br>>  net/ethtool/features.c                          |  56 +++--<br>>  net/ethtool/ioctl.c                             |  93 +++++---<br>>  14 files changed, 493 insertions(+), 337 deletions(-)<br>><br>> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c<br>> b/drivers/net/ethernet/hisilicon/hns/hns_enet.c<br>> index ad534f9..4f245cf 100644<br>> --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c<br>> +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c<br>> @@ -479,7 +479,7 @@ static void hns_nic_rx_checksum(struct<br>> hns_nic_ring_data *ring_data,<br>>         u32 l4id;<br>><br>>         /* check if RX checksum offload is enabled */<br>> -       if (unlikely(!(netdev->features & NETIF_F_RXCSUM)))<br>> +       if (unlikely(!(netdev->features[0] & NETIF_F_RXCSUM)))<br>>                 return;<br>><br>>         /* In hardware, we only support checksum for the following protocols:<br>> @@ -1768,17 +1768,17 @@ static int hns_nic_change_mtu(struct<br>> net_device *ndev, int new_mtu)<br>>  }<br>><br>>  static int hns_nic_set_features(struct net_device *netdev,<br>> -                               netdev_features_t features)<br>> +                               netdev_features_t *features)<br>>  {<br>>         struct hns_nic_priv *priv = netdev_priv(netdev);<br>><br>>         switch (priv->enet_ver) {<br>>         case AE_VERSION_1:<br>> -               if (features & (NETIF_F_TSO | NETIF_F_TSO6))<br>> +               if (features[0] & (NETIF_F_TSO | NETIF_F_TSO6))<br>>                         netdev_info(netdev, "enet v1 do not support tso!\n");<br>>                 break;<br>>         default:<br>> -               if (features & (NETIF_F_TSO | NETIF_F_TSO6)) {<br>> +               if (features[0] & (NETIF_F_TSO | NETIF_F_TSO6)) {<br>>                         priv->ops.fill_desc = fill_tso_desc;<br>>                         priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;<br>>                         /* The chip only support 7*4096 */<br>> @@ -1789,24 +1789,23 @@ static int hns_nic_set_features(struct<br>> net_device *netdev,<br>>                 }<br>>                 break;<br>>         }<br>> -       netdev->features = features;<br>> +       netdev->features[0] = features[0];<br>>         return 0;<br>>  }<br>><br>> -static netdev_features_t hns_nic_fix_features(<br>> -               struct net_device *netdev, netdev_features_t features)<br>> +static void hns_nic_fix_features(struct net_device *netdev,<br>> +                                netdev_features_t *features)<br>>  {<br>>         struct hns_nic_priv *priv = netdev_priv(netdev);<br>><br>>         switch (priv->enet_ver) {<br>>         case AE_VERSION_1:<br>> -               features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |<br>> +               features[0] &= ~(NETIF_F_TSO | NETIF_F_TSO6 |<br>>                                 NETIF_F_HW_VLAN_CTAG_FILTER);<br>>                 break;<br>>         default:<br>>                 break;<br>>         }<br>> -       return features;<br>>  }<br>><br>>  static int hns_nic_uc_sync(struct net_device *netdev, const unsigned<br>> char *addr)<br>> @@ -2163,8 +2162,8 @@ static void hns_nic_set_priv_ops(struct<br>> net_device *netdev)<br>>                 priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;<br>>         } else {<br>>                 priv->ops.get_rxd_bnum = get_v2rx_desc_bnum;<br>> -               if ((netdev->features & NETIF_F_TSO) ||<br>> -                   (netdev->features & NETIF_F_TSO6)) {<br>> +               if ((netdev->features[0] & NETIF_F_TSO) ||<br>> +                   (netdev->features[0] & NETIF_F_TSO6)) {<br>>                         priv->ops.fill_desc = fill_tso_desc;<br>>                         priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;<br>>                         /* This chip only support 7*4096 */<br>> @@ -2325,22 +2324,23 @@ static int hns_nic_dev_probe(struct<br>> platform_device *pdev)<br>>         ndev->netdev_ops = &hns_nic_netdev_ops;<br>>         hns_ethtool_set_ops(ndev);<br>><br>> -       ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |<br>> +       ndev->features[0] |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |<br>>                 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>>                 NETIF_F_GRO;<br>> -       ndev->vlan_features |=<br>> +       ndev->vlan_features[0] |=<br>>                 NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;<br>> -       ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;<br>> +       ndev->vlan_features[0] |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;<br>><br>>         /* MTU range: 68 - 9578 (v1) or 9706 (v2) */<br>>         ndev->min_mtu = MAC_MIN_MTU;<br>>         switch (priv->enet_ver) {<br>>         case AE_VERSION_2:<br>> -               ndev->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_NTUPLE;<br>> -               ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |<br>> +               ndev->features[0] |=<br>> +                               NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_NTUPLE;<br>> +               ndev->hw_features[0] |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |<br>>                         NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>>                         NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6;<br>> -               ndev->vlan_features |= NETIF_F_TSO | NETIF_F_TSO6;<br>> +               ndev->vlan_features[0] |= NETIF_F_TSO | NETIF_F_TSO6;<br>>                 ndev->max_mtu = MAC_MAX_MTU_V2 -<br>>                                 (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);<br>>                 break;<br>> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c<br>> b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c<br>> index cdb5f14..ba56907 100644<br>> --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c<br>> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c<br>> @@ -1481,7 +1481,7 @@ static int hns3_handle_vtags(struct<br>> hns3_enet_ring *tx_ring,<br>>                 return -EINVAL;<br>><br>>         if (skb->protocol == htons(ETH_P_8021Q) &&<br>> -           !(handle->kinfo.netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {<br>> +           !(handle->kinfo.netdev->features[0] & NETIF_F_HW_VLAN_CTAG_TX)) {<br>>                 /* When HW VLAN acceleration is turned off, and the stack<br>>                  * sets the protocol to 802.1q, the driver just need to<br>>                  * set the protocol to the encapsulated ethertype.<br>> @@ -2300,56 +2300,57 @@ static int hns3_nic_do_ioctl(struct net_device *netdev,<br>>  }<br>><br>>  static int hns3_nic_set_features(struct net_device *netdev,<br>> -                                netdev_features_t features)<br>> +                                netdev_features_t *features)<br>>  {<br>> -       netdev_features_t changed = netdev->features ^ features;<br>> +       netdev_features_t changed[NETDEV_FEATURE_DWORDS];<br>>         struct hns3_nic_priv *priv = netdev_priv(netdev);<br>>         struct hnae3_handle *h = priv->ae_handle;<br>>         bool enable;<br>>         int ret;<br>><br>> -       if (changed & (NETIF_F_GRO_HW) && h->ae_algo->ops->set_gro_en) {<br>> -               enable = !!(features & NETIF_F_GRO_HW);<br>> +       netdev_features_xor(changed, netdev->features, features);<br>> +       if (changed[0] & (NETIF_F_GRO_HW) && h->ae_algo->ops->set_gro_en) {<br>> +               enable = !!(features[0] & NETIF_F_GRO_HW);<br>>                 ret = h->ae_algo->ops->set_gro_en(h, enable);<br>>                 if (ret)<br>>                         return ret;<br>>         }<br>><br>> -       if ((changed & NETIF_F_HW_VLAN_CTAG_RX) &&<br>> +       if ((changed[0] & NETIF_F_HW_VLAN_CTAG_RX) &&<br>>             h->ae_algo->ops->enable_hw_strip_rxvtag) {<br>> -               enable = !!(features & NETIF_F_HW_VLAN_CTAG_RX);<br>> +               enable = !!(features[0] & NETIF_F_HW_VLAN_CTAG_RX);<br>>                 ret = h->ae_algo->ops->enable_hw_strip_rxvtag(h, enable);<br>>                 if (ret)<br>>                         return ret;<br>>         }<br>><br>> -       if ((changed & NETIF_F_NTUPLE) && h->ae_algo->ops->enable_fd) {<br>> -               enable = !!(features & NETIF_F_NTUPLE);<br>> +       if ((changed[0] & NETIF_F_NTUPLE) && h->ae_algo->ops->enable_fd) {<br>> +               enable = !!(features[0] & NETIF_F_NTUPLE);<br>>                 h->ae_algo->ops->enable_fd(h, enable);<br>>         }<br>><br>> -       if ((netdev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&<br>> +       if ((netdev->features[0] & NETIF_F_HW_TC) ><br>> +            (features[0] & NETIF_F_HW_TC) &&<br>>             h->ae_algo->ops->cls_flower_active(h)) {<br>>                 netdev_err(netdev,<br>>                            "there are offloaded TC filters active,<br>> cannot disable HW TC offload");<br>>                 return -EINVAL;<br>>         }<br>><br>> -       if ((changed & NETIF_F_HW_VLAN_CTAG_FILTER) &&<br>> +       if ((changed[0] & NETIF_F_HW_VLAN_CTAG_FILTER) &&<br>>             h->ae_algo->ops->enable_vlan_filter) {<br>> -               enable = !!(features & NETIF_F_HW_VLAN_CTAG_FILTER);<br>> +               enable = !!(features[0] & NETIF_F_HW_VLAN_CTAG_FILTER);<br>>                 ret = h->ae_algo->ops->enable_vlan_filter(h, enable);<br>>                 if (ret)<br>>                         return ret;<br>>         }<br>><br>> -       netdev->features = features;<br>> +       netdev_features_copy(netdev->features, features);<br>>         return 0;<br>>  }<br>><br>> -static netdev_features_t hns3_features_check(struct sk_buff *skb,<br>> -                                            struct net_device *dev,<br>> -                                            netdev_features_t features)<br>> +static void hns3_features_check(struct sk_buff *skb, struct net_device *dev,<br>> +                               netdev_features_t *features)<br>>  {<br>>  #define HNS3_MAX_HDR_LEN       480U<br>>  #define HNS3_MAX_L4_HDR_LEN    60U<br>> @@ -2373,9 +2374,7 @@ static netdev_features_t<br>> hns3_features_check(struct sk_buff *skb,<br>>          * len of 480 bytes.<br>>          */<br>>         if (len > HNS3_MAX_HDR_LEN)<br>> -               features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);<br>> -<br>> -       return features;<br>> +               features[0] &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);<br>>  }<br>><br>>  static void hns3_nic_get_stats64(struct net_device *netdev,<br>> @@ -3127,27 +3126,28 @@ static void hns3_set_default_feature(struct<br>> net_device *netdev)<br>><br>>         netdev->priv_flags |= IFF_UNICAST_FLT;<br>><br>> -       netdev->hw_enc_features |= NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>> +       netdev->hw_enc_features[0] |=<br>> +               NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>>                 NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |<br>>                 NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |<br>>                 NETIF_F_SCTP_CRC | NETIF_F_TSO_MANGLEID | NETIF_F_FRAGLIST;<br>><br>>         netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM;<br>><br>> -       netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |<br>> +       netdev->features[0] |= NETIF_F_HW_VLAN_CTAG_FILTER |<br>>                 NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |<br>>                 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>>                 NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |<br>>                 NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |<br>>                 NETIF_F_SCTP_CRC | NETIF_F_FRAGLIST;<br>><br>> -       netdev->vlan_features |= NETIF_F_RXCSUM |<br>> +       netdev->vlan_features[0] |= NETIF_F_RXCSUM |<br>>                 NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO |<br>>                 NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |<br>>                 NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |<br>>                 NETIF_F_SCTP_CRC | NETIF_F_FRAGLIST;<br>><br>> -       netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |<br>> +       netdev->hw_features[0] |= NETIF_F_HW_VLAN_CTAG_TX |<br>>                 NETIF_F_HW_VLAN_CTAG_RX |<br>>                 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |<br>>                 NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |<br>> @@ -3155,48 +3155,49 @@ static void hns3_set_default_feature(struct<br>> net_device *netdev)<br>>                 NETIF_F_SCTP_CRC | NETIF_F_FRAGLIST;<br>><br>>         if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) {<br>> -               netdev->hw_features |= NETIF_F_GRO_HW;<br>> -               netdev->features |= NETIF_F_GRO_HW;<br>> +               netdev->hw_features[0] |= NETIF_F_GRO_HW;<br>> +               netdev->features[0] |= NETIF_F_GRO_HW;<br>><br>>                 if (!(h->flags & HNAE3_SUPPORT_VF)) {<br>> -                       netdev->hw_features |= NETIF_F_NTUPLE;<br>> -                       netdev->features |= NETIF_F_NTUPLE;<br>> +                       netdev->hw_features[0] |= NETIF_F_NTUPLE;<br>> +                       netdev->features[0] |= NETIF_F_NTUPLE;<br>>                 }<br>>         }<br>><br>>         if (test_bit(HNAE3_DEV_SUPPORT_UDP_GSO_B, ae_dev->caps)) {<br>> -               netdev->hw_features |= NETIF_F_GSO_UDP_L4;<br>> -               netdev->features |= NETIF_F_GSO_UDP_L4;<br>> -               netdev->vlan_features |= NETIF_F_GSO_UDP_L4;<br>> -               netdev->hw_enc_features |= NETIF_F_GSO_UDP_L4;<br>> +               netdev->hw_features[0] |= NETIF_F_GSO_UDP_L4;<br>> +               netdev->features[0] |= NETIF_F_GSO_UDP_L4;<br>> +               netdev->vlan_features[0] |= NETIF_F_GSO_UDP_L4;<br>> +               netdev->hw_enc_features[0] |= NETIF_F_GSO_UDP_L4;<br>>         }<br>><br>>         if (test_bit(HNAE3_DEV_SUPPORT_HW_TX_CSUM_B, ae_dev->caps)) {<br>> -               netdev->hw_features |= NETIF_F_HW_CSUM;<br>> -               netdev->features |= NETIF_F_HW_CSUM;<br>> -               netdev->vlan_features |= NETIF_F_HW_CSUM;<br>> -               netdev->hw_enc_features |= NETIF_F_HW_CSUM;<br>> +               netdev->hw_features[0] |= NETIF_F_HW_CSUM;<br>> +               netdev->features[0] |= NETIF_F_HW_CSUM;<br>> +               netdev->vlan_features[0] |= NETIF_F_HW_CSUM;<br>> +               netdev->hw_enc_features[0] |= NETIF_F_HW_CSUM;<br>>         } else {<br>> -               netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> -               netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> -               netdev->vlan_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> -               netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> +               netdev->hw_features[0] |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> +               netdev->features[0] |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> +               netdev->vlan_features[0] |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>> +               netdev->hw_enc_features[0] |=<br>> +                                       NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;<br>>         }<br>><br>>         if (test_bit(HNAE3_DEV_SUPPORT_UDP_TUNNEL_CSUM_B, ae_dev->caps)) {<br>> -               netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> -               netdev->features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> -               netdev->vlan_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> -               netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> +               netdev->hw_features[0] |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> +               netdev->features[0] |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> +               netdev->vlan_features[0] |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> +               netdev->hw_enc_features[0] |= NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>>         }<br>><br>>         if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) {<br>> -               netdev->hw_features |= NETIF_F_HW_TC;<br>> -               netdev->features |= NETIF_F_HW_TC;<br>> +               netdev->hw_features[0] |= NETIF_F_HW_TC;<br>> +               netdev->features[0] |= NETIF_F_HW_TC;<br>>         }<br>><br>>         if (test_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, ae_dev->caps))<br>> -               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;<br>> +               netdev->hw_features[0] |= NETIF_F_HW_VLAN_CTAG_FILTER;<br>>  }<br>><br>>  static int hns3_alloc_buffer(struct hns3_enet_ring *ring,<br>> @@ -3727,7 +3728,7 @@ static void hns3_rx_checksum(struct<br>> hns3_enet_ring *ring, struct sk_buff *skb,<br>><br>>         skb_checksum_none_assert(skb);<br>><br>> -       if (!(netdev->features & NETIF_F_RXCSUM))<br>> +       if (!(netdev->features[0] & NETIF_F_RXCSUM))<br>>                 return;<br>><br>>         if (test_bit(HNS3_NIC_STATE_RXD_ADV_LAYOUT_ENABLE, &priv->state))<br>> @@ -4024,7 +4025,7 @@ static int hns3_handle_bdinfo(struct<br>> hns3_enet_ring *ring, struct sk_buff *skb)<br>>          * ot_vlan_tag in two layer tag case, and stored at vlan_tag<br>>          * in one layer tag case.<br>>          */<br>> -       if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {<br>> +       if (netdev->features[0] & NETIF_F_HW_VLAN_CTAG_RX) {<br>>                 u16 vlan_tag;<br>><br>>                 if (hns3_parse_vlan_tag(ring, desc, l234info, &vlan_tag))<br>> diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c<br>> b/drivers/net/ethernet/huawei/hinic/hinic_main.c<br>> index 405ee4d..b193ee4 100644<br>> --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c<br>> +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c<br>> @@ -79,8 +79,8 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for<br>> NAPI budget (default=64)");<br>>  static int change_mac_addr(struct net_device *netdev, const u8 *addr);<br>><br>>  static int set_features(struct hinic_dev *nic_dev,<br>> -                       netdev_features_t pre_features,<br>> -                       netdev_features_t features, bool force_change);<br>> +                       netdev_features_t *pre_features,<br>> +                       netdev_features_t *features, bool force_change);<br>><br>>  static void update_rx_stats(struct hinic_dev *nic_dev, struct hinic_rxq *rxq)<br>>  {<br>> @@ -880,7 +880,7 @@ static void hinic_get_stats64(struct net_device *netdev,<br>>  }<br>><br>>  static int hinic_set_features(struct net_device *netdev,<br>> -                             netdev_features_t features)<br>> +                             netdev_features_t *features)<br>>  {<br>>         struct hinic_dev *nic_dev = netdev_priv(netdev);<br>><br>> @@ -888,18 +888,16 @@ static int hinic_set_features(struct net_device *netdev,<br>>                             features, false);<br>>  }<br>><br>> -static netdev_features_t hinic_fix_features(struct net_device *netdev,<br>> -                                           netdev_features_t features)<br>> +static void hinic_fix_features(struct net_device *netdev,<br>> +                              netdev_features_t features)<br>>  {<br>>         struct hinic_dev *nic_dev = netdev_priv(netdev);<br>><br>>         /* If Rx checksum is disabled, then LRO should also be disabled */<br>> -       if (!(features & NETIF_F_RXCSUM)) {<br>> +       if (!(features[0] & NETIF_F_RXCSUM)) {<br>>                 netif_info(nic_dev, drv, netdev, "disabling LRO as<br>> RXCSUM is off\n");<br>> -               features &= ~NETIF_F_LRO;<br>> +               features[0] &= ~NETIF_F_LRO;<br>>         }<br>> -<br>> -       return features;<br>>  }<br>><br>>  static const struct net_device_ops hinic_netdev_ops = {<br>> @@ -943,19 +941,22 @@ static const struct net_device_ops hinicvf_netdev_ops = {<br>><br>>  static void netdev_features_init(struct net_device *netdev)<br>>  {<br>> -       netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |<br>> -                             NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |<br>> -                             NETIF_F_RXCSUM | NETIF_F_LRO |<br>> -                             NETIF_F_HW_VLAN_CTAG_TX |<br>> NETIF_F_HW_VLAN_CTAG_RX |<br>> -                             NETIF_F_GSO_UDP_TUNNEL |<br>> NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> -<br>> -       netdev->vlan_features = netdev->hw_features;<br>> -<br>> -       netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;<br>> -<br>> -       netdev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM<br>> | NETIF_F_SCTP_CRC |<br>> -                                 NETIF_F_SG | NETIF_F_TSO |<br>> NETIF_F_TSO6 | NETIF_F_TSO_ECN |<br>> -                                 NETIF_F_GSO_UDP_TUNNEL_CSUM |<br>> NETIF_F_GSO_UDP_TUNNEL;<br>> +       netdev->hw_features[0] =<br>> +                       NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |<br>> +                       NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |<br>> +                       NETIF_F_RXCSUM | NETIF_F_LRO |<br>> +                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |<br>> +                       NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;<br>> +<br>> +       netdev_features_copy(netdev->vlan_features, netdev->hw_features);<br>> +<br>> +       netdev->features[0] =<br>> +                       netdev->hw_features[0] | NETIF_F_HW_VLAN_CTAG_FILTER;<br>> +<br>> +       netdev->hw_enc_features[0] =<br>> +               NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SCTP_CRC |<br>> +               NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN |<br>> +               NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_UDP_TUNNEL;<br>>  }<br>><br>>  static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev)<br>> @@ -1072,21 +1073,22 @@ static void link_err_event(void *handle,<br>>  }<br>><br>>  static int set_features(struct hinic_dev *nic_dev,<br>> -                       netdev_features_t pre_features,<br>> -                       netdev_features_t features, bool force_change)<br>> +                       netdev_features_t *pre_features,<br>> +                       netdev_features_t *features, bool force_change)<br>>  {<br>> -       netdev_features_t changed = force_change ? ~0 : pre_features ^ features;<br>> +       netdev_features_t failed_features[NETDEV_FEATURE_DWORDS] = {0};<br>>         u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;<br>> -       netdev_features_t failed_features = 0;<br>> +       netdev_features_t changed;<br>>         int ret = 0;<br>>         int err = 0;<br>><br>> +       changed = force_change ? ~0 : pre_features[0] ^ features[0];<br>>         if (changed & NETIF_F_TSO) {<br>> -               ret = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?<br>> +               ret = hinic_port_set_tso(nic_dev, (features[0] & NETIF_F_TSO) ?<br>>                                          HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);<br>>                 if (ret) {<br>>                         err = ret;<br>> -                       failed_features |= NETIF_F_TSO;<br>> +                       failed_features[0] |= NETIF_F_TSO;<br>>                 }<br>>         }<br>><br>> @@ -1094,33 +1096,34 @@ static int set_features(struct hinic_dev *nic_dev,<br>>                 ret = hinic_set_rx_csum_offload(nic_dev, csum_en);<br>>                 if (ret) {<br>>                         err = ret;<br>> -                       failed_features |= NETIF_F_RXCSUM;<br>> +                       failed_features[0] |= NETIF_F_RXCSUM;<br>>                 }<br>>         }<br>><br>>         if (changed & NETIF_F_LRO) {<br>>                 ret = hinic_set_rx_lro_state(nic_dev,<br>> -                                            !!(features & NETIF_F_LRO),<br>> +                                            !!(features[0] & NETIF_F_LRO),<br>>                                              HINIC_LRO_RX_TIMER_DEFAULT,<br>>                                              HINIC_LRO_MAX_WQE_NUM_DEFAULT);<br>>                 if (ret) {<br>>                         err = ret;<br>> -                       failed_features |= NETIF_F_LRO;<br>> +                       failed_features[0] |= NETIF_F_LRO;<br>>                 }<br>>         }<br>><br>>         if (changed & NETIF_F_HW_VLAN_CTAG_RX) {<br>>                 ret = hinic_set_rx_vlan_offload(nic_dev,<br>> -                                               !!(features &<br>> +                                               !!(features[0] &<br>>                                                    NETIF_F_HW_VLAN_CTAG_RX));<br>>                 if (ret) {<br>>                         err = ret;<br>> -                       failed_features |= NETIF_F_HW_VLAN_CTAG_RX;<br>> +                       failed_features[0] |= NETIF_F_HW_VLAN_CTAG_RX;<br>>                 }<br>>         }<br>><br>>         if (err) {<br>> -               nic_dev->netdev->features = features ^ failed_features;<br>> +               netdev_features_xor(nic_dev->netdev->features, features,<br>> +                                   failed_features)<br>>                 return -EIO;<br>>         }<br>><br>> diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c<br>> b/drivers/net/ethernet/huawei/hinic/hinic_rx.c<br>> index fed3b6b..452a91b 100644<br>> --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c<br>> +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c<br>> @@ -106,7 +106,7 @@ static void rx_csum(struct hinic_rxq *rxq, u32 status,<br>><br>>         csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR);<br>><br>> -       if (!(netdev->features & NETIF_F_RXCSUM))<br>> +       if (!(netdev->features[0] & NETIF_F_RXCSUM))<br>>                 return;<br>><br>>         if (!csum_err) {<br>> @@ -411,7 +411,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)<br>><br>>                 offload_type = be32_to_cpu(cqe->offload_type);<br>>                 vlan_len = be32_to_cpu(cqe->len);<br>> -               if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&<br>> +               if ((netdev->features[0] & NETIF_F_HW_VLAN_CTAG_RX) &&<br>>                     HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) {<br>>                         vid = HINIC_GET_RX_VLAN_TAG(vlan_len);<br>>                         __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);<br>> diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h<br>> index 41a5183..4173464 100644<br>> --- a/include/linux/if_vlan.h<br>> +++ b/include/linux/if_vlan.h<br>> @@ -563,7 +563,7 @@ static inline int __vlan_hwaccel_get_tag(const<br>> struct sk_buff *skb,<br>>   */<br>>  static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)<br>>  {<br>> -       if (skb->dev->features & NETIF_F_HW_VLAN_CTAG_TX) {<br>> +       if (skb->dev->features[0] & NETIF_F_HW_VLAN_CTAG_TX) {<br>>                 return __vlan_hwaccel_get_tag(skb, vlan_tci);<br>>         } else {<br>>                 return __vlan_get_tag(skb, vlan_tci);<br>> diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h<br>> index 2c6b9e4..9184963 100644<br>> --- a/include/linux/netdev_features.h<br>> +++ b/include/linux/netdev_features.h<br>> @@ -102,7 +102,8 @@ enum {<br>>  };<br>><br>>  /* copy'n'paste compression ;) */<br>> -#define __NETIF_F_BIT(bit)     ((netdev_features_t)1 << (bit))<br>> +#define __NETIF_F_BIT(bit)     ((netdev_features_t)1 << (bit & 0x3F))<br>> +<br>>  #define __NETIF_F(name)                __NETIF_F_BIT(NETIF_F_##name##_BIT)<br>><br>>  #define NETIF_F_FCOE_CRC       __NETIF_F(FCOE_CRC)<br>> @@ -169,6 +170,8 @@ enum {<br>>  #define NETIF_F_HW_HSR_FWD     __NETIF_F(HW_HSR_FWD)<br>>  #define NETIF_F_HW_HSR_DUP     __NETIF_F(HW_HSR_DUP)<br>><br>> +#define NETDEV_FEATURE_DWORDS  DIV_ROUND_UP(NETDEV_FEATURE_COUNT, 64)<br>> +<br>>  /* Finds the next feature with the highest number of the range of start till 0.<br>>   */<br>>  static inline int find_next_netdev_feature(u64 feature, unsigned long start)<br>> @@ -185,8 +188,7 @@ static inline int find_next_netdev_feature(u64<br>> feature, unsigned long start)<br>>   * mask_addr should be a u64 and bit an int<br>>   */<br>>  #define for_each_netdev_feature(mask_addr, bit)<br>>          \<br>> -       for ((bit) = find_next_netdev_feature((mask_addr),              \<br>> -                                             NETDEV_FEATURE_COUNT);    \<br>> +       for ((bit) = find_next_netdev_feature((mask_addr), 64);         \<br>>              (bit) >= 0;                                                \<br>>              (bit) = find_next_netdev_feature((mask_addr), (bit) - 1))<br>><br>> @@ -195,11 +197,6 @@ static inline int find_next_netdev_feature(u64<br>> feature, unsigned long start)<br>>  #define NETIF_F_NEVER_CHANGE   (NETIF_F_VLAN_CHALLENGED | \<br>>                                  NETIF_F_LLTX | NETIF_F_NETNS_LOCAL)<br>><br>> -/* remember that ((t)1 << t_BITS) is undefined in C99 */<br>> -#define NETIF_F_ETHTOOL_BITS   ((__NETIF_F_BIT(NETDEV_FEATURE_COUNT - 1) | \<br>> -               (__NETIF_F_BIT(NETDEV_FEATURE_COUNT - 1) - 1)) & \<br>> -               ~NETIF_F_NEVER_CHANGE)<br>> -<br>>  /* Segmentation offload feature mask */<br>>  #define NETIF_F_GSO_MASK       (__NETIF_F_BIT(NETIF_F_GSO_LAST + 1) - \<br>>                 __NETIF_F_BIT(NETIF_F_GSO_SHIFT))<br>> @@ -261,4 +258,96 @@ static inline int find_next_netdev_feature(u64<br>> feature, unsigned long start)<br>>                                  NETIF_F_GSO_UDP_TUNNEL |               \<br>>                                  NETIF_F_GSO_UDP_TUNNEL_CSUM)<br>><br>> +static inline void netdev_features_copy(netdev_features_t *dst,<br>> +                                       const netdev_features_t *src)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               dst[i] = src[i];<br>> +}<br>> +<br>> +static inline void netdev_features_and(netdev_features_t *dst,<br>> +                                      const netdev_features_t *a,<br>> +                                      const netdev_features_t *b)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               dst[i] = a[i] & b[i];<br>> +}<br>> +<br>> +static inline void netdev_features_andnot(netdev_features_t *dst,<br>> +                                         const netdev_features_t *a,<br>> +                                         const netdev_features_t *b)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               dst[i] = a[i] & ~b[i];<br>> +}<br>> +<br>> +static inline void netdev_features_or(netdev_features_t *dst,<br>> +                                     const netdev_features_t *a,<br>> +                                     const netdev_features_t *b)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               dst[i] = a[i] | b[i];<br>> +}<br>> +<br>> +static inline void netdev_features_xor(netdev_features_t *dst,<br>> +                                      const netdev_features_t *a,<br>> +                                      const netdev_features_t *b)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               dst[i] = a[i] ^ b[i];<br>> +}<br>> +<br>> +static inline void netdev_features_set(netdev_features_t *dst,<br>> +                                      unsigned int bit)<br>> +{<br>> +       dst[bit / 64] |= __NETIF_F_BIT(bit);<br>> +}<br>> +<br>> +static inline bool netdev_features_equal(const netdev_features_t *a,<br>> +                                        const netdev_features_t *b)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               if (a[i] != b[i])<br>> +                       return false;<br>> +<br>> +       return true;<br>> +}<br>> +<br>> +static inline void netdev_features_empty(netdev_features_t *src)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               if (src[i])<br>> +                       return false;<br>> +<br>> +       return true;<br>> +}<br>> +<br>> +static inline void netdev_features_ethtool_bits(netdev_features_t *dst)<br>> +{<br>> +       unsigned int i;<br>> +<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++) {<br>> +               if (NETDEV_FEATURE_COUNT >= (i + 1) * 64)<br>> +                       dst[i] = GENMASK_ULL(63, 0);<br>> +               else<br>> +                       dst[i] = GENMASK_ULL(NETDEV_FEATURE_COUNT - i * 64,<br>> +                                                 0);<br>> +       }<br>> +       dst[0] &= ~NETIF_F_NEVER_CHANGE;<br>> +}<br>> +<br>>  #endif /* _LINUX_NETDEV_FEATURES_H */<br>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h<br>> index eaf5bb0..4a29487 100644<br>> --- a/include/linux/netdevice.h<br>> +++ b/include/linux/netdevice.h<br>> @@ -1347,9 +1347,9 @@ struct net_device_ops {<br>>         int                     (*ndo_stop)(struct net_device *dev);<br>>         netdev_tx_t             (*ndo_start_xmit)(struct sk_buff *skb,<br>>                                                   struct net_device *dev);<br>> -       netdev_features_t       (*ndo_features_check)(struct sk_buff *skb,<br>> +       void                    (*ndo_features_check)(struct sk_buff *skb,<br>>                                                       struct net_device *dev,<br>> -<br>> netdev_features_t features);<br>> +<br>> netdev_features_t *features);<br>>         u16                     (*ndo_select_queue)(struct net_device *dev,<br>>                                                     struct sk_buff *skb,<br>>                                                     struct net_device *sb_dev);<br>> @@ -1467,10 +1467,10 @@ struct net_device_ops {<br>>                                                       bool all_slaves);<br>>         struct net_device*      (*ndo_sk_get_lower_dev)(struct net_device *dev,<br>>                                                         struct sock *sk);<br>> -       netdev_features_t       (*ndo_fix_features)(struct net_device *dev,<br>> -                                                   netdev_features_t features);<br>> +       void                    (*ndo_fix_features)(struct net_device *dev,<br>> +                                                   netdev_features_t<br>> *features);<br>>         int                     (*ndo_set_features)(struct net_device *dev,<br>> -                                                   netdev_features_t features);<br>> +                                                   netdev_features_t<br>> *features);<br>>         int                     (*ndo_neigh_construct)(struct net_device *dev,<br>>                                                        struct neighbour *n);<br>>         void                    (*ndo_neigh_destroy)(struct net_device *dev,<br>> @@ -1978,12 +1978,12 @@ struct net_device {<br>>         unsigned short          needed_headroom;<br>>         unsigned short          needed_tailroom;<br>><br>> -       netdev_features_t       features;<br>> -       netdev_features_t       hw_features;<br>> -       netdev_features_t       wanted_features;<br>> -       netdev_features_t       vlan_features;<br>> -       netdev_features_t       hw_enc_features;<br>> -       netdev_features_t       mpls_features;<br>> +       netdev_features_t       features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t       hw_features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t       wanted_features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t       vlan_features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t       hw_enc_features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t       mpls_features[NETDEV_FEATURE_DWORDS];<br>>         netdev_features_t       gso_partial_features;<br>><br>>         unsigned int            min_mtu;<br>> @@ -4986,10 +4986,11 @@ static inline netdev_features_t<br>> netdev_intersect_features(netdev_features_t f1,<br>>         return f1 & f2;<br>>  }<br>><br>> -static inline netdev_features_t netdev_get_wanted_features(<br>> -       struct net_device *dev)<br>> +static inline void netdev_get_wanted_features(struct net_device *dev,<br>> +                                             netdev_features_t *wanted)<br>>  {<br>> -       return (dev->features & ~dev->hw_features) | dev->wanted_features;<br>> +       netdev_features_andnot(wanted, dev->features, dev->hw_features);<br>> +       netdev_features_or(wanted, wanted, dev->wanted_features);<br>>  }<br>>  netdev_features_t netdev_increment_features(netdev_features_t all,<br>>         netdev_features_t one, netdev_features_t mask);<br>> @@ -5014,7 +5015,7 @@ void netif_stacked_transfer_operstate(const<br>> struct net_device *rootdev,<br>>  netdev_features_t passthru_features_check(struct sk_buff *skb,<br>>                                           struct net_device *dev,<br>>                                           netdev_features_t features);<br>> -netdev_features_t netif_skb_features(struct sk_buff *skb);<br>> +void netif_skb_features(struct sk_buff *skb, netdev_features_t *features);<br>><br>>  static inline bool net_gso_ok(netdev_features_t features, int gso_type)<br>>  {<br>> diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c<br>> index 4cdf841..7d77692 100644<br>> --- a/net/8021q/vlan.c<br>> +++ b/net/8021q/vlan.c<br>> @@ -328,7 +328,7 @@ static void vlan_transfer_features(struct net_device *dev,<br>>         vlandev->gso_max_size = dev->gso_max_size;<br>>         vlandev->gso_max_segs = dev->gso_max_segs;<br>><br>> -       if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto))<br>> +       if (vlan_hw_offload_capable(dev->features[0], vlan->vlan_proto))<br>>                 vlandev->hard_header_len = dev->hard_header_len;<br>>         else<br>>                 vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN;<br>> @@ -339,7 +339,7 @@ static void vlan_transfer_features(struct net_device *dev,<br>><br>>         vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE;<br>>         vlandev->priv_flags |= (vlan->real_dev->priv_flags &<br>> IFF_XMIT_DST_RELEASE);<br>> -       vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev);<br>> +       vlandev->hw_enc_features[0] = vlan_tnl_features(vlan->real_dev);<br>><br>>         netdev_update_features(vlandev);<br>>  }<br>> diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h<br>> index 1a705a4..4e784a1 100644<br>> --- a/net/8021q/vlan.h<br>> +++ b/net/8021q/vlan.h<br>> @@ -107,7 +107,7 @@ static inline netdev_features_t<br>> vlan_tnl_features(struct net_device *real_dev)<br>>  {<br>>         netdev_features_t ret;<br>><br>> -       ret = real_dev->hw_enc_features &<br>> +       ret = real_dev->hw_enc_features[0] &<br>>               (NETIF_F_CSUM_MASK | NETIF_F_GSO_SOFTWARE |<br>>                NETIF_F_GSO_ENCAP_ALL);<br>><br>> diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c<br>> index a0367b3..6d49761 100644<br>> --- a/net/8021q/vlan_dev.c<br>> +++ b/net/8021q/vlan_dev.c<br>> @@ -566,21 +566,21 @@ static int vlan_dev_init(struct net_device *dev)<br>>         if (vlan->flags & VLAN_FLAG_BRIDGE_BINDING)<br>>                 dev->state |= (1 << __LINK_STATE_NOCARRIER);<br>><br>> -       dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |<br>> -                          NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |<br>> -                          NETIF_F_GSO_ENCAP_ALL |<br>> -                          NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |<br>> -                          NETIF_F_ALL_FCOE;<br>> +       dev->hw_features[0] = NETIF_F_HW_CSUM | NETIF_F_SG |<br>> +                             NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |<br>> +                             NETIF_F_GSO_ENCAP_ALL |<br>> +                             NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |<br>> +                             NETIF_F_ALL_FCOE;<br>><br>> -       dev->features |= dev->hw_features | NETIF_F_LLTX;<br>> +       dev->features[0] |= dev->hw_features[0] | NETIF_F_LLTX;<br>>         dev->gso_max_size = real_dev->gso_max_size;<br>>         dev->gso_max_segs = real_dev->gso_max_segs;<br>> -       if (dev->features & NETIF_F_VLAN_FEATURES)<br>> +       if (dev->features[0] & NETIF_F_VLAN_FEATURES)<br>>                 netdev_warn(real_dev, "VLAN features are set<br>> incorrectly.  Q-in-Q configurations may not work correctly.\n");<br>><br>> -       dev->vlan_features = real_dev->vlan_features & ~NETIF_F_ALL_FCOE;<br>> -       dev->hw_enc_features = vlan_tnl_features(real_dev);<br>> -       dev->mpls_features = real_dev->mpls_features;<br>> +       dev->vlan_features[0] = real_dev->vlan_features[0] & ~NETIF_F_ALL_FCOE;<br>> +       dev->hw_enc_features[0] = vlan_tnl_features(real_dev);<br>> +       netdev_features_copy(dev->mpls_features, real_dev->mpls_features);<br>><br>>         /* ipv6 shared card related stuff */<br>>         dev->dev_id = real_dev->dev_id;<br>> @@ -633,27 +633,30 @@ void vlan_dev_uninit(struct net_device *dev)<br>>         }<br>>  }<br>><br>> -static netdev_features_t vlan_dev_fix_features(struct net_device *dev,<br>> -       netdev_features_t features)<br>> +static void vlan_dev_fix_features(struct net_device *dev,<br>> +                                 netdev_features_t *features)<br>>  {<br>>         struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;<br>> -       netdev_features_t old_features = features;<br>> -       netdev_features_t lower_features;<br>> +       netdev_features_t lower_features[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t old_features[NETDEV_FEATURE_DWORDS];<br>><br>> -       lower_features = netdev_intersect_features((real_dev->vlan_features |<br>> -                                                   NETIF_F_RXCSUM),<br>> -                                                  real_dev->features);<br>> +       netdev_features_copy(lower_features, features);<br>> +<br>> +       lower_features[0] =<br>> +               netdev_intersect_features((real_dev->vlan_features[0] |<br>> +                                          NETIF_F_RXCSUM),<br>> +                                         real_dev->features[0]);<br>><br>>         /* Add HW_CSUM setting to preserve user ability to control<br>>          * checksum offload on the vlan device.<br>>          */<br>> -       if (lower_features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))<br>> -               lower_features |= NETIF_F_HW_CSUM;<br>> -       features = netdev_intersect_features(features, lower_features);<br>> -       features |= old_features & (NETIF_F_SOFT_FEATURES |<br>> NETIF_F_GSO_SOFTWARE);<br>> -       features |= NETIF_F_LLTX;<br>> +       if (lower_features[0] & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))<br>> +               lower_features[0] |= NETIF_F_HW_CSUM;<br>><br>> -       return features;<br>> +       features[0] = netdev_intersect_features(features[0], lower_features[0]);<br>> +       features[0] |= old_features[0] &<br>> +                       (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE);<br>> +       features[0] |= NETIF_F_LLTX;<br>>  }<br>><br>>  static int vlan_ethtool_get_link_ksettings(struct net_device *dev,<br>> diff --git a/net/core/dev.c b/net/core/dev.c<br>> index c253c2a..7066bf3 100644<br>> --- a/net/core/dev.c<br>> +++ b/net/core/dev.c<br>> @@ -1765,7 +1765,7 @@ void dev_disable_lro(struct net_device *dev)<br>>         dev->wanted_features &= ~NETIF_F_LRO;<br>>         netdev_update_features(dev);<br>><br>> -       if (unlikely(dev->features & NETIF_F_LRO))<br>> +       if (unlikely(dev->features[0] & NETIF_F_LRO))<br>>                 netdev_WARN(dev, "failed to disable LRO!\n");<br>><br>>         netdev_for_each_lower_dev(dev, lower_dev, iter)<br>> @@ -1786,7 +1786,7 @@ static void dev_disable_gro_hw(struct net_device *dev)<br>>         dev->wanted_features &= ~NETIF_F_GRO_HW;<br>>         netdev_update_features(dev);<br>><br>> -       if (unlikely(dev->features & NETIF_F_GRO_HW))<br>> +       if (unlikely(dev->features[0] & NETIF_F_GRO_HW))<br>>                 netdev_WARN(dev, "failed to disable GRO_HW!\n");<br>>  }<br>><br>> @@ -3276,7 +3276,7 @@ static void skb_warn_bad_offload(const struct<br>> sk_buff *skb)<br>>         }<br>>         skb_dump(KERN_WARNING, skb, false);<br>>         WARN(1, "%s: caps=(%pNF, %pNF)\n",<br>> -            name, dev ? &dev->features : &null_features,<br>> +            name, dev ? &dev->features[0] : &null_features,<br>>              skb->sk ? &skb->sk->sk_route_caps : &null_features);<br>>  }<br>><br>> @@ -3463,7 +3463,8 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,<br>>                 netdev_features_t partial_features = NETIF_F_GSO_ROBUST;<br>>                 struct net_device *dev = skb->dev;<br>><br>> -               partial_features |= dev->features & dev->gso_partial_features;<br>> +               partial_features |=<br>> +                               dev->features[0] & dev->gso_partial_features;<br>>                 if (!skb_gso_ok(skb, features | partial_features))<br>>                         features &= ~NETIF_F_GSO_PARTIAL;<br>>         }<br>> @@ -3508,7 +3509,7 @@ static int illegal_highdma(struct net_device<br>> *dev, struct sk_buff *skb)<br>>  #ifdef CONFIG_HIGHMEM<br>>         int i;<br>><br>> -       if (!(dev->features & NETIF_F_HIGHDMA)) {<br>> +       if (!(dev->features[0] & NETIF_F_HIGHDMA)) {<br>>                 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {<br>>                         skb_frag_t *frag = &skb_shinfo(skb)->frags[i];<br>><br>> @@ -3612,34 +3613,33 @@ static netdev_features_t<br>> gso_features_check(const struct sk_buff *skb,<br>>         return features;<br>>  }<br>><br>> -netdev_features_t netif_skb_features(struct sk_buff *skb)<br>> +void netif_skb_features(struct sk_buff *skb, netdev_features_t *features)<br>>  {<br>>         struct net_device *dev = skb->dev;<br>> -       netdev_features_t features = dev->features;<br>><br>> +       netdev_features_copy(features, dev->features);<br>>         if (skb_is_gso(skb))<br>> -               features = gso_features_check(skb, dev, features);<br>> +               features[0] = gso_features_check(skb, dev, features[0]);<br>><br>>         /* If encapsulation offload request, verify we are testing<br>>          * hardware encapsulation features instead of standard<br>>          * features for the netdev<br>>          */<br>>         if (skb->encapsulation)<br>> -               features &= dev->hw_enc_features;<br>> +               netdev_features_and(features, dev->hw_enc_features);<br>><br>>         if (skb_vlan_tagged(skb))<br>> -               features = netdev_intersect_features(features,<br>> -                                                    dev->vlan_features |<br>> -                                                    NETIF_F_HW_VLAN_CTAG_TX |<br>> -                                                    NETIF_F_HW_VLAN_STAG_TX);<br>> +               features[0] = netdev_intersect_features(features[0],<br>> +                                                       dev->vlan_features[0] |<br>> +<br>> NETIF_F_HW_VLAN_CTAG_TX |<br>> +<br>> NETIF_F_HW_VLAN_STAG_TX);<br>><br>>         if (dev->netdev_ops->ndo_features_check)<br>> -               features &= dev->netdev_ops->ndo_features_check(skb, dev,<br>> -                                                               features);<br>> +               dev->netdev_ops->ndo_features_check(skb, dev, features);<br>>         else<br>> -               features &= dflt_features_check(skb, dev, features);<br>> +               features[0] &= dflt_features_check(skb, dev, features[0]);<br>><br>> -       return harmonize_features(skb, features);<br>> +       features[0] = harmonize_features(skb, features[0]);<br>>  }<br>>  EXPORT_SYMBOL(netif_skb_features);<br>><br>> @@ -3722,10 +3722,10 @@ EXPORT_SYMBOL(skb_csum_hwoffload_help);<br>><br>>  static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct<br>> net_device *dev, bool *again)<br>>  {<br>> -       netdev_features_t features;<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS];<br>><br>> -       features = netif_skb_features(skb);<br>> -       skb = validate_xmit_vlan(skb, features);<br>> +       netif_skb_features(skb, features);<br>> +       skb = validate_xmit_vlan(skb, features[0]);<br>>         if (unlikely(!skb))<br>>                 goto out_null;<br>><br>> @@ -3733,10 +3733,10 @@ static struct sk_buff<br>> *validate_xmit_skb(struct sk_buff *skb, struct net_device<br>>         if (unlikely(!skb))<br>>                 goto out_null;<br>><br>> -       if (netif_needs_gso(skb, features)) {<br>> +       if (netif_needs_gso(skb, features[0])) {<br>>                 struct sk_buff *segs;<br>><br>> -               segs = skb_gso_segment(skb, features);<br>> +               segs = skb_gso_segment(skb, features[0]);<br>>                 if (IS_ERR(segs)) {<br>>                         goto out_kfree_skb;<br>>                 } else if (segs) {<br>> @@ -3744,7 +3744,7 @@ static struct sk_buff *validate_xmit_skb(struct<br>> sk_buff *skb, struct net_device<br>>                         skb = segs;<br>>                 }<br>>         } else {<br>> -               if (skb_needs_linearize(skb, features) &&<br>> +               if (skb_needs_linearize(skb, features[0]) &&<br>>                     __skb_linearize(skb))<br>>                         goto out_kfree_skb;<br>><br>> @@ -3759,12 +3759,12 @@ static struct sk_buff<br>> *validate_xmit_skb(struct sk_buff *skb, struct net_device<br>>                         else<br>>                                 skb_set_transport_header(skb,<br>><br>> skb_checksum_start_offset(skb));<br>> -                       if (skb_csum_hwoffload_help(skb, features))<br>> +                       if (skb_csum_hwoffload_help(skb, features[0]))<br>>                                 goto out_kfree_skb;<br>>                 }<br>>         }<br>><br>> -       skb = validate_xmit_xfrm(skb, features, again);<br>> +       skb = validate_xmit_xfrm(skb, features[0], again);<br>><br>>         return skb;<br>><br>> @@ -4429,7 +4429,7 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb,<br>><br>>                 /* Should we steer this flow to a different hardware queue? */<br>>                 if (!skb_rx_queue_recorded(skb) || !dev->rx_cpu_rmap ||<br>> -                   !(dev->features & NETIF_F_NTUPLE))<br>> +                   !(dev->features[0] & NETIF_F_NTUPLE))<br>>                         goto out;<br>>                 rxq_index = cpu_rmap_lookup_index(dev->rx_cpu_rmap, next_cpu);<br>>                 if (rxq_index == skb_get_rx_queue(skb))<br>> @@ -9799,171 +9799,179 @@ static void net_set_todo(struct net_device *dev)<br>>         dev_net(dev)->dev_unreg_count++;<br>>  }<br>><br>> -static netdev_features_t netdev_sync_upper_features(struct net_device *lower,<br>> -       struct net_device *upper, netdev_features_t features)<br>> +static void netdev_sync_upper_features(struct net_device *lower,<br>> +                                      struct net_device *upper,<br>> +                                      netdev_features_t *features)<br>>  {<br>>         netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;<br>>         netdev_features_t feature;<br>>         int feature_bit;<br>> +       unsigned int i;<br>><br>> -       for_each_netdev_feature(upper_disables, feature_bit) {<br>> -               feature = __NETIF_F_BIT(feature_bit);<br>> -               if (!(upper->wanted_features & feature)<br>> -                   && (features & feature)) {<br>> -                       netdev_dbg(lower, "Dropping feature %pNF,<br>> upper dev %s has it off.\n",<br>> -                                  &feature, upper->name);<br>> -                       features &= ~feature;<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++) {<br>> +               for_each_netdev_feature(upper_disables, feature_bit) {<br>> +                       feature = __NETIF_F_BIT(feature_bit);<br>> +                       if (!(upper->wanted_features[i] & feature) &&<br>> +                           (features[i] & feature)) {<br>> +                               netdev_dbg(lower, "Dropping<br>> feature[%u] %pNF, upper dev %s has it off.\n",<br>> +                                          i, &feature, upper->name);<br>> +                               features[i] &= ~feature;<br>> +                       }<br>>                 }<br>>         }<br>> -<br>> -       return features;<br>>  }<br>><br>>  static void netdev_sync_lower_features(struct net_device *upper,<br>> -       struct net_device *lower, netdev_features_t features)<br>> +       struct net_device *lower, netdev_features_t *features)<br>>  {<br>>         netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;<br>>         netdev_features_t feature;<br>>         int feature_bit;<br>> +       unsigned int i;<br>><br>> -       for_each_netdev_feature(upper_disables, feature_bit) {<br>> -               feature = __NETIF_F_BIT(feature_bit);<br>> -               if (!(features & feature) && (lower->features & feature)) {<br>> -                       netdev_dbg(upper, "Disabling feature %pNF on<br>> lower dev %s.\n",<br>> -                                  &feature, lower->name);<br>> -                       lower->wanted_features &= ~feature;<br>> -                       __netdev_update_features(lower);<br>> -<br>> -                       if (unlikely(lower->features & feature))<br>> -                               netdev_WARN(upper, "failed to disable<br>> %pNF on %s!\n",<br>> -                                           &feature, lower->name);<br>> -                       else<br>> -                               netdev_features_change(lower);<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++) {<br>> +               for_each_netdev_feature(upper_disables, feature_bit) {<br>> +                       feature = __NETIF_F_BIT(feature_bit);<br>> +                       if (!(features[i] & feature) &&<br>> +                           (lower->features[i] & feature)) {<br>> +                               netdev_dbg(upper, "Disabling<br>> feature[%u] %pNF on lower dev %s.\n",<br>> +                                          i, &feature, lower->name);<br>> +                               lower->wanted_features[i] &= ~feature[i];<br>> +                               __netdev_update_features(lower);<br>> +<br>> +                               if (unlikely(lower->features[i] & feature))<br>> +                                       netdev_WARN(upper, "failed to<br>> disable feature[%u] %pNF on %s!\n",<br>> +                                                   i, &feature, lower->name);<br>> +                               else<br>> +                                       netdev_features_change(lower);<br>> +                       }<br>>                 }<br>>         }<br>>  }<br>><br>> -static netdev_features_t netdev_fix_features(struct net_device *dev,<br>> -       netdev_features_t features)<br>> +static void netdev_fix_features(struct net_device *dev,<br>> +                               netdev_features_t *features)<br>>  {<br>>         /* Fix illegal checksum combinations */<br>> -       if ((features & NETIF_F_HW_CSUM) &&<br>> -           (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {<br>> +       if ((features[0] & NETIF_F_HW_CSUM) &&<br>> +           (features[0] & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) {<br>>                 netdev_warn(dev, "mixed HW and IP checksum settings.\n");<br>> -               features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);<br>> +               features[0] &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);<br>>         }<br>><br>>         /* TSO requires that SG is present as well. */<br>> -       if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) {<br>> +       if ((features[0] & NETIF_F_ALL_TSO) && !(features[0] & NETIF_F_SG)) {<br>>                 netdev_dbg(dev, "Dropping TSO features since no SG feature.\n");<br>> -               features &= ~NETIF_F_ALL_TSO;<br>> +               features[0] &= ~NETIF_F_ALL_TSO;<br>>         }<br>><br>> -       if ((features & NETIF_F_TSO) && !(features & NETIF_F_HW_CSUM) &&<br>> -                                       !(features & NETIF_F_IP_CSUM)) {<br>> +       if ((features[0] & NETIF_F_TSO) && !(features[0] & NETIF_F_HW_CSUM) &&<br>> +           !(features[0] & NETIF_F_IP_CSUM)) {<br>>                 netdev_dbg(dev, "Dropping TSO features since no CSUM<br>> feature.\n");<br>> -               features &= ~NETIF_F_TSO;<br>> -               features &= ~NETIF_F_TSO_ECN;<br>> +               features[0] &= ~NETIF_F_TSO;<br>> +               features[0] &= ~NETIF_F_TSO_ECN;<br>>         }<br>><br>> -       if ((features & NETIF_F_TSO6) && !(features & NETIF_F_HW_CSUM) &&<br>> -                                        !(features & NETIF_F_IPV6_CSUM)) {<br>> +       if ((features[0] & NETIF_F_TSO6) && !(features[0] & NETIF_F_HW_CSUM) &&<br>> +           !(features[0] & NETIF_F_IPV6_CSUM)) {<br>>                 netdev_dbg(dev, "Dropping TSO6 features since no CSUM<br>> feature.\n");<br>> -               features &= ~NETIF_F_TSO6;<br>> +               features[0] &= ~NETIF_F_TSO6;<br>>         }<br>><br>>         /* TSO with IPv4 ID mangling requires IPv4 TSO be enabled */<br>> -       if ((features & NETIF_F_TSO_MANGLEID) && !(features & NETIF_F_TSO))<br>> -               features &= ~NETIF_F_TSO_MANGLEID;<br>> +       if ((features[0] & NETIF_F_TSO_MANGLEID) &&<br>> +           !(features[0] & NETIF_F_TSO))<br>> +               features[0] &= ~NETIF_F_TSO_MANGLEID;<br>><br>>         /* TSO ECN requires that TSO is present as well. */<br>> -       if ((features & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN)<br>> -               features &= ~NETIF_F_TSO_ECN;<br>> +       if ((features[0] & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN)<br>> +               features[0] &= ~NETIF_F_TSO_ECN;<br>><br>>         /* Software GSO depends on SG. */<br>> -       if ((features & NETIF_F_GSO) && !(features & NETIF_F_SG)) {<br>> +       if ((features[0] & NETIF_F_GSO) && !(features[0] & NETIF_F_SG)) {<br>>                 netdev_dbg(dev, "Dropping NETIF_F_GSO since no SG feature.\n");<br>> -               features &= ~NETIF_F_GSO;<br>> +               features[0] &= ~NETIF_F_GSO;<br>>         }<br>><br>>         /* GSO partial features require GSO partial be set */<br>> -       if ((features & dev->gso_partial_features) &&<br>> -           !(features & NETIF_F_GSO_PARTIAL)) {<br>> +       if ((features[0] & dev->gso_partial_features) &&<br>> +           !(features[0] & NETIF_F_GSO_PARTIAL)) {<br>>                 netdev_dbg(dev,<br>>                            "Dropping partially supported GSO features<br>> since no GSO partial.\n");<br>> -               features &= ~dev->gso_partial_features;<br>> +               features[0] &= ~dev->gso_partial_features;<br>>         }<br>><br>> -       if (!(features & NETIF_F_RXCSUM)) {<br>> +       if (!(features[0] & NETIF_F_RXCSUM)) {<br>>                 /* NETIF_F_GRO_HW implies doing RXCSUM since every packet<br>>                  * successfully merged by hardware must also have the<br>>                  * checksum verified by hardware.  If the user does not<br>>                  * want to enable RXCSUM, logically, we should disable GRO_HW.<br>>                  */<br>> -               if (features & NETIF_F_GRO_HW) {<br>> +               if (features[0] & NETIF_F_GRO_HW) {<br>>                         netdev_dbg(dev, "Dropping NETIF_F_GRO_HW since<br>> no RXCSUM feature.\n");<br>> -                       features &= ~NETIF_F_GRO_HW;<br>> +                       features[0] &= ~NETIF_F_GRO_HW;<br>>                 }<br>>         }<br>><br>>         /* LRO/HW-GRO features cannot be combined with RX-FCS */<br>> -       if (features & NETIF_F_RXFCS) {<br>> -               if (features & NETIF_F_LRO) {<br>> +       if (features[0] & NETIF_F_RXFCS) {<br>> +               if (features[0] & NETIF_F_LRO) {<br>>                         netdev_dbg(dev, "Dropping LRO feature since<br>> RX-FCS is requested.\n");<br>> -                       features &= ~NETIF_F_LRO;<br>> +                       features[0] &= ~NETIF_F_LRO;<br>>                 }<br>><br>> -               if (features & NETIF_F_GRO_HW) {<br>> +               if (features[0] & NETIF_F_GRO_HW) {<br>>                         netdev_dbg(dev, "Dropping HW-GRO feature since<br>> RX-FCS is requested.\n");<br>> -                       features &= ~NETIF_F_GRO_HW;<br>> +                       features[0] &= ~NETIF_F_GRO_HW;<br>>                 }<br>>         }<br>><br>> -       if (features & NETIF_F_HW_TLS_TX) {<br>> -               bool ip_csum = (features & (NETIF_F_IP_CSUM |<br>> NETIF_F_IPV6_CSUM)) ==<br>> +       if (features[0] & NETIF_F_HW_TLS_TX) {<br>> +               bool ip_csum = (features[0] & (NETIF_F_IP_CSUM |<br>> NETIF_F_IPV6_CSUM)) ==<br>>                         (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);<br>> -               bool hw_csum = features & NETIF_F_HW_CSUM;<br>> +               bool hw_csum = features[0] & NETIF_F_HW_CSUM;<br>><br>>                 if (!ip_csum && !hw_csum) {<br>>                         netdev_dbg(dev, "Dropping TLS TX HW offload<br>> feature since no CSUM feature.\n");<br>> -                       features &= ~NETIF_F_HW_TLS_TX;<br>> +                       features[0] &= ~NETIF_F_HW_TLS_TX;<br>>                 }<br>>         }<br>><br>> -       if ((features & NETIF_F_HW_TLS_RX) && !(features & NETIF_F_RXCSUM)) {<br>> +       if ((features[0] & NETIF_F_HW_TLS_RX) &&<br>> +           !(features[0] & NETIF_F_RXCSUM)) {<br>>                 netdev_dbg(dev, "Dropping TLS RX HW offload feature<br>> since no RXCSUM feature.\n");<br>> -               features &= ~NETIF_F_HW_TLS_RX;<br>> +               features[0] &= ~NETIF_F_HW_TLS_RX;<br>>         }<br>> -<br>> -       return features;<br>>  }<br>><br>>  int __netdev_update_features(struct net_device *dev)<br>>  {<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS];<br>>         struct net_device *upper, *lower;<br>> -       netdev_features_t features;<br>>         struct list_head *iter;<br>> +       unsigned int i;<br>>         int err = -1;<br>><br>>         ASSERT_RTNL();<br>><br>> -       features = netdev_get_wanted_features(dev);<br>> +       netdev_get_wanted_features(dev, features);<br>><br>>         if (dev->netdev_ops->ndo_fix_features)<br>> -               features = dev->netdev_ops->ndo_fix_features(dev, features);<br>> +               dev->netdev_ops->ndo_fix_features(dev, features);<br>><br>>         /* driver might be less strict about feature dependencies */<br>> -       features = netdev_fix_features(dev, features);<br>> +       netdev_fix_features(dev, features);<br>><br>>         /* some features can't be enabled if they're off on an upper device */<br>>         netdev_for_each_upper_dev_rcu(dev, upper, iter)<br>> -               features = netdev_sync_upper_features(dev, upper, features);<br>> +               netdev_sync_upper_features(dev, upper, features);<br>><br>> -       if (dev->features == features)<br>> +       if (netdev_features_equal(dev->features, features))<br>>                 goto sync_lower;<br>><br>> -       netdev_dbg(dev, "Features changed: %pNF -> %pNF\n",<br>> -               &dev->features, &features);<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               netdev_dbg(dev, "Features[%u] changed: %pNF -> %pNF\n",<br>> +                          i, &dev->features[i], &features[i]);<br>><br>>         if (dev->netdev_ops->ndo_set_features)<br>>                 err = dev->netdev_ops->ndo_set_features(dev, features);<br>> @@ -9971,9 +9979,10 @@ int __netdev_update_features(struct net_device *dev)<br>>                 err = 0;<br>><br>>         if (unlikely(err < 0)) {<br>> -               netdev_err(dev,<br>> -                       "set_features() failed (%d); wanted %pNF, left %pNF\n",<br>> -                       err, &features, &dev->features);<br>> +               for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +                       netdev_err(dev,<br>> +                                  "set_features() failed (%d);<br>> wanted[%u] %pNF, left[%u] %pNF\n",<br>> +                                  err, i, &features[i], i, &dev->features[i]);<br>>                 /* return non-0 since some features might have changed and<br>>                  * it's better to fire a spurious notification than miss it<br>>                  */<br>> @@ -9988,9 +9997,10 @@ int __netdev_update_features(struct net_device *dev)<br>>                 netdev_sync_lower_features(dev, lower, features);<br>><br>>         if (!err) {<br>> -               netdev_features_t diff = features ^ dev->features;<br>> +               netdev_features_t diff[NETDEV_FEATURE_DWORDS];<br>><br>> -               if (diff & NETIF_F_RX_UDP_TUNNEL_PORT) {<br>> +               netdev_features_xor(diff, features, dev->features);<br>> +               if (diff[0] & NETIF_F_RX_UDP_TUNNEL_PORT) {<br>>                         /* udp_tunnel_{get,drop}_rx_info both need<br>>                          * NETIF_F_RX_UDP_TUNNEL_PORT enabled on the<br>>                          * device, or they won't do anything.<br>> @@ -9998,33 +10008,33 @@ int __netdev_update_features(struct net_device *dev)<br>>                          * *before* calling udp_tunnel_get_rx_info,<br>>                          * but *after* calling udp_tunnel_drop_rx_info.<br>>                          */<br>> -                       if (features & NETIF_F_RX_UDP_TUNNEL_PORT) {<br>> -                               dev->features = features;<br>> +                       if (features[0] & NETIF_F_RX_UDP_TUNNEL_PORT) {<br>> +                               dev->features[0] = features[0];<br>>                                 udp_tunnel_get_rx_info(dev);<br>>                         } else {<br>>                                 udp_tunnel_drop_rx_info(dev);<br>>                         }<br>>                 }<br>><br>> -               if (diff & NETIF_F_HW_VLAN_CTAG_FILTER) {<br>> -                       if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {<br>> -                               dev->features = features;<br>> +               if (diff[0] & NETIF_F_HW_VLAN_CTAG_FILTER) {<br>> +                       if (features[0] & NETIF_F_HW_VLAN_CTAG_FILTER) {<br>> +                               dev->features[0] = features[0];<br>>                                 err |= vlan_get_rx_ctag_filter_info(dev);<br>>                         } else {<br>>                                 vlan_drop_rx_ctag_filter_info(dev);<br>>                         }<br>>                 }<br>><br>> -               if (diff & NETIF_F_HW_VLAN_STAG_FILTER) {<br>> +               if (diff[0] & NETIF_F_HW_VLAN_STAG_FILTER) {<br>>                         if (features & NETIF_F_HW_VLAN_STAG_FILTER) {<br>> -                               dev->features = features;<br>> +                               dev->features[0] = features[0];<br>>                                 err |= vlan_get_rx_stag_filter_info(dev);<br>>                         } else {<br>>                                 vlan_drop_rx_stag_filter_info(dev);<br>>                         }<br>>                 }<br>><br>> -               dev->features = features;<br>> +               netdev_features_copy(dev->features, features);<br>>         }<br>><br>>         return err < 0 ? 0 : 1;<br>> @@ -10213,7 +10223,7 @@ int register_netdevice(struct net_device *dev)<br>>         int ret;<br>>         struct net *net = dev_net(dev);<br>><br>> -       BUILD_BUG_ON(sizeof(netdev_features_t) * BITS_PER_BYTE <<br>> +       BUILD_BUG_ON(sizeof(dev->features) * BITS_PER_BYTE <<br>>                      NETDEV_FEATURE_COUNT);<br>>         BUG_ON(dev_boot_phase);<br>>         ASSERT_RTNL();<br>> @@ -10250,7 +10260,7 @@ int register_netdevice(struct net_device *dev)<br>>                 }<br>>         }<br>><br>> -       if (((dev->hw_features | dev->features) &<br>> +       if (((dev->hw_features[0] | dev->features[0]) &<br>>              NETIF_F_HW_VLAN_CTAG_FILTER) &&<br>>             (!dev->netdev_ops->ndo_vlan_rx_add_vid ||<br>>              !dev->netdev_ops->ndo_vlan_rx_kill_vid)) {<br>> @@ -10268,44 +10278,46 @@ int register_netdevice(struct net_device *dev)<br>>         /* Transfer changeable features to wanted_features and enable<br>>          * software offloads (GSO and GRO).<br>>          */<br>> -       dev->hw_features |= (NETIF_F_SOFT_FEATURES | NETIF_F_SOFT_FEATURES_OFF);<br>> -       dev->features |= NETIF_F_SOFT_FEATURES;<br>> +       dev->hw_features[0] |=<br>> +                       (NETIF_F_SOFT_FEATURES | NETIF_F_SOFT_FEATURES_OFF);<br>> +       dev->features[0] |= NETIF_F_SOFT_FEATURES;<br>><br>>         if (dev->udp_tunnel_nic_info) {<br>> -               dev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;<br>> -               dev->hw_features |= NETIF_F_RX_UDP_TUNNEL_PORT;<br>> +               dev->features[0] |= NETIF_F_RX_UDP_TUNNEL_PORT;<br>> +               dev->hw_features[0] |= NETIF_F_RX_UDP_TUNNEL_PORT;<br>>         }<br>><br>> -       dev->wanted_features = dev->features & dev->hw_features;<br>> +       netdev_features_and(dev->wanted_features, dev->features,<br>> +                           dev->hw_features);<br>><br>>         if (!(dev->flags & IFF_LOOPBACK))<br>> -               dev->hw_features |= NETIF_F_NOCACHE_COPY;<br>> +               dev->hw_features[0] |= NETIF_F_NOCACHE_COPY;<br>><br>>         /* If IPv4 TCP segmentation offload is supported we should also<br>>          * allow the device to enable segmenting the frame with the option<br>>          * of ignoring a static IP ID value.  This doesn't enable the<br>>          * feature itself but allows the user to enable it later.<br>>          */<br>> -       if (dev->hw_features & NETIF_F_TSO)<br>> -               dev->hw_features |= NETIF_F_TSO_MANGLEID;<br>> -       if (dev->vlan_features & NETIF_F_TSO)<br>> -               dev->vlan_features |= NETIF_F_TSO_MANGLEID;<br>> -       if (dev->mpls_features & NETIF_F_TSO)<br>> -               dev->mpls_features |= NETIF_F_TSO_MANGLEID;<br>> -       if (dev->hw_enc_features & NETIF_F_TSO)<br>> -               dev->hw_enc_features |= NETIF_F_TSO_MANGLEID;<br>> +       if (dev->hw_features[0] & NETIF_F_TSO)<br>> +               dev->hw_features[0] |= NETIF_F_TSO_MANGLEID;<br>> +       if (dev->vlan_features[0] & NETIF_F_TSO)<br>> +               dev->vlan_features[0] |= NETIF_F_TSO_MANGLEID;<br>> +       if (dev->mpls_features[0] & NETIF_F_TSO)<br>> +               dev->mpls_features[0] |= NETIF_F_TSO_MANGLEID;<br>> +       if (dev->hw_enc_features[0] & NETIF_F_TSO)<br>> +               dev->hw_enc_features[0] |= NETIF_F_TSO_MANGLEID;<br>><br>>         /* Make NETIF_F_HIGHDMA inheritable to VLAN devices.<br>>          */<br>> -       dev->vlan_features |= NETIF_F_HIGHDMA;<br>> +       dev->vlan_features[0] |= NETIF_F_HIGHDMA;<br>><br>>         /* Make NETIF_F_SG inheritable to tunnel devices.<br>>          */<br>> -       dev->hw_enc_features |= NETIF_F_SG | NETIF_F_GSO_PARTIAL;<br>> +       dev->hw_enc_features[0] |= NETIF_F_SG | NETIF_F_GSO_PARTIAL;<br>><br>>         /* Make NETIF_F_SG inheritable to MPLS.<br>>          */<br>> -       dev->mpls_features |= NETIF_F_SG;<br>> +       dev->mpls_features[0] |= NETIF_F_SG;<br>><br>>         ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);<br>>         ret = notifier_to_errno(ret);<br>> @@ -11146,7 +11158,7 @@ int __dev_change_net_namespace(struct<br>> net_device *dev, struct net *net,<br>><br>>         /* Don't allow namespace local devices to be moved. */<br>>         err = -EINVAL;<br>> -       if (dev->features & NETIF_F_NETNS_LOCAL)<br>> +       if (dev->features[0] & NETIF_F_NETNS_LOCAL)<br>>                 goto out;<br>><br>>         /* Ensure the device has been registrered */<br>> @@ -11506,7 +11518,7 @@ static void __net_exit<br>> default_device_exit(struct net *net)<br>>                 char fb_name[IFNAMSIZ];<br>><br>>                 /* Ignore unmoveable devices (i.e. loopback) */<br>> -               if (dev->features & NETIF_F_NETNS_LOCAL)<br>> +               if (dev->features[0] & NETIF_F_NETNS_LOCAL)<br>>                         continue;<br>><br>>                 /* Leave virtual devices for the generic cleanup */<br>> diff --git a/net/core/netpoll.c b/net/core/netpoll.c<br>> index 0a6b047..2c0adf4 100644<br>> --- a/net/core/netpoll.c<br>> +++ b/net/core/netpoll.c<br>> @@ -74,13 +74,13 @@ static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb,<br>>                                       struct net_device *dev,<br>>                                       struct netdev_queue *txq)<br>>  {<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS];<br>>         netdev_tx_t status = NETDEV_TX_OK;<br>> -       netdev_features_t features;<br>><br>> -       features = netif_skb_features(skb);<br>> +       netif_skb_features(skb, features);<br>><br>>         if (skb_vlan_tag_present(skb) &&<br>> -           !vlan_hw_offload_capable(features, skb->vlan_proto)) {<br>> +           !vlan_hw_offload_capable(features[0], skb->vlan_proto)) {<br>>                 skb = __vlan_hwaccel_push_inside(skb);<br>>                 if (unlikely(!skb)) {<br>>                         /* This is actually a packet drop, but we<br>> diff --git a/net/ethtool/features.c b/net/ethtool/features.c<br>> index 1c9f4df..0eedb17 100644<br>> --- a/net/ethtool/features.c<br>> +++ b/net/ethtool/features.c<br>> @@ -25,12 +25,13 @@ const struct nla_policy ethnl_features_get_policy[] = {<br>>                 NLA_POLICY_NESTED(ethnl_header_policy),<br>>  };<br>><br>> -static void ethnl_features_to_bitmap32(u32 *dest, netdev_features_t src)<br>> +static void ethnl_features_to_bitmap32(u32 *dest, netdev_features_t *src)<br>>  {<br>> +       u32 *__src = (u32 *)src;<br>>         unsigned int i;<br>><br>>         for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)<br>> -               dest[i] = src >> (32 * i);<br>> +               dest[i] = __src[i];<br>>  }<br>><br>>  static int features_prepare_data(const struct ethnl_req_info *req_base,<br>> @@ -38,15 +39,23 @@ static int features_prepare_data(const struct<br>> ethnl_req_info *req_base,<br>>                                  struct genl_info *info)<br>>  {<br>>         struct features_reply_data *data = FEATURES_REPDATA(reply_base);<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS] = {0};<br>>         struct net_device *dev = reply_base->dev;<br>> -       netdev_features_t all_features;<br>> +       unsigned int i;<br>><br>>         ethnl_features_to_bitmap32(data->hw, dev->hw_features);<br>>         ethnl_features_to_bitmap32(data->wanted, dev->wanted_features);<br>>         ethnl_features_to_bitmap32(data->active, dev->features);<br>> -       ethnl_features_to_bitmap32(data->nochange, NETIF_F_NEVER_CHANGE);<br>> -       all_features = GENMASK_ULL(NETDEV_FEATURE_COUNT - 1, 0);<br>> -       ethnl_features_to_bitmap32(data->all, all_features);<br>> +       features[0] = NETIF_F_NEVER_CHANGE;<br>> +       ethnl_features_to_bitmap32(data->nochange, features);<br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++) {<br>> +               if (NETDEV_FEATURE_COUNT >= (i + 1) * 64)<br>> +                       features[i] = GENMASK_ULL(63, 0);<br>> +               else<br>> +                       features[i] = GENMASK_ULL(NETDEV_FEATURE_COUNT - i * 64,<br>> +                                                 0);<br>> +       }<br>> +       ethnl_features_to_bitmap32(data->all, features);<br>><br>>         return 0;<br>>  }<br>> @@ -131,27 +140,29 @@ const struct nla_policy ethnl_features_set_policy[] = {<br>>         [ETHTOOL_A_FEATURES_WANTED]     = { .type = NLA_NESTED },<br>>  };<br>><br>> -static void ethnl_features_to_bitmap(unsigned long *dest,<br>> netdev_features_t val)<br>> +static void ethnl_features_to_bitmap(unsigned long *dest,<br>> +                                    netdev_features_t *val)<br>>  {<br>>         const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);<br>>         unsigned int i;<br>><br>>         bitmap_zero(dest, NETDEV_FEATURE_COUNT);<br>>         for (i = 0; i < words; i++)<br>> -               dest[i] = (unsigned long)(val >> (i * BITS_PER_LONG));<br>> +               dest[i] =<br>> +                       (unsigned long)(val[i / 2] >> (i % 2 * BITS_PER_LONG));<br>>  }<br>><br>> -static netdev_features_t ethnl_bitmap_to_features(unsigned long *src)<br>> +static void ethnl_bitmap_to_features(netdev_features_t *val, unsigned<br>> long *src)<br>>  {<br>> -       const unsigned int nft_bits = sizeof(netdev_features_t) * BITS_PER_BYTE;<br>>         const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);<br>> -       netdev_features_t ret = 0;<br>>         unsigned int i;<br>><br>> +       for (i = 0; i < NETDEV_FEATURE_DWORDS; i++)<br>> +               val[i] = 0;<br>> +<br>>         for (i = 0; i < words; i++)<br>> -               ret |= (netdev_features_t)(src[i]) << (i * BITS_PER_LONG);<br>> -       ret &= ~(netdev_features_t)0 >> (nft_bits - NETDEV_FEATURE_COUNT);<br>> -       return ret;<br>> +               val[i / 2] |=<br>> +                       (netdev_features_t)(src[i]) << (i % 2 * BITS_PER_LONG);<br>>  }<br>><br>>  static int features_send_reply(struct net_device *dev, struct genl_info *info,<br>> @@ -212,12 +223,14 @@ int ethnl_set_features(struct sk_buff *skb,<br>> struct genl_info *info)<br>>  {<br>>         DECLARE_BITMAP(wanted_diff_mask, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(active_diff_mask, NETDEV_FEATURE_COUNT);<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS];<br>>         DECLARE_BITMAP(old_active, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(old_wanted, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(new_active, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(new_wanted, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(req_wanted, NETDEV_FEATURE_COUNT);<br>>         DECLARE_BITMAP(req_mask, NETDEV_FEATURE_COUNT);<br>> +       netdev_features_t tmp[NETDEV_FEATURE_DWORDS];<br>>         struct ethnl_req_info req_info = {};<br>>         struct nlattr **tb = info->attrs;<br>>         struct net_device *dev;<br>> @@ -242,7 +255,11 @@ int ethnl_set_features(struct sk_buff *skb,<br>> struct genl_info *info)<br>>                                  netdev_features_strings, info->extack);<br>>         if (ret < 0)<br>>                 goto out_rtnl;<br>> -       if (ethnl_bitmap_to_features(req_mask) & ~NETIF_F_ETHTOOL_BITS) {<br>> +<br>> +       ethnl_bitmap_to_features(features, req_mask);<br>> +       netdev_features_ethtool_bits(tmp);<br>> +       netdev_features_andnot(features, features, tmp);<br>> +       if (!netdev_features_empty(features)) {<br>>                 GENL_SET_ERR_MSG(info, "attempt to change non-ethtool<br>> features");<br>>                 ret = -EINVAL;<br>>                 goto out_rtnl;<br>> @@ -253,8 +270,13 @@ int ethnl_set_features(struct sk_buff *skb,<br>> struct genl_info *info)<br>>         bitmap_andnot(new_wanted, old_wanted, req_mask, NETDEV_FEATURE_COUNT);<br>>         bitmap_or(req_wanted, new_wanted, req_wanted, NETDEV_FEATURE_COUNT);<br>>         if (!bitmap_equal(req_wanted, old_wanted, NETDEV_FEATURE_COUNT)) {<br>> -               dev->wanted_features &= ~dev->hw_features;<br>> -               dev->wanted_features |=<br>> ethnl_bitmap_to_features(req_wanted) & dev->hw_features;<br>> +               netdev_features_andnot(dev->wanted_features,<br>> +                                      dev->wanted_features,<br>> +                                      dev->hw_features);<br>> +               ethnl_bitmap_to_features(features, req_wanted);<br>> +               netdev_features_and(features, features, dev->hw_features);<br>> +               netdev_features_or(dev->wanted_features, dev->wanted_features,<br>> +                                  features);<br>>                 __netdev_update_features(dev);<br>>         }<br>>         ethnl_features_to_bitmap(new_active, dev->features);<br>> diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c<br>> index baa5d10..f213ec9 100644<br>> --- a/net/ethtool/ioctl.c<br>> +++ b/net/ethtool/ioctl.c<br>> @@ -67,12 +67,15 @@ static int ethtool_get_features(struct net_device<br>> *dev, void __user *useraddr)<br>>         int i;<br>><br>>         /* in case feature bits run out again */<br>> -       BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) ><br>> sizeof(netdev_features_t));<br>> +       BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) ><br>> sizeof(dev->features));<br>><br>>         for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {<br>> -               features[i].available = (u32)(dev->hw_features >> (32 * i));<br>> -               features[i].requested = (u32)(dev->wanted_features >> (32 * i));<br>> -               features[i].active = (u32)(dev->features >> (32 * i));<br>> +               features[i].available =<br>> +                       (u32)(dev->hw_features[i / 2] >> (i % 2 * 32));<br>> +               features[i].requested =<br>> +                       (u32)(dev->wanted_features[i / 2] >> (i % 2 * 32));<br>> +               features[i].active =<br>> +                       (u32)(dev->features[i / 2] >> (i % 2 * 32));<br>>                 features[i].never_changed =<br>>                         (u32)(NETIF_F_NEVER_CHANGE >> (32 * i));<br>>         }<br>> @@ -97,7 +100,9 @@ static int ethtool_set_features(struct net_device<br>> *dev, void __user *useraddr)<br>>  {<br>>         struct ethtool_sfeatures cmd;<br>>         struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];<br>> -       netdev_features_t wanted = 0, valid = 0;<br>> +       netdev_features_t wanted[NETDEV_FEATURE_DWORDS] = {0};<br>> +       netdev_features_t valid[NETDEV_FEATURE_DWORDS] = {0};<br>> +       netdev_features_t tmp[NETDEV_FEATURE_DWORDS];<br>>         int i, ret = 0;<br>><br>>         if (copy_from_user(&cmd, useraddr, sizeof(cmd)))<br>> @@ -111,23 +116,33 @@ static int ethtool_set_features(struct<br>> net_device *dev, void __user *useraddr)<br>>                 return -EFAULT;<br>><br>>         for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {<br>> -               valid |= (netdev_features_t)features[i].valid << (32 * i);<br>> -               wanted |= (netdev_features_t)features[i].requested << (32 * i);<br>> +               valid[i / 2] |=<br>> +                       (netdev_features_t)features[i].valid << (32 * i);<br>> +               wanted[i / 2] |=<br>> +                       (netdev_features_t)features[i].requested << (32 * i);<br>>         }<br>><br>> -       if (valid & ~NETIF_F_ETHTOOL_BITS)<br>> +       netdev_features_ethtool_bits(tmp);<br>> +       netdev_features_andnot(tmp, features, tmp);<br>> +       if (!netdev_features_empty(tmp))<br>>                 return -EINVAL;<br>><br>> -       if (valid & ~dev->hw_features) {<br>> -               valid &= dev->hw_features;<br>> +       netdev_features_andnot(tmp, valid, dev->hw_features);<br>> +<br>> +       if (!netdev_features_empty(tmp)) {<br>> +               netdev_features_and(valid, valid, dev->hw_features);<br>>                 ret |= ETHTOOL_F_UNSUPPORTED;<br>>         }<br>><br>> -       dev->wanted_features &= ~valid;<br>> -       dev->wanted_features |= wanted & valid;<br>> +       netdev_features_andnot(dev->wanted_features, dev->wanted_features,<br>> +                              valid);<br>> +       netdev_features_and(wanted, wanted, valid);<br>> +       netdev_features_or(dev->wanted_features, dev->wanted_features, wanted);<br>>         __netdev_update_features(dev);<br>><br>> -       if ((dev->wanted_features ^ dev->features) & valid)<br>> +       netdev_features_xor(tmp, dev->wanted_features, dev->features);<br>> +       netdev_features_and(tmp, tmp, valid);<br>> +       if (!netdev_features_empty(tmp))<br>>                 ret |= ETHTOOL_F_WISH;<br>><br>>         return ret;<br>> @@ -227,7 +242,7 @@ static int ethtool_get_one_feature(struct net_device *dev,<br>>         netdev_features_t mask = ethtool_get_feature_mask(ethcmd);<br>>         struct ethtool_value edata = {<br>>                 .cmd = ethcmd,<br>> -               .data = !!(dev->features & mask),<br>> +               .data = !!(dev->features[0] & mask),<br>>         };<br>><br>>         if (copy_to_user(useraddr, &edata, sizeof(edata)))<br>> @@ -238,21 +253,23 @@ static int ethtool_get_one_feature(struct net_device *dev,<br>>  static int ethtool_set_one_feature(struct net_device *dev,<br>>         void __user *useraddr, u32 ethcmd)<br>>  {<br>> +       netdev_features_t mask[NETDEV_FEATURE_DWORDS] = {0};<br>>         struct ethtool_value edata;<br>> -       netdev_features_t mask;<br>><br>>         if (copy_from_user(&edata, useraddr, sizeof(edata)))<br>>                 return -EFAULT;<br>><br>> -       mask = ethtool_get_feature_mask(ethcmd);<br>> -       mask &= dev->hw_features;<br>> -       if (!mask)<br>> +       mask[0] = ethtool_get_feature_mask(ethcmd);<br>> +       netdev_features_and(mask, mask, dev->hw_features);<br>> +       if (netdev_features_empty(mask))<br>>                 return -EOPNOTSUPP;<br>><br>>         if (edata.data)<br>> -               dev->wanted_features |= mask;<br>> +               netdev_features_or(dev->wanted_features, dev->wanted_features,<br>> +                                  mask)<br>>         else<br>> -               dev->wanted_features &= ~mask;<br>> +               netdev_features_andnot(dev->wanted_features,<br>> +                                      dev->wanted_features, mask);<br>><br>>         __netdev_update_features(dev);<br>><br>> @@ -285,29 +302,37 @@ static u32 __ethtool_get_flags(struct net_device *dev)<br>><br>>  static int __ethtool_set_flags(struct net_device *dev, u32 data)<br>>  {<br>> -       netdev_features_t features = 0, changed;<br>> +       netdev_features_t features[NETDEV_FEATURE_DWORDS] = {0};<br>> +       netdev_features_t changed[NETDEV_FEATURE_DWORDS];<br>> +       netdev_features_t tmp[NETDEV_FEATURE_DWORDS];<br>><br>>         if (data & ~ETH_ALL_FLAGS)<br>>                 return -EINVAL;<br>><br>>         if (data & ETH_FLAG_LRO)<br>> -               features |= NETIF_F_LRO;<br>> +               features[0] |= NETIF_F_LRO;<br>>         if (data & ETH_FLAG_RXVLAN)<br>> -               features |= NETIF_F_HW_VLAN_CTAG_RX;<br>> +               features[0] |= NETIF_F_HW_VLAN_CTAG_RX;<br>>         if (data & ETH_FLAG_TXVLAN)<br>> -               features |= NETIF_F_HW_VLAN_CTAG_TX;<br>> +               features[0] |= NETIF_F_HW_VLAN_CTAG_TX;<br>>         if (data & ETH_FLAG_NTUPLE)<br>> -               features |= NETIF_F_NTUPLE;<br>> +               features[0] |= NETIF_F_NTUPLE;<br>>         if (data & ETH_FLAG_RXHASH)<br>> -               features |= NETIF_F_RXHASH;<br>> +               features[0] |= NETIF_F_RXHASH;<br>><br>>         /* allow changing only bits set in hw_features */<br>> -       changed = (features ^ dev->features) & ETH_ALL_FEATURES;<br>> -       if (changed & ~dev->hw_features)<br>> -               return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;<br>> +       netdev_features_xor(changed, features, dev->features);<br>> +       changed[0] &= ETH_ALL_FEATURES;<br>> +<br>> +       netdev_features_andnot(tmp, changed, dev->hw_features);<br>> +       if (!netdev_features_empty(tmp)) {<br>> +               netdev_features_and(tmp, changed, dev->hw_features);<br>> +               return (!netdev_features_empty(tmp)) ? -EINVAL : -EOPNOTSUPP;<br>> +       }<br>><br>> -       dev->wanted_features =<br>> -               (dev->wanted_features & ~changed) | (features & changed);<br>> +       netdev_features_andnot(tmp, dev->wanted_features, changed);<br>> +       netdev_features_and(features, features, changed);<br>> +       netdev_features_or(dev->wanted_features, tmp, features);<br>><br>>         __netdev_update_features(dev);<br>><br>> @@ -2587,7 +2612,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)<br>>         void __user *useraddr = ifr->ifr_data;<br>>         u32 ethcmd, sub_cmd;<br>>         int rc;<br>> -       netdev_features_t old_features;<br>> +       netdev_features_t old_features[NETDEV_FEATURE_DWORDS];<br>><br>>         if (!dev || !netif_device_present(dev))<br>>                 return -ENODEV;<br>> @@ -2650,7 +2675,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)<br>>                 if (rc  < 0)<br>>                         return rc;<br>>         }<br>> -       old_features = dev->features;<br>> +       netdev_features_copy(old_features, dev->features);<br>><br>>         switch (ethcmd) {<br>>         case ETHTOOL_GSET:<br>> @@ -2865,7 +2890,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)<br>>         if (dev->ethtool_ops->complete)<br>>                 dev->ethtool_ops->complete(dev);<br>><br>> -       if (old_features != dev->features)<br>> +       if (!netdev_features_equal(old_features, dev->features))<br>>                 netdev_features_change(dev);<br>><br>>         return rc;<br>> --<br>> 2.8.1<br>><br>><br>><br>> --<br>> Latest Podcast:<br>> <a href="https://www.linkedin.com/feed/update/urn:li:activity:6791014284936785920/" target="_blank">https://www.linkedin.com/feed/update/urn:li:activity:6791014284936785920/</a><br>><br>> Dave Täht CTO, TekLibre, LLC<br>> _______________________________________________<br>> Cake mailing list<br>> <a href="mailto:Cake@lists.bufferbloat.net" target="_blank">Cake@lists.bufferbloat.net</a><br>> <a href="https://lists.bufferbloat.net/listinfo/cake" target="_blank">https://lists.bufferbloat.net/listinfo/cake</a><br><br><br><br>--<br>Robert Chacón<br><a href="mailto:robert.chacon@jackrabbitwireless.com" target="_blank">robert.chacon@jackrabbitwireless.com</a></div>