[Cake] Fwd: [RFC net-next] net: extend netdev features

Robert Chacón robert.chacon at jackrabbitwireless.com
Sat Jul 10 13:00:37 EDT 2021


I have a question regarding this, and the current maximum number of htb
leaf classes and/or qdiscs per interface.
I recently integrated Jesper's xdp-cpumap-tc
<https://github.com/xdp-project/xdp-cpumap-tc> code into LibreQoS
<https://github.com/rchac/LibreQoS>, which increased throughput to 10Gbps
on tests.
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.
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.

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?
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.
There may be some more immediate limitations I'm not understanding. Just
curious about the practical limitations there.

Thanks!
Robert

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



--
Robert Chacón
robert.chacon at jackrabbitwireless.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.bufferbloat.net/pipermail/cake/attachments/20210710/3bda916c/attachment-0001.html>


More information about the Cake mailing list