Soft Patch Panel
 help / color / mirror / Atom feed
From: x-fn-spp-ml@ntt-tx.co.jp
To: ferruh.yigit@intel.com, yasufum.o@gmail.com
Cc: spp@dpdk.org
Subject: [spp] [PATCH 01/17] shared: add support of multi-queue
Date: Tue, 18 Feb 2020 15:37:04 +0900	[thread overview]
Message-ID: <20200218063720.6597-2-x-fn-spp-ml@ntt-tx.co.jp> (raw)
In-Reply-To: <20200218063720.6597-1-x-fn-spp-ml@ntt-tx.co.jp>

From: Hideyuki Yamashita <yamashita.hideyuki@ntt-tx.co.jp>

To support multi-queue in SPP,changes like following should be
introduced:
- add queue number parameter in port command
- parse newly added parameter
- add queue number parameter in status response

Under shared directory, common logic among secondary process exist
and those should be affected.

Signed-off-by: Hideyuki Yamashita <yamashita.hideyuki@ntt-tx.co.jp>
Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
---
 src/shared/basic_forwarder.c                  |  62 +++---
 src/shared/basic_forwarder.h                  |   2 +-
 src/shared/common.h                           |  30 ++-
 src/shared/port_manager.c                     | 180 ++++++++++++++----
 src/shared/port_manager.h                     |  13 +-
 .../secondary/spp_worker_th/cmd_parser.c      | 137 ++++++++++---
 .../spp_worker_th/cmd_res_formatter.c         |  63 ++++--
 .../secondary/spp_worker_th/cmd_utils.c       | 111 ++++++++---
 .../secondary/spp_worker_th/cmd_utils.h       |  19 +-
 .../secondary/spp_worker_th/data_types.h      |  18 +-
 .../secondary/spp_worker_th/port_capability.c |   8 +-
 .../secondary/spp_worker_th/port_capability.h |   4 +-
 src/shared/secondary/spp_worker_th/vf_deps.h  |  15 +-
 src/shared/secondary/utils.c                  |  28 ++-
 src/shared/secondary/utils.h                  |   3 +-
 15 files changed, 535 insertions(+), 158 deletions(-)

diff --git a/src/shared/basic_forwarder.c b/src/shared/basic_forwarder.c
index 7aefaaa..66f8643 100644
--- a/src/shared/basic_forwarder.c
+++ b/src/shared/basic_forwarder.c
@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include "shared/common.h"
 #include "shared/basic_forwarder.h"
+#include "shared/port_manager.h"
 
 void
 forward(void)
@@ -14,42 +15,53 @@ forward(void)
 	int in_port;
 	int out_port;
 	uint16_t buf;
-	int i;
+	int i, j;
+	uint16_t max_queue, in_queue, out_queue;
 
 	/* Go through every possible port numbers*/
 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
-		struct rte_mbuf *bufs[MAX_PKT_BURST];
 
-		if (ports_fwd_array[i].in_port_id == PORT_RESET)
-			continue;
+		max_queue = get_port_max_queues(i);
 
-		if (ports_fwd_array[i].out_port_id == PORT_RESET)
-			continue;
+		/* Checks the number of rxq declared for the port */
+		for (j = 0; j < max_queue; j++) {
 
-		/* if status active, i count is in port*/
-		in_port = i;
-		out_port = ports_fwd_array[i].out_port_id;
+			struct rte_mbuf *bufs[MAX_PKT_BURST];
 
-		/* Get burst of RX packets, from first port of pair. */
-		/*first port rx, second port tx*/
-		nb_rx = ports_fwd_array[in_port].rx_func(in_port, 0, bufs,
-			MAX_PKT_BURST);
-		if (unlikely(nb_rx == 0))
-			continue;
+			if (ports_fwd_array[i][j].in_port_id == PORT_RESET)
+				continue;
 
-		port_map[in_port].stats->rx += nb_rx;
+			if (ports_fwd_array[i][j].out_port_id == PORT_RESET)
+				continue;
 
-		/* Send burst of TX packets, to second port of pair. */
-		nb_tx = ports_fwd_array[out_port].tx_func(out_port, 0, bufs,
-			nb_rx);
+			/* if status active, i count is in port*/
+			in_port = i;
+			in_queue = j;
+			out_port = ports_fwd_array[i][j].out_port_id;
+			out_queue = ports_fwd_array[i][j].out_queue_id;
 
-		port_map[out_port].stats->tx += nb_tx;
+			/* Get burst of RX packets, from first port of pair. */
+			/*first port rx, second port tx*/
+			nb_rx = ports_fwd_array[in_port][in_queue].rx_func(
+				in_port, in_queue, bufs, MAX_PKT_BURST);
+			if (unlikely(nb_rx == 0))
+				continue;
 
-		/* Free any unsent packets. */
-		if (unlikely(nb_tx < nb_rx)) {
-			port_map[out_port].stats->tx_drop += nb_rx - nb_tx;
-			for (buf = nb_tx; buf < nb_rx; buf++)
-				rte_pktmbuf_free(bufs[buf]);
+			port_map[in_port].stats->rx += nb_rx;
+
+			/* Send burst of TX packets, to second port of pair. */
+			nb_tx = ports_fwd_array[out_port][out_queue].tx_func(
+				out_port, out_queue, bufs, nb_rx);
+
+			port_map[out_port].stats->tx += nb_tx;
+
+			/* Free any unsent packets. */
+			if (unlikely(nb_tx < nb_rx)) {
+				port_map[out_port].stats->tx_drop += (nb_rx -
+					nb_tx);
+				for (buf = nb_tx; buf < nb_rx; buf++)
+					rte_pktmbuf_free(bufs[buf]);
+			}
 		}
 	}
 }
diff --git a/src/shared/basic_forwarder.h b/src/shared/basic_forwarder.h
index 2e8225d..915b5cc 100644
--- a/src/shared/basic_forwarder.h
+++ b/src/shared/basic_forwarder.h
@@ -8,7 +8,7 @@
 #include "shared/common.h"
 
 struct port_map port_map[RTE_MAX_ETHPORTS];
