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

Dave Taht dave.taht at gmail.com
Sat Jul 10 11:33:20 EDT 2021


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


More information about the Cake mailing list