DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH dpdk-dev] net/mlx5: support hairpin between different ports
@ 2019-12-01  9:35 xiangxia.m.yue
  2020-01-16 11:40 ` Ori Kam
  0 siblings, 1 reply; 2+ messages in thread
From: xiangxia.m.yue @ 2019-12-01  9:35 UTC (permalink / raw)
  To: orika; +Cc: dev, Tonghao Zhang

From: Tonghao Zhang <xiangxia.m.yue@gmail.com>

In the dpdk upstream, each hairpin Rxq can be connected
Txq which can belong to a same port. This patch allows
Rxq connected to different port Txq.

rte_eth_hairpin_conf_check will check the hairpin_conf valid
in high level.

Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
---
 drivers/net/mlx5/mlx5_rxq.c     |  5 ++-
 drivers/net/mlx5/mlx5_trigger.c | 41 ++++++++++++++++-------
 drivers/net/mlx5/mlx5_txq.c     |  4 +--
 lib/librte_ethdev/rte_ethdev.c  | 73 ++++++++++++++++++++++++++++-------------
 4 files changed, 83 insertions(+), 40 deletions(-)

diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
index 986ec01..c2daebd 100644
--- a/drivers/net/mlx5/mlx5_rxq.c
+++ b/drivers/net/mlx5/mlx5_rxq.c
@@ -547,9 +547,8 @@
 	res = mlx5_rx_queue_pre_setup(dev, idx, desc);
 	if (res)
 		return res;
-	if (hairpin_conf->peer_count != 1 ||
-	    hairpin_conf->peers[0].port != dev->data->port_id ||
-	    hairpin_conf->peers[0].queue >= priv->txqs_n) {
+
+	if (hairpin_conf->peer_count != 1) {
 		DRV_LOG(ERR, "port %u unable to setup hairpin queue index %u "
 			" invalid hairpind configuration", dev->data->port_id,
 			idx);
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index ab6937a..3eb2984 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -185,6 +185,10 @@
 	struct mlx5_rxq_ctrl *rxq_ctrl;
 	struct mlx5_devx_obj *sq;
 	struct mlx5_devx_obj *rq;
+	struct rte_eth_dev *rxq_dev;
+	struct mlx5_priv *rxq_priv;
+	uint16_t peer_queue_id;
+	uint16_t peer_port_id;
 	unsigned int i;
 	int ret = 0;
 
@@ -203,37 +207,50 @@
 			mlx5_txq_release(dev, i);
 			return -rte_errno;
 		}
-		sq = txq_ctrl->obj->sq;
-		rxq_ctrl = mlx5_rxq_get(dev,
-					txq_ctrl->hairpin_conf.peers[0].queue);
+		peer_port_id = txq_ctrl->hairpin_conf.peers[0].port;
+		peer_queue_id = txq_ctrl->hairpin_conf.peers[0].queue;
+		rxq_dev = &rte_eth_devices[peer_port_id];
+		rxq_priv = rxq_dev->data->dev_private;
+
+		rxq_ctrl = mlx5_rxq_get(rxq_dev, peer_queue_id);
 		if (!rxq_ctrl) {
 			mlx5_txq_release(dev, i);
 			rte_errno = EINVAL;
 			DRV_LOG(ERR, "port %u no rxq object found: %d",
-				dev->data->port_id,
-				txq_ctrl->hairpin_conf.peers[0].queue);
+				peer_port_id, peer_queue_id);
 			return -rte_errno;
 		}
 		if (rxq_ctrl->type != MLX5_RXQ_TYPE_HAIRPIN ||
 		    rxq_ctrl->hairpin_conf.peers[0].queue != i) {
 			rte_errno = ENOMEM;
 			DRV_LOG(ERR, "port %u Tx queue %d can't be binded to "
-				"Rx queue %d", dev->data->port_id,
-				i, txq_ctrl->hairpin_conf.peers[0].queue);
+				"port %u Rx queue %d",
+				dev->data->port_id, i,
+				peer_port_id, peer_queue_id);
 			goto error;
 		}
+		if (!rxq_ctrl->obj) {
+			DRV_LOG(ERR, "port %u rxq obj not created, "
+				"you may start it firstly.",
+				peer_port_id);
+			goto error;
+		}
+
+		sq = txq_ctrl->obj->sq;
 		rq = rxq_ctrl->obj->rq;
 		if (!rq) {
 			rte_errno = ENOMEM;
-			DRV_LOG(ERR, "port %u hairpin no matching rxq: %d",
+			DRV_LOG(ERR, "port %u hairpin no matching port"
+				" %u rxq %d",
 				dev->data->port_id,
-				txq_ctrl->hairpin_conf.peers[0].queue);
+				peer_port_id,
+				peer_queue_id);
 			goto error;
 		}
 		sq_attr.state = MLX5_SQC_STATE_RDY;
 		sq_attr.sq_state = MLX5_SQC_STATE_RST;
 		sq_attr.hairpin_peer_rq = rq->id;
-		sq_attr.hairpin_peer_vhca = priv->config.hca_attr.vhca_id;
+		sq_attr.hairpin_peer_vhca = rxq_priv->config.hca_attr.vhca_id;
 		ret = mlx5_devx_cmd_modify_sq(sq, &sq_attr);
 		if (ret)
 			goto error;
@@ -245,12 +262,12 @@
 		if (ret)
 			goto error;
 		mlx5_txq_release(dev, i);
-		mlx5_rxq_release(dev, txq_ctrl->hairpin_conf.peers[0].queue);
+		mlx5_rxq_release(rxq_dev, peer_queue_id);
 	}
 	return 0;
 error:
 	mlx5_txq_release(dev, i);
-	mlx5_rxq_release(dev, txq_ctrl->hairpin_conf.peers[0].queue);
+	mlx5_rxq_release(dev, peer_queue_id);
 	return -rte_errno;
 }
 
diff --git a/drivers/net/mlx5/mlx5_txq.c b/drivers/net/mlx5/mlx5_txq.c
index bac4f71..98ee35e 100644
--- a/drivers/net/mlx5/mlx5_txq.c
+++ b/drivers/net/mlx5/mlx5_txq.c
@@ -254,9 +254,7 @@
 	res = mlx5_tx_queue_pre_setup(dev, idx, desc);
 	if (res)
 		return res;
-	if (hairpin_conf->peer_count != 1 ||
-	    hairpin_conf->peers[0].port != dev->data->port_id ||
-	    hairpin_conf->peers[0].queue >= priv->rxqs_n) {
+	if (hairpin_conf->peer_count != 1) {
 		DRV_LOG(ERR, "port %u unable to setup hairpin queue index %u "
 			" invalid hairpind configuration", dev->data->port_id,
 			idx);
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 8d2ce31..0bd6d87 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -1923,6 +1923,53 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+static int
+rte_eth_hairpin_conf_check(const struct rte_eth_hairpin_conf *conf,
+			      const struct rte_eth_hairpin_cap *cap,
+			      bool is_rx_hairpin)
+{
+	uint16_t max_queues_mapping = is_rx_hairpin ?
+				      cap->max_rx_2_tx : cap->max_tx_2_rx;
+	struct rte_eth_dev *dev;
+	uint16_t nb_queues;
+	uint16_t port_id;
+	int i;
+
+	if (conf->peer_count == 0) {
+		RTE_ETHDEV_LOG(ERR, "Invalid value for number of peers(=%hu),"
+				" should be: > 0",
+			       conf->peer_count);
+		return -EINVAL;
+	}
+	if (conf->peer_count > max_queues_mapping) {
+		RTE_ETHDEV_LOG(ERR, "Invalid value for number of peers(=%hu),"
+			       " should be: <= %hu",
+			       conf->peer_count, max_queues_mapping);
+		return -EINVAL;
+	}
+	for (i = 0; i < conf->peer_count; i++) {
+		port_id = conf->peers[i].port;
+
+		if (!rte_eth_dev_is_valid_port(port_id)) {
+			RTE_ETHDEV_LOG(ERR, "Invalid port_id(=%hu) for"
+				       " hairpin peers", conf->peer_count);
+			return -EINVAL;
+		}
+
+		dev = &rte_eth_devices[port_id];
+		nb_queues = is_rx_hairpin ?
+			    dev->data->nb_tx_queues : dev->data->nb_rx_queues;
+		if (conf->peers[i].queue >= nb_queues) {
+			RTE_ETHDEV_LOG(ERR, "Invalid queue_id(=%hu) for"
+				       " hairpin peers, shoud be < %hu",
+				       conf->peer_count, nb_queues);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 int
 rte_eth_rx_hairpin_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
 			       uint16_t nb_rx_desc,
@@ -1956,18 +2003,9 @@ struct rte_eth_dev *
 			nb_rx_desc, cap.max_nb_desc);
 		return -EINVAL;
 	}
-	if (conf->peer_count > cap.max_rx_2_tx) {
-		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Rx queue(=%hu), should be: <= %hu",
-			conf->peer_count, cap.max_rx_2_tx);
-		return -EINVAL;
-	}
-	if (conf->peer_count == 0) {
-		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Rx queue(=%hu), should be: > 0",
-			conf->peer_count);
+	if (rte_eth_hairpin_conf_check(conf, &cap, true))
 		return -EINVAL;
-	}
+
 	for (i = 0, count = 0; i < dev->data->nb_rx_queues &&
 	     cap.max_nb_queues != UINT16_MAX; i++) {
 		if (i == rx_queue_id || rte_eth_dev_is_rx_hairpin_queue(dev, i))
@@ -2126,18 +2164,9 @@ struct rte_eth_dev *
 			nb_tx_desc, cap.max_nb_desc);
 		return -EINVAL;
 	}
-	if (conf->peer_count > cap.max_tx_2_rx) {
-		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: <= %hu",
-			conf->peer_count, cap.max_tx_2_rx);
+	if (rte_eth_hairpin_conf_check(conf, &cap, false))
 		return -EINVAL;
-	}
-	if (conf->peer_count == 0) {
-		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
-			conf->peer_count);
-		return -EINVAL;
-	}
+
 	for (i = 0, count = 0; i < dev->data->nb_tx_queues &&
 	     cap.max_nb_queues != UINT16_MAX; i++) {
 		if (i == tx_queue_id || rte_eth_dev_is_tx_hairpin_queue(dev, i))
-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [dpdk-dev] [PATCH dpdk-dev] net/mlx5: support hairpin between different ports
  2019-12-01  9:35 [dpdk-dev] [PATCH dpdk-dev] net/mlx5: support hairpin between different ports xiangxia.m.yue
@ 2020-01-16 11:40 ` Ori Kam
  0 siblings, 0 replies; 2+ messages in thread
