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.
  *
