net/i40e: disable source pruning

Message ID 20230109022027.190627-1-ke1x.zhang@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series net/i40e: disable source pruning |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-testing warning apply patch failure
ci/Intel-compilation fail Compilation issues
ci/intel-Testing success Testing PASS

Commit Message

Zhang, Ke1X Jan. 9, 2023, 2:20 a.m. UTC
  VRRP advertisement packets are dropped on i40e PF devices because
when a MAC address is added to a device, packets originating from
that MAC address are dropped.

This patch adds a interface in lib/ethdev to support disabling
source pruning to work around above issue.

Bugzilla ID: 648

Signed-off-by: Ke Zhang <ke1x.zhang@intel.com>
---
 app/test-pmd/cmdline.c         | 77 ++++++++++++++++++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.c | 53 +++++++++++++++++++++++
 lib/ethdev/ethdev_driver.h     |  6 +++
 lib/ethdev/rte_ethdev.c        | 16 +++++++
 lib/ethdev/rte_ethdev.h        | 15 +++++++
 5 files changed, 167 insertions(+)
  

Comments

Morten Brørup Jan. 9, 2023, 7:40 a.m. UTC | #1
+CC: Andrew, as Ethernet API maintainer

> From: Ke Zhang [mailto:ke1x.zhang@intel.com]
> Sent: Monday, 9 January 2023 03.20
> 
> VRRP advertisement packets are dropped on i40e PF devices because
> when a MAC address is added to a device, packets originating from
> that MAC address are dropped.
> 
> This patch adds a interface in lib/ethdev to support disabling
> source pruning to work around above issue.

Thank you for the improved patch.

> 
> Bugzilla ID: 648
> 
> Signed-off-by: Ke Zhang <ke1x.zhang@intel.com>
> ---

[...]

+static cmdline_parse_token_string_t cmd_setllb_enalbe =

Typo: enalbe -> enable.

[...]

> +/* i40e_enable_pf_local_lb
> + * @pf: pointer to the pf structure
> + *
> + * allow local loopback on pf
> + */
> +static int
> +i40e_enable_pf_local_lb(struct rte_eth_dev *dev, int on)
> +{
> +	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data-
> >dev_private);
> +	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
> +	struct i40e_vsi_context ctxt;
> +	int ret;
> +
> +	/* Use the FW API if FW >= v5.0 */
> +	if (hw->aq.fw_maj_ver < 5 && hw->mac.type != I40E_MAC_X722) {
> +#ifdef TREX_PATCH
> +		/* Most of our customers do not have latest FW */
> +		PMD_INIT_LOG(INFO, "FW < v5.0, cannot enable local
> loopback");
> +#else
> +		PMD_INIT_LOG(ERR, "FW < v5.0, cannot enable local
> loopback");
> +#endif
> +		return I40E_NOT_SUPPORTED;
> +	}

Can this bug not be fixed without requiring FW >= 5.0?

If VRRP is not supported with older FW, perhaps you could also log a warning when a VRRP MAC address is added to the NIC (and the FW is too old, or local_lb is disabled). Just an idea; it will help people debug issues with VRRP in production.

> +
> +	memset(&ctxt, 0, sizeof(ctxt));
> +	ctxt.seid = pf->main_vsi_seid;
> +	ctxt.pf_num = hw->pf_id;
> +	ret = i40e_aq_get_vsi_params(hw, &ctxt, NULL);
> +	if (ret) {
> +		PMD_DRV_LOG(ERR, "cannot get pf vsi config, err %d, aq_err
> %d",
> +			    ret, hw->aq.asq_last_status);
> +		return ret;
> +	}
> +	ctxt.flags = I40E_AQ_VSI_TYPE_PF;
> +	ctxt.info.valid_sections =
> +		rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SWITCH_VALID);
> +	if (on)
> +		ctxt.info.switch_id |=
> +			rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
> +	else
> +		ctxt.info.switch_id &=
> +			~rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
> +
> +	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
> +	if (ret)
> +		PMD_DRV_LOG(ERR, "update vsi switch failed, aq_err=%d",
> +			    hw->aq.asq_last_status);
> +
> +	return ret;
> +}

