QinQ enable, when enable strip function, it is wrong to strip inner
VLAN of double VLAN package. The correct action is outer VLAN is
stripped. So, need to configure 'outer_vlan_flags' to update vsi.

When enable QinQ strip function, need to set 'port_vlan_flags' to
configure inner VLAN strip.

Signed-off-by: Kevin Liu <kevinx....@intel.com>
---
 doc/guides/nics/i40e.rst       |   2 -
 drivers/net/i40e/i40e_ethdev.c | 170 +++++++++++++++++++++++++++++++--
 drivers/net/i40e/i40e_ethdev.h |   4 +
 3 files changed, 164 insertions(+), 12 deletions(-)

diff --git a/doc/guides/nics/i40e.rst b/doc/guides/nics/i40e.rst
index 15b796e67a..ffcb2a2220 100644
--- a/doc/guides/nics/i40e.rst
+++ b/doc/guides/nics/i40e.rst
@@ -982,8 +982,6 @@ Vlan related Features miss when FW >= 8.4
 If FW version >= 8.4, there'll be some Vlan related issues:
 
 #. TCI input set for QinQ  is invalid.
-#. Fail to configure TPID for QinQ.
-#. Fail to strip outer Vlan.
 
 Example of getting best performance with l3fwd example
 ------------------------------------------------------
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 27cfda6ff8..0c3009ebfa 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -52,6 +52,8 @@
 #define I40E_VSI_TSR_QINQ_STRIP                0x4010
 #define I40E_VSI_TSR(_i)       (0x00050800 + ((_i) * 4))
 
+#define I40E_OVLAN_EMOD_SHIFT(x) ((x) << I40E_AQ_VSI_OVLAN_EMOD_SHIFT)
+
 /* Maximun number of capability elements */
 #define I40E_MAX_CAP_ELE_NUM       128
 
@@ -4011,10 +4013,15 @@ i40e_vlan_offload_set(struct rte_eth_dev *dev, int mask)
 
        if (mask & RTE_ETH_VLAN_STRIP_MASK) {
                /* Enable or disable VLAN stripping */
-               if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP)
-                       i40e_vsi_config_vlan_stripping(vsi, TRUE);
-               else
+               if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
+                       if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_EXTEND)
+                               i40e_vsi_config_vlan_stripping_v1(vsi, TRUE);
+                       else
+                               i40e_vsi_config_vlan_stripping(vsi, TRUE);
+               } else {
                        i40e_vsi_config_vlan_stripping(vsi, FALSE);
+                       i40e_vsi_config_vlan_stripping_v1(vsi, FALSE);
+               }
        }
 
        if (mask & RTE_ETH_VLAN_EXTEND_MASK) {
@@ -4068,6 +4075,10 @@ i40e_vlan_offload_set(struct rte_eth_dev *dev, int mask)
                if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_EXTEND) {
                        if (pf->fw8_3gt) {
                                i40e_vsi_config_qinq(vsi, TRUE);
+                               if (rxmode->offloads & 
RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
+                                       i40e_vsi_config_vlan_stripping(vsi, 
FALSE);
+                                       i40e_vsi_config_vlan_stripping_v1(vsi, 
TRUE);
+                               }
                        } else {
                                i40e_vsi_config_double_vlan(vsi, TRUE);
                                /* Set global registers with default ethertype. 
*/
@@ -4077,10 +4088,15 @@ i40e_vlan_offload_set(struct rte_eth_dev *dev, int mask)
                                                        RTE_ETHER_TYPE_VLAN);
                        }
                } else {
-                       if (pf->fw8_3gt)
+                       if (pf->fw8_3gt) {
                                i40e_vsi_config_qinq(vsi, FALSE);
-                       else
+                               if (rxmode->offloads & 
RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
+                                       i40e_vsi_config_vlan_stripping_v1(vsi, 
FALSE);
+                                       i40e_vsi_config_vlan_stripping(vsi, 
TRUE);
+                               }
+                       } else {
                                i40e_vsi_config_double_vlan(vsi, FALSE);
+                       }
                }
                /*restore mac/vlan filters of all ports*/
                for (j = 0; j < port_num; j++) {
@@ -4096,10 +4112,17 @@ i40e_vlan_offload_set(struct rte_eth_dev *dev, int mask)
 
        if (mask & RTE_ETH_QINQ_STRIP_MASK) {
                /* Enable or disable outer VLAN stripping */
-               if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_QINQ_STRIP)
-                       i40e_vsi_config_outer_vlan_stripping(vsi, TRUE);
-               else
-                       i40e_vsi_config_outer_vlan_stripping(vsi, FALSE);
+               if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_QINQ_STRIP) {
+                       if (pf->fw8_3gt)
+                               i40e_vsi_config_inner_vlan_stripping(vsi, TRUE);
+                       else
+                               i40e_vsi_config_outer_vlan_stripping(vsi, TRUE);
+               } else {
+                       if (pf->fw8_3gt)
+                               i40e_vsi_config_inner_vlan_stripping(vsi, 
FALSE);
+                       else
+                               i40e_vsi_config_outer_vlan_stripping(vsi, 
FALSE);
+               }
        }
 
        return 0;
