nfp: support RX VLAN ctag/stag strip
authorDiana Wang <na.wang@corigine.com>
Sat, 2 Jul 2022 07:35:50 +0000 (09:35 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 Jul 2022 09:44:08 +0000 (10:44 +0100)
Add support for RX VLAN ctag/stag strip
which may be configured via ethtool.

e.g.
     # ethtool -K $DEV rx-vlan-offload on
     # ethtool -K $DEV rx-vlan-stag-hw-parse on

Ctag-stripped and stag-stripped cannot be enabled at the same time
because currently the kernel supports only one layer of VLAN stripping.

The NIC supplies VLAN strip information as packet metadata.
The fields of this VLAN metadata are:

* strip flag: 1 for stripped; 0 for unstripped
* tci: VLAN TCI ID
* tpid: 1 for ETH_P_8021AD; 0 for ETH_P_8021Q

Configuration control bits NFP_NET_CFG_CTRL_RXVLAN_V2 and
NFP_NET_CFG_CTRL_RXQINQ are to signal availability of
ctag-strip and stag-strip features of the firmware.

Signed-off-by: Diana Wang <na.wang@corigine.com>
Reviewed-by: Louis Peens <louis.peens@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfd3/dp.c
drivers/net/ethernet/netronome/nfp/nfd3/rings.c
drivers/net/ethernet/netronome/nfp/nfd3/xsk.c
drivers/net/ethernet/netronome/nfp/nfdk/dp.c
drivers/net/ethernet/netronome/nfp/nfdk/rings.c
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/netronome/nfp/nfp_net_dp.c
drivers/net/ethernet/netronome/nfp/nfp_net_dp.h
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c

index 8a9d366196591dd56f2dd418f355039d04272792..f4f6a8cb0ad3b7e559ae7acb055cacab8916810e 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/bpf_trace.h>
 #include <linux/netdevice.h>
+#include <linux/bitfield.h>
 
 #include "../nfp_app.h"
 #include "../nfp_net.h"
@@ -703,7 +704,7 @@ bool
 nfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
                    void *data, void *pkt, unsigned int pkt_len, int meta_len)
 {
-       u32 meta_info;
+       u32 meta_info, vlan_info;
 
        meta_info = get_unaligned_be32(data);
        data += 4;
@@ -721,6 +722,17 @@ nfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
                        meta->mark = get_unaligned_be32(data);
                        data += 4;
                        break;
+               case NFP_NET_META_VLAN:
+                       vlan_info = get_unaligned_be32(data);
+                       if (FIELD_GET(NFP_NET_META_VLAN_STRIP, vlan_info)) {
+                               meta->vlan.stripped = true;
+                               meta->vlan.tpid = FIELD_GET(NFP_NET_META_VLAN_TPID_MASK,
+                                                           vlan_info);
+                               meta->vlan.tci = FIELD_GET(NFP_NET_META_VLAN_TCI_MASK,
+                                                          vlan_info);
+                       }
+                       data += 4;
+                       break;
                case NFP_NET_META_PORTID:
                        meta->portid = get_unaligned_be32(data);
                        data += 4;
@@ -1049,9 +1061,11 @@ static int nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget)
                }
 #endif
 
-               if (rxd->rxd.flags & PCIE_DESC_RX_VLAN)
-                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
-                                              le16_to_cpu(rxd->rxd.vlan));
+               if (unlikely(!nfp_net_vlan_strip(skb, rxd, &meta))) {
+                       nfp_nfd3_rx_drop(dp, r_vec, rx_ring, NULL, skb);
+                       continue;
+               }
+
                if (meta_len_xdp)
                        skb_metadata_set(skb, meta_len_xdp);
 