[...]

> diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
> index c129ca1eaf..c4888ebd62 100644
> --- a/lib/ethdev/rte_ethdev.h
> +++ b/lib/ethdev/rte_ethdev.h
> @@ -3437,6 +3437,21 @@ int rte_eth_dev_set_mtu(uint16_t port_id,
> uint16_t mtu);
>   */
>  int rte_eth_dev_vlan_filter(uint16_t port_id, uint16_t vlan_id, int
> on);
> 
> +/**
> + * Enable/Disable local Loopback for VSIs that are used as uplink of a
> software
> + * (cascaded) VEB, VEPA or Port Virtualizer.
> + *
> + * @param port_id
> + *   The port identifier of the Ethernet device.
> + * @param on
> + *   If > 0, enable local Loopback.
> + *   Otherwise, disable local Loopback.
> + * @return
> + *   - (0) if successful.
> + *   - negative if failed.
> + */
> +int rte_eth_dev_enable_local_lb(uint16_t port_id, int on);
> +

Local Loopback usually means that packets queued for TX are looped back into RX by the NIC or PHY.

If the Intel X710/XXV710/XL710 "Source Pruning" feature only applies to egressing packets internally looped back to ingress by VEB, this patch makes some sense, although I wish you could come up with a better name for it than Local Loopback.

However, if the "Source Pruning" feature also applies to packets ingressing from the physical Ethernet interface, this naming and behavior is counterintuitive. In this case, "Source Pruning" is a special filter, which other NICs don't apply to ingressing packets, so the PMD should not enable the filter (and thus behave differently than all other NICs) unless explicitly enabled by the application.
  
Ferruh Yigit Jan. 13, 2023, 12:50 p.m. UTC | #2
On 1/9/2023 2:20 AM, Ke Zhang wrote:
> VRRP advertisement packets are dropped on i40e PF devices because
> when a MAC address is added to a device, packets originating from
> that MAC address are dropped.
> 
> This patch adds a interface in lib/ethdev to support disabling
> source pruning to work around above issue.
> 

Hi Ke,

We have a dilemma between enabling as much HW config/feature as possible
and having common/portable APIs.

If we add a new API for each specific HW config/feature, it will be
confusing for users and DPDK APIs won't be portable. We may end up
slightly specific version of an API specific to a NIC.
For this we used PMD specific APIs time to time (like
drivers/net/i40e/rte_pmd_i40e.h), although is not also a good solution.


If this is a common config/feature for more than one NIC, I am OK to
procced with the patch as it is, for this I have cc'ed a few more folks,
but first can you please describe the config/feature a little more
detailed please.

What I understand is if packet target MAC address is same as device MAC
address, packet is dropped, which is called 'source pruning' feature.
What is the use case for this, is the expectation send a packet to
router/switch and expect to receive it back?

Is all egress packets dropped as described? Becase feture referred as
"local loopback" in the code, is this only applied to packets switched
locally in NIC?

Can it be solution to always disable source pruning by default?


If this is specific to i40e, I suggest adding it as PMD specific API.
I put some comments below but first need to clarify how to procced with
the patch.



