diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 4e8b0d8..b780f3f 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -604,6 +604,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" pfc (on|off)\n"
 			"    Set the DCB mode.\n\n"
 
+			"port config (port_id) vmdq\n"
+			"    Configure VMDq.\n\n"
+
 			"port config all burst (value)\n"
 			"    Set the number of packets per burst.\n\n"
 
@@ -2297,6 +2300,62 @@ cmdline_parse_inst_t cmd_config_dcb = {
         },
 };
 
+/* *** Configure VMDq *** */
+struct cmd_config_vmdq {
+	cmdline_fixed_string_t port;
+	cmdline_fixed_string_t config;
+	uint8_t port_id;
+	cmdline_fixed_string_t vmdq;
+};
+
+static void
+cmd_config_vmdq_parsed(void *parsed_result,
+		       __attribute__((unused)) struct cmdline *cl,
+		       __attribute__((unused)) void *data)
+{
+	struct cmd_config_vmdq *res = parsed_result;
+	portid_t port_id = res->port_id;
+	struct rte_port *port;
+	int ret;
+
+	port = &ports[port_id];
+	/** Check if the port is not started **/
+	if (port->port_status != RTE_PORT_STOPPED) {
+		printf("Please stop port %d first\n", port_id);
+		return;
+	}
+
+	ret = init_port_vmdq_config(port_id);
+	if (ret != 0) {
+		printf("Cannot initialize network ports.\n");
+		return;
+	}
+
+	cmd_reconfig_device_queue(port_id, 0, 1);
+}
+
+cmdline_parse_token_string_t cmd_config_vmdq_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_vmdq, port, "port");
+cmdline_parse_token_string_t cmd_config_vmdq_config =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_vmdq, config, "config");
+cmdline_parse_token_num_t cmd_config_vmdq_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_config_vmdq, port_id, UINT8);
+cmdline_parse_token_string_t cmd_config_vmdq_vmdq =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_vmdq, vmdq, "vmdq");
+
+cmdline_parse_inst_t cmd_config_vmdq = {
+	.f = cmd_config_vmdq_parsed,
+	.data = NULL,
+	.help_str = "port config <port-id> vmdq",
+	.tokens = {
+		(void *)&cmd_config_vmdq_port,
+		(void *)&cmd_config_vmdq_config,
+		(void *)&cmd_config_vmdq_port_id,
+		(void *)&cmd_config_vmdq_vmdq,
+		NULL,
+	},
+};
+
 /* *** configure number of packets per burst *** */
 struct cmd_config_burst {
 	cmdline_fixed_string_t port;
@@ -11572,6 +11631,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set_autoneg,
 	(cmdline_parse_inst_t *)&cmd_priority_flow_control_set,
 	(cmdline_parse_inst_t *)&cmd_config_dcb,
+	(cmdline_parse_inst_t *)&cmd_config_vmdq,
 	(cmdline_parse_inst_t *)&cmd_read_reg,
 	(cmdline_parse_inst_t *)&cmd_read_reg_bit_field,
 	(cmdline_parse_inst_t *)&cmd_read_reg_bit,
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index bfb2f8e..9850b7f 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -196,6 +196,34 @@ uint8_t dcb_test = 0;
 queueid_t nb_rxq = 1; /**< Number of RX queues per port. */
 queueid_t nb_txq = 1; /**< Number of TX queues per port. */
 
+static const struct rte_eth_conf vmdq_conf_default = {
+	.rxmode = {
+		.mq_mode        = ETH_MQ_RX_VMDQ_ONLY,
+		.split_hdr_size = 0,
+		.header_split   = 0, /**< Header Split disabled */
+		.hw_ip_checksum = 0, /**< IP checksum offload disabled */
+		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
+		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
+	},
+
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+	.rx_adv_conf = {
+		/*
+		 * should be overridden separately in code with
+		 * appropriate values
+		 */
+		.vmdq_rx_conf = {
+			.nb_queue_pools = ETH_8_POOLS,
+			.enable_default_pool = 0,
+			.default_pool = 0,
+			.nb_pool_maps = 0,
+			.pool_map = {{0, 0},},
+		},
+	},
+};
+
 /*
  * Configurable number of RX/TX ring descriptors.
  */
@@ -1895,6 +1923,104 @@ const uint16_t vlan_tags[] = {
 		24, 25, 26, 27, 28, 29, 30, 31
 };
 
+const uint16_t num_vlans = RTE_DIM(vlan_tags);
+static uint16_t num_pf_queues, num_vmdq_queues;
+static uint16_t vmdq_pool_base, vmdq_queue_base;
+/* number of pools (if user does not specify any, 8 by default */
+static uint32_t num_queues = 8;
+static uint32_t num_pools = 8;
+
+/**
+ * Builds up the correct configuration for vmdq based on the vlan tags array
+ * given above, and determine the queue number and pool map number according to
+ * valid pool number
+ */
+static int
+get_eth_vmdq_conf(struct rte_eth_conf *eth_conf, uint32_t num_pools)
+{
+	struct rte_eth_vmdq_rx_conf conf;
+	unsigned i;
+
+	conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
+	conf.nb_pool_maps = num_pools;
+	conf.enable_default_pool = 0;
+	conf.default_pool = 0; /* set explicit value, even if not used */
+
+	for (i = 0; i < conf.nb_pool_maps; i++) {
+		conf.pool_map[i].vlan_id = vlan_tags[i];
+		conf.pool_map[i].pools = (1UL << (i % num_pools));
+	}
+
+	(void)(rte_memcpy(eth_conf, &vmdq_conf_default, sizeof(*eth_conf)));
+	(void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_rx_conf, &conf,
+	       sizeof(eth_conf->rx_adv_conf.vmdq_rx_conf)));
+	return 0;
+}
+
+/**
+ * Configures VMDq for  a given port using global settings.
+ */
+int
+init_port_vmdq_config(uint8_t port)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf;
+	uint16_t rx_queues, tx_queues;
+	int retval;
+	uint16_t queues_per_pool;
+	uint32_t max_nb_pools;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+	/**
+	 * The max pool number from dev_info will be used to validate the pool
+	 * number.
+	 */
+	rte_eth_dev_info_get(port, &dev_info);
+	max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
+	/**
+	 * We allow to process part of VMDQ pools specified by num_pools in
+	 * command line.
+	 */
+	if (num_pools > max_nb_pools) {
+		printf("num_pools %d >max_nb_pools %d\n",
+			num_pools, max_nb_pools);
+		return -1;
+	}
+
+	retval = get_eth_vmdq_conf(&port_conf, num_pools);
+	if (retval < 0)
+		return retval;
+
+	/*
+	 * NIC queues are divided into pf queues and vmdq queues.
+	 */
+	/* There is assumption here all ports have the same configuration! */
+	num_pf_queues = dev_info.max_rx_queues - dev_info.vmdq_queue_num;
+	queues_per_pool = dev_info.vmdq_queue_num / dev_info.max_vmdq_pools;
+	num_vmdq_queues = num_pools * queues_per_pool;
+	num_queues = num_pf_queues + num_vmdq_queues;
+	vmdq_queue_base = dev_info.vmdq_queue_base;
+	vmdq_pool_base  = dev_info.vmdq_pool_base;
+
+	printf("pf queue num: %u, configured vmdq pool num: %u,"
+		" each vmdq pool has %u queues\n",
+		num_pf_queues, num_pools, queues_per_pool);
+	printf("vmdq queue base: %d pool base %d\n",
+		vmdq_queue_base, vmdq_pool_base);
+
+	/*
+	 * All queues including pf queues are setup.
+	 * This is because VMDQ queues doesn't always start from zero, and the
+	 * PMD layer doesn't support selectively initialising part of rx/tx
+	 * queues.
+	 */
+	rx_queues = (uint16_t)dev_info.max_rx_queues;
+	tx_queues = (uint16_t)dev_info.max_tx_queues;
+	retval = rte_eth_dev_configure(port, rx_queues, tx_queues, &port_conf);
+	return retval;
+}
+
 static  int
 get_eth_dcb_conf(struct rte_eth_conf *eth_conf,
 		 enum dcb_mode_enable dcb_mode,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 22ce2d6..6e72420 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -580,6 +580,7 @@ uint8_t port_is_bonding_slave(portid_t slave_pid);
 int init_port_dcb_config(portid_t pid, enum dcb_mode_enable dcb_mode,
 		     enum rte_eth_nb_tcs num_tcs,
 		     uint8_t pfc_en);
+int init_port_vmdq_config(uint8_t port);
 int start_port(portid_t pid);
 void stop_port(portid_t pid);
 void close_port(portid_t pid);
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index c611dc5..d9839cc 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -1374,6 +1374,13 @@ Set the DCB mode for an individual port::
 
 The traffic class should be 4 or 8.
 
+port config - VMDq
+~~~~~~~~~~~~~~~~~~
+
+Configure VMDq for an individual port::
+
+   testpmd> port config (port_id) vmdq
+
 port config - Burst
 ~~~~~~~~~~~~~~~~~~~
 