-struct port ports_fwd_array[RTE_MAX_ETHPORTS];
+struct port ports_fwd_array[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
 
 void forward(void);
 
diff --git a/src/shared/common.h b/src/shared/common.h
index b4af73c..d311e82 100644
--- a/src/shared/common.h
+++ b/src/shared/common.h
@@ -10,7 +10,23 @@
 #include <unistd.h>
 #include <rte_ethdev_driver.h>
 
-#define MSG_SIZE 2048  /* socket buffer len */
+/*
+ * TODO(tx_h-yamashita): Remove this definition because it was used from
+ * spp_primary and spp_nfv and its value was 2048, but the value should
+ * be changed to 32KiB for containing status of spp_primary.
+ * For spp_nfv, 2048 is still enough. This buff len should be defined in
+ * each of spp_primary and spp_nfv as appropriate size.
+ */
+/*
+ * NOTE: The size of 32768(32Kbyte) is enough to set maximum size of JSON
+ * response string. This maximum size comes from primary`s `status` command
+ * response which includes `LCORE`, `PHY` and `RING` information.
+ * For supporting rte_flow API, `PHY` information may contain a lot of
+ * flow data which defined by user. Our design choice is that `PHY`
+ * information size would be 30Kbyte and `LCORE` size plus `PHY` size
+ * would be 2Kbyte.
+ */
+#define MSG_SIZE 32768  /* socket buffer max len */
 
 #define SOCK_RESET  -1
 #define PORT_RESET  UINT16_MAX
@@ -70,11 +86,19 @@ struct stats {
 	uint64_t tx_drop;
 } __rte_cache_aligned;
 
+/* rx_queue and tx_queue set to port. */
+struct port_queue {
+	uint16_t rxq;
+	uint16_t txq;
+};
+
 struct port_info {
 	uint16_t num_ports;
 	uint16_t id[RTE_MAX_ETHPORTS];
 	struct stats port_stats[RTE_MAX_ETHPORTS];
 	struct stats client_stats[MAX_CLIENT];
+	/* num of queues per port */
+	struct port_queue queue_info[RTE_MAX_ETHPORTS];
 };
 
 enum port_type {
@@ -93,11 +117,15 @@ struct port_map {
 	enum port_type port_type;
 	struct stats *stats;
 	struct stats default_stats;
+	/* num of queues per port */
+	struct port_queue *queue_info;
 };
 
 struct port {
 	uint16_t in_port_id;
+	uint16_t in_queue_id;
 	uint16_t out_port_id;
+	uint16_t out_queue_id;
 	uint16_t (*rx_func)(uint16_t, uint16_t, struct rte_mbuf **, uint16_t);
 	uint16_t (*tx_func)(uint16_t, uint16_t, struct rte_mbuf **, uint16_t);
 };
diff --git a/src/shared/port_manager.c b/src/shared/port_manager.c
index 534af99..6beeca7 100644
--- a/src/shared/port_manager.c
+++ b/src/shared/port_manager.c
@@ -16,35 +16,46 @@ struct porttype_map portmap[] = {
 };
 
 void
-forward_array_init_one(unsigned int i)
+forward_array_init_one(unsigned int i, unsigned int j)
 {
-	ports_fwd_array[i].in_port_id = PORT_RESET;
-	ports_fwd_array[i].out_port_id = PORT_RESET;
+	ports_fwd_array[i][j].in_port_id = PORT_RESET;
+	ports_fwd_array[i][j].in_queue_id = 0;
+	ports_fwd_array[i][j].out_port_id = PORT_RESET;
+	ports_fwd_array[i][j].out_queue_id = 0;
 }
 
 /* initialize forward array with default value */
 void
 forward_array_init(void)
 {
-	unsigned int i;
+	unsigned int i, j;
 
 	/* initialize port forward array*/
-	for (i = 0; i < RTE_MAX_ETHPORTS; i++)
-		forward_array_init_one(i);
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		for (j = 0; j < RTE_MAX_QUEUES_PER_PORT; j++)
+			forward_array_init_one(i, j);
+	}
 }
 
 void
 forward_array_reset(void)
 {
-	unsigned int i;
+	unsigned int i, j;
+	uint16_t max_queue;
 
 	/* initialize port forward array*/
 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
-		if (ports_fwd_array[i].in_port_id != PORT_RESET) {
-			ports_fwd_array[i].out_port_id = PORT_RESET;
-			RTE_LOG(INFO, SHARED, "Port ID %d\n", i);
-			RTE_LOG(INFO, SHARED, "out_port_id %d\n",
-				ports_fwd_array[i].out_port_id);
+
+		max_queue = get_port_max_queues(i);
+
+		for (j = 0; j < max_queue; j++) {
+			if (ports_fwd_array[i][j].in_port_id != PORT_RESET) {
+				ports_fwd_array[i][j].out_port_id = PORT_RESET;
+				RTE_LOG(INFO, SHARED, "Port ID %d\n", i);
+				RTE_LOG(INFO, SHARED, "Queue ID %d\n", j);
+				RTE_LOG(INFO, SHARED, "out_port_id %d\n",
+					ports_fwd_array[i][j].out_port_id);
+			}
 		}
 	}
 }
@@ -55,6 +66,7 @@ port_map_init_one(unsigned int i)
 	port_map[i].id = PORT_RESET;
 	port_map[i].port_type = UNDEF;
 	port_map[i].stats = &port_map[i].default_stats;
+	port_map[i].queue_info = NULL;
 }
 
 void
@@ -68,28 +80,46 @@ port_map_init(void)
 
 /* Return -1 as an error if given patch is invalid */
 int
-add_patch(uint16_t in_port, uint16_t out_port)
+add_patch(uint16_t in_port, uint16_t in_queue,
+		uint16_t out_port, uint16_t out_queue)
 {
-	if (!is_valid_port(in_port) || !is_valid_port(out_port))
+	if (!is_valid_port(in_port, in_queue) ||
+		!is_valid_port(out_port, out_queue))
 		return -1;
 
+	if (!is_valid_port_rxq(in_port, in_queue) ||
+		!is_valid_port_txq(out_port, out_queue))
+		return 1;
+
 	/* Populate in port data */
-	ports_fwd_array[in_port].in_port_id = in_port;
-	ports_fwd_array[in_port].rx_func = &rte_eth_rx_burst;
-	ports_fwd_array[in_port].tx_func = &rte_eth_tx_burst;
-	ports_fwd_array[in_port].out_port_id = out_port;
+	ports_fwd_array[in_port][in_queue].in_port_id = in_port;
+	ports_fwd_array[in_port][in_queue].in_queue_id = in_queue;
+	ports_fwd_array[in_port][in_queue].rx_func = &rte_eth_rx_burst;
+	ports_fwd_array[in_port][in_queue].tx_func = &rte_eth_tx_burst;
+	ports_fwd_array[in_port][in_queue].out_port_id = out_port;
+	ports_fwd_array[in_port][in_queue].out_queue_id = out_queue;
 
 	/* Populate out port data */
-	ports_fwd_array[out_port].in_port_id = out_port;
-	ports_fwd_array[out_port].rx_func = &rte_eth_rx_burst;
-	ports_fwd_array[out_port].tx_func = &rte_eth_tx_burst;
+	ports_fwd_array[out_port][out_queue].in_port_id = out_port;
+	ports_fwd_array[out_port][out_queue].in_queue_id = out_queue;
+	ports_fwd_array[out_port][out_queue].rx_func = &rte_eth_rx_burst;
+	ports_fwd_array[out_port][out_queue].tx_func = &rte_eth_tx_burst;
 
-	RTE_LOG(DEBUG, SHARED, "STATUS: in port %d in_port_id %d\n", in_port,
-		ports_fwd_array[in_port].in_port_id);
-	RTE_LOG(DEBUG, SHARED, "STATUS: in port %d patch out port id %d\n",
-		in_port, ports_fwd_array[in_port].out_port_id);
-	RTE_LOG(DEBUG, SHARED, "STATUS: outport %d in_port_id %d\n", out_port,
-		ports_fwd_array[out_port].in_port_id);
+	RTE_LOG(DEBUG, SHARED, "STATUS: in port %d in queue %d"
+		" in_port_id %d in_queue_id %d\n",
+		in_port, in_queue,
+		ports_fwd_array[in_port][in_queue].in_port_id,
+		ports_fwd_array[in_port][in_queue].in_queue_id);
+	RTE_LOG(DEBUG, SHARED, "STATUS: in port %d in queue %d"
+		" patch out_port_id %d out_queue_id %d\n",
+		in_port, in_queue,
+		ports_fwd_array[in_port][in_queue].out_port_id,
+		ports_fwd_array[in_port][in_queue].out_queue_id);
+	RTE_LOG(DEBUG, SHARED, "STATUS: out port %d out queue %d"
+		" in_port_id %d in_queue_id %d\n",
+		out_port, out_queue,
+		ports_fwd_array[out_port][out_queue].in_port_id,
+		ports_fwd_array[out_port][out_queue].in_queue_id);
 
 	return 0;
 }
@@ -119,30 +149,92 @@ find_port_id(int id, enum port_type type)
 
 /* Return 0 if invalid */
 int