index f65851ed5b504b13eab2b27c0718c987bc4d6e61..0390b754a39922c7caacd8edfbf4e8c9ad7b481b 100644 (file)
@@ -247,6 +247,7 @@ nfp_nfd3_print_tx_descs(struct seq_file *file,
         NFP_NET_CFG_CTRL_L2BC | NFP_NET_CFG_CTRL_L2MC |                \
         NFP_NET_CFG_CTRL_RXCSUM | NFP_NET_CFG_CTRL_TXCSUM |            \
         NFP_NET_CFG_CTRL_RXVLAN | NFP_NET_CFG_CTRL_TXVLAN |            \
+        NFP_NET_CFG_CTRL_RXVLAN_V2 | NFP_NET_CFG_CTRL_RXQINQ |         \
         NFP_NET_CFG_CTRL_GATHER | NFP_NET_CFG_CTRL_LSO |               \
         NFP_NET_CFG_CTRL_CTAG_FILTER | NFP_NET_CFG_CTRL_CMSG_DATA |    \
         NFP_NET_CFG_CTRL_RINGCFG | NFP_NET_CFG_CTRL_RSS |              \
index 454fea4c8be203f943fa644f0cd38e9b8621213f..65e24316876551b0262950a21adbe5a0c3741afa 100644 (file)
@@ -94,9 +94,12 @@ static void nfp_nfd3_xsk_rx_skb(struct nfp_net_rx_ring *rx_ring,
 
        nfp_nfd3_rx_csum(dp, r_vec, rxd, meta, skb);
 
-       if (rxd->rxd.flags & PCIE_DESC_RX_VLAN)
-               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
-                                      le16_to_cpu(rxd->rxd.vlan));
+       if (unlikely(!nfp_net_vlan_strip(skb, rxd, meta))) {
+               dev_kfree_skb_any(skb);
+               nfp_net_xsk_rx_drop(r_vec, xrxbuf);
+               return;
+       }
+
        if (meta_xdp)
                skb_metadata_set(skb,
                                 xrxbuf->xdp->data - xrxbuf->xdp->data_meta);
index 85dd376a6adc25a8adffe6325d2dee98b08c116e..34fe179513bf7c0a7ab3e697c544d4658e4519c3 100644 (file)
@@ -716,7 +716,7 @@ static bool
 nfp_nfdk_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
                    void *data, void *pkt, unsigned int pkt_len, int meta_len)
 {
-       u32 meta_info;
+       u32 meta_info, vlan_info;
 
        meta_info = get_unaligned_be32(data);
        data += 4;
@@ -734,6 +734,17 @@ nfp_nfdk_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
                        meta->mark = get_unaligned_be32(data);
                        data += 4;
                        break;
+               case NFP_NET_META_VLAN:
+                       vlan_info = get_unaligned_be32(data);
+                       if (FIELD_GET(NFP_NET_META_VLAN_STRIP, vlan_info)) {
+                               meta->vlan.stripped = true;
+                               meta->vlan.tpid = FIELD_GET(NFP_NET_META_VLAN_TPID_MASK,
+                                                           vlan_info);
+                               meta->vlan.tci = FIELD_GET(NFP_NET_META_VLAN_TCI_MASK,
+                                                          vlan_info);
+                       }
+                       data += 4;
+                       break;
                case NFP_NET_META_PORTID:
                        meta->portid = get_unaligned_be32(data);
                        data += 4;
@@ -1169,9 +1180,11 @@ static int nfp_nfdk_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 
                nfp_nfdk_rx_csum(dp, r_vec, rxd, &meta, skb);
 
-               if (rxd->rxd.flags & PCIE_DESC_RX_VLAN)
-                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
-                                              le16_to_cpu(rxd->rxd.vlan));
+               if (unlikely(!nfp_net_vlan_strip(skb, rxd, &meta))) {
+                       nfp_nfdk_rx_drop(dp, r_vec, rx_ring, NULL, skb);
+                       continue;
+               }
+
                if (meta_len_xdp)
                        skb_metadata_set(skb, meta_len_xdp);
 
