diff mbox series

[RFCv8,net-next,55/55] net: redefine the prototype of netdev_features_t

Message ID 20220918094336.28958-56-shenjian15@huawei.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series net: extend the type of netdev_features_t to bitmap | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count fail Series longer than 15 patches (and no cover letter)
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 5397 this patch: 5399
netdev/cc_maintainers warning 33 maintainers not CCed: jesse.brandeburg@intel.com senozhatsky@chromium.org linux@rasmusvillemoes.dk saeedb@amazon.com tariqt@nvidia.com oss-drivers@corigine.com drivers@pensando.io thomas.lendacky@amd.com yoshfuji@linux-ipv6.org akiyano@amazon.com rmody@marvell.com pabeni@redhat.com Shyam-sundar.S-k@amd.com ndagan@amazon.com pmladek@suse.com dave@thedillows.org edumazet@google.com snelson@pensando.io skalluru@marvell.com dsahern@kernel.org simon.horman@corigine.com gal@nvidia.com shayagr@amazon.com rostedt@goodmis.org imagedong@tencent.com andriy.shevchenko@linux.intel.com intel-wired-lan@lists.osuosl.org chris.snook@gmail.com Jose.Abreu@synopsys.com niklas.soderlund@corigine.com darinzon@amazon.com anthony.l.nguyen@intel.com GR-Linux-NIC-Dev@marvell.com
netdev/build_clang fail Errors and warnings before: 14992 this patch: 1063
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 4918 this patch: 4703
netdev/checkpatch warning WARNING: do not add new typedefs WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

shenjian (K) Sept. 18, 2022, 9:43 a.m. UTC
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. Change the prototype of netdev_features_t
from u64 to structure below:
	typedef struct {
		DECLARE_BITMAP(bits, NETDEV_FEATURE_COUNT);
	} netdev_features_t;

Rewrite the netdev_features helpers to adapt with new prototype.

To avoid mistake using NETIF_F_XXX as NETIF_F_XXX_BIT as
input macroes for above helpers, remove all the macroes
of NETIF_F_XXX for single feature bit.

With the prototype is no longer u64, the implementation of print
interface for netdev features(%pNF) is changed to bitmap. So
does the implementation of net/ethtool/.

Signed-off-by: Jian Shen <shenjian15@huawei.com>
---
 drivers/net/ethernet/3com/typhoon.c           |   7 -
 drivers/net/ethernet/amazon/ena/ena_netdev.c  |  12 +-
 drivers/net/ethernet/amd/xgbe/xgbe-drv.c      |   2 +-
 drivers/net/ethernet/atheros/atlx/atl2.c      |   4 +-
 drivers/net/ethernet/brocade/bna/bnad.c       |   2 -
 .../net/ethernet/intel/i40e/i40e_debugfs.c    |  12 +-
 .../net/ethernet/intel/ixgbe/ixgbe_sriov.c    |   2 -
 .../ethernet/netronome/nfp/nfp_net_common.c   |   4 +-
 .../net/ethernet/pensando/ionic/ionic_lif.c   |   4 +-
 .../net/ethernet/synopsys/dwc-xlgmac-net.c    |   2 +-
 include/linux/netdev_feature_helpers.h        |  37 +++---
 include/linux/netdev_features.h               |  91 +------------
 include/linux/netdevice.h                     |   6 +-
 include/linux/skbuff.h                        |   8 +-
 include/net/ip_tunnels.h                      |   3 +-
 lib/vsprintf.c                                |   9 +-
 net/ethtool/features.c                        | 123 ++++++++----------
 net/ethtool/ioctl.c                           |  33 +++--
 18 files changed, 136 insertions(+), 225 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