-is_valid_port(uint16_t port_id)
+is_valid_port(uint16_t port_id, uint16_t queue_id)
 {
+	uint16_t max_queue;
+
 	if (port_id > RTE_MAX_ETHPORTS)
 		return 0;
 
-	return port_map[port_id].id != PORT_RESET;
+	if (port_map[port_id].id == PORT_RESET)
+		return 0;
+
+	max_queue = get_port_max_queues(port_id);
+	if (queue_id >= max_queue)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Check if rxq exceeds the number of queues defined for the port.
+ * Return 0 if invalid.
+ */
+int
+is_valid_port_rxq(uint16_t port_id, uint16_t rxq)
+{
+	uint16_t nof_queues;
+
+	if (port_map[port_id].queue_info != NULL) {
+		nof_queues = port_map[port_id].queue_info->rxq;
+	} else {
+		/* default number of queues is 1 */
+		nof_queues = 1;
+	}
+	if (rxq >= nof_queues)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Check if txq exceeds the number of queues defined for the port.
+ * Return 0 if invalid.
+ */
+int
+is_valid_port_txq(uint16_t port_id, uint16_t txq)
+{
+	uint16_t nof_queues;
+
+	if (port_map[port_id].queue_info != NULL) {
+		nof_queues = port_map[port_id].queue_info->txq;
+	} else {
+		/* default number of queues is 1 */
+		nof_queues = 1;
+	}
+	if (txq >= nof_queues)
+		return 0;
+
+	return 1;
 }
 
 void
-forward_array_remove(int port_id)
+forward_array_remove(int port_id, uint16_t queue_id)
 {
-	unsigned int i;
+	unsigned int i, j;
+	uint16_t max_queue;
+	int remove_flg = 0;
 
 	/* Update ports_fwd_array */
-	forward_array_init_one(port_id);
+	forward_array_init_one(port_id, queue_id);
 
 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
-		if (ports_fwd_array[i].in_port_id == PORT_RESET)
-			continue;
 
-		if (ports_fwd_array[i].out_port_id == port_id) {
-			ports_fwd_array[i].out_port_id = PORT_RESET;
+		max_queue = get_port_max_queues(i);
+
+		for (j = 0; j < max_queue; j++) {
+			if (ports_fwd_array[i][j].in_port_id == PORT_RESET &&
+				(ports_fwd_array[i][j].out_port_id != port_id ||
+				ports_fwd_array[i][j].out_queue_id != queue_id))
+				continue;
+
+			ports_fwd_array[i][j].out_port_id = PORT_RESET;
+			remove_flg = 1;
 			break;
 		}
+
+		if (remove_flg)
+			break;
 	}
 }
 
@@ -157,3 +249,19 @@ enum port_type get_port_type(char *portname)
 	}
 	return UNDEF;
 }
+
+/* Returns a larger number of queues of RX or TX port as the maximum number */
+uint16_t
+get_port_max_queues(uint16_t port_id)
+{
+	uint16_t max_queue = 1; /* default max_queue is 1 */
+
+	if (port_map[port_id].queue_info != NULL) {
+		if (port_map[port_id].queue_info->rxq >=
+			port_map[port_id].queue_info->txq)
+			max_queue = port_map[port_id].queue_info->rxq;
+		else
+			max_queue = port_map[port_id].queue_info->txq;
+	}
+	return max_queue;
+}
diff --git a/src/shared/port_manager.h b/src/shared/port_manager.h
index c529998..8317687 100644
--- a/src/shared/port_manager.h
+++ b/src/shared/port_manager.h
@@ -17,20 +17,25 @@ struct porttype_map {
 };
 
 /* initialize forward array with default value */
-void forward_array_init_one(unsigned int i);
+void forward_array_init_one(unsigned int i, unsigned int j);
 void forward_array_init(void);
 void forward_array_reset(void);
-void forward_array_remove(int port_id);
+void forward_array_remove(int port_id, uint16_t queue_id);
 
 void port_map_init_one(unsigned int i);
 void port_map_init(void);
 
 enum port_type get_port_type(char *portname);
 
-int add_patch(uint16_t in_port, uint16_t out_port);
+int add_patch(uint16_t in_port, uint16_t in_queue,
+	uint16_t out_port, uint16_t out_queue);
 
 uint16_t find_port_id(int id, enum port_type type);
 
-int is_valid_port(uint16_t port_id);
+int is_valid_port(uint16_t port_id, uint16_t queue_id);
+int is_valid_port_rxq(uint16_t port_id, uint16_t rxq);
+int is_valid_port_txq(uint16_t port_id, uint16_t txq);
+
+uint16_t get_port_max_queues(uint16_t port_id);
 
 #endif  // __SHARED_PORT_MANAGER_H__
diff --git a/src/shared/secondary/spp_worker_th/cmd_parser.c b/src/shared/secondary/spp_worker_th/cmd_parser.c
index 3f947f5..6e66f35 100644
--- a/src/shared/secondary/spp_worker_th/cmd_parser.c
+++ b/src/shared/secondary/spp_worker_th/cmd_parser.c
@@ -120,10 +120,10 @@ const char *PORT_ABILITY_LIST[] = {
 static int
 is_used_with_addr(
 		int vid, uint64_t mac_addr,
-		enum port_type iface_type, int iface_no)
+		enum port_type iface_type, int iface_no, int queue_no)
 {
 	struct sppwk_port_info *wk_port = get_sppwk_port(
-			iface_type, iface_no);
+			iface_type, iface_no, queue_no);
 
 	return ((mac_addr == wk_port->cls_attrs.mac_addr) &&
 		(vid == wk_port->cls_attrs.vlantag.vid));