index 222ee0e5302fcbf0fa62759b3d4bf786dbcdbbde..6cd895d3b5711b5d93c1f6ba0af0602ec3a2948f 100644 (file)
@@ -168,6 +168,7 @@ nfp_nfdk_print_tx_descs(struct seq_file *file,
         NFP_NET_CFG_CTRL_L2BC | NFP_NET_CFG_CTRL_L2MC |                \
         NFP_NET_CFG_CTRL_RXCSUM | NFP_NET_CFG_CTRL_TXCSUM |            \
         NFP_NET_CFG_CTRL_RXVLAN |                                      \
+        NFP_NET_CFG_CTRL_RXVLAN_V2 | NFP_NET_CFG_CTRL_RXQINQ |         \
         NFP_NET_CFG_CTRL_GATHER | NFP_NET_CFG_CTRL_LSO |               \
         NFP_NET_CFG_CTRL_CTAG_FILTER | NFP_NET_CFG_CTRL_CMSG_DATA |    \
         NFP_NET_CFG_CTRL_RINGCFG | NFP_NET_CFG_CTRL_IRQMOD |           \
index b07cea8e354c92f08d345343b9613fb15095a311..a101ff30a1aee23dfbdf77e27504518bc83b3916 100644 (file)
@@ -248,6 +248,8 @@ struct nfp_net_rx_desc {
 };
 
 #define NFP_NET_META_FIELD_MASK GENMASK(NFP_NET_META_FIELD_SIZE - 1, 0)
+#define NFP_NET_VLAN_CTAG      0
+#define NFP_NET_VLAN_STAG      1
 
 struct nfp_meta_parsed {
        u8 hash_type;
@@ -256,6 +258,11 @@ struct nfp_meta_parsed {
        u32 mark;
        u32 portid;
        __wsum csum;
+       struct {
+               bool stripped;
+               u8 tpid;
+               u16 tci;
+       } vlan;
 };
 
 struct nfp_net_rx_hash {
index 3e2ebe42e679960e7e47f08837af2ef926b9967b..b2f714fb0947ab0baec3284262a681ffcd3211d1 100644 (file)
@@ -1695,9 +1695,10 @@ static int nfp_net_set_features(struct net_device *netdev,
 
        if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
                if (features & NETIF_F_HW_VLAN_CTAG_RX)
-                       new_ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+                       new_ctrl |= nn->cap & NFP_NET_CFG_CTRL_RXVLAN_V2 ?:
+                                   NFP_NET_CFG_CTRL_RXVLAN;
                else
-                       new_ctrl &= ~NFP_NET_CFG_CTRL_RXVLAN;
+                       new_ctrl &= ~NFP_NET_CFG_CTRL_RXVLAN_ANY;
        }
 
        if (changed & NETIF_F_HW_VLAN_CTAG_TX) {
@@ -1714,6 +1715,13 @@ static int nfp_net_set_features(struct net_device *netdev,
                        new_ctrl &= ~NFP_NET_CFG_CTRL_CTAG_FILTER;
        }
 
+       if (changed & NETIF_F_HW_VLAN_STAG_RX) {
+               if (features & NETIF_F_HW_VLAN_STAG_RX)
+                       new_ctrl |= NFP_NET_CFG_CTRL_RXQINQ;
+               else
+                       new_ctrl &= ~NFP_NET_CFG_CTRL_RXQINQ;
+       }
+
        if (changed & NETIF_F_SG) {
                if (features & NETIF_F_SG)
                        new_ctrl |= NFP_NET_CFG_CTRL_GATHER;
@@ -1742,6 +1750,27 @@ static int nfp_net_set_features(struct net_device *netdev,
        return 0;
 }
 
+static netdev_features_t
+nfp_net_fix_features(struct net_device *netdev,
+                    netdev_features_t features)
+{
+       if ((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+           (features & NETIF_F_HW_VLAN_STAG_RX)) {
+               if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+                       features &= ~NETIF_F_HW_VLAN_CTAG_RX;
+                       netdev->wanted_features &= ~NETIF_F_HW_VLAN_CTAG_RX;
+                       netdev_warn(netdev,
+                                   "S-tag and C-tag stripping can't be enabled at the same time. Enabling S-tag stripping and disabling C-tag stripping\n");
+               } else if (netdev->features & NETIF_F_HW_VLAN_STAG_RX) {
+                       features &= ~NETIF_F_HW_VLAN_STAG_RX;
+                       netdev->wanted_features &= ~NETIF_F_HW_VLAN_STAG_RX;
+                       netdev_warn(netdev,
+                                   "S-tag and C-tag stripping can't be enabled at the same time. Enabling C-tag stripping and disabling S-tag stripping\n");
+               }
+       }
+       return features;
+}
+
 static netdev_features_t
 nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
                       netdev_features_t features)
@@ -1977,6 +2006,7 @@ const struct net_device_ops nfp_nfd3_netdev_ops = {
        .ndo_change_mtu         = nfp_net_change_mtu,
        .ndo_set_mac_address    = nfp_net_set_mac_address,
        .ndo_set_features       = nfp_net_set_features,
+       .ndo_fix_features       = nfp_net_fix_features,
        .ndo_features_check     = nfp_net_features_check,
        .ndo_get_phys_port_name = nfp_net_get_phys_port_name,
        .ndo_bpf                = nfp_net_xdp,
@@ -2008,6 +2038,7 @@ const struct net_device_ops nfp_nfdk_netdev_ops = {
        .ndo_change_mtu         = nfp_net_change_mtu,
        .ndo_set_mac_address    = nfp_net_set_mac_address,
        .ndo_set_features       = nfp_net_set_features,
+       .ndo_fix_features       = nfp_net_fix_features,
        .ndo_features_check     = nfp_net_features_check,
        .ndo_get_phys_port_name = nfp_net_get_phys_port_name,
        .ndo_bpf                = nfp_net_xdp,
@@ -2061,7 +2092,7 @@ void nfp_net_info(struct nfp_net *nn)
                nn->fw_ver.extend, nn->fw_ver.class,
                nn->fw_ver.major, nn->fw_ver.minor,
                nn->max_mtu);
-       nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+       nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                nn->cap,
                nn->cap & NFP_NET_CFG_CTRL_PROMISC  ? "PROMISC "  : "",
                nn->cap & NFP_NET_CFG_CTRL_L2BC     ? "L2BCFILT " : "",
@@ -2070,6 +2101,8 @@ void nfp_net_info(struct nfp_net *nn)
                nn->cap & NFP_NET_CFG_CTRL_TXCSUM   ? "TXCSUM "   : "",
                nn->cap & NFP_NET_CFG_CTRL_RXVLAN   ? "RXVLAN "   : "",
                nn->cap & NFP_NET_CFG_CTRL_TXVLAN   ? "TXVLAN "   : "",
+               nn->cap & NFP_NET_CFG_CTRL_RXQINQ   ? "RXQINQ "   : "",
+               nn->cap & NFP_NET_CFG_CTRL_RXVLAN_V2 ? "RXVLANv2 "   : "",
                nn->cap & NFP_NET_CFG_CTRL_SCATTER  ? "SCATTER "  : "",
                nn->cap & NFP_NET_CFG_CTRL_GATHER   ? "GATHER "   : "",
                nn->cap & NFP_NET_CFG_CTRL_LSO      ? "TSO1 "     : "",
@@ -2357,9 +2390,10 @@ static void nfp_net_netdev_init(struct nfp_net *nn)
 
        netdev->vlan_features = netdev->hw_features;
 
-       if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN) {
+       if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN_ANY) {
                netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
-               nn->dp.ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+               nn->dp.ctrl |= nn->cap & NFP_NET_CFG_CTRL_RXVLAN_V2 ?:
+                              NFP_NET_CFG_CTRL_RXVLAN;
        }
        if (nn->cap & NFP_NET_CFG_CTRL_TXVLAN) {
                if (nn->cap & NFP_NET_CFG_CTRL_LSO2) {
@@ -2373,15 +2407,22 @@ static void nfp_net_netdev_init(struct nfp_net *nn)
                netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
                nn->dp.ctrl |= NFP_NET_CFG_CTRL_CTAG_FILTER;
        }
+       if (nn->cap & NFP_NET_CFG_CTRL_RXQINQ) {
+               netdev->hw_features |= NETIF_F_HW_VLAN_STAG_RX;
+               nn->dp.ctrl |= NFP_NET_CFG_CTRL_RXQINQ;
+       }
 
        netdev->features = netdev->hw_features;
 
        if (nfp_app_has_tc(nn->app) && nn->port)
                netdev->hw_features |= NETIF_F_HW_TC;
 
-       /* Advertise but disable TSO by default. */
-       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
-       nn->dp.ctrl &= ~NFP_NET_CFG_CTRL_LSO_ANY;
+       /* Advertise but disable TSO by default.
+        * C-Tag strip and S-Tag strip can't be supported simultaneously,
+        * so enable C-Tag strip and disable S-Tag strip by default.
+        */
+       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_STAG_RX);
+       nn->dp.ctrl &= ~(NFP_NET_CFG_CTRL_LSO_ANY | NFP_NET_CFG_CTRL_RXQINQ);
 
        /* Finalise the netdev setup */
        switch (nn->dp.ops->version) {
index 9007675db67f9383bc51961b526fd3075a7ba58d..e03234fc94753328df634336792649238269f3b2 100644 (file)
 #define NFP_NET_LSO_MAX_HDR_SZ         255
 #define NFP_NET_LSO_MAX_SEGS           64
 
+/* working with metadata vlan api (NFD version >= 2.0) */
+#define NFP_NET_META_VLAN_STRIP                        BIT(31)
+#define NFP_NET_META_VLAN_TPID_MASK            GENMASK(19, 16)
+#define NFP_NET_META_VLAN_TCI_MASK             GENMASK(15, 0)
+
 /* Prepend field types */
 #define NFP_NET_META_FIELD_SIZE                4
 #define NFP_NET_META_HASH              1 /* next field carries hash type */
 #define NFP_NET_META_MARK              2
+#define NFP_NET_META_VLAN              4 /* ctag or stag type */
 #define NFP_NET_META_PORTID            5
 #define NFP_NET_META_CSUM              6 /* checksum complete type */
 #define NFP_NET_META_CONN_HANDLE       7
@@ -89,6 +95,8 @@
 #define   NFP_NET_CFG_CTRL_LSO           (0x1 << 10) /* LSO/TSO (version 1) */
 #define   NFP_NET_CFG_CTRL_CTAG_FILTER   (0x1 << 11) /* VLAN CTAG filtering */
 #define   NFP_NET_CFG_CTRL_CMSG_DATA     (0x1 << 12) /* RX cmsgs on data Qs */
+#define   NFP_NET_CFG_CTRL_RXQINQ        (0x1 << 13) /* Enable S-tag strip */
+#define   NFP_NET_CFG_CTRL_RXVLAN_V2     (0x1 << 15) /* Enable C-tag strip */
 #define   NFP_NET_CFG_CTRL_RINGCFG       (0x1 << 16) /* Ring runtime changes */
 #define   NFP_NET_CFG_CTRL_RSS           (0x1 << 17) /* RSS (version 1) */
 #define   NFP_NET_CFG_CTRL_IRQMOD        (0x1 << 18) /* Interrupt moderation */
                                         NFP_NET_CFG_CTRL_CSUM_COMPLETE)
 #define NFP_NET_CFG_CTRL_CHAIN_META    (NFP_NET_CFG_CTRL_RSS2 | \
                                         NFP_NET_CFG_CTRL_CSUM_COMPLETE)
+#define NFP_NET_CFG_CTRL_RXVLAN_ANY    (NFP_NET_CFG_CTRL_RXVLAN | \
+                                        NFP_NET_CFG_CTRL_RXVLAN_V2)
 
 #define NFP_NET_CFG_UPDATE             0x0004
 #define   NFP_NET_CFG_UPDATE_GEN         (0x1 <<  0) /* General update */
index 34dd94811df3745835d2ccbb7e60aed36705ac9d..550df83b798c91f9c3c3bdee5c823447d19fd0e2 100644 (file)
@@ -440,3 +440,27 @@ bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb)
 
        return ret;
 }