> Bugzilla ID: 648
> 
> Signed-off-by: Ke Zhang <ke1x.zhang@intel.com>
> ---
>  app/test-pmd/cmdline.c         | 77 ++++++++++++++++++++++++++++++++++
>  drivers/net/i40e/i40e_ethdev.c | 53 +++++++++++++++++++++++
>  lib/ethdev/ethdev_driver.h     |  6 +++
>  lib/ethdev/rte_ethdev.c        | 16 +++++++
>  lib/ethdev/rte_ethdev.h        | 15 +++++++
>  5 files changed, 167 insertions(+)
> 
> diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
> index cb8c174020..a9602a2ed0 100644
> --- a/app/test-pmd/cmdline.c
> +++ b/app/test-pmd/cmdline.c
> @@ -482,6 +482,9 @@ static void cmd_help_long_parsed(void *parsed_result,
>  			"set promisc (port_id|all) (on|off)\n"
>  			"    Set the promiscuous mode on port_id, or all.\n\n"
>  
> +			"set llb (port_id|all) (on|off)\n"
> +			"    Set vsi local loopback on port_id, or all.\n\n"
> +

Please don't use abreviations, specially for not commonly knows
features, 'llb' won't make sense to many people, and as number of this
unkown commonds increase it has a risk to make testpmd unusable,
please prefer 'local_loopback'.

And please don't refer to 'vsi' here.

And personally I don't prefer using 'set xxx' commands to configure
device, what I prefer:

"set xxx"                   --> set testpmd specific configuration
"port config <port_id> xxx" --> configure device


Although I am aware this separation is not clear and there is a mixuture
of usage, some of them historical.


>  			"set allmulti (port_id|all) (on|off)\n"
>  			"    Set the allmulti mode on port_id, or all.\n\n"
>  
> @@ -5775,6 +5778,78 @@ static cmdline_parse_inst_t cmd_set_promisc_mode_one = {
>  	},
>  };
>  
> +/* *** SET VSI LOCAL LOOPBACK *** *> +struct cmd_set_vsi_local_lb_result {
> +	cmdline_fixed_string_t set;
> +	cmdline_fixed_string_t llb;
> +	cmdline_fixed_string_t port_all; /* valid if "allports" argument == 1 */
> +	uint16_t port_num;               /* valid if "allports" argument == 0 */
> +	cmdline_fixed_string_t enable;
> +};
> +
> +static void cmd_set_vsi_llb_parsed(void *parsed_result,
> +					__rte_unused struct cmdline *cl,
> +					void *allports)
> +{
> +	struct cmd_set_vsi_local_lb_result *res = parsed_result;
> +	int enable;
> +	portid_t i;
> +
> +	if (!strcmp(res->enable, "on"))
> +		enable = 1;
> +	else
> +		enable = 0;
> +
> +	/* all ports */
> +	if (allports) {
> +		RTE_ETH_FOREACH_DEV(i)
> +			rte_eth_dev_enable_local_lb(i, enable);
> +	} else {
> +		rte_eth_dev_enable_local_lb(res->port_num, enable);
> +	}
> +}
> +
> +static cmdline_parse_token_string_t cmd_setllb_set =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, set, "set");
> +static cmdline_parse_token_string_t cmd_setllb_llb =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, llb,
> +				 "llb");
> +static cmdline_parse_token_string_t cmd_setllb_portall =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, port_all,
> +				 "all");
> +static cmdline_parse_token_num_t cmd_setllb_portnum =
> +	TOKEN_NUM_INITIALIZER(struct cmd_set_vsi_local_lb_result, port_num,
> +			      RTE_UINT16);
> +static cmdline_parse_token_string_t cmd_setllb_enalbe =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, enable,
> +				 "on#off");
> +
> +static cmdline_parse_inst_t cmd_set_vsi_enable_all = {
> +	.f = cmd_set_vsi_llb_parsed,
> +	.data = (void *)1,
> +	.help_str = "set llb all on|off: Set vsi local loopback for all ports",
> +	.tokens = {
> +		(void *)&cmd_setllb_set,
> +		(void *)&cmd_setllb_llb,
> +		(void *)&cmd_setllb_portall,
> +		(void *)&cmd_setllb_enalbe,
> +		NULL,
> +	},
> +};
> +
> +static cmdline_parse_inst_t cmd_set_vsi_enable_one = {
> +	.f = cmd_set_vsi_llb_parsed,
> +	.data = (void *)0,
> +	.help_str = "set llb <port_id> on|off: Set vsi local loopback on port id",
> +	.tokens = {
> +		(void *)&cmd_setllb_set,
> +		(void *)&cmd_setllb_llb,
> +		(void *)&cmd_setllb_portnum,
> +		(void *)&cmd_setllb_enalbe,
> +		NULL,
> +	},
> +};

please dont use 'vsi' in the scope of testpmd commands.