@@ -5231,6 +5254,7 @@ int
 i40e_vsi_vlan_pvid_set(struct i40e_vsi *vsi,
                                struct i40e_vsi_vlan_pvid_info *info)
 {
+       struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
        struct i40e_hw *hw;
        struct i40e_vsi_context ctxt;
        uint8_t vlan_flags = 0;
@@ -5241,6 +5265,9 @@ i40e_vsi_vlan_pvid_set(struct i40e_vsi *vsi,
                return I40E_ERR_PARAM;
        }
 
+       if (pf->fw8_3gt)
+               return i40e_vsi_vlan_ovid_set(vsi, info);
+
        if (info->on) {
                vsi->info.pvid = info->config.pvid;
                /**
@@ -5880,8 +5907,16 @@ i40e_vsi_setup(struct i40e_pf *pf,
                memset(&ctxt, 0, sizeof(ctxt));
                vsi->info.valid_sections |=
                        rte_cpu_to_le_16(I40E_AQ_VSI_PROP_VLAN_VALID);
-               vsi->info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL |
+               if (pf->fw8_3gt) {
+                       vsi->info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL |
+                                       I40E_AQ_VSI_PVLAN_EMOD_NOTHING;
+                       vsi->info.outer_vlan_flags = I40E_AQ_VSI_OVLAN_MODE_ALL 
|
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_EMOD_SHOW_ALL) |
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_CTRL_ENA);
+               } else {
+                       vsi->info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL |
                                        I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH;
+               }
                rte_memcpy(&ctxt.info, &vsi->info,
                        sizeof(struct i40e_aqc_vsi_properties_data));
                ret = i40e_vsi_config_tc_queue_mapping(vsi, &ctxt.info,
@@ -6175,6 +6210,121 @@ i40e_vsi_config_vlan_stripping(struct i40e_vsi *vsi, 
bool on)
        return ret;
 }
 
+int i40e_vsi_config_vlan_stripping_v1(struct i40e_vsi *vsi, bool on)
+{
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       struct i40e_vsi_context ctxt;
+       uint8_t vlan_flags;
+       int ret = I40E_SUCCESS;
+
+       /* Check if it has been already on or off */
+       if (vsi->info.valid_sections &
+               rte_cpu_to_le_16(I40E_AQ_VSI_PROP_VLAN_VALID)) {
+               if (on) {
+                       if ((vsi->info.outer_vlan_flags &
+                                       I40E_AQ_VSI_OVLAN_EMOD_MASK) == 0)
+                               return 0; /* already on */
+               } else {
+                       if ((vsi->info.outer_vlan_flags &
+                               I40E_AQ_VSI_OVLAN_EMOD_MASK) ==
+                               I40E_AQ_VSI_OVLAN_EMOD_MASK)
+                               return 0; /* already off */
+               }
+       }
+
+       if (on)
+               vlan_flags = I40E_AQ_VSI_OVLAN_MODE_ALL |
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_EMOD_SHOW_ALL) |
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_CTRL_ENA);
+       else
+               vlan_flags = I40E_AQ_VSI_OVLAN_MODE_ALL |
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_EMOD_NOTHING) |
+                                       
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_CTRL_ENA);
+
+       vsi->info.valid_sections =
+               rte_cpu_to_le_16(I40E_AQ_VSI_PROP_VLAN_VALID);
+       vsi->info.outer_vlan_flags = vlan_flags;
+       ctxt.seid = vsi->seid;
+       rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+       ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+       if (ret)
+               PMD_DRV_LOG(INFO, "Update VSI failed to %s outer vlan 
stripping",
+                           on ? "enable" : "disable");
+
+       return ret;
+}
+
+int i40e_vsi_config_inner_vlan_stripping(struct i40e_vsi *vsi, bool on)
+{
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       struct i40e_vsi_context ctxt;
+       uint8_t vlan_flags;
+       int ret = I40E_SUCCESS;
+
+       if (on)
+               vlan_flags = I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH;
+       else
+               vlan_flags = I40E_AQ_VSI_PVLAN_EMOD_NOTHING;
+       vsi->info.valid_sections =
+               rte_cpu_to_le_16(I40E_AQ_VSI_PROP_VLAN_VALID);
+       vsi->info.port_vlan_flags &= ~(I40E_AQ_VSI_PVLAN_EMOD_MASK);
+       vsi->info.port_vlan_flags |= vlan_flags;
+       ctxt.seid = vsi->seid;
+       rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+       ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+       if (ret)
+               PMD_DRV_LOG(INFO, "Update VSI failed to %s inner stripping",
+                           on ? "enable" : "disable");
+
+       return ret;
+}
+
+int
+i40e_vsi_vlan_ovid_set(struct i40e_vsi *vsi,
+                               struct i40e_vsi_vlan_pvid_info *info)
+{
+       struct i40e_hw *hw;
+       struct i40e_vsi_context ctxt;
+       uint8_t vlan_flags = 0;
+       int ret;
+
+       if (vsi == NULL || info == NULL) {
+               PMD_DRV_LOG(ERR, "invalid parameters");
+               return I40E_ERR_PARAM;
+       }
+
+       if (info->on) {
+               vsi->info.outer_vlan = info->config.pvid;
+               vlan_flags = I40E_AQ_VSI_OVLAN_MODE_UNTAGGED |
+                               I40E_AQ_VSI_OVLAN_INSERT_PVID |
+                               
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_EMOD_HIDE_ALL) |
+                               
I40E_OVLAN_EMOD_SHIFT(I40E_AQ_VSI_OVLAN_CTRL_ENA);
+       } else {
+               vsi->info.outer_vlan = 0;
+               if (info->config.reject.tagged == 0)
+                       vlan_flags |= I40E_AQ_VSI_OVLAN_MODE_TAGGED;
+
+               if (info->config.reject.untagged == 0)
+                       vlan_flags |= I40E_AQ_VSI_OVLAN_MODE_UNTAGGED;
+       }
+       vsi->info.outer_vlan_flags |= vlan_flags;
+       vsi->info.outer_vlan_flags &= ~(I40E_AQ_VSI_OVLAN_INSERT_PVID |
+                                       I40E_AQ_VSI_OVLAN_MODE_MASK);
+       vsi->info.valid_sections =
+               rte_cpu_to_le_16(I40E_AQ_VSI_PROP_VLAN_VALID);
+       memset(&ctxt, 0, sizeof(ctxt));
+       rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+       ctxt.seid = vsi->seid;
+
+       hw = I40E_VSI_TO_HW(vsi);
+       ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+       if (ret != I40E_SUCCESS)
+               PMD_DRV_LOG(ERR, "Failed to update VSI params");
+
+       return ret;
+}
+
+
 static int
 i40e_dev_init_vlan(struct rte_eth_dev *dev)
 {
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index fe943a45ff..2f513fce52 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -1316,6 +1316,10 @@ void i40e_vsi_disable_queues_intr(struct i40e_vsi *vsi);
 int i40e_vsi_vlan_pvid_set(struct i40e_vsi *vsi,
                           struct i40e_vsi_vlan_pvid_info *info);
 int i40e_vsi_config_vlan_stripping(struct i40e_vsi *vsi, bool on);
+int i40e_vsi_config_vlan_stripping_v1(struct i40e_vsi *vsi, bool on);
+int i40e_vsi_config_inner_vlan_stripping(struct i40e_vsi *vsi, bool on);
+int i40e_vsi_vlan_ovid_set(struct i40e_vsi *vsi,
+                               struct i40e_vsi_vlan_pvid_info *info);
 int i40e_vsi_config_vlan_filter(struct i40e_vsi *vsi, bool on);
 uint64_t i40e_config_hena(const struct i40e_adapter *adapter, uint64_t flags);
 uint64_t i40e_parse_hena(const struct i40e_adapter *adapter, uint64_t flags);
-- 
2.34.1

Reply via email to