@@ -131,25 +131,32 @@ is_used_with_addr(
 
 /* Return 1 as true if given port is already used. */
 static int
-is_added_port(enum port_type iface_type, int iface_no)
+is_added_port(enum port_type iface_type, int iface_no, int queue_no)
 {
-	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no);
+	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no,
+			queue_no);
 	return port->iface_type != UNDEF;
 }
 
 /**
- * Separate resource UID of combination of iface type and number and assign to
- * given argument, iface_type and iface_no. For instance, 'ring:0' is separated
- * to 'ring' and '0'. The supported types are `phy`, `vhost` and `ring`.
+ * Extract `iface_type`, `iface_no` and `queue_no` from `res_uid`.
+ * Only phy port has `queue_no`, such as 'phy:0nq1', if using multi-queue.
+ * For other port types other than `phy`,
+ * it returnes `DEFAULT_QUEUE_ID` as `queue_no`.
  */
 static int
 parse_resource_uid(const char *res_uid,
-		    enum port_type *iface_type,
-		    int *iface_no)
+			enum port_type *iface_type,
+			int *iface_no,
+			int *queue_no)
 {
 	enum port_type ptype = UNDEF;
+	const char *iface_no_and_queue_no_str = NULL;
+	const char *queue_no_with_separator_str = NULL;
 	const char *iface_no_str = NULL;
-	char *endptr = NULL;
+	char *queue_no_endptr = NULL;
+	char *iface_no_endptr = NULL;
+	int queue_id, multi_queue_flg;
 
 	/**
 	 * TODO(yasufum) consider this checking of zero value is recommended
@@ -158,33 +165,84 @@ parse_resource_uid(const char *res_uid,
 	if (strncmp(res_uid, SPPWK_PHY_STR ":",
 			strlen(SPPWK_PHY_STR)+1) == 0) {
 		ptype = PHY;
-		iface_no_str = &res_uid[strlen(SPPWK_PHY_STR)+1];
+		iface_no_and_queue_no_str = &res_uid[strlen(SPPWK_PHY_STR)+1];
 	} else if (strncmp(res_uid, SPPWK_VHOST_STR ":",
 			strlen(SPPWK_VHOST_STR)+1) == 0) {
 		ptype = VHOST;
-		iface_no_str = &res_uid[strlen(SPPWK_VHOST_STR)+1];
+		iface_no_and_queue_no_str = &res_uid[strlen(SPPWK_VHOST_STR)+1];
 	} else if (strncmp(res_uid, SPPWK_RING_STR ":",
 			strlen(SPPWK_RING_STR)+1) == 0) {
 		ptype = RING;
-		iface_no_str = &res_uid[strlen(SPPWK_RING_STR)+1];
+		iface_no_and_queue_no_str = &res_uid[strlen(SPPWK_RING_STR)+1];
+	} else {
+		RTE_LOG(ERR, WK_CMD_PARSER, "Unexpected port type in '%s'.\n",
+				res_uid);
+		return SPPWK_RET_NG;
+	}
+
+	/* Parse queue number. */
+	queue_no_with_separator_str = strstr(iface_no_and_queue_no_str,
+			DELIM_PHY_MQ);
+	if (queue_no_with_separator_str == NULL) {
+		iface_no_str = iface_no_and_queue_no_str;
+		queue_id = DEFAULT_QUEUE_ID;
+		multi_queue_flg = 0;
+	} else if (ptype == PHY) {
+		const char *queue_no_str =
+				&queue_no_with_separator_str[
+				strlen(DELIM_PHY_MQ)];
+		queue_id = strtol(queue_no_str, &queue_no_endptr, 0);
+		if (unlikely(queue_no_str == queue_no_endptr) ||
+				unlikely(*queue_no_endptr != '\0')) {
+			RTE_LOG(ERR, WK_CMD_PARSER, "No queue number in '%s'.\n",
+					res_uid);
+			return SPPWK_RET_NG;
+		}
+		iface_no_str = iface_no_and_queue_no_str;
+		multi_queue_flg = 1;
 	} else {
 		RTE_LOG(ERR, WK_CMD_PARSER, "Unexpected port type in '%s'.\n",
 				res_uid);
 		return SPPWK_RET_NG;
 	}
 
-	int port_id = strtol(iface_no_str, &endptr, 0);
-	if (unlikely(iface_no_str == endptr) || unlikely(*endptr != '\0')) {
+	/* Parse interface number. */
+	int port_id = strtol(iface_no_str, &iface_no_endptr, 0);
+	if (unlikely(iface_no_str == iface_no_endptr) ||
+		(unlikely(*iface_no_endptr != '\0') &&
+		(unlikely(strstr(iface_no_endptr,
+			DELIM_PHY_MQ) != iface_no_endptr)))) {
 		RTE_LOG(ERR, WK_CMD_PARSER, "No interface number in '%s'.\n",
 				res_uid);
 		return SPPWK_RET_NG;
 	}
+	if (unlikely(port_id > RTE_MAX_ETHPORTS) || unlikely(port_id < 0)) {
+		RTE_LOG(ERR, WK_CMD_PARSER, "Unexpected interface number in '%s'.\n",
+				res_uid);
+		return SPPWK_RET_NG;
+	}
+
+	/**
+	 * Check whether the queue number is specified according
+	 * to the port format.
+	 */
+	if (ptype == PHY) {
+		int port_multi_queue_flg =
+			(get_port_max_queues(ptype, port_id) > 1);
+		if (unlikely(multi_queue_flg != port_multi_queue_flg)) {
+			RTE_LOG(ERR, WK_CMD_PARSER,
+					"Unexpected queue number format in"
+					" '%s'.\n", res_uid);
+			return SPPWK_RET_NG;
+		}
+	}
 
 	*iface_type = ptype;
 	*iface_no = port_id;
+	*queue_no = queue_id;
 
-	RTE_LOG(DEBUG, WK_CMD_PARSER, "Parsed '%s' to '%d' and '%d'.\n",
-			res_uid, *iface_type, *iface_no);
+	RTE_LOG(DEBUG, WK_CMD_PARSER, "Parsed '%s' to '%d' and '%d' and '%d'.\n",
+			res_uid, *iface_type, *iface_no, *queue_no);
 	return SPPWK_RET_OK;
 }
 
@@ -302,7 +360,8 @@ parse_port_uid(void *output, const char *arg_val)
 {
 	int ret;
 	struct sppwk_port_idx *port = output;
-	ret = parse_resource_uid(arg_val, &port->iface_type, &port->iface_no);
+	ret = parse_resource_uid(arg_val, &port->iface_type, &port->iface_no,
+			&port->queue_no);
 	if (unlikely(ret != 0)) {
 		RTE_LOG(ERR, WK_CMD_PARSER,
 				"Invalid resource UID '%s'.\n", arg_val);
@@ -465,9 +524,11 @@ parse_port(void *output, const char *arg_val, int allow_override)
 		if ((port->wk_action == SPPWK_ACT_ADD) &&
 				(sppwk_check_used_port(tmp_port.iface_type,
 						tmp_port.iface_no,
+						tmp_port.queue_no,
 						SPPWK_PORT_DIR_RX) >= 0) &&
 				(sppwk_check_used_port(tmp_port.iface_type,
 						tmp_port.iface_no,
+						tmp_port.queue_no,
 						SPPWK_PORT_DIR_TX) >= 0)) {
 			RTE_LOG(ERR, WK_CMD_PARSER,
 				"Port `%s` is already used.\n",
@@ -478,6 +539,7 @@ parse_port(void *output, const char *arg_val, int allow_override)
 
 	port->port.iface_type = tmp_port.iface_type;
 	port->port.iface_no   = tmp_port.iface_no;
+	port->port.queue_no   = tmp_port.queue_no;
 	return SPPWK_RET_OK;
 }
 
@@ -495,11 +557,36 @@ parse_port_direction(void *output, const char *arg_val, int allow_override)
 		return SPPWK_RET_NG;
 	}
 
-	/* add vlantag command check */
+	/* Add queue number command check. */
+	if ((port->wk_action == SPPWK_ACT_ADD) &&
+			(port->port.iface_type == PHY)) {
+		const struct port_info *ports = NULL;
+		const struct rte_memzone *mz;
+		mz = rte_memzone_lookup(MZ_PORT_INFO);
+		ports = mz->addr;
+		int max_port_queue = -1;
+		if (ret == SPPWK_PORT_DIR_RX)
+			max_port_queue = ports->queue_info
+					[port->port.iface_no].rxq - 1;
+		else if (ret == SPPWK_PORT_DIR_TX)
+			max_port_queue = ports->queue_info
+					[port->port.iface_no].txq - 1;
+		if (unlikely(port->port.queue_no > max_port_queue)) {
+			RTE_LOG(ERR, WK_CMD_PARSER,
+					"The queue number exceeds the %s max value."
+					" queue=%d, max=%d.\n",
+					PORT_DIR_LIST[ret], port->port.queue_no,
+					max_port_queue);
+			return SPPWK_RET_NG;
+		}
+	}
+
+	/* Add vlantag command check. */
 	if (allow_override == 0) {
 		if ((port->wk_action == SPPWK_ACT_ADD) &&
 				(sppwk_check_used_port(port->port.iface_type,
-					port->port.iface_no, ret) >= 0)) {
+					port->port.iface_no,
+					port->port.queue_no, ret) >= 0)) {
 			RTE_LOG(ERR, WK_CMD_PARSER,
 				"Port in used. (port command) val=%s\n",
 				arg_val);
@@ -726,7 +813,8 @@ parse_cls_port(void *cls_cmd_attr, const char *arg_val,
 	if (ret < SPPWK_RET_OK)
 		return SPPWK_RET_NG;
 
-	if (is_added_port(tmp_port.iface_type, tmp_port.iface_no) == 0) {
+	if (is_added_port(tmp_port.iface_type, tmp_port.iface_no,
+			tmp_port.queue_no) == 0) {
 		RTE_LOG(ERR, WK_CMD_PARSER, "Port not added. val=%s\n",
 				arg_val);
 		return SPPWK_RET_NG;
@@ -737,7 +825,8 @@ parse_cls_port(void *cls_cmd_attr, const char *arg_val,
 
 	if (unlikely(cls_attrs->wk_action == SPPWK_ACT_ADD)) {
 		if (!is_used_with_addr(ETH_VLAN_ID_MAX, 0,
-				tmp_port.iface_type, tmp_port.iface_no)) {
+				tmp_port.iface_type, tmp_port.iface_no,
+				tmp_port.queue_no)) {
 			RTE_LOG(ERR, WK_CMD_PARSER, "Port in used. "
 					"(classifier_table command) val=%s\n",
 					arg_val);
@@ -750,7 +839,8 @@ parse_cls_port(void *cls_cmd_attr, const char *arg_val,
 
 		if (!is_used_with_addr(cls_attrs->vid,
 				(uint64_t)mac_addr,
-				tmp_port.iface_type, tmp_port.iface_no)) {
+				tmp_port.iface_type, tmp_port.iface_no,
+				tmp_port.queue_no)) {
 			RTE_LOG(ERR, WK_CMD_PARSER, "Port in used. "
 					"(classifier_table command) val=%s\n",
 					arg_val);
@@ -760,6 +850,7 @@ parse_cls_port(void *cls_cmd_attr, const char *arg_val,
 
 	cls_attrs->port.iface_type = tmp_port.iface_type;
 	cls_attrs->port.iface_no   = tmp_port.iface_no;
+	cls_attrs->port.queue_no   = tmp_port.queue_no;
 	return SPPWK_RET_OK;
 }
 
diff --git a/src/shared/secondary/spp_worker_th/cmd_res_formatter.c b/src/shared/secondary/spp_worker_th/cmd_res_formatter.c
index 85b48af..a04bc91 100644
--- a/src/shared/secondary/spp_worker_th/cmd_res_formatter.c
+++ b/src/shared/secondary/spp_worker_th/cmd_res_formatter.c
@@ -79,9 +79,10 @@ append_error_details_value(const char *name, char **output, void *tmp)
 
 /* Check if port is already flushed. */
 static int
-is_port_flushed(enum port_type iface_type, int iface_no)
+is_port_flushed(enum port_type iface_type, int iface_no, int queue_no)
 {
-	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no);
+	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no,
+			queue_no);
 	return port->ethdev_port_id >= 0;
 }
 
@@ -89,23 +90,43 @@ is_port_flushed(enum port_type iface_type, int iface_no)
 int
 append_interface_array(char **output, const enum port_type type)
 {
-	int i, port_cnt = 0;
+	int port_cnt, str_cnt = 0;
+	uint16_t queue_cnt;
 	char tmp_str[CMD_TAG_APPEND_SIZE];
 
-	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
-		if (!is_port_flushed(type, i))
-			continue;
+	for (port_cnt = 0; port_cnt < RTE_MAX_ETHPORTS; port_cnt++) {
+		int max_queue_port = get_port_max_queues(type, port_cnt);
+		if (unlikely(max_queue_port < 0))
+			return SPPWK_RET_NG;
 
-		sprintf(tmp_str, "%s%d", JSON_APPEND_COMMA(port_cnt), i);
+		for (queue_cnt = 0; queue_cnt < max_queue_port;
+			queue_cnt++) {
+			if (!is_port_flushed(type, port_cnt, queue_cnt))
+				continue;
+
+			if (max_queue_port <= 1) {
+				sprintf(tmp_str, "%s\"%d\"",
+						JSON_APPEND_COMMA(str_cnt),
+						port_cnt);
+			} else {
+				sprintf(tmp_str, "%s\"%d nq %d\"",
+						JSON_APPEND_COMMA(str_cnt),
+						port_cnt, queue_cnt);
+			}
 
-		*output = spp_strbuf_append(*output, tmp_str, strlen(tmp_str));
-		if (unlikely(*output == NULL)) {
-			RTE_LOG(ERR, WK_CMD_RES_FMT,
-				/* TODO(yasufum) replace %d to string. */
-				"Failed to add index for type `%d`.\n", type);
-			return SPPWK_RET_NG;
+			*output = spp_strbuf_append(*output,
+					tmp_str, strlen(tmp_str));
+			if (unlikely(*output == NULL)) {
+				RTE_LOG(ERR, WK_CMD_RES_FMT,
+					/**
+					 * TODO(yasufum) replace %d to string.
+					 */
+					"Failed to add index for type `%d`.\n",
+							type);
+				return SPPWK_RET_NG;
+			}
+			str_cnt++;
 		}
-		port_cnt++;
 	}
 	return SPPWK_RET_OK;
 }
@@ -195,14 +216,14 @@ append_vlan_block(const char *name, char **output,
  * It returns a port ID, or error code if it's failed to.
  */
 static int
-get_ethdev_port_id(enum port_type iface_type, int iface_no)
+get_ethdev_port_id(enum port_type iface_type, int iface_no, int queue_no)
 {
 	struct iface_info *iface_info = NULL;
 
 	sppwk_get_mng_data(&iface_info, NULL, NULL, NULL, NULL, NULL);
 	switch (iface_type) {
 	case PHY:
-		return iface_info->phy[iface_no].ethdev_port_id;
+		return iface_info->phy[iface_no][queue_no].ethdev_port_id;
 	case RING:
 		return iface_info->ring[iface_no].ethdev_port_id;
 	case VHOST:
@@ -227,15 +248,17 @@ append_port_block(char **output, const struct sppwk_port_idx *port,
 		return SPPWK_RET_NG;
 	}
 
-	sppwk_port_uid(port_str, port->iface_type, port->iface_no);
+	sppwk_port_uid(port_str, port->iface_type, port->iface_no,
+			port->queue_no);
 	ret = append_json_str_value(&tmp_buff, "port", port_str);
 	if (unlikely(ret < SPPWK_RET_OK))
 		return SPPWK_RET_NG;
 
 	ret = append_vlan_block("vlan", &tmp_buff,
 			get_ethdev_port_id(
-				port->iface_type, port->iface_no),
-			dir);
+				port->iface_type, port->iface_no,
+				port->queue_no),
+				dir);
 	if (unlikely(ret < SPPWK_RET_OK))
 		return SPPWK_RET_NG;
 
@@ -519,7 +542,7 @@ add_client_id(const char *name, char **output,
 	return append_json_int_value(output, name, get_client_id());
 }
 
-/* Add entry of port to a response in JSON such as "phy:0". */
+/* Add entry of port to a response in JSON such as "phy:0nq0". */
 int
 add_interface(const char *name, char **output,
 		void *tmp __attribute__ ((unused)))
diff --git a/src/shared/secondary/spp_worker_th/cmd_utils.c b/src/shared/secondary/spp_worker_th/cmd_utils.c
index 69d7222..3dea02c 100644
--- a/src/shared/secondary/spp_worker_th/cmd_utils.c
+++ b/src/shared/secondary/spp_worker_th/cmd_utils.c
@@ -154,13 +154,13 @@ stop_process(int signal)
  * if given type is invalid.
  */
 struct sppwk_port_info *
-get_sppwk_port(enum port_type iface_type, int iface_no)
+get_sppwk_port(enum port_type iface_type, int iface_no, int queue_no)
 {
 	struct iface_info *iface_info = g_mng_data.p_iface_info;
 
 	switch (iface_type) {
 	case PHY:
-		return &iface_info->phy[iface_no];
+		return &iface_info->phy[iface_no][queue_no];
 	case VHOST:
 		return &iface_info->vhost[iface_no];
 	case RING:
@@ -229,19 +229,24 @@ log_interface_info(const struct iface_info *iface_info)
 {
 	const struct sppwk_port_info *port = NULL;
 	int cnt = 0;
+	int queue_cnt;
 	for (cnt = 0; cnt < RTE_MAX_ETHPORTS; cnt++) {
-		port = &iface_info->phy[cnt];
-		if (port->iface_type == UNDEF)
-			continue;
-
-		RTE_LOG(DEBUG, WK_CMD_UTILS,
-				"phy  [%d] type=%d, no=%d, port=%d, "
-				"vid = %u, mac=%08lx(%s)\n",
-				cnt, port->iface_type, port->iface_no,
-				port->ethdev_port_id,
-				port->cls_attrs.vlantag.vid,
-				port->cls_attrs.mac_addr,
-				port->cls_attrs.mac_addr_str);
+		int max_queue_port = get_port_max_queues(PHY, cnt);
+		for (queue_cnt = 0; queue_cnt < max_queue_port;
+				queue_cnt++) {
+			port = &iface_info->phy[cnt][queue_cnt];
+			if (port->iface_type == UNDEF)
+				continue;
+
+			RTE_LOG(DEBUG, WK_CMD_UTILS,
+					"phy [%d] type=%d, no=%d, port=%d, "
+					"queue=%d, vid = %u, mac=%08lx(%s)\n",
+					cnt, port->iface_type, port->iface_no,
+					port->ethdev_port_id, port->queue_no,
+					port->cls_attrs.vlantag.vid,
+					port->cls_attrs.mac_addr,
+					port->cls_attrs.mac_addr_str);
+		}
 	}
 	for (cnt = 0; cnt < RTE_MAX_ETHPORTS; cnt++) {
 		port = &iface_info->vhost[cnt];
@@ -356,21 +361,33 @@ static void
 init_iface_info(void)
 {
 	int port_cnt;  /* increment ether ports */
+	int queue_cnt;  /* increment ether queue per ports */
 	struct iface_info *p_iface_info = g_mng_data.p_iface_info;
 	memset(p_iface_info, 0x00, sizeof(struct iface_info));
 	for (port_cnt = 0; port_cnt < RTE_MAX_ETHPORTS; port_cnt++) {
-		p_iface_info->phy[port_cnt].iface_type = UNDEF;
-		p_iface_info->phy[port_cnt].iface_no = port_cnt;
-		p_iface_info->phy[port_cnt].ethdev_port_id = -1;
-		p_iface_info->phy[port_cnt].cls_attrs.vlantag.vid =
-			ETH_VLAN_ID_MAX;
+		for (queue_cnt = 0; queue_cnt < RTE_MAX_QUEUES_PER_PORT;
+				queue_cnt++) {
+			p_iface_info->phy[port_cnt][queue_cnt].iface_type =
+					UNDEF;
+			p_iface_info->phy[port_cnt][queue_cnt].iface_no =
+					port_cnt;
+			p_iface_info->phy[port_cnt][queue_cnt].queue_no =
+					queue_cnt;
+			p_iface_info->phy[port_cnt][queue_cnt]
+					.ethdev_port_id = -1;
+			p_iface_info->phy[port_cnt][queue_cnt]
+					.cls_attrs.vlantag.vid =
+					ETH_VLAN_ID_MAX;
+		}
 		p_iface_info->vhost[port_cnt].iface_type = UNDEF;
 		p_iface_info->vhost[port_cnt].iface_no = port_cnt;
+		p_iface_info->vhost[port_cnt].queue_no = DEFAULT_QUEUE_ID;
 		p_iface_info->vhost[port_cnt].ethdev_port_id = -1;
 		p_iface_info->vhost[port_cnt].cls_attrs.vlantag.vid =
 			ETH_VLAN_ID_MAX;
 		p_iface_info->ring[port_cnt].iface_type = UNDEF;
 		p_iface_info->ring[port_cnt].iface_no = port_cnt;
+		p_iface_info->ring[port_cnt].queue_no = DEFAULT_QUEUE_ID;
 		p_iface_info->ring[port_cnt].ethdev_port_id = -1;
 		p_iface_info->ring[port_cnt].cls_attrs.vlantag.vid =
 			ETH_VLAN_ID_MAX;
@@ -411,7 +428,7 @@ static int
 init_host_port_info(void)
 {
 	int port_type, port_id;
-	int i, ret;
+	int i, ret, queue_id;
 	int nof_phys = 0;
 	char dev_name[RTE_DEV_NAME_MAX_LEN] = { 0 };
 	struct iface_info *p_iface_info = g_mng_data.p_iface_info;
@@ -433,8 +450,13 @@ init_host_port_info(void)
 
 		switch (port_type) {
 		case PHY:
-			p_iface_info->phy[port_id].iface_type = port_type;
-			p_iface_info->phy[port_id].ethdev_port_id = port_id;
+			for (queue_id = 0; queue_id < RTE_MAX_QUEUES_PER_PORT;
+					queue_id++) {
+				p_iface_info->phy[port_id][queue_id]
+						.iface_type = port_type;
+				p_iface_info->phy[port_id][queue_id]
+						.ethdev_port_id = port_id;
+			}
 			break;
 		case VHOST:
 			/* NOTE: a vhost can be used by one process.
@@ -526,12 +548,14 @@ int
 sppwk_check_used_port(
 		enum port_type iface_type,
 		int iface_no,
+		int queue_no,
 		enum sppwk_port_dir dir)
 {
 	int cnt, port_cnt, max = 0;
 	struct sppwk_comp_info *component = NULL;
 	struct sppwk_port_info **port_array = NULL;
-	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no);
+	struct sppwk_port_info *port = get_sppwk_port(iface_type, iface_no,
+			queue_no);
 	struct sppwk_comp_info *component_info =
 					g_mng_data.p_component_info;
 
@@ -567,14 +591,14 @@ set_component_change_port(struct sppwk_port_info *port,
 	int ret = 0;
 	if ((dir == SPPWK_PORT_DIR_RX) || (dir == SPPWK_PORT_DIR_BOTH)) {
 		ret = sppwk_check_used_port(port->iface_type, port->iface_no,
-				SPPWK_PORT_DIR_RX);
+				port->queue_no, SPPWK_PORT_DIR_RX);
 		if (ret >= 0)
 			*(g_mng_data.p_change_component + ret) = 1;
 	}
 
 	if ((dir == SPPWK_PORT_DIR_TX) || (dir == SPPWK_PORT_DIR_BOTH)) {
 		ret = sppwk_check_used_port(port->iface_type, port->iface_no,
-				SPPWK_PORT_DIR_TX);
+				port->queue_no, SPPWK_PORT_DIR_TX);
 		if (ret >= 0)
 			*(g_mng_data.p_change_component + ret) = 1;
 	}
@@ -628,6 +652,28 @@ get_idx_port_info(struct sppwk_port_info *p_info, int nof_ports,
 	return ret;
 }
 
+/* Returns a larger number of queues of RX or TX port as the maximum number */
+int
+get_port_max_queues(const enum port_type iface_type, int iface_no)
+{
+	if (unlikely(iface_no > RTE_MAX_ETHPORTS) || unlikely(iface_no < 0))
+		return SPPWK_RET_NG;
+
+	if (iface_type != PHY)
+		return 1;
+
+	const struct port_info *ports = NULL;
+	const struct rte_memzone *mz;
+	mz = rte_memzone_lookup(MZ_PORT_INFO);
+	ports = mz->addr;
+	int max_q_rx = ports->queue_info[iface_no].rxq;
+	int max_q_tx = ports->queue_info[iface_no].txq;
+	if (max_q_rx > max_q_tx)
+		return max_q_rx;
+	else
+		return max_q_tx;
+}
+
 /* Delete given port info from the port info array. */
 int
 delete_port_info(struct sppwk_port_info *p_info, int nof_ports,
@@ -716,8 +762,9 @@ update_lcore_info(void)
 	}
 }
 
-/* Return port uid such as `phy:0`, `ring:1` or so. */
-int sppwk_port_uid(char *port_uid, enum port_type p_type, int iface_no)
+/* Return port uid such as `phy:0nq0`, `ring:1` or so. */
+int sppwk_port_uid(char *port_uid, enum port_type p_type, int iface_no,
+			int queue_no)
 {
 	const char *p_type_str;
 
@@ -735,7 +782,15 @@ int sppwk_port_uid(char *port_uid, enum port_type p_type, int iface_no)
 		return SPPWK_RET_NG;
 	}
 
-	sprintf(port_uid, "%s:%d", p_type_str, iface_no);
+	int max_queue_port = get_port_max_queues(p_type, iface_no);
+	if (unlikely(max_queue_port == SPPWK_RET_NG))
+		return SPPWK_RET_NG;
+
+	if (max_queue_port <= 1)
+		sprintf(port_uid, "%s:%d", p_type_str, iface_no);
+	else
+		sprintf(port_uid, "%s:%d nq %d",
+			p_type_str, iface_no, queue_no);
 
 	return SPPWK_RET_OK;
 }
diff --git a/src/shared/secondary/spp_worker_th/cmd_utils.h b/src/shared/secondary/spp_worker_th/cmd_utils.h
index df1b0dc..eda55c6 100644
--- a/src/shared/secondary/spp_worker_th/cmd_utils.h
+++ b/src/shared/secondary/spp_worker_th/cmd_utils.h
@@ -174,7 +174,7 @@ void stop_process(int signal);
 
 /* Return sppwk_port_info of given type and num of interface. */
 struct sppwk_port_info *
-get_sppwk_port(enum port_type iface_type, int iface_no);
+get_sppwk_port(enum port_type iface_type, int iface_no, int queue_no);
 
 /* Output log message for core information */
 void log_core_info(const struct core_mng_info *core_info);
@@ -221,6 +221,7 @@ int sppwk_is_lcore_updated(unsigned int lcore_id);
  *
  * @param iface_type Interface type to be validated.
  * @param iface_no Interface number to be validated.
+ * @param queue_no Queue number of interface to be validated.
  * @param rxtx Value of spp_port_rxtx to be validated.
  * @retval 0~127      If match component ID
  * @retval SPPWK_RET_NG If failed.
@@ -228,6 +229,7 @@ int sppwk_is_lcore_updated(unsigned int lcore_id);
 int sppwk_check_used_port(
 		enum port_type iface_type,
 		int iface_no,
+		int queue_no,
 		enum sppwk_port_dir dir);
 
 /**
@@ -269,6 +271,15 @@ int sppwk_get_lcore_id(const char *comp_name);
 int get_idx_port_info(struct sppwk_port_info *p_info, int nof_ports,
 		struct sppwk_port_info *p_info_ary[]);
 
+/**
+ * Returns max queue number of the target port.
+ *
+ * @param[in] iface_type Interface type such as PHY or so.
+ * @param[in] iface_no Interface number.
+ * @return Max queue number if succeeded, or SPPWK_RET_NG if failed.
+ */
+int get_port_max_queues(const enum port_type iface_type, int iface_no);
+
 /**
  *  search matched port_info from array and delete it.
  *
@@ -294,15 +305,17 @@ int update_port_info(void);
 void update_lcore_info(void);
 
 /**
- * Return port uid such as `phy:0`, `ring:1` or so.
+ * Return port uid such as `phy:0nq0`, `ring:1` or so.
  *
  * @param[in,out] port_uid String of port type to be converted.
  * @param[in] iface_type Interface type such as PHY or so.
  * @param[in] iface_no Interface number.
+ * @param[in] queue_no Queue number of interface.
  * @return SPPWK_RET_OK If succeeded, or SPPWK_RET_NG if failed.
  */
 int
-sppwk_port_uid(char *port_uid, enum port_type iface_type, int iface_no);
+sppwk_port_uid(char *port_uid, enum port_type iface_type, int iface_no,
+		int queue_no);
 
 /**
  * Change string of MAC address to int64.
diff --git a/src/shared/secondary/spp_worker_th/data_types.h b/src/shared/secondary/spp_worker_th/data_types.h
index 57bf182..a05eb75 100644
--- a/src/shared/secondary/spp_worker_th/data_types.h
+++ b/src/shared/secondary/spp_worker_th/data_types.h
@@ -15,9 +15,17 @@
 #define SPPWK_VHOST_STR "vhost"
 #define SPPWK_RING_STR "ring"
 
+/*
+ * Delimiter for phy port supporting multi queue.
+ * Example of phy port supporting multi-queue: `phy: 0nq1`.
+ */
+#define DELIM_PHY_MQ "nq"
+
 /* TODO(yasufum) confirm usage of this value and why it is 4. */
 #define PORT_CAPABL_MAX 4  /* Max num of port abilities. */
 
+#define DEFAULT_QUEUE_ID 0  /* Queue ID is counted up from 0. */
+
 /* Status of a component on lcore. */
 enum sppwk_lcore_status {
 	SPPWK_LCORE_UNUSED,
@@ -89,6 +97,7 @@ struct sppwk_cls_attrs {
 struct sppwk_port_idx {
 	enum port_type iface_type;  /**< phy, vhost or ring. */
 	int iface_no;
+	int queue_no;
 };
 
 /* Define detailed port params in addition to `sppwk_port_idx`. */
@@ -96,6 +105,7 @@ struct sppwk_port_info {
 	enum port_type iface_type;  /**< phy, vhost or ring */
 	int iface_no;
 	int ethdev_port_id;  /**< Consistent ID of ethdev */
+	int queue_no;
 	struct sppwk_cls_attrs cls_attrs;
 	struct sppwk_port_attrs port_attrs[PORT_CAPABL_MAX];
 };
@@ -108,8 +118,10 @@ struct sppwk_comp_info {
 	int comp_id;  /**< Component ID */
 	int nof_rx;  /**< The number of rx ports */
 	int nof_tx;  /**< The number of tx ports */
-	struct sppwk_port_info *rx_ports[RTE_MAX_ETHPORTS]; /**< rx ports */
-	struct sppwk_port_info *tx_ports[RTE_MAX_ETHPORTS]; /**< tx ports */
+	/**< rx ports */
+	struct sppwk_port_info *rx_ports[RTE_MAX_QUEUES_PER_PORT];
+	/**< tx ports */
+	struct sppwk_port_info *tx_ports[RTE_MAX_QUEUES_PER_PORT];
 };
 
 /* Manage number of interfaces  and port information as global variable. */
@@ -120,7 +132,7 @@ struct sppwk_comp_info {
  * or not.
  */
 struct iface_info {
-	struct sppwk_port_info phy[RTE_MAX_ETHPORTS];
+	struct sppwk_port_info phy[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
 	struct sppwk_port_info vhost[RTE_MAX_ETHPORTS];
 	struct sppwk_port_info ring[RTE_MAX_ETHPORTS];
 };
diff --git a/src/shared/secondary/spp_worker_th/port_capability.c b/src/shared/secondary/spp_worker_th/port_capability.c
index 5435db0..5bd759f 100644
--- a/src/shared/secondary/spp_worker_th/port_capability.c
+++ b/src/shared/secondary/spp_worker_th/port_capability.c
@@ -378,11 +378,11 @@ vlan_operation(uint16_t port_id, struct rte_mbuf **pkts, const uint16_t nb_pkts,
 /* Wrapper function for rte_eth_rx_burst() with VLAN feature. */
 uint16_t
 sppwk_eth_vlan_rx_burst(uint16_t port_id,
-		uint16_t queue_id __attribute__ ((unused)),
+		uint16_t queue_id,
 		struct rte_mbuf **rx_pkts, const uint16_t nb_pkts)
 {
 	uint16_t nb_rx;
-	nb_rx = rte_eth_rx_burst(port_id, 0, rx_pkts, nb_pkts);
+	nb_rx = rte_eth_rx_burst(port_id, queue_id, rx_pkts, nb_pkts);
 	if (unlikely(nb_rx == 0))
 		return SPPWK_RET_OK;
 
@@ -393,7 +393,7 @@ sppwk_eth_vlan_rx_burst(uint16_t port_id,
 /* Wrapper function for rte_eth_tx_burst() with VLAN feature. */
 uint16_t
 sppwk_eth_vlan_tx_burst(uint16_t port_id,
-		uint16_t queue_id __attribute__ ((unused)),
+		uint16_t queue_id,
 		struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 {
 	uint16_t nb_tx;
@@ -404,7 +404,7 @@ sppwk_eth_vlan_tx_burst(uint16_t port_id,
 	if (unlikely(nb_tx == 0))
 		return SPPWK_RET_OK;
 
-	return rte_eth_tx_burst(port_id, 0, tx_pkts, nb_tx);
+	return rte_eth_tx_burst(port_id, queue_id, tx_pkts, nb_tx);
 }
 
 #ifdef SPP_RINGLATENCYSTATS_ENABLE
diff --git a/src/shared/secondary/spp_worker_th/port_capability.h b/src/shared/secondary/spp_worker_th/port_capability.h
index 284ba30..17d3a16 100644
--- a/src/shared/secondary/spp_worker_th/port_capability.h
+++ b/src/shared/secondary/spp_worker_th/port_capability.h
@@ -62,7 +62,7 @@ void sppwk_update_port_dir(const struct sppwk_comp_info *comp);
  * Wrapper function for rte_eth_rx_burst() with VLAN feature.
  *
  * @param[in] port_id Etherdev ID.
- * @param[in] queue_id RX queue ID, but fixed value 0 in SPP.
+ * @param[in] queue_id RX queue ID.
  * @param[in] rx_pkts Pointers to mbuf should be enough to store nb_pkts.
  * @param nb_pkts Maximum number of RX packets.
  * @return Number of RX packets as number of pointers to mbuf.
@@ -74,7 +74,7 @@ uint16_t sppwk_eth_vlan_rx_burst(uint16_t port_id, uint16_t queue_id,
  * Wrapper function for rte_eth_tx_burst() with VLAN feature.
  *
  * @param port_id Etherdev ID.
- * @param[in] queue_id TX queue ID, but fixed value 0 in SPP.
+ * @param[in] queue_id TX queue ID.
  * @param[in] tx_pkts Pointers to mbuf should be enough to store nb_pkts.
  * @param nb_pkts Maximum number of TX packets.
  * @return Number of TX packets as number of pointers to mbuf.
diff --git a/src/shared/secondary/spp_worker_th/vf_deps.h b/src/shared/secondary/spp_worker_th/vf_deps.h
index f2ea62a..dadcf7a 100644
--- a/src/shared/secondary/spp_worker_th/vf_deps.h
+++ b/src/shared/secondary/spp_worker_th/vf_deps.h
@@ -24,7 +24,12 @@
 struct mac_classifier {
 	struct rte_hash *cls_tbl;  /* Hash table for MAC classification. */
 	int nof_cls_ports;  /* Num of ports classified validly. */
-	int cls_ports[RTE_MAX_ETHPORTS];  /* Ports for classification. */
+	/**
+	 * Ports for classification.
+	 * For multi-queue case, size of ports should be
+	 * RTE_MAX_QUEUES_PER_PORT. RTE_MAX_ETHPORTS is not enough.
+	 */
+	int cls_ports[RTE_MAX_QUEUES_PER_PORT];
 	int default_cls_idx;  /* Default index for classification. */
 };
 
@@ -33,6 +38,7 @@ struct mac_classifier {
 struct cls_port_info {
 	enum port_type iface_type;
 	int iface_no;   /* Index of ports handled by classifier. */
+	int queue_no;   /* Index of queue per port handled by classifier. */
 	int iface_no_global;  /* ID for interface generated by spp_vf */
 	uint16_t ethdev_port_id;  /* Ethdev port ID. */
 	uint16_t nof_pkts;  /* Number of packets in pkts[]. */
@@ -47,7 +53,12 @@ struct cls_comp_info {
 	int nof_tx_ports;  /* Number of TX ports info entries. */
 	/* Classifier has one RX port and several TX ports. */
 	struct cls_port_info rx_port_i;  /* RX port info classified. */
-	struct cls_port_info tx_ports_i[RTE_MAX_ETHPORTS];  /* TX info. */
+	/**
+	 * TX info.
+	 * For multi-queue case, size of ports should be
+	 * RTE_MAX_QUEUES_PER_PORT. RTE_MAX_ETHPORTS is not enough.
+	 */
+	struct cls_port_info tx_ports_i[RTE_MAX_QUEUES_PER_PORT];
 };
 
 int add_core(const char *name, char **output,
diff --git a/src/shared/secondary/utils.c b/src/shared/secondary/utils.c
index c80a478..7cca4a4 100644
--- a/src/shared/secondary/utils.c
+++ b/src/shared/secondary/utils.c
@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include "shared/common.h"
 #include "shared/secondary/utils.h"
+#include "shared/secondary/spp_worker_th/data_types.h"
 
 #define RTE_LOGTYPE_SHARED RTE_LOGTYPE_USER1
 
@@ -66,15 +67,18 @@ parse_client_id(int *cli_id, const char *cli_id_str)
 }
 
 /**
- * Retieve port type and ID from resource UID. For example, resource UID
- * 'ring:0' is  parsed to retrieve port tyep 'ring' and ID '0'.
+ * Retieve port type, ID and queue id from resource UID. For example,
+ * resource UID 'ring:0' is  parsed to retrieve port type 'ring' and ID '0'.
  */
 int
-parse_resource_uid(char *str, char **port_type, int *port_id)
+parse_resource_uid(char *str, char **port_type, int *port_id,
+		uint16_t *queue_id)
 {
 	char *token;
 	char delim[] = ":";
 	char *endp;
+	uint16_t default_queue = DEFAULT_QUEUE_ID;
+	char *queue_id_str = NULL;
 
 	RTE_LOG(DEBUG, SHARED, "Parsing resource UID: '%s'\n", str);
 	if (strstr(str, delim) == NULL) {
@@ -86,13 +90,27 @@ parse_resource_uid(char *str, char **port_type, int *port_id)
 	*port_type = strtok(str, delim);
 
 	token = strtok(NULL, delim);
+	queue_id_str = strstr(token, DELIM_PHY_MQ);
 	*port_id = strtol(token, &endp, 10);
-
-	if (*endp) {
+	if ((queue_id_str == NULL && *endp) ||
+		(queue_id_str != NULL && endp != queue_id_str)) {
 		RTE_LOG(ERR, SHARED, "Bad integer value: %s\n", str);
 		return -1;
 	}
 
+	if (queue_id_str == NULL) {
+		*queue_id = default_queue;
+	} else {
+		*queue_id = strtol(&queue_id_str[strlen(DELIM_PHY_MQ)],
+			&endp, 10);
+
+		if (*endp) {
+			RTE_LOG(ERR, SHARED,
+				"queue_id is bad integer value: %s\n", str);
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/src/shared/secondary/utils.h b/src/shared/secondary/utils.h
index eba221f..4ae0a8e 100644
--- a/src/shared/secondary/utils.h
+++ b/src/shared/secondary/utils.h
@@ -5,7 +5,8 @@
 #ifndef _SHARED_SECONDARY_UTILS_H_
 #define _SHARED_SECONDARY_UTILS_H_
 
-int parse_resource_uid(char *str, char **port_type, int *port_id);
+int parse_resource_uid(char *str, char **port_type, int *port_id,
+	uint16_t *queue_id);
 
 int spp_atoi(const char *str, int *val);
 
-- 
2.17.1


  reply	other threads:[~2020-02-18  6:37 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-18  6:37 [spp] [PATCH 00/17] Adding Hardware offload capability x-fn-spp-ml
2020-02-18  6:37 ` x-fn-spp-ml [this message]
2020-02-18  6:37 ` [spp] [PATCH 02/17] spp_vf: add support of multi-queue x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 03/17] spp_mirror: " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 04/17] spp_pcap: " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 05/17] spp_primary: " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 06/17] spp_primary: add support of rte_flow x-fn-spp-ml
2020-02-19  2:24   ` Yasufumi Ogawa
2020-02-19 11:57     ` [spp] (x-fn-spp-ml 118) " Hideyuki Yamashita
2020-02-18  6:37 ` [spp] [PATCH 07/17] spp_primary: add common function " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 08/17] spp_primary: add attribute " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 09/17] spp_primary: add patterns " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 10/17] spp_primary: add actions " x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 11/17] bin: add parameter for hardrare offload x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 12/17] cli: add support of hardware offload x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 13/17] cli: add support of rte_flow in vf x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 14/17] cli: add support of rte_flow in mirror x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 15/17] cli: add support of rte_flow in nfv x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 16/17] spp-ctl: add APIs for flow rules x-fn-spp-ml
2020-02-18  6:37 ` [spp] [PATCH 17/17] spp_nfv: add support of multi-queue x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 00/17] Adding Hardware offload capability x-fn-spp-ml
2020-02-21  8:17   ` Yasufumi Ogawa
2020-02-25  5:49     ` [spp] (x-fn-spp-ml 177) " Hideyuki Yamashita
2020-02-19 11:49 ` [spp] [PATCH v2 01/17] shared: add support of multi-queue x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 02/17] spp_vf: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 03/17] spp_mirror: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 04/17] spp_pcap: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 05/17] spp_primary: " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 06/17] spp_primary: add support of rte_flow x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 07/17] spp_primary: add common function " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 08/17] spp_primary: add attribute " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 09/17] spp_primary: add patterns " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 10/17] spp_primary: add actions " x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 11/17] bin: add parameter for hardrare offload x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 12/17] cli: add support of hardware offload x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 13/17] cli: add support of rte_flow in vf x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 14/17] cli: add support of rte_flow in mirror x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 15/17] cli: add support of rte_flow in nfv x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 16/17] spp-ctl: add APIs for flow rules x-fn-spp-ml
2020-02-19 11:49 ` [spp] [PATCH v2 17/17] spp_nfv: add support of multi-queue x-fn-spp-ml

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200218063720.6597-2-x-fn-spp-ml@ntt-tx.co.jp \
    --to=x-fn-spp-ml@ntt-tx.co.jp \
    --cc=ferruh.yigit@intel.com \
    --cc=spp@dpdk.org \
    --cc=yasufum.o@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).