> +
>  /* *** SET ALLMULTI MODE *** */
>  struct cmd_set_allmulti_mode_result {
>  	cmdline_fixed_string_t set;
> @@ -12866,6 +12941,8 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
>  	(cmdline_parse_inst_t *)&cmd_show_port_cman_capa,
>  	(cmdline_parse_inst_t *)&cmd_show_port_cman_config,
>  	(cmdline_parse_inst_t *)&cmd_set_port_cman_config,
> +	(cmdline_parse_inst_t *)&cmd_set_vsi_enable_all,
> +	(cmdline_parse_inst_t *)&cmd_set_vsi_enable_one,

Instead of adding to end better to try it grouping with relevant commands.

>  	NULL,
>  };
>  
> diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
> index 7726a89d99..dd07c38f0f 100644
> --- a/drivers/net/i40e/i40e_ethdev.c
> +++ b/drivers/net/i40e/i40e_ethdev.c
> @@ -406,6 +406,7 @@ static void i40e_ethertype_filter_restore(struct i40e_pf *pf);
>  static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
>  static void i40e_filter_restore(struct i40e_pf *pf);
>  static void i40e_notify_all_vfs_link_status(struct rte_eth_dev *dev);
> +static int i40e_enable_pf_local_lb(struct rte_eth_dev *dev, int on);
>  
>  static const char *const valid_keys[] = {
>  	ETH_I40E_FLOATING_VEB_ARG,
> @@ -517,6 +518,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
>  	.tm_ops_get                   = i40e_tm_ops_get,
>  	.tx_done_cleanup              = i40e_tx_done_cleanup,
>  	.get_monitor_addr             = i40e_get_monitor_addr,
> +	.enable_local_lb              = i40e_enable_pf_local_lb,
>  };
>  
>  /* store statistics names and its offset in stats structure */
> @@ -5634,6 +5636,57 @@ i40e_enable_pf_lb(struct i40e_pf *pf)
>  			    hw->aq.asq_last_status);
>  }
>  
> +/* i40e_enable_pf_local_lb
> + * @pf: pointer to the pf structure
> + *
> + * allow local loopback on pf
> + */
> +static int
> +i40e_enable_pf_local_lb(struct rte_eth_dev *dev, int on)
> +{
> +	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
> +	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
> +	struct i40e_vsi_context ctxt;
> +	int ret;
> +
> +	/* Use the FW API if FW >= v5.0 */
> +	if (hw->aq.fw_maj_ver < 5 && hw->mac.type != I40E_MAC_X722) {
> +#ifdef TREX_PATCH
> +		/* Most of our customers do not have latest FW */
> +		PMD_INIT_LOG(INFO, "FW < v5.0, cannot enable local loopback");
> +#else
> +		PMD_INIT_LOG(ERR, "FW < v5.0, cannot enable local loopback");
> +#endif
> +		return I40E_NOT_SUPPORTED;
> +	}
> +
> +	memset(&ctxt, 0, sizeof(ctxt));
> +	ctxt.seid = pf->main_vsi_seid;
> +	ctxt.pf_num = hw->pf_id;
> +	ret = i40e_aq_get_vsi_params(hw, &ctxt, NULL);
> +	if (ret) {
> +		PMD_DRV_LOG(ERR, "cannot get pf vsi config, err %d, aq_err %d",
> +			    ret, hw->aq.asq_last_status);
> +		return ret;
> +	}
> +	ctxt.flags = I40E_AQ_VSI_TYPE_PF;
> +	ctxt.info.valid_sections =
> +		rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SWITCH_VALID);
> +	if (on)
> +		ctxt.info.switch_id |=
> +			rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
> +	else
> +		ctxt.info.switch_id &=
> +			~rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
> +
> +	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
> +	if (ret)
> +		PMD_DRV_LOG(ERR, "update vsi switch failed, aq_err=%d",
> +			    hw->aq.asq_last_status);
> +
> +	return ret;
> +}
> +
>  /* Setup a VSI */
>  struct i40e_vsi *
>  i40e_vsi_setup(struct i40e_pf *pf,
> diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
> index 6a550cfc83..3a11a7fab4 100644
> --- a/lib/ethdev/ethdev_driver.h
> +++ b/lib/ethdev/ethdev_driver.h
> @@ -668,6 +668,10 @@ typedef int (*eth_get_module_info_t)(struct rte_eth_dev *dev,
>  typedef int (*eth_get_module_eeprom_t)(struct rte_eth_dev *dev,
>  				       struct rte_dev_eeprom_info *info);
>  
> +/** @internal Function used to enable/disable the vsi loop back of an Ethernet device. */
> +typedef int (*eth_enable_local_lb_t)(struct rte_eth_dev *dev,
> +				  int on);
> +

What is 'VSI'? Is it common dpdk or ethdev concept? If not please don't
refer it in ethdev.



>  struct rte_flow_ops;
>  /**
>   * @internal
> @@ -1403,6 +1407,8 @@ struct eth_dev_ops {
>  	eth_cman_config_set_t cman_config_set;
>  	/** Retrieve congestion management configuration */
>  	eth_cman_config_get_t cman_config_get;
> +	/** Enable/disable the vsi loop back of an Ethernet device */
> +	eth_enable_local_lb_t enable_local_lb;

The name 'enable_local_lb' needs to refined.
Why it is called as 'local_lb'?
And again please use loopback instead of 'lb'.

>  };
>  
>  /**
> diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
> index 5d5e18db1e..3991688e2b 100644
> --- a/lib/ethdev/rte_ethdev.c
> +++ b/lib/ethdev/rte_ethdev.c
> @@ -6318,6 +6318,22 @@ rte_eth_buffer_split_get_supported_hdr_ptypes(uint16_t port_id, uint32_t *ptypes
>  	return j;
>  }
>  
> +int
> +rte_eth_dev_enable_local_lb(uint16_t port_id, int on)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
> +	dev = &rte_eth_devices[port_id];
> +
> +	if (*dev->dev_ops->enable_local_lb == NULL)
> +		return -ENOTSUP;
> +
> +	ret = (*dev->dev_ops->enable_local_lb)(dev, on);
> +	return eth_err(port_id, ret);
> +}
> +
>  RTE_LOG_REGISTER_DEFAULT(rte_eth_dev_logtype, INFO);
>  
>  RTE_INIT(ethdev_init_telemetry)
> diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
> index c129ca1eaf..c4888ebd62 100644
> --- a/lib/ethdev/rte_ethdev.h
> +++ b/lib/ethdev/rte_ethdev.h
> @@ -3437,6 +3437,21 @@ int rte_eth_dev_set_mtu(uint16_t port_id, uint16_t mtu);
>   */
>  int rte_eth_dev_vlan_filter(uint16_t port_id, uint16_t vlan_id, int on);
>  
> +/**
> + * Enable/Disable local Loopback for VSIs that are used as uplink of a software
> + * (cascaded) VEB, VEPA or Port Virtualizer.
> + *
> + * @param port_id
> + *   The port identifier of the Ethernet device.
> + * @param on
> + *   If > 0, enable local Loopback.
> + *   Otherwise, disable local Loopback.
> + * @return
> + *   - (0) if successful.
> + *   - negative if failed.
> + */
> +int rte_eth_dev_enable_local_lb(uint16_t port_id, int on);
> +

API enable/disable the feature but API name has 'enable',
so it will be calling enable_X API to disable X,
if API is to enable/disable, perhaps it can be names as 'set_X'?
  

Patch

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index cb8c174020..a9602a2ed0 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -482,6 +482,9 @@  static void cmd_help_long_parsed(void *parsed_result,
 			"set promisc (port_id|all) (on|off)\n"
 			"    Set the promiscuous mode on port_id, or all.\n\n"
 
+			"set llb (port_id|all) (on|off)\n"
+			"    Set vsi local loopback on port_id, or all.\n\n"
+
 			"set allmulti (port_id|all) (on|off)\n"
 			"    Set the allmulti mode on port_id, or all.\n\n"
 
@@ -5775,6 +5778,78 @@  static cmdline_parse_inst_t cmd_set_promisc_mode_one = {
 	},
 };
 
+/* *** SET VSI LOCAL LOOPBACK *** */
+struct cmd_set_vsi_local_lb_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t llb;
+	cmdline_fixed_string_t port_all; /* valid if "allports" argument == 1 */
+	uint16_t port_num;               /* valid if "allports" argument == 0 */
+	cmdline_fixed_string_t enable;
+};
+
+static void cmd_set_vsi_llb_parsed(void *parsed_result,
+					__rte_unused struct cmdline *cl,
+					void *allports)
+{
+	struct cmd_set_vsi_local_lb_result *res = parsed_result;
+	int enable;
+	portid_t i;
+
+	if (!strcmp(res->enable, "on"))
+		enable = 1;
+	else
+		enable = 0;
+
+	/* all ports */
+	if (allports) {
+		RTE_ETH_FOREACH_DEV(i)
+			rte_eth_dev_enable_local_lb(i, enable);
+	} else {
+		rte_eth_dev_enable_local_lb(res->port_num, enable);
+	}
+}
+
+static cmdline_parse_token_string_t cmd_setllb_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, set, "set");
+static cmdline_parse_token_string_t cmd_setllb_llb =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, llb,
+				 "llb");
+static cmdline_parse_token_string_t cmd_setllb_portall =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, port_all,
+				 "all");
+static cmdline_parse_token_num_t cmd_setllb_portnum =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_vsi_local_lb_result, port_num,
+			      RTE_UINT16);
+static cmdline_parse_token_string_t cmd_setllb_enalbe =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_vsi_local_lb_result, enable,
+				 "on#off");
+
+static cmdline_parse_inst_t cmd_set_vsi_enable_all = {
+	.f = cmd_set_vsi_llb_parsed,
+	.data = (void *)1,
+	.help_str = "set llb all on|off: Set vsi local loopback for all ports",
+	.tokens = {
+		(void *)&cmd_setllb_set,
+		(void *)&cmd_setllb_llb,
+		(void *)&cmd_setllb_portall,
+		(void *)&cmd_setllb_enalbe,
+		NULL,
+	},
+};
+
+static cmdline_parse_inst_t cmd_set_vsi_enable_one = {
+	.f = cmd_set_vsi_llb_parsed,
+	.data = (void *)0,
+	.help_str = "set llb <port_id> on|off: Set vsi local loopback on port id",
+	.tokens = {
+		(void *)&cmd_setllb_set,
+		(void *)&cmd_setllb_llb,
+		(void *)&cmd_setllb_portnum,
+		(void *)&cmd_setllb_enalbe,
+		NULL,
+	},
+};
+
 /* *** SET ALLMULTI MODE *** */
 struct cmd_set_allmulti_mode_result {
 	cmdline_fixed_string_t set;
@@ -12866,6 +12941,8 @@  static cmdline_parse_ctx_t builtin_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_show_port_cman_capa,
 	(cmdline_parse_inst_t *)&cmd_show_port_cman_config,
 	(cmdline_parse_inst_t *)&cmd_set_port_cman_config,
+	(cmdline_parse_inst_t *)&cmd_set_vsi_enable_all,
+	(cmdline_parse_inst_t *)&cmd_set_vsi_enable_one,
 	NULL,
 };
 
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 7726a89d99..dd07c38f0f 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -406,6 +406,7 @@  static void i40e_ethertype_filter_restore(struct i40e_pf *pf);
 static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
 static void i40e_filter_restore(struct i40e_pf *pf);
 static void i40e_notify_all_vfs_link_status(struct rte_eth_dev *dev);