From: Ori Kam @ 2020-01-16 11:40 UTC (permalink / raw)
  To: xiangxia.m.yue; +Cc: dev

Sorry for the late response,

Thank you very much for this patch.

We are planning to add such support, but this patch is not the best
way.

I will keep you updated about the progress, and will be very to hear 
your inputs.

Best,
Ori


> -----Original Message-----
> From: xiangxia.m.yue@gmail.com <xiangxia.m.yue@gmail.com>
> Sent: Sunday, December 1, 2019 11:35 AM
> To: Ori Kam <orika@mellanox.com>
> Cc: dev@dpdk.org; Tonghao Zhang <xiangxia.m.yue@gmail.com>
> Subject: [PATCH dpdk-dev] net/mlx5: support hairpin between different
> ports
> 
> From: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> 
> In the dpdk upstream, each hairpin Rxq can be connected
> Txq which can belong to a same port. This patch allows
> Rxq connected to different port Txq.
> 
> rte_eth_hairpin_conf_check will check the hairpin_conf valid
> in high level.
> 
> Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> ---
>  drivers/net/mlx5/mlx5_rxq.c     |  5 ++-
>  drivers/net/mlx5/mlx5_trigger.c | 41 ++++++++++++++++-------
>  drivers/net/mlx5/mlx5_txq.c     |  4 +--
>  lib/librte_ethdev/rte_ethdev.c  | 73 ++++++++++++++++++++++++++++----
> ---------
>  4 files changed, 83 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
> index 986ec01..c2daebd 100644
> --- a/drivers/net/mlx5/mlx5_rxq.c
> +++ b/drivers/net/mlx5/mlx5_rxq.c
> @@ -547,9 +547,8 @@
>  	res = mlx5_rx_queue_pre_setup(dev, idx, desc);
>  	if (res)
>  		return res;
> -	if (hairpin_conf->peer_count != 1 ||
> -	    hairpin_conf->peers[0].port != dev->data->port_id ||
> -	    hairpin_conf->peers[0].queue >= priv->txqs_n) {
> +
> +	if (hairpin_conf->peer_count != 1) {
>  		DRV_LOG(ERR, "port %u unable to setup hairpin queue index
> %u "
>  			" invalid hairpind configuration", dev->data->port_id,
>  			idx);
> diff --git a/drivers/net/mlx5/mlx5_trigger.c
> b/drivers/net/mlx5/mlx5_trigger.c
> index ab6937a..3eb2984 100644
> --- a/drivers/net/mlx5/mlx5_trigger.c
> +++ b/drivers/net/mlx5/mlx5_trigger.c
> @@ -185,6 +185,10 @@
>  	struct mlx5_rxq_ctrl *rxq_ctrl;
>  	struct mlx5_devx_obj *sq;
>  	struct mlx5_devx_obj *rq;
> +	struct rte_eth_dev *rxq_dev;
> +	struct mlx5_priv *rxq_priv;
> +	uint16_t peer_queue_id;
> +	uint16_t peer_port_id;
>  	unsigned int i;
>  	int ret = 0;
> 
> @@ -203,37 +207,50 @@
>  			mlx5_txq_release(dev, i);
>  			return -rte_errno;
>  		}
> -		sq = txq_ctrl->obj->sq;
> -		rxq_ctrl = mlx5_rxq_get(dev,
> -					txq_ctrl-
> >hairpin_conf.peers[0].queue);
> +		peer_port_id = txq_ctrl->hairpin_conf.peers[0].port;
> +		peer_queue_id = txq_ctrl->hairpin_conf.peers[0].queue;
> +		rxq_dev = &rte_eth_devices[peer_port_id];
> +		rxq_priv = rxq_dev->data->dev_private;
> +
> +		rxq_ctrl = mlx5_rxq_get(rxq_dev, peer_queue_id);
>  		if (!rxq_ctrl) {
>  			mlx5_txq_release(dev, i);
>  			rte_errno = EINVAL;
>  			DRV_LOG(ERR, "port %u no rxq object found: %d",
> -				dev->data->port_id,
> -				txq_ctrl->hairpin_conf.peers[0].queue);
> +				peer_port_id, peer_queue_id);
>  			return -rte_errno;
>  		}
>  		if (rxq_ctrl->type != MLX5_RXQ_TYPE_HAIRPIN ||
>  		    rxq_ctrl->hairpin_conf.peers[0].queue != i) {
>  			rte_errno = ENOMEM;
>  			DRV_LOG(ERR, "port %u Tx queue %d can't be
> binded to "
> -				"Rx queue %d", dev->data->port_id,
> -				i, txq_ctrl->hairpin_conf.peers[0].queue);
> +				"port %u Rx queue %d",
> +				dev->data->port_id, i,
> +				peer_port_id, peer_queue_id);
>  			goto error;
>  		}
> +		if (!rxq_ctrl->obj) {
> +			DRV_LOG(ERR, "port %u rxq obj not created, "
> +				"you may start it firstly.",
> +				peer_port_id);
> +			goto error;
> +		}
> +
> +		sq = txq_ctrl->obj->sq;
>  		rq = rxq_ctrl->obj->rq;
>  		if (!rq) {
>  			rte_errno = ENOMEM;
> -			DRV_LOG(ERR, "port %u hairpin no matching rxq:
> %d",
> +			DRV_LOG(ERR, "port %u hairpin no matching port"
> +				" %u rxq %d",
>  				dev->data->port_id,
> -				txq_ctrl->hairpin_conf.peers[0].queue);
> +				peer_port_id,
> +				peer_queue_id);
>  			goto error;
>  		}
>  		sq_attr.state = MLX5_SQC_STATE_RDY;
>  		sq_attr.sq_state = MLX5_SQC_STATE_RST;
>  		sq_attr.hairpin_peer_rq = rq->id;
> -		sq_attr.hairpin_peer_vhca = priv->config.hca_attr.vhca_id;
> +		sq_attr.hairpin_peer_vhca = rxq_priv-
> >config.hca_attr.vhca_id;
>  		ret = mlx5_devx_cmd_modify_sq(sq, &sq_attr);
>  		if (ret)
>  			goto error;
> @@ -245,12 +262,12 @@
>  		if (ret)
>  			goto error;
>  		mlx5_txq_release(dev, i);
> -		mlx5_rxq_release(dev, txq_ctrl-
> >hairpin_conf.peers[0].queue);
> +		mlx5_rxq_release(rxq_dev, peer_queue_id);
>  	}
>  	return 0;
>  error:
>  	mlx5_txq_release(dev, i);
> -	mlx5_rxq_release(dev, txq_ctrl->hairpin_conf.peers[0].queue);
> +	mlx5_rxq_release(dev, peer_queue_id);
>  	return -rte_errno;
>  }
> 
> diff --git a/drivers/net/mlx5/mlx5_txq.c b/drivers/net/mlx5/mlx5_txq.c
> index bac4f71..98ee35e 100644
> --- a/drivers/net/mlx5/mlx5_txq.c
> +++ b/drivers/net/mlx5/mlx5_txq.c
> @@ -254,9 +254,7 @@
>  	res = mlx5_tx_queue_pre_setup(dev, idx, desc);
>  	if (res)
>  		return res;
> -	if (hairpin_conf->peer_count != 1 ||
> -	    hairpin_conf->peers[0].port != dev->data->port_id ||
> -	    hairpin_conf->peers[0].queue >= priv->rxqs_n) {
> +	if (hairpin_conf->peer_count != 1) {
>  		DRV_LOG(ERR, "port %u unable to setup hairpin queue index
> %u "
>  			" invalid hairpind configuration", dev->data->port_id,
>  			idx);
> diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
> index 8d2ce31..0bd6d87 100644
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -1923,6 +1923,53 @@ struct rte_eth_dev *
>  	return eth_err(port_id, ret);
>  }
> 
> +static int
> +rte_eth_hairpin_conf_check(const struct rte_eth_hairpin_conf *conf,
> +			      const struct rte_eth_hairpin_cap *cap,
> +			      bool is_rx_hairpin)
> +{
> +	uint16_t max_queues_mapping = is_rx_hairpin ?
> +				      cap->max_rx_2_tx : cap->max_tx_2_rx;
> +	struct rte_eth_dev *dev;
> +	uint16_t nb_queues;
> +	uint16_t port_id;
> +	int i;
> +
> +	if (conf->peer_count == 0) {
> +		RTE_ETHDEV_LOG(ERR, "Invalid value for number of
> peers(=%hu),"
> +				" should be: > 0",
> +			       conf->peer_count);
> +		return -EINVAL;
> +	}
> +	if (conf->peer_count > max_queues_mapping) {
> +		RTE_ETHDEV_LOG(ERR, "Invalid value for number of
> peers(=%hu),"
> +			       " should be: <= %hu",
> +			       conf->peer_count, max_queues_mapping);
> +		return -EINVAL;
> +	}
> +	for (i = 0; i < conf->peer_count; i++) {
> +		port_id = conf->peers[i].port;
> +
> +		if (!rte_eth_dev_is_valid_port(port_id)) {
> +			RTE_ETHDEV_LOG(ERR, "Invalid port_id(=%hu) for"
> +				       " hairpin peers", conf->peer_count);
> +			return -EINVAL;
> +		}
> +
> +		dev = &rte_eth_devices[port_id];
> +		nb_queues = is_rx_hairpin ?
> +			    dev->data->nb_tx_queues : dev->data-
> >nb_rx_queues;
> +		if (conf->peers[i].queue >= nb_queues) {
> +			RTE_ETHDEV_LOG(ERR, "Invalid queue_id(=%hu)
> for"
> +				       " hairpin peers, shoud be < %hu",
> +				       conf->peer_count, nb_queues);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  int
>  rte_eth_rx_hairpin_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
>  			       uint16_t nb_rx_desc,
> @@ -1956,18 +2003,9 @@ struct rte_eth_dev *
>  			nb_rx_desc, cap.max_nb_desc);
>  		return -EINVAL;
>  	}
> -	if (conf->peer_count > cap.max_rx_2_tx) {
> -		RTE_ETHDEV_LOG(ERR,
> -			"Invalid value for number of peers for Rx
> queue(=%hu), should be: <= %hu",
> -			conf->peer_count, cap.max_rx_2_tx);
> -		return -EINVAL;
> -	}
> -	if (conf->peer_count == 0) {
> -		RTE_ETHDEV_LOG(ERR,
> -			"Invalid value for number of peers for Rx
> queue(=%hu), should be: > 0",
> -			conf->peer_count);
> +	if (rte_eth_hairpin_conf_check(conf, &cap, true))
>  		return -EINVAL;
> -	}
> +
>  	for (i = 0, count = 0; i < dev->data->nb_rx_queues &&
>  	     cap.max_nb_queues != UINT16_MAX; i++) {
>  		if (i == rx_queue_id ||
> rte_eth_dev_is_rx_hairpin_queue(dev, i))
> @@ -2126,18 +2164,9 @@ struct rte_eth_dev *
>  			nb_tx_desc, cap.max_nb_desc);
>  		return -EINVAL;
>  	}
> -	if (conf->peer_count > cap.max_tx_2_rx) {
> -		RTE_ETHDEV_LOG(ERR,
> -			"Invalid value for number of peers for Tx
> queue(=%hu), should be: <= %hu",
> -			conf->peer_count, cap.max_tx_2_rx);
> +	if (rte_eth_hairpin_conf_check(conf, &cap, false))
>  		return -EINVAL;
> -	}
> -	if (conf->peer_count == 0) {
> -		RTE_ETHDEV_LOG(ERR,
> -			"Invalid value for number of peers for Tx
> queue(=%hu), should be: > 0",
> -			conf->peer_count);
> -		return -EINVAL;
> -	}
> +
>  	for (i = 0, count = 0; i < dev->data->nb_tx_queues &&
>  	     cap.max_nb_queues != UINT16_MAX; i++) {
>  		if (i == tx_queue_id ||
> rte_eth_dev_is_tx_hairpin_queue(dev, i))
> --
> 1.8.3.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-01-16 11:40 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-01  9:35 [dpdk-dev] [PATCH dpdk-dev] net/mlx5: support hairpin between different ports xiangxia.m.yue
2020-01-16 11:40 ` Ori Kam

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