+
+bool nfp_net_vlan_strip(struct sk_buff *skb, const struct nfp_net_rx_desc *rxd,
+                       const struct nfp_meta_parsed *meta)
+{
+       u16 tpid = 0, tci = 0;
+
+       if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) {
+               tpid = ETH_P_8021Q;
+               tci = le16_to_cpu(rxd->rxd.vlan);
+       } else if (meta->vlan.stripped) {
+               if (meta->vlan.tpid == NFP_NET_VLAN_CTAG)
+                       tpid = ETH_P_8021Q;
+               else if (meta->vlan.tpid == NFP_NET_VLAN_STAG)
+                       tpid = ETH_P_8021AD;
+               else
+                       return false;
+
+               tci = meta->vlan.tci;
+       }
+       if (tpid)
+               __vlan_hwaccel_put_tag(skb, htons(tpid), tci);
+
+       return true;
+}
index 83becb338478200465747268b1913021f579cae2..831c83ce0d3d0e6a6cd571d5e40c729e102ba196 100644 (file)
@@ -106,6 +106,8 @@ int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp);
 void nfp_net_rx_rings_free(struct nfp_net_dp *dp);
 void nfp_net_tx_rings_free(struct nfp_net_dp *dp);
 void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring);
+bool nfp_net_vlan_strip(struct sk_buff *skb, const struct nfp_net_rx_desc *rxd,
+                       const struct nfp_meta_parsed *meta);
 
 enum nfp_nfd_version {
        NFP_NFD_VER_NFD3,
index 75b5018f2e1b848a42322cb826a3a6c7af06cd43..066cce1db85d6e40685b970840504964daaa75e2 100644 (file)
@@ -365,7 +365,7 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
 
        netdev->vlan_features = netdev->hw_features;
 
-       if (repr_cap & NFP_NET_CFG_CTRL_RXVLAN)
+       if (repr_cap & NFP_NET_CFG_CTRL_RXVLAN_ANY)
                netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
        if (repr_cap & NFP_NET_CFG_CTRL_TXVLAN) {
                if (repr_cap & NFP_NET_CFG_CTRL_LSO2)
@@ -375,11 +375,16 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
        }
        if (repr_cap & NFP_NET_CFG_CTRL_CTAG_FILTER)
                netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+       if (repr_cap & NFP_NET_CFG_CTRL_RXQINQ)
+               netdev->hw_features |= NETIF_F_HW_VLAN_STAG_RX;
 
        netdev->features = netdev->hw_features;
 
-       /* Advertise but disable TSO by default. */
-       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+       /* Advertise but disable TSO by default.
+        * C-Tag strip and S-Tag strip can't be supported simultaneously,
+        * so enable C-Tag strip and disable S-Tag strip by default.
+        */
+       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_STAG_RX);
        netif_set_tso_max_segs(netdev, NFP_NET_LSO_MAX_SEGS);
 
        netdev->priv_flags |= IFF_NO_QUEUE | IFF_DISABLE_NETPOLL;