+static int i40e_enable_pf_local_lb(struct rte_eth_dev *dev, int on);
 
 static const char *const valid_keys[] = {
 	ETH_I40E_FLOATING_VEB_ARG,
@@ -517,6 +518,7 @@  static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.tm_ops_get                   = i40e_tm_ops_get,
 	.tx_done_cleanup              = i40e_tx_done_cleanup,
 	.get_monitor_addr             = i40e_get_monitor_addr,
+	.enable_local_lb              = i40e_enable_pf_local_lb,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -5634,6 +5636,57 @@  i40e_enable_pf_lb(struct i40e_pf *pf)
 			    hw->aq.asq_last_status);
 }
 
+/* i40e_enable_pf_local_lb
+ * @pf: pointer to the pf structure
+ *
+ * allow local loopback on pf
+ */
+static int
+i40e_enable_pf_local_lb(struct rte_eth_dev *dev, int on)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	struct i40e_vsi_context ctxt;
+	int ret;
+
+	/* Use the FW API if FW >= v5.0 */
+	if (hw->aq.fw_maj_ver < 5 && hw->mac.type != I40E_MAC_X722) {
+#ifdef TREX_PATCH
+		/* Most of our customers do not have latest FW */
+		PMD_INIT_LOG(INFO, "FW < v5.0, cannot enable local loopback");
+#else
+		PMD_INIT_LOG(ERR, "FW < v5.0, cannot enable local loopback");
+#endif
+		return I40E_NOT_SUPPORTED;
+	}
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	ctxt.seid = pf->main_vsi_seid;
+	ctxt.pf_num = hw->pf_id;
+	ret = i40e_aq_get_vsi_params(hw, &ctxt, NULL);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "cannot get pf vsi config, err %d, aq_err %d",
+			    ret, hw->aq.asq_last_status);
+		return ret;
+	}
+	ctxt.flags = I40E_AQ_VSI_TYPE_PF;
+	ctxt.info.valid_sections =
+		rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SWITCH_VALID);
+	if (on)
+		ctxt.info.switch_id |=
+			rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
+	else
+		ctxt.info.switch_id &=
+			~rte_cpu_to_le_16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB);
+
+	ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+	if (ret)
+		PMD_DRV_LOG(ERR, "update vsi switch failed, aq_err=%d",
+			    hw->aq.asq_last_status);
+
+	return ret;
+}
+
 /* Setup a VSI */
 struct i40e_vsi *
 i40e_vsi_setup(struct i40e_pf *pf,
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 6a550cfc83..3a11a7fab4 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -668,6 +668,10 @@  typedef int (*eth_get_module_info_t)(struct rte_eth_dev *dev,
 typedef int (*eth_get_module_eeprom_t)(struct rte_eth_dev *dev,
 				       struct rte_dev_eeprom_info *info);
 
+/** @internal Function used to enable/disable the vsi loop back of an Ethernet device. */
+typedef int (*eth_enable_local_lb_t)(struct rte_eth_dev *dev,
+				  int on);
+
 struct rte_flow_ops;
 /**
  * @internal
@@ -1403,6 +1407,8 @@  struct eth_dev_ops {
 	eth_cman_config_set_t cman_config_set;
 	/** Retrieve congestion management configuration */
 	eth_cman_config_get_t cman_config_get;
+	/** Enable/disable the vsi loop back of an Ethernet device */
+	eth_enable_local_lb_t enable_local_lb;
 };
 
 /**
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 5d5e18db1e..3991688e2b 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6318,6 +6318,22 @@  rte_eth_buffer_split_get_supported_hdr_ptypes(uint16_t port_id, uint32_t *ptypes
 	return j;
 }
 
+int
+rte_eth_dev_enable_local_lb(uint16_t port_id, int on)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->enable_local_lb == NULL)
+		return -ENOTSUP;
+
+	ret = (*dev->dev_ops->enable_local_lb)(dev, on);
+	return eth_err(port_id, ret);
+}
+
 RTE_LOG_REGISTER_DEFAULT(rte_eth_dev_logtype, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index c129ca1eaf..c4888ebd62 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -3437,6 +3437,21 @@  int rte_eth_dev_set_mtu(uint16_t port_id, uint16_t mtu);
  */
 int rte_eth_dev_vlan_filter(uint16_t port_id, uint16_t vlan_id, int on);
 
+/**
+ * Enable/Disable local Loopback for VSIs that are used as uplink of a software
+ * (cascaded) VEB, VEPA or Port Virtualizer.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param on
+ *   If > 0, enable local Loopback.
+ *   Otherwise, disable local Loopback.
+ * @return
+ *   - (0) if successful.
+ *   - negative if failed.
+ */
+int rte_eth_dev_enable_local_lb(uint16_t port_id, int on);
+
 /**
  * Enable/Disable hardware VLAN Strip by a Rx queue of an Ethernet device.
  *