index d42a53748f40..4f9bc14ed44b 100644
--- a/drivers/net/ethernet/3com/typhoon.c
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -315,16 +315,9 @@  enum state_values {
 #define TYPHOON_RESET_TIMEOUT_NOSLEEP	((6 * 1000000) / TYPHOON_UDELAY)
 #define TYPHOON_WAIT_TIMEOUT		((1000000 / 2) / TYPHOON_UDELAY)
 
-#if defined(NETIF_F_TSO)
 #define skb_tso_size(x)		(skb_shinfo(x)->gso_size)
 #define TSO_NUM_DESCRIPTORS	2
 #define TSO_OFFLOAD_ON		TYPHOON_OFFLOAD_TCP_SEGMENT
-#else
-#define NETIF_F_TSO 		0
-#define skb_tso_size(x) 	0
-#define TSO_NUM_DESCRIPTORS	0
-#define TSO_OFFLOAD_ON		0
-#endif
 
 static inline void
 typhoon_inc_index(u32 *index, const int count, const int num_entries)
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index 47beaab0d963..487409678cfb 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -3936,10 +3936,14 @@  static void ena_update_hints(struct ena_adapter *adapter,
 static void ena_update_host_info(struct ena_admin_host_info *host_info,
 				 struct net_device *netdev)
 {
-	host_info->supported_network_features[0] =
-		netdev->features & GENMASK_ULL(31, 0);
-	host_info->supported_network_features[1] =
-		(netdev->features & GENMASK_ULL(63, 32)) >> 32;
+#define DEV_FEATURE_WORDS	DIV_ROUND_UP(NETDEV_FEATURE_COUNT, 32)
+
+	u32 features[DEV_FEATURE_WORDS] = {0};
+
+	bitmap_to_arr32(features, netdev->features.bits, DEV_FEATURE_WORDS);
+
+	host_info->supported_network_features[0] = features[0];
+	host_info->supported_network_features[1] = features[1];
 }
 
 static void ena_timer_service(struct timer_list *t)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 3709f8674724..17647ebbfdf0 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2227,7 +2227,7 @@  static int xgbe_set_features(struct net_device *netdev,
 {
 	struct xgbe_prv_data *pdata = netdev_priv(netdev);
 	struct xgbe_hw_if *hw_if = &pdata->hw_if;
-	netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter;
+	bool rxhash, rxcsum, rxvlan, rxvlan_filter;
 	int ret = 0;
 
 	rxhash = netdev_feature_test(NETIF_F_RXHASH_BIT, pdata->netdev_features);
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 2bc574a1cc25..11d82ae06d14 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -866,7 +866,7 @@  static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
 			skb->len-copy_len);
 		offset = ((u32)(skb->len-copy_len + 3) & ~3);
 	}
-#ifdef NETIF_F_HW_VLAN_CTAG_TX
+
 	if (skb_vlan_tag_present(skb)) {
 		u16 vlan_tag = skb_vlan_tag_get(skb);
 		vlan_tag = (vlan_tag << 4) |
@@ -875,7 +875,7 @@  static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb,
 		txph->ins_vlan = 1;
 		txph->vlan = vlan_tag;
 	}
-#endif
+
 	if (offset >= adapter->txd_ring_size)
 		offset -= adapter->txd_ring_size;
 	adapter->txd_write_ptr = offset;
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index cef5f7704d69..418c52c976f0 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -2857,12 +2857,10 @@  bnad_txq_wi_prepare(struct bnad *bnad, struct bna_tcb *tcb,
 
 			if (net_proto == htons(ETH_P_IP))
 				proto = ip_hdr(skb)->protocol;
-#ifdef NETIF_F_IPV6_CSUM
 			else if (net_proto == htons(ETH_P_IPV6)) {
 				/* nexthdr may not be TCP immediately. */
 				proto = ipv6_hdr(skb)->nexthdr;
 			}
-#endif
 			if (proto == IPPROTO_TCP) {
 				flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
 				txqent->hdr.wi.l4_hdr_size_n_offset =
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index c9dcd6d92c83..046882ab5593 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -131,12 +131,12 @@  static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
 
 		dev_info(&pf->pdev->dev, "    netdev: name = %s, state = %lu, flags = 0x%08x\n",
 			 nd->name, nd->state, nd->flags);
-		dev_info(&pf->pdev->dev, "        features      = 0x%08lx\n",
-			 (unsigned long int)nd->features);
-		dev_info(&pf->pdev->dev, "        hw_features   = 0x%08lx\n",
-			 (unsigned long int)nd->hw_features);
-		dev_info(&pf->pdev->dev, "        vlan_features = 0x%08lx\n",
-			 (unsigned long int)nd->vlan_features);
+		dev_info(&pf->pdev->dev, "        features      = %pNF\n",
+			 &nd->features);
+		dev_info(&pf->pdev->dev, "        hw_features   = %pNF\n",
+			 &nd->hw_features);
+		dev_info(&pf->pdev->dev, "        vlan_features = %pNF\n",
+			 &nd->vlan_features);
 	}
 	dev_info(&pf->pdev->dev,
 		 "    flags = 0x%08lx, netdev_registered = %i, current_netdev_flags = 0x%04x\n",
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index ae9e2259fe10..83a11924da60 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -12,9 +12,7 @@ 
 #include <linux/tcp.h>
 #include <linux/ipv6.h>
 #include <linux/if_bridge.h>
-#ifdef NETIF_F_HW_VLAN_CTAG_TX
 #include <linux/if_vlan.h>
-#endif
 
 #include "ixgbe.h"
 #include "ixgbe_type.h"
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index fad9d4337eb5..f10f3c0362e3 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -1739,8 +1739,8 @@  static int nfp_net_set_features(struct net_device *netdev,
 	if (err)
 		return err;
 
-	nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n",
-	       netdev->features, *features, changed);
+	nn_dbg(nn, "Feature change %pNF -> %pNF (changed=%pNF)\n",
+	       &netdev->features, features, &changed);
 
 	if (new_ctrl == nn->dp.ctrl)
 		return 0;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 79f5d936a49f..caa6a7200790 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -1562,8 +1562,8 @@  static int ionic_set_features(struct net_device *netdev,
 	struct ionic_lif *lif = netdev_priv(netdev);
 	int err;
 
-	netdev_dbg(netdev, "%s: lif->features=0x%08llx new_features=0x%08llx\n",
-		   __func__, (u64)lif->netdev->features, (u64)*features);
+	netdev_dbg(netdev, "%s: lif->features=%pNF new_features=%pNF\n",
+		   __func__, &lif->netdev->features, features);
 
 	err = ionic_set_nic_features(lif, features);
 
diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
index 9f283d13e4f7..ff9d21aea3aa 100644
--- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
+++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c
@@ -879,9 +879,9 @@  static void xlgmac_poll_controller(struct net_device *netdev)
 static int xlgmac_set_features(struct net_device *netdev,
 			       const netdev_features_t *features)
 {
-	netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter;
 	struct xlgmac_pdata *pdata = netdev_priv(netdev);
 	struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
+	bool rxhash, rxcsum, rxvlan, rxvlan_filter;
 	int ret = 0;
 
 	rxhash = netdev_feature_test(NETIF_F_RXHASH_BIT, pdata->netdev_features);
diff --git a/include/linux/netdev_feature_helpers.h b/include/linux/netdev_feature_helpers.h
index a15bd3a3b574..4e0a84dd8017 100644
--- a/include/linux/netdev_feature_helpers.h
+++ b/include/linux/netdev_feature_helpers.h
@@ -9,7 +9,7 @@ 
 
 static inline void __netdev_features_zero(netdev_features_t *dst)
 {
-	*dst = 0;
+	bitmap_zero(dst->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_zero(features) __netdev_features_zero(&(features))
@@ -38,14 +38,14 @@  static inline void __netdev_features_zero(netdev_features_t *dst)
 
 static inline void __netdev_features_fill(netdev_features_t *dst)
 {
-	*dst = ~0ULL;
+	bitmap_fill(dst->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_fill(features) __netdev_features_fill(&(features))
 
 static inline bool __netdev_features_empty(const netdev_features_t *src)
 {
-	return *src == 0;
+	return bitmap_empty(src->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_empty(features) __netdev_features_empty(&(features))
@@ -75,7 +75,7 @@  static inline bool __netdev_features_empty(const netdev_features_t *src)
 static inline bool __netdev_features_equal(const netdev_features_t *feats1,
 					   const netdev_features_t *feats2)
 {
-	return *feats1 == *feats2;
+	return bitmap_equal(feats1->bits, feats2->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_equal(feat1, feat2)	\
@@ -107,8 +107,7 @@  static inline bool
 __netdev_features_and(netdev_features_t *dst, const netdev_features_t *a,
 		      const netdev_features_t *b)
 {
-	*dst = *a & *b;
-	return *dst != 0;
+	return bitmap_and(dst->bits, a->bits, b->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_and(dst, a, b) __netdev_features_and(&(dst), &(a), &(b))
@@ -171,7 +170,7 @@  static inline void
 __netdev_features_or(netdev_features_t *dst, const netdev_features_t *a,
 		     const netdev_features_t *b)
 {
-	*dst = *a | *b;
+	bitmap_or(dst->bits, a->bits, b->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_or(dst, a, b) __netdev_features_or(&(dst), &(a), &(b))
@@ -230,10 +229,7 @@  __netdev_features_set(netdev_features_t *dst, const netdev_features_t *features)
 
 static inline void __netdev_feature_change(int nr, netdev_features_t *src)
 {
-	if (*src & __NETIF_F_BIT(nr))
-		*src &= ~(__NETIF_F_BIT(nr));
-	else
-		*src |= __NETIF_F_BIT(nr);
+	__change_bit(nr, src->bits);
 }
 
 #define netdev_feature_change(nr, src)	\
@@ -265,7 +261,7 @@  static inline void
 __netdev_features_xor(netdev_features_t *dst, const netdev_features_t *a,
 		      const netdev_features_t *b)
 {
-	*dst = *a ^ *b;
+	bitmap_xor(dst->bits, a->bits, b->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_xor(dst, a, b) __netdev_features_xor(&(dst), &(a), &(b))
@@ -328,8 +324,7 @@  static inline bool
 __netdev_features_andnot(netdev_features_t *dst, const netdev_features_t *a,
 			 const netdev_features_t *b)
 {
-	*dst = *a & ~(*b);
-	return *dst == 0;
+	return bitmap_andnot(dst->bits, a->bits, b->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_andnot(dst, a, b)	\
@@ -360,7 +355,8 @@  static inline void
 __netdev_features_clear(netdev_features_t *dst,
 			const netdev_features_t *features)
 {
-	__netdev_features_andnot(dst, dst, features);
+	bitmap_andnot(dst->bits, dst->bits, features->bits,
+		      NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_clear(dst, __features)	\
@@ -390,7 +386,7 @@  __netdev_features_clear(netdev_features_t *dst,
 /* helpers for netdev features 'set bit' operation */
 static inline void __netdev_feature_add(int nr, netdev_features_t *src)
 {
-	*src |= __NETIF_F_BIT(nr);
+	__set_bit(nr, src->bits);
 }
 
 #define netdev_feature_add(nr, src) __netdev_feature_add(nr, &(src))
@@ -454,7 +450,7 @@  __netdev_features_set_array(const struct netdev_feature_set *set,
 /* helpers for netdev features 'clear bit' operation */
 static inline void __netdev_feature_del(int nr, netdev_features_t *src)
 {
-	*src &= ~__NETIF_F_BIT(nr);
+	__clear_bit(nr, src->bits);
 }
 
 #define netdev_feature_del(nr, src) __netdev_feature_del(nr, &(src))
@@ -518,7 +514,8 @@  __netdev_features_clear_array(const struct netdev_feature_set *set,
 static inline bool __netdev_features_intersects(const netdev_features_t *feats1,
 						const netdev_features_t *feats2)
 {
-	return (*feats1 & *feats2) > 0;
+	return bitmap_intersects(feats1->bits, feats2->bits,
+				 NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_intersects(feats1, feats2)	\
@@ -549,7 +546,7 @@  static inline bool __netdev_features_intersects(const netdev_features_t *feats1,
 static inline void __netdev_features_copy(netdev_features_t *dst,
 					  const netdev_features_t *src)
 {
-	*dst = *src;
+	bitmap_copy(dst->bits, src->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_copy(dst, src)	__netdev_features_copy(&(dst), &(src))
@@ -579,7 +576,7 @@  static inline void __netdev_features_copy(netdev_features_t *dst,
 static inline bool __netdev_features_subset(const netdev_features_t *feats1,
 					    const netdev_features_t *feats2)
 {
-	return (*feats1 & *feats2) == *feats2;
+	return bitmap_subset(feats1->bits, feats2->bits, NETDEV_FEATURE_COUNT);
 }
 
 #define netdev_features_subset(feats1, feats2)	\
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index 664055209b2e..d41e74748927 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -10,8 +10,6 @@ 
 #include <linux/cache.h>
 #include <asm/byteorder.h>
 
-typedef u64 netdev_features_t;
-
 struct netdev_feature_set {
 	unsigned int cnt;
 	unsigned short feature_bits[];
@@ -113,6 +111,10 @@  enum {
 	/**/NETDEV_FEATURE_COUNT
 };
 
+typedef struct {
+	DECLARE_BITMAP(bits, NETDEV_FEATURE_COUNT);
+} netdev_features_t;
+
 extern netdev_features_t netdev_ethtool_features __ro_after_init;
 extern netdev_features_t netdev_never_change_features __ro_after_init;
 extern netdev_features_t netdev_gso_features_mask __ro_after_init;
@@ -144,94 +146,11 @@  extern netdev_features_t netdev_tls_features __ro_after_init;
 extern netdev_features_t netdev_csum_gso_features_mask __ro_after_init;
 extern netdev_features_t netdev_empty_features __ro_after_init;
 
-/* copy'n'paste compression ;) */
-#define __NETIF_F_BIT(bit)	((netdev_features_t)1 << (bit))
-#define __NETIF_F(name)		__NETIF_F_BIT(NETIF_F_##name##_BIT)
-
-#define NETIF_F_FCOE_CRC	__NETIF_F(FCOE_CRC)
-#define NETIF_F_FCOE_MTU	__NETIF_F(FCOE_MTU)
-#define NETIF_F_FRAGLIST	__NETIF_F(FRAGLIST)
-#define NETIF_F_FSO		__NETIF_F(FSO)
-#define NETIF_F_GRO		__NETIF_F(GRO)
-#define NETIF_F_GRO_HW		__NETIF_F(GRO_HW)
-#define NETIF_F_GSO		__NETIF_F(GSO)
-#define NETIF_F_GSO_ROBUST	__NETIF_F(GSO_ROBUST)
-#define NETIF_F_HIGHDMA		__NETIF_F(HIGHDMA)
-#define NETIF_F_HW_CSUM		__NETIF_F(HW_CSUM)
-#define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER)
-#define NETIF_F_HW_VLAN_CTAG_RX	__NETIF_F(HW_VLAN_CTAG_RX)
-#define NETIF_F_HW_VLAN_CTAG_TX	__NETIF_F(HW_VLAN_CTAG_TX)
-#define NETIF_F_IP_CSUM		__NETIF_F(IP_CSUM)
-#define NETIF_F_IPV6_CSUM	__NETIF_F(IPV6_CSUM)
-#define NETIF_F_LLTX		__NETIF_F(LLTX)
-#define NETIF_F_LOOPBACK	__NETIF_F(LOOPBACK)
-#define NETIF_F_LRO		__NETIF_F(LRO)
-#define NETIF_F_NETNS_LOCAL	__NETIF_F(NETNS_LOCAL)
-#define NETIF_F_NOCACHE_COPY	__NETIF_F(NOCACHE_COPY)
-#define NETIF_F_NTUPLE		__NETIF_F(NTUPLE)
-#define NETIF_F_RXCSUM		__NETIF_F(RXCSUM)
-#define NETIF_F_RXHASH		__NETIF_F(RXHASH)
-#define NETIF_F_SCTP_CRC	__NETIF_F(SCTP_CRC)
-#define NETIF_F_SG		__NETIF_F(SG)
-#define NETIF_F_TSO6		__NETIF_F(TSO6)
-#define NETIF_F_TSO_ECN		__NETIF_F(TSO_ECN)
-#define NETIF_F_TSO		__NETIF_F(TSO)
-#define NETIF_F_VLAN_CHALLENGED	__NETIF_F(VLAN_CHALLENGED)
-#define NETIF_F_RXFCS		__NETIF_F(RXFCS)
-#define NETIF_F_RXALL		__NETIF_F(RXALL)
-#define NETIF_F_GSO_GRE		__NETIF_F(GSO_GRE)
-#define NETIF_F_GSO_GRE_CSUM	__NETIF_F(GSO_GRE_CSUM)
-#define NETIF_F_GSO_IPXIP4	__NETIF_F(GSO_IPXIP4)
-#define NETIF_F_GSO_IPXIP6	__NETIF_F(GSO_IPXIP6)
-#define NETIF_F_GSO_UDP_TUNNEL	__NETIF_F(GSO_UDP_TUNNEL)
-#define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM)
-#define NETIF_F_TSO_MANGLEID	__NETIF_F(TSO_MANGLEID)
-#define NETIF_F_GSO_PARTIAL	 __NETIF_F(GSO_PARTIAL)
-#define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM)
-#define NETIF_F_GSO_SCTP	__NETIF_F(GSO_SCTP)
-#define NETIF_F_GSO_ESP		__NETIF_F(GSO_ESP)
-#define NETIF_F_GSO_UDP		__NETIF_F(GSO_UDP)
-#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
-#define NETIF_F_HW_VLAN_STAG_RX	__NETIF_F(HW_VLAN_STAG_RX)
-#define NETIF_F_HW_VLAN_STAG_TX	__NETIF_F(HW_VLAN_STAG_TX)
-#define NETIF_F_HW_L2FW_DOFFLOAD	__NETIF_F(HW_L2FW_DOFFLOAD)
-#define NETIF_F_HW_TC		__NETIF_F(HW_TC)
-#define NETIF_F_HW_ESP		__NETIF_F(HW_ESP)
-#define NETIF_F_HW_ESP_TX_CSUM	__NETIF_F(HW_ESP_TX_CSUM)
-#define	NETIF_F_RX_UDP_TUNNEL_PORT  __NETIF_F(RX_UDP_TUNNEL_PORT)
-#define NETIF_F_HW_TLS_RECORD	__NETIF_F(HW_TLS_RECORD)
-#define NETIF_F_GSO_UDP_L4	__NETIF_F(GSO_UDP_L4)
-#define NETIF_F_HW_TLS_TX	__NETIF_F(HW_TLS_TX)
-#define NETIF_F_HW_TLS_RX	__NETIF_F(HW_TLS_RX)
-#define NETIF_F_GRO_FRAGLIST	__NETIF_F(GRO_FRAGLIST)
-#define NETIF_F_GSO_FRAGLIST	__NETIF_F(GSO_FRAGLIST)
-#define NETIF_F_HW_MACSEC	__NETIF_F(HW_MACSEC)
-#define NETIF_F_GRO_UDP_FWD	__NETIF_F(GRO_UDP_FWD)
-#define NETIF_F_HW_HSR_TAG_INS	__NETIF_F(HW_HSR_TAG_INS)
-#define NETIF_F_HW_HSR_TAG_RM	__NETIF_F(HW_HSR_TAG_RM)
-#define NETIF_F_HW_HSR_FWD	__NETIF_F(HW_HSR_FWD)
-#define NETIF_F_HW_HSR_DUP	__NETIF_F(HW_HSR_DUP)
-
-/* Finds the next feature with the highest number of the range of start-1 till 0.
- */
-static inline int find_next_netdev_feature(u64 feature, unsigned long start)
-{
-	/* like BITMAP_LAST_WORD_MASK() for u64
-	 * this sets the most significant 64 - start to 0.
-	 */
-	feature &= ~0ULL >> (-start & ((sizeof(feature) * 8) - 1));
-
-	return fls64(feature) - 1;
-}
-
 /* This goes for the MSB to the LSB through the set feature bits,
  * 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);	\
-	     (bit) >= 0;						\
-	     (bit) = find_next_netdev_feature((mask_addr), (bit)))
+	for_each_set_bit(bit, (unsigned long *)(mask_addr.bits), NETDEV_FEATURE_COUNT)
 
 /* Features valid for ethtool to change */
 /* = all defined minus driver/device-class-related */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 4620af4591fc..33486b2aa9f1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2348,7 +2348,7 @@  struct net_device {
 /* helpers for netdev features 'test bit' operation */
 static inline bool __netdev_feature_test(int nr, const netdev_features_t *src)
 {
-	return (*src & __NETIF_F_BIT(nr)) > 0;
+	return test_bit(nr, src->bits);
 }
 
 #define netdev_feature_test(nr, __features)	\
@@ -4947,12 +4947,12 @@  static inline bool net_gso_ok(const netdev_features_t *features, int gso_type)
 	ASSERT_GSO_TYPE(SKB_GSO_FRAGLIST_BIT, NETIF_F_GSO_FRAGLIST_BIT);
 
 	if (classic_gso_type)
-		feature = (netdev_features_t)gso_type << NETIF_F_GSO_SHIFT;
+		bitmap_from_u64(feature.bits, (u64)gso_type << NETIF_F_GSO_SHIFT);
 
 	if (new_gso_type) { /* placeholder for new gso type */
 	}
 
-	return (*features & feature) == feature;
+	return bitmap_subset(features->bits, feature.bits, NETDEV_FEATURE_COUNT);
 }
 
 static inline bool skb_gso_ok(struct sk_buff *skb,
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 122190c334cb..33df942ed6da 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -693,6 +693,10 @@  enum {
 	SKB_GSO_FRAGLIST = __SKB_GSO_FLAG(FRAGLIST),
 };
 
+#define SKB_GSO_ENCAP_ALL	\
+		(SKB_GSO_GRE_CSUM | SKB_GSO_GRE_CSUM | SKB_GSO_IPXIP4 |	\
+		 SKB_GSO_IPXIP6 | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)
+
 #if BITS_PER_LONG > 32
 #define NET_SKBUFF_DATA_USES_OFFSET 1
 #endif
@@ -4038,8 +4042,8 @@  static inline bool skb_needs_linearize(struct sk_buff *skb,
 				       const netdev_features_t *features)
 {
 	return skb_is_nonlinear(skb) &&
-	       ((skb_has_frag_list(skb) && !(*features & NETIF_F_FRAGLIST)) ||
-		(skb_shinfo(skb)->nr_frags && !(*features & NETIF_F_SG)));
+	       ((skb_has_frag_list(skb) && !test_bit(NETIF_F_FRAGLIST_BIT, features->bits)) ||
+		(skb_shinfo(skb)->nr_frags && !test_bit(NETIF_F_SG_BIT, features->bits)));
 }
 
 static inline void skb_copy_from_linear_data(const struct sk_buff *skb,
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index ced80e2f8b58..d3f028d31e0b 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -449,8 +449,7 @@  static inline int iptunnel_pull_offloads(struct sk_buff *skb)
 		err = skb_unclone(skb, GFP_ATOMIC);
 		if (unlikely(err))
 			return err;
-		skb_shinfo(skb)->gso_type &= ~(NETIF_F_GSO_ENCAP_ALL >>
-					       NETIF_F_GSO_SHIFT);
+		skb_shinfo(skb)->gso_type &= ~SKB_GSO_ENCAP_ALL;
 	}
 
 	skb->encapsulation = 0;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c1853a9d1c0..fa1cb5ff6de2 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1732,22 +1732,21 @@  static noinline_for_stack
 char *netdev_bits(char *buf, char *end, const void *addr,
 		  struct printf_spec spec,  const char *fmt)
 {
-	unsigned long long num;
-	int size;
+	const netdev_features_t *features;
 
 	if (check_pointer(&buf, end, addr, spec))
 		return buf;
 
 	switch (fmt[1]) {
 	case 'F':
-		num = *(const netdev_features_t *)addr;
-		size = sizeof(netdev_features_t);
+		features = (netdev_features_t *)addr;
+		spec.field_width = NETDEV_FEATURE_COUNT;
 		break;
 	default:
 		return error_string(buf, end, "(%pN?)", spec);
 	}
 
-	return special_hex_number(buf, end, num, size);
+	return bitmap_string(buf, end, features->bits, spec, fmt);
 }
 
 static noinline_for_stack
diff --git a/net/ethtool/features.c b/net/ethtool/features.c
index 496dfd45faac..2607a8b46cee 100644
--- a/net/ethtool/features.c
+++ b/net/ethtool/features.c
@@ -25,12 +25,9 @@  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, const netdev_features_t *src)
 {
-	unsigned int i;
-
-	for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)
-		dest[i] = src >> (32 * i);
+	bitmap_to_arr32(dest, src->bits, NETDEV_FEATURE_COUNT);
 }
 
 static int features_prepare_data(const struct ethnl_req_info *req_base,
@@ -41,12 +38,12 @@  static int features_prepare_data(const struct ethnl_req_info *req_base,
 	struct net_device *dev = reply_base->dev;
 	netdev_features_t all_features;
 
-	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);
+	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);
+	netdev_features_fill(all_features);
+	ethnl_features_to_bitmap32(data->all, &all_features);
 
 	return 0;
 }
@@ -131,28 +128,6 @@  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)
-{
-	const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);
-	unsigned int i;
-
-	for (i = 0; i < words; i++)
-		dest[i] = (unsigned long)(val >> (i * BITS_PER_LONG));
-}
-
-static netdev_features_t ethnl_bitmap_to_features(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 < words; i++)
-		ret |= (netdev_features_t)(src[i]) << (i * BITS_PER_LONG);
-	ret &= ~(netdev_features_t)0 >> (nft_bits - NETDEV_FEATURE_COUNT);
-	return ret;
-}
-
 static int features_send_reply(struct net_device *dev, struct genl_info *info,
 			       const unsigned long *wanted,
 			       const unsigned long *wanted_mask,
@@ -207,18 +182,23 @@  static int features_send_reply(struct net_device *dev, struct genl_info *info,
 	return ret;
 }
 
+enum {
+	OLD_ACTIVE = 0,
+	OLD_WANTED = 1,
+	NEW_ACTIVE = 2,
+	NEW_WANTED = 3,
+	REQ_WANTED = 4,
+	REQ_MASK = 5,
+	ACTIVE_DIFF_MASK = 6,
+	WANTED_DIFF_MASK = 7,
+	FEATURES_NUM = 8
+};
+
 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);
-	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);
 	struct ethnl_req_info req_info = {};
 	struct nlattr **tb = info->attrs;
+	netdev_features_t *features;
 	struct net_device *dev;
 	netdev_features_t tmp;
 	bool mod;
@@ -234,52 +214,60 @@  int ethnl_set_features(struct sk_buff *skb, struct genl_info *info)
 		return ret;
 	dev = req_info.dev;
 
+	features = kzalloc(sizeof(netdev_features_t) * FEATURES_NUM, GFP_KERNEL);
+	if (!features)
+		return -ENOMEM;
+
 	rtnl_lock();
-	ethnl_features_to_bitmap(old_active, dev->features);
-	ethnl_features_to_bitmap(old_wanted, dev->wanted_features);
-	ret = ethnl_parse_bitset(req_wanted, req_mask, NETDEV_FEATURE_COUNT,
+	netdev_features_copy(features[OLD_ACTIVE], dev->features);
+	netdev_features_copy(features[OLD_WANTED], dev->wanted_features);
+	ret = ethnl_parse_bitset(features[REQ_WANTED].bits, features[REQ_MASK].bits,
+				 NETDEV_FEATURE_COUNT,
 				 tb[ETHTOOL_A_FEATURES_WANTED],
 				 netdev_features_strings, info->extack);
 	if (ret < 0)
 		goto out_rtnl;
-	if (ethnl_bitmap_to_features(req_mask) & ~NETIF_F_ETHTOOL_BITS) {
+	if (!netdev_features_subset(features[REQ_MASK], NETIF_F_ETHTOOL_BITS)) {
 		GENL_SET_ERR_MSG(info, "attempt to change non-ethtool features");
 		ret = -EINVAL;
 		goto out_rtnl;
 	}
 
 	/* set req_wanted bits not in req_mask from old_wanted */
-	bitmap_and(req_wanted, req_wanted, req_mask, NETDEV_FEATURE_COUNT);
-	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)) {
+	netdev_features_mask(features[REQ_WANTED], features[REQ_MASK]);
+	netdev_features_andnot(features[NEW_WANTED], features[OLD_WANTED],
+			       features[REQ_MASK]);
+	netdev_features_set(features[REQ_WANTED], features[NEW_WANTED]);
+	if (!netdev_features_equal(features[REQ_WANTED], features[OLD_WANTED])) {
 		netdev_wanted_features_clear(dev, dev->hw_features);
-		tmp = ethnl_bitmap_to_features(req_wanted);
-		netdev_features_mask(tmp, dev->hw_features);
+		netdev_features_and(tmp, dev->hw_features, features[REQ_WANTED]);
 		netdev_wanted_features_set(dev, tmp);
 		__netdev_update_features(dev);
 	}
-	ethnl_features_to_bitmap(new_active, dev->features);
-	mod = !bitmap_equal(old_active, new_active, NETDEV_FEATURE_COUNT);
+	netdev_features_copy(features[NEW_ACTIVE], dev->features);
+	mod = !netdev_features_equal(features[OLD_ACTIVE], features[NEW_ACTIVE]);
 
 	ret = 0;
 	if (!(req_info.flags & ETHTOOL_FLAG_OMIT_REPLY)) {
 		bool compact = req_info.flags & ETHTOOL_FLAG_COMPACT_BITSETS;
 
-		bitmap_xor(wanted_diff_mask, req_wanted, new_active,
-			   NETDEV_FEATURE_COUNT);
-		bitmap_xor(active_diff_mask, old_active, new_active,
-			   NETDEV_FEATURE_COUNT);
-		bitmap_and(wanted_diff_mask, wanted_diff_mask, req_mask,
-			   NETDEV_FEATURE_COUNT);
-		bitmap_and(req_wanted, req_wanted, wanted_diff_mask,
-			   NETDEV_FEATURE_COUNT);
-		bitmap_and(new_active, new_active, active_diff_mask,
-			   NETDEV_FEATURE_COUNT);
-
-		ret = features_send_reply(dev, info, req_wanted,
-					  wanted_diff_mask, new_active,
-					  active_diff_mask, compact);
+		netdev_features_xor(features[WANTED_DIFF_MASK],
+				    features[REQ_WANTED], features[NEW_ACTIVE]);
+		netdev_features_xor(features[ACTIVE_DIFF_MASK],
+				    features[OLD_ACTIVE],
+				    features[NEW_ACTIVE]);
+		netdev_features_mask(features[WANTED_DIFF_MASK],
+				     features[REQ_MASK]);
+		netdev_features_mask(features[REQ_WANTED],
+				     features[WANTED_DIFF_MASK]);
+		netdev_features_mask(features[NEW_ACTIVE],
+				     features[ACTIVE_DIFF_MASK]);
+
+		ret = features_send_reply(dev, info, features[REQ_WANTED].bits,
+					  features[WANTED_DIFF_MASK].bits,
+					  features[NEW_ACTIVE].bits,
+					  features[ACTIVE_DIFF_MASK].bits,
+					  compact);
 	}
 	if (mod)
 		netdev_features_change(dev);
@@ -287,5 +275,6 @@  int ethnl_set_features(struct sk_buff *skb, struct genl_info *info)
 out_rtnl:
 	rtnl_unlock();
 	ethnl_parse_header_dev_put(&req_info);
+	kfree(features);
 	return ret;
 }
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 12bf982a5d2d..c4537aacc6f1 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -89,6 +89,10 @@  static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
 		.size = ETHTOOL_DEV_FEATURE_WORDS,
 	};
 	struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
+	u32 never_changed_arr[ETHTOOL_DEV_FEATURE_WORDS];
+	u32 wanted_arr[ETHTOOL_DEV_FEATURE_WORDS];
+	u32 active_arr[ETHTOOL_DEV_FEATURE_WORDS];
+	u32 hw_arr[ETHTOOL_DEV_FEATURE_WORDS];
 	u32 __user *sizeaddr;
 	u32 copy_size;
 	int i;
@@ -96,12 +100,16 @@  static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
 	/* in case feature bits run out again */
 	BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t));
 
+	bitmap_to_arr32(hw_arr, dev->hw_features.bits, NETDEV_FEATURE_COUNT);
+	bitmap_to_arr32(wanted_arr, dev->wanted_features.bits, NETDEV_FEATURE_COUNT);
+	bitmap_to_arr32(active_arr, dev->features.bits, NETDEV_FEATURE_COUNT);
+	bitmap_to_arr32(never_changed_arr, netdev_never_change_features.bits,
+			NETDEV_FEATURE_COUNT);
 	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].never_changed =
-			(u32)(NETIF_F_NEVER_CHANGE >> (32 * i));
+		features[i].available = hw_arr[i];
+		features[i].requested = wanted_arr[i];
+		features[i].active = active_arr[i];
+		features[i].never_changed = never_changed_arr[i];
 	}
 
 	sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
@@ -125,6 +133,8 @@  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];
+	u32 wanted_arr[ETHTOOL_DEV_FEATURE_WORDS];
+	u32 valid_arr[ETHTOOL_DEV_FEATURE_WORDS];
 	netdev_features_t wanted;
 	netdev_features_t valid;
 	netdev_features_t tmp;
@@ -143,16 +153,18 @@  static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
 	netdev_features_zero(wanted);
 	netdev_features_zero(valid);
 	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_arr[i] = features[i].valid;
+		wanted_arr[i] = features[i].requested;
 	}
+	bitmap_from_arr32(valid.bits, valid_arr, NETDEV_FEATURE_COUNT);
+	bitmap_from_arr32(wanted.bits, wanted_arr, NETDEV_FEATURE_COUNT);
 
 	netdev_features_andnot(tmp, valid, NETIF_F_ETHTOOL_BITS);
-	if (tmp)
+	if (!netdev_features_empty(tmp))
 		return -EINVAL;
 
 	netdev_features_andnot(tmp, valid, dev->hw_features);
-	if (tmp) {
+	if (!netdev_features_empty(tmp)) {
 		netdev_features_mask(valid, dev->hw_features);
 		ret |= ETHTOOL_F_UNSUPPORTED;
 	}
@@ -163,8 +175,7 @@  static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
 	__netdev_update_features(dev);
 
 	netdev_features_xor(tmp, dev->wanted_features, dev->features);
-
-	if (tmp & valid)
+	if (netdev_features_intersects(tmp, valid))
 		ret |= ETHTOOL_F_WISH;
 
 	return ret;