DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [RFC] introduce support for hairpin between two ports
@ 2020-09-11  4:51 Bing Zhao
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-09-11  4:51 UTC (permalink / raw)
  To: Ori Kam, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko, dev

Hairpin functionality only supports one single port mode (e.g testpmd
application) in the current implementation. It means that the traffic
will be sent out from the same port it comes. There is no such
restriction for some NICs, and strong demand to support two ports
hairpin mode in real-life cases.
Two ports hairpin mode does not really mean hairpin will only support
two ports in a single application. Indeed, it also needs to support
the single port hairpin today for compatibility. In the meanwhile,
'two ports' means the ingress and egress ports of the traffic could
Be different. And also, there is no restriction that
  1. traffic from the same ingress port must go to the same egress
     port
  2. traffic from the port that as 'egress' for other traffic flows
     must go to their 'ingress' port
The configuration should be flexible and the behavior of traffic will
be decided by the rte flows.

Usually, during the startup phase, all the hairpin configurations
except flows should be done. It means that hairpin TXQ and peer RXQ
should be bound together. It is feasible in single port mode and
transparent to the application. In two ports mode, there may be some
problems for the queues configuring and binding.
  1. Once TXQ & RXQ belong to different ports, it would be hard to
     configure the first port when the initialization of the second
     port is not done. Also, it is not proper to configure the first
     port during the second one starting.
  2. The port could be attached and detached dynamically. Hairpin
     between these ports should support dynamic configuration.

In two ports hairpin mode, since the TXQ and RXQ belong to different
ports. If some actions need to be done in the TX part, the egress flow
could be inserted explicitly and managed separately from the RX part.
What's more, one egress flow could be shared for different ingress
flows from the same or different ports.

In order to satisfy these, some changes on the current rte ethdev and
flow APIs are needed and some new APIs will be introduced.

1. Data structures in 'rte_ethdev.h'
Two new members are added.
struct rte_eth_hairpin_conf {
	uint16_t peer_count; /**< The number of peers. */
	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
	uint16_t tx_explicit;
	uint16_t manual_bind;
};
'tx_explicit': If 0, PMD will help to insert the egress flow in a
implicit way. If 1, the application will insert it by itself.
'manual_bind': If 0, PMD will try to bind hairpin TXQ and RXQ peer
automatically, like in today's single port hairpin mode and this is
for backward compatibility. If 1, then manual bind API will be called.
The application should ensure there is no conflict for the hairpin
peer configurations between TX & RX as today and PMD could check
them inside. For new member 'tx_explicit', all queue pairs from one
ingress port to the same egress are suggested to have the same value
in order not to create chaos, like in RSS cases.
For new member 'manual_bind', the same suggestion is applicable.
The support for the new members will be decided by the NICs' capacity
and real-life usage from the application.

2. New macros in 'rte_ethdev.h'
RTE_ETH_HAIRPIN_BIND_AUTO (0)
RTE_ETH_HAIRPIN_BIND_MANUAL (1)
RTE_ETH_HAIRPIN_TXRULE_IMPLICIT (0)
RTE_ETH_HAIRPIN_TXRULE_EXPLICIT (1)
These are used for the new members in 'struct rte_eth_hairpin_conf'.

3. New function APIs in 'rte_ethdev.h'
* int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
* typedef int (*eth_hairpin_bind)(struct rte_eth_dev *dev,
                                uint16_t rx_port);
This function will be used to bind one port egress to the peer port
ingress. If 'rx_port' is equal to RTE_MAX_ETHPORTS, then all the ports
will be traversed to bind hairpin egress queues to all of their
ingress queues configured. The application needs to call it repeatedly
to bind all egress ports.
This should be called after the hairpin queues are set up and devices
are started. If 'manual_bind' is not specified, no need to call this
API. A function pointer with 'eth_hairpin_bind' type should be
provided by the PMD to execute the hardware setting in the driver.
0 return value means success and a negative value will be returned to
indicate the actual failure.

* int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
* typedef int (*eth_hairpin_unbind)(struct rte_eth_dev *dev,
                                    uint16_t rx_port);
This function will unbind one port egress to the peer port ingress,
only one direction hairpin will be unbound. Unbinding of the opposite
direction needs another call of this API.
If 'rx_port' is equal to RTE_MAX_ETHPORTS, all the ports will be
traversed to do the queues unbind (if any). The application needs to
call it repeatedly to unbind all egress ports.
The API could be called without stopping or closing the eth device,
but the application should ensure the flows inserted for the hairpin
port pairs be handled properly. The traffic behavior should be
divinable after unbound. It is suggested to remove all the flows for
the same direction of a port pairs to be unbound, on both ports.
A function pointer with 'eth_hairpin_unbind' type should be provided
by the PMD to execute the hardware setting in the driver.
0 return value means success and a negative value will be returned to
indicate the actual failure.
After unbinding, the bind API could be called again to enable it. No
peer reconfiguring is supported now without closing the devices.

4. New rte_flow item
* RTE_FLOW_ITEM_TYPE_TX_QUEUE
struct rte_flow_item_tx_queue {
	uint32_t queue;
};
This provides a new item to match for an egress packet. In two ports
hairpin mode, since the TX rules could be inserted explicitly on the
egress port, it is hard to distinguish the hairpin packets from the
software packets. Even if with metadata, it may require complex
management. The support new rte_flow item is optional, depending on
the NIC's capacity. With this item, a few wildcard rules could be
inserted for hairpin to support some common actions.

When switching to two ports hairpin mode with explicit TX rules, the
metadata could be used to provide the 'connection' for a packet
between ingress & egress.
1. The packet header might be changed due to the NAT of DECAP in the
   ingress, and the inner header or other parts may be different.
2. Different ingress flow rules could share the same egress rule to
   simplify rules management.
The rte_flow examples are like below (port 0 RX X -> port 1 TX Y):

flow create 0 ingress group M pattern eth / ... / end actions queue index is X / set_meta data is V / end
X is the ingress hairpin queue index.

flow create 1 egress group N pattern eth / meta data is V / end actions vxlan_encap / end

flow create 1 egress group 0 pattern eth / tx_queue index is Y / end actions jump group N / end
Y is the egress hairpin queue index. This wildcard flow will help to
redirect all the ethernet packets from hairpin TX queue Y to some
specific group for further handling. In the meanwhile, other traffic
sent from software will not be impacted by this wildcard rule.

To verify this in testpmd, some changes are also required.
1. During startup phase, hairpin binding will use the chaining mode.
   E.g. if 3 ports are probed, hairpin traffic will be like this
   port A -> port B, Port B -> port C, port C -> port A
   In only a single port is probed
   port A -> port A
2. flow command line will add support to parse tx queue index
   pattern format: tx_queue index is UNSIGNED / ...

Thanks

Signed-off-by: Bing Zhao <bingz@nvidia.com>

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

* [dpdk-dev] [RFC PATCH v2 0/4] introduce support for hairpin between two ports
  2020-09-11  4:51 [dpdk-dev] [RFC] introduce support for hairpin between two ports Bing Zhao
@ 2020-09-13 15:48 ` Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 1/4] ethdev: add support for flow item transmit queue Bing Zhao
                     ` (4 more replies)
  0 siblings, 5 replies; 81+ messages in thread
From: Bing Zhao @ 2020-09-13 15:48 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko; +Cc: dev

Hairpin functionality only supports one single port mode (e.g testpmd
application) in the current implementation. It means that the traffic
will be sent out from the same port it comes. There is no such
restriction for some NICs, and strong demand to support two ports
hairpin mode in real-life cases.
Two ports hairpin mode does not really mean hairpin will only support
two ports in a single application. Indeed, it also needs to support
the single port hairpin today for compatibility. In the meanwhile,
'two ports' means the ingress and egress ports of the traffic could
Be different. And also, there is no restriction that
  1. traffic from the same ingress port must go to the same egress
     port
  2. traffic from the port that as 'egress' for other traffic flows
     must go to their 'ingress' port
The configuration should be flexible and the behavior of traffic will
be decided by the rte flows.

Usually, during the startup phase, all the hairpin configurations
except flows should be done. It means that hairpin TXQ and peer RXQ
should be bound together. It is feasible in single port mode and
transparent to the application. In two ports mode, there may be some
problems for the queues configuring and binding.
  1. Once TXQ & RXQ belong to different ports, it would be hard to
     configure the first port when the initialization of the second
     port is not done. Also, it is not proper to configure the first
     port during the second one starting.
  2. The port could be attached and detached dynamically. Hairpin
     between these ports should support dynamic configuration.

In two ports hairpin mode, since the TXQ and RXQ belong to different
ports. If some actions need to be done in the TX part, the egress flow
could be inserted explicitly and managed separately from the RX part.
What's more, one egress flow could be shared for different ingress
flows from the same or different ports.

In order to satisfy these, some changes on the current rte ethdev and
flow APIs are needed and some new APIs will be introduced.

1. Data structures in 'rte_ethdev.h'
Two new members are added.
struct rte_eth_hairpin_conf {
	uint16_t peer_count; /**< The number of peers. */
	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
	uint16_t tx_explicit;
	uint16_t manual_bind;
};
'tx_explicit': If 0, PMD will help to insert the egress flow in a
implicit way. If 1, the application will insert it by itself.
'manual_bind': If 0, PMD will try to bind hairpin TXQ and RXQ peer
automatically, like in today's single port hairpin mode and this is
for backward compatibility. If 1, then manual bind API will be called.
The application should ensure there is no conflict for the hairpin
peer configurations between TX & RX as today and PMD could check
them inside. For new member 'tx_explicit', all queue pairs from one
ingress port to the same egress are suggested to have the same value
in order not to create chaos, like in RSS cases.
For new member 'manual_bind', the same suggestion is applicable.
The support for the new members will be decided by the NICs' capacity
and real-life usage from the application.

2. New macros in 'rte_ethdev.h'
RTE_ETH_HAIRPIN_BIND_AUTO (0)
RTE_ETH_HAIRPIN_BIND_MANUAL (1)
RTE_ETH_HAIRPIN_TXRULE_IMPLICIT (0)
RTE_ETH_HAIRPIN_TXRULE_EXPLICIT (1)
These are used for the new members in 'struct rte_eth_hairpin_conf'.

3. New function APIs in 'rte_ethdev.h'
* int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
* typedef int (*eth_hairpin_bind)(struct rte_eth_dev *dev,
                                uint16_t rx_port);
This function will be used to bind one port egress to the peer port
ingress. If 'rx_port' is equal to RTE_MAX_ETHPORTS, then all the ports
will be traversed to bind hairpin egress queues to all of their
ingress queues configured. The application needs to call it repeatedly
to bind all egress ports.
This should be called after the hairpin queues are set up and devices
are started. If 'manual_bind' is not specified, no need to call this
API. A function pointer with 'eth_hairpin_bind' type should be
provided by the PMD to execute the hardware setting in the driver.
0 return value means success and a negative value will be returned to
indicate the actual failure.

* int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
* typedef int (*eth_hairpin_unbind)(struct rte_eth_dev *dev,
                                    uint16_t rx_port);
This function will unbind one port egress to the peer port ingress,
only one direction hairpin will be unbound. Unbinding of the opposite
direction needs another call of this API.
If 'rx_port' is equal to RTE_MAX_ETHPORTS, all the ports will be
traversed to do the queues unbind (if any). The application needs to
call it repeatedly to unbind all egress ports.
The API could be called without stopping or closing the eth device,
but the application should ensure the flows inserted for the hairpin
port pairs be handled properly. The traffic behavior should be
divinable after unbound. It is suggested to remove all the flows for
the same direction of a port pairs to be unbound, on both ports.
A function pointer with 'eth_hairpin_unbind' type should be provided
by the PMD to execute the hardware setting in the driver.
0 return value means success and a negative value will be returned to
indicate the actual failure.
After unbinding, the bind API could be called again to enable it. No
peer reconfiguring is supported now without closing the devices.

4. New rte_flow item
* RTE_FLOW_ITEM_TYPE_TX_QUEUE
struct rte_flow_item_tx_queue {
	uint32_t queue;
};
This provides a new item to match for an egress packet. In two ports
hairpin mode, since the TX rules could be inserted explicitly on the
egress port, it is hard to distinguish the hairpin packets from the
software packets. Even if with metadata, it may require complex
management. The support new rte_flow item is optional, depending on
the NIC's capacity. With this item, a few wildcard rules could be
inserted for hairpin to support some common actions.

When switching to two ports hairpin mode with explicit TX rules, the
metadata could be used to provide the 'connection' for a packet
between ingress & egress.
1. The packet header might be changed due to the NAT of DECAP in the
   ingress, and the inner header or other parts may be different.
2. Different ingress flow rules could share the same egress rule to
   simplify rules management.
The rte_flow examples are like below (port 0 RX X -> port 1 TX Y):

flow create 0 ingress group M pattern eth / … / end actions queue index is X / set_meta data is V / end
X is the ingress hairpin queue index.

flow create 1 egress group N pattern eth / meta data is V / end actions vxlan_encap / end

flow create 1 egress group 0 pattern eth / tx_queue index is Y / end actions jump group N / end
Y is the egress hairpin queue index. This wildcard flow will help to
redirect all the ethernet packets from hairpin TX queue Y to some
specific group for further handling. In the meanwhile, other traffic
sent from software will not be impacted by this wildcard rule.

To verify this in testpmd, some changes are also required.
1. During startup phase, hairpin binding will use the chaining mode.
   E.g. if 3 ports are probed, hairpin traffic will be like this
   port A -> port B, Port B -> port C, port C -> port A
   In only a single port is probed
   port A -> port A
2. flow command line will add support to parse tx queue index
   pattern format: tx_queue index is UNSIGNED / ...

Thanks

Signed-off-by: Bing Zhao <bingz@nvidia.com>

Bing Zhao (4):
  ethdev: add support for flow item transmit queue
  testpmd: add item transmit queue in flow CLI
  ethdev: add hairpin bind APIs
  ethdev: add new attributes to hairpin queues config

 app/test-pmd/cmdline_flow.c           |  18 ++++++
 lib/librte_ethdev/rte_ethdev.c        | 100 ++++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h        |  68 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h |  52 ++++++++++++++++++
 lib/librte_ethdev/rte_flow.c          |   1 +
 lib/librte_ethdev/rte_flow.h          |  30 ++++++++++
 6 files changed, 269 insertions(+)

-- 
2.5.5


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

* [dpdk-dev] [RFC PATCH v2 1/4] ethdev: add support for flow item transmit queue
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
@ 2020-09-13 15:48   ` Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 2/4] testpmd: add item transmit queue in flow CLI Bing Zhao
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-09-13 15:48 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko; +Cc: dev

New rte_flow_item_tx_queue is introduced to support matching on the
traffic from a specific transmit queue. This is only for the egress
direction. For ingress, the receive queue index will be part of the
actions with ACTION QUEUE or RSS and there is no needs to match.

By adding this flow item, it will be easy for the application to
create some wildcard rules to distribute the egress traffic for the
further handling.

Normally, all the traffic from software to wire will have the same
wildcard rule. If the packets need to be handled in different ways,
packets' headers and metadata, etc. will be used for matching.

But in some cases, for example:
1. Packets from different TX queues will have different next common
   behaviors.
2. Hairpin TX traffic should have different behavior from software
   egress traffic.
Matching on the TX queue will help to reduce the rules number and
simplify the rules management.

The support for this new item will be decided by the PMD driver and
the capacity of the NIC.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_flow.c |  1 +
 lib/librte_ethdev/rte_flow.h | 30 ++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index f8fdd68..4600e27 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -96,6 +96,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV3OIP, sizeof(struct rte_flow_item_l2tpv3oip)),
 	MK_FLOW_ITEM(PFCP, sizeof(struct rte_flow_item_pfcp)),
 	MK_FLOW_ITEM(ECPRI, sizeof(struct rte_flow_item_ecpri)),
+	MK_FLOW_ITEM(TX_QUEUE, sizeof(struct rte_flow_item_tx_queue)),
 };
 
 /** Generate flow_action[] entry. */
diff --git a/lib/librte_ethdev/rte_flow.h b/lib/librte_ethdev/rte_flow.h
index da8bfa5..264755a 100644
--- a/lib/librte_ethdev/rte_flow.h
+++ b/lib/librte_ethdev/rte_flow.h
@@ -537,6 +537,13 @@ enum rte_flow_item_type {
 	 */
 	RTE_FLOW_ITEM_TYPE_ECPRI,
 
+	/**
+	 * Matches TX queue ID of a specific port for egress traffic.
+	 *
+	 * See struct rte_flow_item_tx_queue.
+	 */
+	RTE_FLOW_ITEM_TYPE_TX_QUEUE,
+
 };
 
 /**
@@ -1580,6 +1587,29 @@ static const struct rte_flow_item_ecpri rte_flow_item_ecpri_mask = {
 #endif
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this structure may change without prior notice
+ *
+ * RTE_FLOW_ITEM_TYPE_TX_QUEUE
+ *
+ * Match egress traffic originating from a TX queue of a port. Port refers to
+ * a struct rte_eth_dev object on the application side and the transmit queue
+ * index must be in the range [0, nb_tx_queue - 1] previously supplied to
+ * rte_eth_dev_configure(). Normally only supported if the TX queue ID is known
+ * by the underlying PMD.
+ */
+struct rte_flow_item_tx_queue {
+	uint32_t queue; /**< DPDK TX queue ID of a specific port. */
+};
+
+/** Default mask for RTE_FLOW_ITEM_TYPE_TX_QUEUE. */
+#ifndef __cplusplus
+static const struct rte_flow_item_tx_queue rte_flow_item_tx_queue_mask = {
+	.queue = 0xffffffff,
+};
+#endif
+
+/**
  * Matching pattern item definition.
  *
  * A pattern is formed by stacking items starting from the lowest protocol
-- 
2.5.5


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

* [dpdk-dev] [RFC PATCH v2 2/4] testpmd: add item transmit queue in flow CLI
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 1/4] ethdev: add support for flow item transmit queue Bing Zhao
@ 2020-09-13 15:48   ` Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 3/4] ethdev: add hairpin bind APIs Bing Zhao
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-09-13 15:48 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko; +Cc: dev

In the testpmd command line for flow creation, add the keyword
'tx_queue' in the pattern scope so the item transmit queue ID
could be specified during the flow creation.
The index of the transmit queue should be in the range
[0, nb_tx_queue - 1] supplied to rte_eth_dev_configure() during
initialization. Normal TX queues and hairpin TX queues will have a
unified index number sequence.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 app/test-pmd/cmdline_flow.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 6263d30..f7816fc 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -109,6 +109,8 @@ enum index {
 	ITEM_PHY_PORT_INDEX,
 	ITEM_PORT_ID,
 	ITEM_PORT_ID_ID,
+	ITEM_TX_QUEUE,
+	ITEM_TX_QUEUE_ID,
 	ITEM_MARK,
 	ITEM_MARK_ID,
 	ITEM_RAW,
@@ -759,6 +761,7 @@ static const enum index next_item[] = {
 	ITEM_VF,
 	ITEM_PHY_PORT,
 	ITEM_PORT_ID,
+	ITEM_TX_QUEUE,
 	ITEM_MARK,
 	ITEM_RAW,
 	ITEM_ETH,
@@ -1954,6 +1957,21 @@ static const struct token token_list[] = {
 		.next = NEXT(item_port_id, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port_id, id)),
 	},
+	[ITEM_TX_QUEUE] = {
+		.name = "tx_queue",
+		.help = "match traffic from a given transmit queue",
+		.priv = PRIV_ITEM(QUEUE,
+				  sizeof(struct rte_flow_item_tx_queue)),
+		.next = NEXT(NEXT_ENTRY(ITEM_TX_QUEUE_ID)),
+		.call = parse_vc,
+	},
+	[ITEM_TX_QUEUE_ID] = {
+		.name = "index",
+		.help = "TX queue index of this port",
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT), NEXT_ENTRY(UNSIGNED),
+			     item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_tx_queue, queue)),
+	},
 	[ITEM_MARK] = {
 		.name = "mark",
 		.help = "match traffic against value set in previously matched rule",
-- 
2.5.5


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

* [dpdk-dev] [RFC PATCH v2 3/4] ethdev: add hairpin bind APIs
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 1/4] ethdev: add support for flow item transmit queue Bing Zhao
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 2/4] testpmd: add item transmit queue in flow CLI Bing Zhao
@ 2020-09-13 15:48   ` Bing Zhao
  2020-09-13 15:49   ` [dpdk-dev] [RFC PATCH v2 4/4] ethdev: add new attributes to hairpin queues config Bing Zhao
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-09-13 15:48 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko; +Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required for binding will be configured and
initialized before.
But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the master thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not configured done with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.
To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto bind attribute. Only after the required ports pair
started, the rte_eth_hairpin_bind() API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
The connection between the egress and ingress ports pair will be
established.
rte_eth_hairpin_unbind() API could be used to disconnect the egress
and the peer ingress ports. This should only be called before the
device is closed if needed.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.c        | 100 ++++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h        |  51 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h |  52 ++++++++++++++++++
 3 files changed, 203 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 066751f..8a3dc73 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2175,6 +2175,106 @@ rte_eth_tx_hairpin_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev *rdev;
+	uint16_t p;
+	uint16_t rp;
+	int ret = 0;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
+		return -EBUSY;
+	}
+
+	if (rx_port == RTE_MAX_ETHPORTS) {
+		RTE_ETH_FOREACH_DEV(p) {
+			rdev = &rte_eth_devices[p];
+			if (!rdev->data->dev_started) {
+				RTE_ETHDEV_LOG(ERR,
+					       "RX port %d is not started", p);
+				ret = -EBUSY;
+				goto error;
+			}
+			ret = (*dev->dev_ops->hairpin_bind)(dev, p);
+			if (ret) {
+				RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX "
+					       "%d to RX %d", tx_port, p);
+				goto error;
+			}
+		}
+	} else {
+		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
+		rdev = &rte_eth_devices[rx_port];
+		if (!rdev->data->dev_started) {
+			RTE_ETHDEV_LOG(ERR,
+				       "RX port %d is not started", rx_port);
+			return -EBUSY;
+		}
+		ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+		if (ret)
+			RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+				       "to RX %d", tx_port, rx_port);
+	}
+
+	return ret;
+
+error:
+	RTE_ETH_FOREACH_DEV(rp) {
+		if (rp < p)
+			(*dev->dev_ops->hairpin_unbind)(dev, rp);
+	}
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev *rdev;
+	uint16_t p;
+	uint16_t rp;
+	int ret = 0;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is stopped", tx_port);
+		return -EBUSY;
+	}
+
+	if (rx_port == RTE_MAX_ETHPORTS) {
+		RTE_ETH_FOREACH_DEV(p) {
+			rdev = &rte_eth_devices[p];
+			if (!rdev->data->dev_started) {
+				RTE_ETHDEV_LOG(ERR, "RX port %d is stopped", p);
+				ret = -EBUSY;
+				break;
+			}
+			ret = (*dev->dev_ops->hairpin_unbind)(dev, p);
+			if (ret) {
+				RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+					       "TX %d from RX %d", tx_port, p);
+				break;
+			}
+		}
+	} else {
+		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
+		rdev = &rte_eth_devices[rx_port];
+		if (!rdev->data->dev_started) {
+			RTE_ETHDEV_LOG(ERR, "RX port %d is stopped", rx_port);
+			return -EBUSY;
+		}
+		ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	}
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index e7733d8..fb217b4 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2101,6 +2101,57 @@ int rte_eth_tx_hairpin_queue_setup
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * Only allowed after all hairpin queues are configured properly and the
+ * devices of TX and peer RX are in started state.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices (optional). After
+ * unbind the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping a device.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 23cc1e0..b4efad6 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 	 const struct rte_eth_hairpin_conf *hairpin_conf);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -713,6 +761,10 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	eth_hairpin_bind hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
-- 
2.5.5


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

* [dpdk-dev] [RFC PATCH v2 4/4] ethdev: add new attributes to hairpin queues config
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
                     ` (2 preceding siblings ...)
  2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 3/4] ethdev: add hairpin bind APIs Bing Zhao
@ 2020-09-13 15:49   ` Bing Zhao
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-09-13 15:49 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko; +Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for application, two new attribute members of hairpin queue config
structure are added.

`tx_explicit` means if PMD or application itself will insert the TX
part flow rules.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommend that all paired queues between one egress and its
peer ingress ports have the same values, in order not to bring any
chaos to the system. The actual support of these attribute parameters
will be checked and decided by the PMD driver.

In a single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means no bind API needs
to be called and no TX flow rules need to be inserted manually by
the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index fb217b4..9560d60 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -996,6 +996,21 @@ struct rte_eth_hairpin_cap {
 
 #define RTE_ETH_MAX_HAIRPIN_PEERS 32
 
+/*
+ * Hairpin queue attribute parameters.
+ * Each TX queue and peer RX queue should have the same value.
+ * Default value 0 is for backward-compatibility, the same behaviors should
+ * remain if the value is not set (0).
+ */
+/**< Hairpin queues will be bound automatically */
+#define RTE_ETH_HAIRPIN_BIND_AUTO		(0)
+/**< Hairpin queues will be bound manually with bind API */
+#define RTE_ETH_HAIRPIN_BIND_MANUAL		(1)
+/**< Hairpin TX part flow rule will be inserted implicitly by PMD */
+#define RTE_ETH_HAIRPIN_TXRULE_IMPLICIT		(0)
+/**< Hairpin TX part flow rule will be inserted explicitly by APP */
+#define RTE_ETH_HAIRPIN_TXRULE_EXPLICIT		(1)
+
 /**
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
@@ -1016,6 +1031,8 @@ struct rte_eth_hairpin_peer {
 struct rte_eth_hairpin_conf {
 	uint16_t peer_count; /**< The number of peers. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
+	uint16_t tx_explicit; /**< Explicit TX flow rule by APP. */
+	uint16_t manual_bind; /**< Manually binding hairpin queues. */
 };
 
 /**
-- 
2.5.5


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

* [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports
  2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
                     ` (3 preceding siblings ...)
  2020-09-13 15:49   ` [dpdk-dev] [RFC PATCH v2 4/4] ethdev: add new attributes to hairpin queues config Bing Zhao
@ 2020-10-01  0:25   ` Bing Zhao
  2020-10-01  0:25     ` [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs Bing Zhao
                       ` (7 more replies)
  4 siblings, 8 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-01  0:25 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

This patch set will add the support for hairpin between two ports.
In the meanwhile, the compatibility of the previous single port mode
is taken into consideration and kept.

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
3. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
4. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.

Bing Zhao (4):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   |  68 ++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 lib/librte_ethdev/rte_ethdev.c           | 162 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           |  69 +++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 160 ++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   5 +
 7 files changed, 477 insertions(+), 4 deletions(-)

-- 
2.5.5


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

* [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-01  0:25     ` Bing Zhao
  2020-10-04  9:20       ` Ori Kam
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config Bing Zhao
                       ` (6 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-01  0:25 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.c           | 107 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           |  51 +++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    |  52 +++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   2 +
 4 files changed, 212 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index dfe5c1b..72f567b 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2175,6 +2175,113 @@ rte_eth_tx_hairpin_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev *rdev;
+	uint16_t p;
+	uint16_t rp;
+	int ret = 0;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
+		return -EBUSY;
+	}
+
+	/*
+	 * If the all the ports probed belong to two or more separate NICs, it
+	 * is recommended that each pair is bound independently but not in the
+	 * loop to bind all ports.
+	 */
+	if (rx_port == RTE_MAX_ETHPORTS) {
+		RTE_ETH_FOREACH_DEV(p) {
+			rdev = &rte_eth_devices[p];
+			if (!rdev->data->dev_started) {
+				RTE_ETHDEV_LOG(ERR,
+					       "RX port %d is not started", p);
+				ret = -EBUSY;
+				goto unbind;
+			}
+			ret = (*dev->dev_ops->hairpin_bind)(dev, p);
+			if (ret) {
+				RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX "
+					       "%d to RX %d", tx_port, p);
+				goto unbind;
+			}
+		}
+	} else {
+		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
+		rdev = &rte_eth_devices[rx_port];
+		if (!rdev->data->dev_started) {
+			RTE_ETHDEV_LOG(ERR,
+				       "RX port %d is not started", rx_port);
+			return -EBUSY;
+		}
+		ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+		if (ret)
+			RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+				       "to RX %d", tx_port, rx_port);
+	}
+
+	return ret;
+
+unbind:
+	/* Roll back the previous binding process. */
+	RTE_ETH_FOREACH_DEV(rp) {
+		if (rp < p)
+			(*dev->dev_ops->hairpin_unbind)(dev, rp);
+		else
+			break;
+	}
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev *rdev;
+	uint16_t p;
+	int ret = 0;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is stopped", tx_port);
+		return -EBUSY;
+	}
+
+	if (rx_port == RTE_MAX_ETHPORTS) {
+		RTE_ETH_FOREACH_DEV(p) {
+			rdev = &rte_eth_devices[p];
+			if (!rdev->data->dev_started) {
+				RTE_ETHDEV_LOG(ERR, "RX port %d is stopped", p);
+				ret = -EBUSY;
+				break;
+			}
+			ret = (*dev->dev_ops->hairpin_unbind)(dev, p);
+			if (ret) {
+				RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+					       "TX %d from RX %d", tx_port, p);
+				break;
+			}
+		}
+	} else {
+		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
+		rdev = &rte_eth_devices[rx_port];
+		if (!rdev->data->dev_started) {
+			RTE_ETHDEV_LOG(ERR, "RX port %d is stopped", rx_port);
+			return -EBUSY;
+		}
+		ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	}
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 645a186..c3fb684 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2133,6 +2133,57 @@ int rte_eth_tx_hairpin_queue_setup
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * It is only allowed to call this API after all hairpin queues are configured
+ * properly and the devices of TX and peer RX are in started state.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices (optional). After
+ * unbind the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 04ac8e9..910433f 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 	 const struct rte_eth_hairpin_conf *hairpin_conf);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -713,6 +761,10 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index c95ef51..18efe4e 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 };
-- 
2.5.5


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

* [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
  2020-10-01  0:25     ` [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-01  0:26     ` Bing Zhao
  2020-10-04  9:22       ` Ori Kam
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation Bing Zhao
                       ` (5 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-01  0:26 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of hairpin queue
config structure are added.

`tx_explicit` means if the application itself will insert the TX part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommend that all paired queues between one egress and its
peer ingress ports have the same values, in order not to bring any
chaos to the system. The actual support of these attribute parameters
will be checked and decided by the PMD driver.

In a single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means no bind API needs
to be called and no TX flow rules need to be inserted manually by
the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.h | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index c3fb684..0cabff0 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1027,6 +1027,21 @@ struct rte_eth_hairpin_cap {
 
 #define RTE_ETH_MAX_HAIRPIN_PEERS 32
 
+/*
+ * Hairpin queue attribute parameters.
+ * Each TX queue and peer RX queue should have the same value.
+ * Default value 0 is for backward-compatibility, the same behaviors should
+ * remain if the value is not set (0).
+ */
+/**< Hairpin queues will be bound automatically */
+#define RTE_ETH_HAIRPIN_BIND_AUTO		(0)
+/**< Hairpin queues will be bound manually with bind API */
+#define RTE_ETH_HAIRPIN_BIND_MANUAL		(1)
+/**< Hairpin TX part flow rule will be inserted implicitly by PMD */
+#define RTE_ETH_HAIRPIN_TXRULE_IMPLICIT		(0)
+/**< Hairpin TX part flow rule will be inserted explicitly by APP */
+#define RTE_ETH_HAIRPIN_TXRULE_EXPLICIT		(1)
+
 /**
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
@@ -1046,6 +1061,9 @@ struct rte_eth_hairpin_peer {
  */
 struct rte_eth_hairpin_conf {
 	uint16_t peer_count; /**< The number of peers. */
+	uint32_t reserved : 30; /**< Reserved bits. */
+	uint32_t tx_explicit : 1; /**< Explicit TX flow rule mode. */
+	uint32_t manual_bind : 1; /**< Manually bind hairpin queues. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
2.5.5


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

* [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
  2020-10-01  0:25     ` [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-01  0:26     ` Bing Zhao
  2020-10-04  9:34       ` Ori Kam
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup Bing Zhao
                       ` (4 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-01  0:26 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between TX and RX queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the TX, RX queues separately.
The peer unbind API resets the current queue configuraion and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect TX, RX queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.c           |  55 ++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 108 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 166 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 72f567b..4bfc26e 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5515,6 +5515,61 @@ handle_port_link_status(const char *cmd __rte_unused,
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 910433f..d759c58 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -622,6 +625,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, bool direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, bool direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, bool direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -765,6 +783,9 @@ struct eth_dev_ops {
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin TX queues from the peer port RX queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
 };
 
 /**
@@ -1120,6 +1141,93 @@ __rte_internal
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * @internal
+ * Pass the current hairpin queue HW and/or HW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  true - pass TX queue information and get peer RX queue information
+ *  false - pass RX queue information and get peer TX queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  bool direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connection.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  true - bind current TX queue to peer RX queue
+ *  false - bind current RX queue to peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				bool direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to create the connection.
+ *  true - unbind current TX queue from peer RX queue
+ *  false - unbind current RX queue from peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  bool direction);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 18efe4e..d05cd97 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -250,6 +250,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 	rte_flow_expand_rss;
-- 
2.5.5


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

* [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
                       ` (2 preceding siblings ...)
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-01  0:26     ` Bing Zhao
  2020-10-04  9:39       ` Ori Kam
  2020-10-04  9:45     ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Ori Kam
                       ` (3 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-01  0:26 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide more flexible configuration.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port RX queue will be connected to the current port TX
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of probed port is odd, then the last one will be paired
to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
TX flow mode.

If not set, default value zero will be used and the behavior will
try to get align with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 app/test-pmd/parameters.c | 15 +++++++++++
 app/test-pmd/testpmd.c    | 68 ++++++++++++++++++++++++++++++++++++++++++++---
 app/test-pmd/testpmd.h    |  2 ++
 3 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 1ead595..991029d 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -221,6 +221,9 @@ usage(char* progname)
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -644,6 +647,7 @@ launch_args_parse(int argc, char** argv)
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1111,6 +1115,17 @@ launch_args_parse(int argc, char** argv)
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index fe6450c..c604045 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ bool setup_on_probe_event = true;
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ port_is_started(portid_t port_id)
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,48 @@ setup_hairpin_queues(portid_t pi)
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2418,10 @@ setup_hairpin_queues(portid_t pi)
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2448,8 @@ start_port(portid_t pid)
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	uint16_t cnt_pi = 0;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,8 +2589,10 @@ start_port(portid_t pid)
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
+			p_pi = pi;
+			cnt_pi++;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
 		if (clear_ptypes) {
@@ -3775,6 +3822,19 @@ main(int argc, char** argv)
 		rte_exit(EXIT_FAILURE, "Start ports failed\n");
 
 	/* set all ports to promiscuous mode by default */
+	if (hairpin_mode & 0x3) {
+		RTE_ETH_FOREACH_DEV(port_id) {
+			ret = rte_eth_hairpin_bind(port_id, RTE_MAX_ETHPORTS);
+			if (ret != 0) {
+				RTE_LOG(ERR, EAL, "Error during binding "
+					"hairpin tx port %u: %s",
+					port_id, rte_strerror(-ret));
+				return -1;
+			}
+		}
+	}
+
+	/* set all ports to promiscuous mode by default */
 	RTE_ETH_FOREACH_DEV(port_id) {
 		ret = rte_eth_promiscuous_enable(port_id);
 		if (ret != 0)
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c7e7e41..29ede20 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -398,6 +398,8 @@ extern uint32_t param_total_num_mbufs;
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
-- 
2.5.5


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

* Re: [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
  2020-10-01  0:25     ` [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-04  9:20       ` Ori Kam
  2020-10-07 11:21         ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Ori Kam @ 2020-10-04  9:20 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

PSB,

Thanks,
Ori
> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 1, 2020 3:26 AM
> Cc: dev@dpdk.org
> Subject: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> 
> In single port hairpin mode, all the hairpin TX and RX queues belong
> to the same device. After the queues are set up properly, there is
> no other dependency between the TX queue and its RX peer queue. The
> binding process that connected the TX and RX queues together from
> hardware level will be done automatically during the device start
> procedure. Everything required is configured and initialized already
> for the binding process.
> 
> But in two ports hairpin mode, there will be some cross-dependences
> between two different ports. Usually, the ports will be initialized
> serially by the main thread but not in parallel. The earlier port
> will not be able to enable the bind if the following peer port is
> not yet configured with HW resources. What's more, if one port is
> detached / attached dynamically, it would introduce more trouble
> for the hairpin binding.
> 
> To overcome these, new APIs for binding and unbinding are added.
> During startup, only the hairpin TX and RX peer queues will be set
> up. Nothing will be done when starting the device if the queues are
> without auto-bind attribute. Only after the required ports pair
> started, the `rte_eth_hairpin_bind()` API can be called to bind the
> all TX queues of the egress port to the RX queues of the peer port.
> Then the connection between the egress and ingress ports pair will
> be established.
> 
> The `rte_eth_hairpin_unbind()` API could be used to disconnect the
> egress and the peer ingress ports. This should only be called before
> the device is closed if needed. When doing the clean up, all the
> egress and ingress pairs related to a single port should be taken
> into consideration.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  lib/librte_ethdev/rte_ethdev.c           | 107
> +++++++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev.h           |  51 +++++++++++++++
>  lib/librte_ethdev/rte_ethdev_driver.h    |  52 +++++++++++++++
>  lib/librte_ethdev/rte_ethdev_version.map |   2 +
>  4 files changed, 212 insertions(+)
> 
> diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
> index dfe5c1b..72f567b 100644
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -2175,6 +2175,113 @@ rte_eth_tx_hairpin_queue_setup(uint16_t port_id,
> uint16_t tx_queue_id,
>  	return eth_err(port_id, ret);
>  }
> 
> +int
> +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
> +{
> +	struct rte_eth_dev *dev;
> +	struct rte_eth_dev *rdev;
> +	uint16_t p;
> +	uint16_t rp;
> +	int ret = 0;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> +	dev = &rte_eth_devices[tx_port];
> +	if (!dev->data->dev_started) {
> +		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
> +		return -EBUSY;
> +	}
> +
> +	/*
> +	 * If the all the ports probed belong to two or more separate NICs, it
> +	 * is recommended that each pair is bound independently but not in the
> +	 * loop to bind all ports.
> +	 */

I don't understand your comment. 

> +	if (rx_port == RTE_MAX_ETHPORTS) {

I think maybe this should be done in the tx queue. Since if the bind don't need some port why do
we care if it is started?
So either add a new function to get all peer ports from the tx port, or move this logic to the 
Target PMD.

> +		RTE_ETH_FOREACH_DEV(p) {
> +			rdev = &rte_eth_devices[p];
> +			if (!rdev->data->dev_started) {
> +				RTE_ETHDEV_LOG(ERR,
> +					       "RX port %d is not started", p);
> +				ret = -EBUSY;
> +				goto unbind;
> +			}
> +			ret = (*dev->dev_ops->hairpin_bind)(dev, p);
> +			if (ret) {
> +				RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin
> TX "
> +					       "%d to RX %d", tx_port, p);
> +				goto unbind;
> +			}
> +		}
> +	} else {
> +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> +		rdev = &rte_eth_devices[rx_port];
> +		if (!rdev->data->dev_started) {
> +			RTE_ETHDEV_LOG(ERR,
> +				       "RX port %d is not started", rx_port);
> +			return -EBUSY;
> +		}
> +		ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
> +		if (ret)
> +			RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
> +				       "to RX %d", tx_port, rx_port);
> +	}
> +
> +	return ret;
> +
> +unbind:
> +	/* Roll back the previous binding process. */
> +	RTE_ETH_FOREACH_DEV(rp) {
> +		if (rp < p)
> +			(*dev->dev_ops->hairpin_unbind)(dev, rp);
> +		else
> +			break;
> +	}
> +	return ret;
> +}
> +
> +int
> +rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
> +{
> +	struct rte_eth_dev *dev;
> +	struct rte_eth_dev *rdev;
> +	uint16_t p;
> +	int ret = 0;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> +	dev = &rte_eth_devices[tx_port];
> +	if (!dev->data->dev_started) {
> +		RTE_ETHDEV_LOG(ERR, "TX port %d is stopped", tx_port);
> +		return -EBUSY;
> +	}
> +
> +	if (rx_port == RTE_MAX_ETHPORTS) {
> +		RTE_ETH_FOREACH_DEV(p) {
> +			rdev = &rte_eth_devices[p];
> +			if (!rdev->data->dev_started) {

This condition should never be true.
First see my comment above about the list of devices, second port should fail to
stop if it is bounded.

> +				RTE_ETHDEV_LOG(ERR, "RX port %d is
> stopped", p);
> +				ret = -EBUSY;
> +				break;
> +			}
> +			ret = (*dev->dev_ops->hairpin_unbind)(dev, p);
> +			if (ret) {
> +				RTE_ETHDEV_LOG(ERR, "Failed to unbind
> hairpin "
> +					       "TX %d from RX %d", tx_port, p);
> +				break;
> +			}
> +		}
> +	} else {
> +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> +		rdev = &rte_eth_devices[rx_port];
> +		if (!rdev->data->dev_started) {
> +			RTE_ETHDEV_LOG(ERR, "RX port %d is stopped",
> rx_port);
> +			return -EBUSY;
> +		}
> +		ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
> +	}
> +
> +	return ret;
> +}
> +
>  void
>  rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
>  		void *userdata __rte_unused)
> diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
> index 645a186..c3fb684 100644
> --- a/lib/librte_ethdev/rte_ethdev.h
> +++ b/lib/librte_ethdev/rte_ethdev.h
> @@ -2133,6 +2133,57 @@ int rte_eth_tx_hairpin_queue_setup
>  	 const struct rte_eth_hairpin_conf *conf);
> 
>  /**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
> + * Bind all hairpin TX queues of one port to the RX queues of the peer port.
> + * It is only allowed to call this API after all hairpin queues are configured
> + * properly and the devices of TX and peer RX are in started state.
> + *
> + * @param tx_port
> + *   The TX port identifier of the Ethernet device.
> + * @param rx_port
> + *   The peer RX port identifier of the Ethernet device.
> + *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
> + *   RX port ID could have the same value with TX port ID.
> + *
> + * @return
> + *   - (0) if successful.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-EBUSY) if device is not in started state.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.
> + */
> +__rte_experimental
> +int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
> + * Unbind all hairpin TX queues of one port from the RX queues of the peer
> port.
> + * This should be called before closing the TX or RX devices (optional). After
> + * unbind the hairpin ports pair, it is allowed to bind them again.
> + * Changing queues configuration should be after stopping the device.
> + *
> + * @param tx_port
> + *   The TX port identifier of the Ethernet device.
> + * @param rx_port
> + *   The peer RX port identifier of the Ethernet device.
> + *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
> + *   RX port ID could have the same value with TX port ID.
> + *
> + * @return
> + *   - (0) if successful.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-EBUSY) if device is in stopped state.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.
> + */
> +__rte_experimental
> +int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
> +
> +/**
>   * Return the NUMA socket to which an Ethernet device is connected
>   *
>   * @param port_id
> diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> b/lib/librte_ethdev/rte_ethdev_driver.h
> index 04ac8e9..910433f 100644
> --- a/lib/librte_ethdev/rte_ethdev_driver.h
> +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> @@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
>  	 const struct rte_eth_hairpin_conf *hairpin_conf);
> 
>  /**
> + * @internal
> + * Bind all hairpin TX queues of one port to the RX queues of the peer port.
> + *
> + * @param dev
> + *   ethdev handle of port.
> + * @param rx_port
> + *   the peer RX port.
> + *
> + * @return
> + *   Negative errno value on error, 0 on success.
> + *
> + * @retval 0
> + *   Success, bind successfully.
> + * @retval -ENOTSUP
> + *   Bind API is not supported.
> + * @retval -EINVAL
> + *   One of the parameters is invalid.
> + * @retval -EBUSY
> + *   Device is not started.
> + */
> +typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
> +				uint16_t rx_port);
> +
> +/**
> + * @internal
> + * Unbind all hairpin TX queues of one port from the RX queues of the peer
> port.
> + *
> + * @param dev
> + *   ethdev handle of port.
> + * @param rx_port
> + *   the peer RX port.
> + *
> + * @return
> + *   Negative errno value on error, 0 on success.
> + *
> + * @retval 0
> + *   Success, bind successfully.
> + * @retval -ENOTSUP
> + *   Bind API is not supported.
> + * @retval -EINVAL
> + *   One of the parameters is invalid.
> + * @retval -EBUSY
> + *   Device is already stopped.
> + */
> +typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
> +				  uint16_t rx_port);
> +
> +/**
>   * @internal A structure containing the functions exported by an Ethernet
> driver.
>   */
>  struct eth_dev_ops {
> @@ -713,6 +761,10 @@ struct eth_dev_ops {
>  	/**< Set up device RX hairpin queue. */
>  	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
>  	/**< Set up device TX hairpin queue. */
> +	eth_hairpin_bind_t hairpin_bind;
> +	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
> +	eth_hairpin_unbind_t hairpin_unbind;
> +	/**< Unbind all hairpin TX queues from the peer port RX queues. */
>  };
> 
>  /**
> diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> b/lib/librte_ethdev/rte_ethdev_version.map
> index c95ef51..18efe4e 100644
> --- a/lib/librte_ethdev/rte_ethdev_version.map
> +++ b/lib/librte_ethdev/rte_ethdev_version.map
> @@ -227,6 +227,8 @@ EXPERIMENTAL {
>  	rte_tm_wred_profile_delete;
> 
>  	# added in 20.11
> +	rte_eth_hairpin_bind;
> +	rte_eth_hairpin_unbind;
>  	rte_eth_link_speed_to_str;
>  	rte_eth_link_to_str;
>  };
> --
> 2.5.5


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

* Re: [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-04  9:22       ` Ori Kam
  2020-10-07 11:32         ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Ori Kam @ 2020-10-04  9:22 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

PSB,
Best,
Ori

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 1, 2020 3:26 AM
> Subject: [PATCH 2/4] ethdev: add new attributes to hairpin config
> 
> To support two ports hairpin mode and keep the backward compatibility
> for the application, two new attribute members of hairpin queue
> config structure are added.
> 
> `tx_explicit` means if the application itself will insert the TX part
> flow rules. If not set, PMD will insert the rules implicitly.
> `manual_bind` means if the hairpin TX queue and peer RX queue will be
> bound automatically during device start stage.
> 
> Different TX and RX queue pairs could have different values, but it
> is highly recommend that all paired queues between one egress and its
> peer ingress ports have the same values, in order not to bring any
> chaos to the system. The actual support of these attribute parameters
> will be checked and decided by the PMD driver.
> 
> In a single port hairpin, if both are zero without any setting, the
> behavior will remain the same as before. It means no bind API needs
> to be called and no TX flow rules need to be inserted manually by
> the application.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  lib/librte_ethdev/rte_ethdev.h | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 
> diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
> index c3fb684..0cabff0 100644
> --- a/lib/librte_ethdev/rte_ethdev.h
> +++ b/lib/librte_ethdev/rte_ethdev.h
> @@ -1027,6 +1027,21 @@ struct rte_eth_hairpin_cap {
> 
>  #define RTE_ETH_MAX_HAIRPIN_PEERS 32
> 
> +/*
> + * Hairpin queue attribute parameters.
> + * Each TX queue and peer RX queue should have the same value.
> + * Default value 0 is for backward-compatibility, the same behaviors should
> + * remain if the value is not set (0).
> + */
> +/**< Hairpin queues will be bound automatically */
> +#define RTE_ETH_HAIRPIN_BIND_AUTO		(0)
> +/**< Hairpin queues will be bound manually with bind API */
> +#define RTE_ETH_HAIRPIN_BIND_MANUAL		(1)
> +/**< Hairpin TX part flow rule will be inserted implicitly by PMD */
> +#define RTE_ETH_HAIRPIN_TXRULE_IMPLICIT		(0)
> +/**< Hairpin TX part flow rule will be inserted explicitly by APP */
> +#define RTE_ETH_HAIRPIN_TXRULE_EXPLICIT		(1)
> +

Why do you need those defines if you are using bit fields?

>  /**
>   * @warning
>   * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> @@ -1046,6 +1061,9 @@ struct rte_eth_hairpin_peer {
>   */
>  struct rte_eth_hairpin_conf {
>  	uint16_t peer_count; /**< The number of peers. */
> +	uint32_t reserved : 30; /**< Reserved bits. */
> +	uint32_t tx_explicit : 1; /**< Explicit TX flow rule mode. */
> +	uint32_t manual_bind : 1; /**< Manually bind hairpin queues. */

Why not place the new bits at the end?
Also why do you place the reserved first?

>  	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
>  };
> 
> --
> 2.5.5


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

* Re: [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-04  9:34       ` Ori Kam
  2020-10-07 11:34         ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Ori Kam @ 2020-10-04  9:34 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

PSB
Best,
Ori

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 1, 2020 3:26 AM
> Subject: [PATCH 3/4] ethdev: add APIs for hairpin queue operation
> 
> Every hairpin queue pair should be configured properly and the
> connection between TX and RX queues should be established, before
> hairpin function works. In single port hairpin mode, the queues of
> each pair belong to the same device. It is easy to get the hardware
> and software information of each queue and configure the hairpin
> connection with such information. In two ports hairpin mode, it is
> not easy or inappropriate to access one queue's information from
> another device.
> 
> Since hairpin is configured per queue pair, three new APIs are
> introduced and they are internal for the PMD using.
> 
> The peer update API helps to pass one queue's information to the
> peer queue and get the peer's information back for the next step.
> The peer bind API configures the current queue with the peer's
> information. For each hairpin queue pair, this API may need to be
> called twice to configure the TX, RX queues separately.
> The peer unbind API resets the current queue configuraion and state
> to disconnect it from the peer queue. Also, it may need to be called
> twice to disconnect TX, RX queues from each other.
> 
> Some parameter of the above APIs might not be mandatory, and it
> depends on the PMD implementation.
> 
> The structure of `rte_hairpin_peer_info` is only a declaration and
> the actual members will be defined in each PMD when being used.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  lib/librte_ethdev/rte_ethdev.c           |  55 ++++++++++++++++
>  lib/librte_ethdev/rte_ethdev_driver.h    | 108
> +++++++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev_version.map |   3 +
>  3 files changed, 166 insertions(+)
> 
> diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
> index 72f567b..4bfc26e 100644
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -5515,6 +5515,61 @@ handle_port_link_status(const char *cmd
> __rte_unused,
>  	return 0;
>  }
> 
> +int
> +rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t
> peer_queue,
> +				  struct rte_hairpin_peer_info *cur_info,
> +				  struct rte_hairpin_peer_info *peer_info,
> +				  bool direction)
> +{
> +	struct rte_eth_dev *dev;
> +
> +	/* Current queue information is not mandatory. */
> +	if (peer_info == NULL)
> +		return -EINVAL;
> +
> +	/* No need to check the validity again. */
> +	dev = &rte_eth_devices[peer_port];
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> >hairpin_queue_peer_update,
> +				-ENOTSUP);
> +
> +	return (*dev->dev_ops->hairpin_queue_peer_update)(dev,
> peer_queue,
> +					cur_info, peer_info, direction);
> +}
> +
> +int
> +rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
> +				struct rte_hairpin_peer_info *peer_info,
> +				bool direction)
> +{
> +	struct rte_eth_dev *dev;
> +
> +	if (peer_info == NULL)
> +		return -EINVAL;
> +
> +	/* No need to check the validity again. */
> +	dev = &rte_eth_devices[cur_port];
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> >hairpin_queue_peer_bind,
> +				-ENOTSUP);
> +
> +	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
> +							peer_info, direction);
> +}
> +
> +int
> +rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
> +				  bool direction)
> +{
> +	struct rte_eth_dev *dev;
> +
> +	/* No need to check the validity again. */
> +	dev = &rte_eth_devices[cur_port];
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> >hairpin_queue_peer_bind,
> +				-ENOTSUP);
> +
> +	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
> +							  direction);
> +}
> +
>  RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
> 
>  RTE_INIT(ethdev_init_telemetry)
> diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> b/lib/librte_ethdev/rte_ethdev_driver.h
> index 910433f..d759c58 100644
> --- a/lib/librte_ethdev/rte_ethdev_driver.h
> +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> @@ -21,6 +21,9 @@
>  extern "C" {
>  #endif
> 
> +/**< @internal Declaration of the hairpin peer queue information structure.
> */
> +struct rte_hairpin_peer_info;
> +
>  /*
>   * Definitions of all functions exported by an Ethernet driver through the
>   * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
> @@ -622,6 +625,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev
> *dev,
>  typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
>  				  uint16_t rx_port);
> 
> +typedef int (*eth_hairpin_queue_peer_update_t)
> +	(struct rte_eth_dev *dev, uint16_t peer_queue,
> +	 struct rte_hairpin_peer_info *current_info,
> +	 struct rte_hairpin_peer_info *peer_info, bool direction);
> +/**< @internal Update and fetch peer queue information. */
> +
> +typedef int (*eth_hairpin_queue_peer_bind_t)
> +	(struct rte_eth_dev *dev, uint16_t cur_queue,
> +	 struct rte_hairpin_peer_info *peer_info, bool direction);
> +/**< @internal Bind peer queue to the current queue with fetched
> information. */
> +
> +typedef int (*eth_hairpin_queue_peer_unbind_t)
> +	(struct rte_eth_dev *dev, uint16_t cur_queue, bool direction);
> +/**< @internal Unbind peer queue from the current queue. */
> +
>  /**
>   * @internal A structure containing the functions exported by an Ethernet
> driver.
>   */
> @@ -765,6 +783,9 @@ struct eth_dev_ops {
>  	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
>  	eth_hairpin_unbind_t hairpin_unbind;
>  	/**< Unbind all hairpin TX queues from the peer port RX queues. */
> +	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
> +	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
> +	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
>  };
> 
>  /**
> @@ -1120,6 +1141,93 @@ __rte_internal
>  int
>  rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t
> ethdev_uninit);
> 
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
> + * @internal
> + * Pass the current hairpin queue HW and/or HW information to the peer
> queue

I don't think you need experimental tag when using internal.

> + * and fetch back the information of the peer queue.
> + *
> + * @param peer_port
> + *  Peer port identifier of the Ethernet device.
> + * @param peer_queue
> + *  Peer queue index of the port.
> + * @param cur_info
> + *  Pointer to the current information structure.
> + * @param peer_info
> + *  Pointer to the peer information, output.
> + * @param direction
> + *  Direction to pass the information.
> + *  true - pass TX queue information and get peer RX queue information
> + *  false - pass RX queue information and get peer TX queue information
> + *
> + * @return
> + *  Negative errno value on error, 0 on success.
> + */
> +__rte_internal
> +int
> +rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t
> peer_queue,
> +				  struct rte_hairpin_peer_info *cur_info,
> +				  struct rte_hairpin_peer_info *peer_info,
> +				  bool direction);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
> + * @internal
> + * Configure current hairpin queue with the peer information fetched to
> create

Same comment as above.

> + * the connection (bind) with peer queue in the specified direction.
> + * This function might need to be called twice to fully create the connection.
> + *
> + * @param cur_port
> + *  Current port identifier of the Ethernet device.
> + * @param cur_queue
> + *  Current queue index of the port.
> + * @param peer_info
> + *  Pointer to the peer information, input.
> + * @param direction
> + *  Direction to create the connection.
> + *  true - bind current TX queue to peer RX queue
> + *  false - bind current RX queue to peer TX queue
> + *
> + * @return
> + *  Negative errno value on error, 0 on success.
> + */
> +__rte_internal
> +int
> +rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
> +				struct rte_hairpin_peer_info *peer_info,
> +				bool direction);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
> + * @internal
> + * Reset the current queue state and configuration to disconnect (unbind) it
> + * from the peer queue.
> + * This function might need to be called twice to disconnect each other.
> + *
> + * @param cur_port
> + *  Current port identifier of the Ethernet device.
> + * @param cur_queue
> + *  Current queue index of the port.
> + * @param direction
> + *  Direction to create the connection.
> + *  true - unbind current TX queue from peer RX queue
> + *  false - unbind current RX queue from peer TX queue
> + *
> + * @return
> + *  Negative errno value on error, 0 on success.
> + */
> +__rte_internal
> +int
> +rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
> +				  bool direction);
> +
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> b/lib/librte_ethdev/rte_ethdev_version.map
> index 18efe4e..d05cd97 100644
> --- a/lib/librte_ethdev/rte_ethdev_version.map
> +++ b/lib/librte_ethdev/rte_ethdev_version.map
> @@ -250,6 +250,9 @@ INTERNAL {
>  	rte_eth_devargs_parse;
>  	rte_eth_dma_zone_free;
>  	rte_eth_dma_zone_reserve;
> +	rte_eth_hairpin_queue_peer_bind;
> +	rte_eth_hairpin_queue_peer_unbind;
> +	rte_eth_hairpin_queue_peer_update;
>  	rte_eth_switch_domain_alloc;
>  	rte_eth_switch_domain_free;
>  	rte_flow_expand_rss;
> --
> 2.5.5


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

* Re: [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-04  9:39       ` Ori Kam
  2020-10-07 11:36         ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Ori Kam @ 2020-10-04  9:39 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

PSB,

Best,
Ori

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 1, 2020 3:26 AM
> Subject: [PATCH 4/4] app/testpmd: change hairpin queues setup
> 
> A new parameter `hairpin-mode` is introduced to the testpmd command
> line. Bitmask value is used to provide more flexible configuration.
> 
> Bit 0 in the LSB indicates the hairpin will use the loop mode. The
> previous port RX queue will be connected to the current port TX
> queue.
> Bit 1 in the LSB indicates the hairpin will use pair port mode. The
> even index port will be paired with the next odd index port. If the
> total number of probed port is odd, then the last one will be paired
> to itself.
> If this byte is zero, then each port will be paired to itself.
> Bit 0 takes a higher priority in the checking.
> 
> Bit 4 in the second bytes indicate if the hairpin will use explicit
> TX flow mode.
> 
I think some basic example will be great here.

> If not set, default value zero will be used and the behavior will
> try to get align with the previous single port mode. If the ports
> belong to different vendors' NICs, it is suggested to use the `self`
> hairpin mode only.
> 
> Since hairpin configures the hardware resources, the port mask of
> packets forwarding engine will not be used here.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  app/test-pmd/parameters.c | 15 +++++++++++
>  app/test-pmd/testpmd.c    | 68
> ++++++++++++++++++++++++++++++++++++++++++++---
>  app/test-pmd/testpmd.h    |  2 ++
>  3 files changed, 81 insertions(+), 4 deletions(-)
> 
> diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
> index 1ead595..991029d 100644
> --- a/app/test-pmd/parameters.c
> +++ b/app/test-pmd/parameters.c
> @@ -221,6 +221,9 @@ usage(char* progname)
>  	       "enabled\n");
>  	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
>  	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
> +	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
> +	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
> +	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
>  }
> 
>  #ifdef RTE_LIBRTE_CMDLINE
> @@ -644,6 +647,7 @@ launch_args_parse(int argc, char** argv)
>  		{ "rxd",			1, 0, 0 },
>  		{ "txd",			1, 0, 0 },
>  		{ "hairpinq",			1, 0, 0 },
> +		{ "hairpin-mode",		1, 0, 0 },
>  		{ "burst",			1, 0, 0 },
>  		{ "mbcache",			1, 0, 0 },
>  		{ "txpt",			1, 0, 0 },
> @@ -1111,6 +1115,17 @@ launch_args_parse(int argc, char** argv)
>  				rte_exit(EXIT_FAILURE, "Either rx or tx queues
> should "
>  						"be non-zero\n");
>  			}
> +			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
> +				char *end = NULL;
> +				unsigned int n;
> +
> +				errno = 0;
> +				n = strtoul(optarg, &end, 0);
> +				if (errno != 0 || end == optarg)
> +					rte_exit(EXIT_FAILURE, "hairpin mode
> invalid\n");
> +				else
> +					hairpin_mode = (uint16_t)n;
> +			}
>  			if (!strcmp(lgopts[opt_idx].name, "burst")) {
>  				n = atoi(optarg);
>  				if (n == 0) {
> diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
> index fe6450c..c604045 100644
> --- a/app/test-pmd/testpmd.c
> +++ b/app/test-pmd/testpmd.c
> @@ -367,6 +367,9 @@ bool setup_on_probe_event = true;
>  /* Clear ptypes on port initialization. */
>  uint8_t clear_ptypes = true;
> 
> +/* Hairpin ports configuration mode. */
> +uint16_t hairpin_mode;
> +
>  /* Pretty printing of ethdev events */
>  static const char * const eth_event_desc[] = {
>  	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
> @@ -2345,7 +2348,7 @@ port_is_started(portid_t port_id)
> 
>  /* Configure the Rx and Tx hairpin queues for the selected port. */
>  static int
> -setup_hairpin_queues(portid_t pi)
> +setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
>  {
>  	queueid_t qi;
>  	struct rte_eth_hairpin_conf hairpin_conf = {
> @@ -2354,10 +2357,48 @@ setup_hairpin_queues(portid_t pi)
>  	int i;
>  	int diag;
>  	struct rte_port *port = &ports[pi];
> +	uint16_t peer_rx_port = pi;
> +	uint16_t peer_tx_port = pi;
> +	uint32_t manual = 1;
> +	uint32_t tx_exp = hairpin_mode & 0x10;
> +
> +	if (!(hairpin_mode & 0xf)) {
> +		peer_rx_port = pi;
> +		peer_tx_port = pi;
> +		manual = 0;
> +	} else if (hairpin_mode & 0x1) {
> +		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
> +
> RTE_ETH_DEV_NO_OWNER);
> +		if (peer_tx_port >= RTE_MAX_ETHPORTS)
> +			peer_tx_port = rte_eth_find_next_owned_by(0,
> +						RTE_ETH_DEV_NO_OWNER);
> +		if (p_pi != RTE_MAX_ETHPORTS) {
> +			peer_rx_port = p_pi;
> +		} else {
> +			uint16_t next_pi;
> +
> +			RTE_ETH_FOREACH_DEV(next_pi)
> +				peer_rx_port = next_pi;
> +		}
> +		manual = 1;
> +	} else if (hairpin_mode & 0x2) {
> +		if (cnt_pi & 0x1) {
> +			peer_rx_port = p_pi;
> +		} else {
> +			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
> +						RTE_ETH_DEV_NO_OWNER);
> +			if (peer_rx_port >= RTE_MAX_ETHPORTS)
> +				peer_rx_port = pi;
> +		}
> +		peer_tx_port = peer_rx_port;
> +		manual = 1;
> +	}
> 
>  	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
> -		hairpin_conf.peers[0].port = pi;
> +		hairpin_conf.peers[0].port = peer_rx_port;
>  		hairpin_conf.peers[0].queue = i + nb_rxq;
> +		hairpin_conf.manual_bind = !!manual;
> +		hairpin_conf.tx_explicit = !!tx_exp;
>  		diag = rte_eth_tx_hairpin_queue_setup
>  			(pi, qi, nb_txd, &hairpin_conf);
>  		i++;
> @@ -2377,8 +2418,10 @@ setup_hairpin_queues(portid_t pi)
>  		return -1;
>  	}
>  	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
> -		hairpin_conf.peers[0].port = pi;
> +		hairpin_conf.peers[0].port = peer_tx_port;
>  		hairpin_conf.peers[0].queue = i + nb_txq;
> +		hairpin_conf.manual_bind = !!manual;
> +		hairpin_conf.tx_explicit = !!tx_exp;
>  		diag = rte_eth_rx_hairpin_queue_setup
>  			(pi, qi, nb_rxd, &hairpin_conf);
>  		i++;
> @@ -2405,6 +2448,8 @@ start_port(portid_t pid)
>  {
>  	int diag, need_check_link_status = -1;
>  	portid_t pi;
> +	portid_t p_pi = RTE_MAX_ETHPORTS;
> +	uint16_t cnt_pi = 0;
>  	queueid_t qi;
>  	struct rte_port *port;
>  	struct rte_ether_addr mac_addr;
> @@ -2544,8 +2589,10 @@ start_port(portid_t pid)
>  				return -1;
>  			}
>  			/* setup hairpin queues */
> -			if (setup_hairpin_queues(pi) != 0)
> +			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
>  				return -1;
> +			p_pi = pi;
> +			cnt_pi++;
>  		}
>  		configure_rxtx_dump_callbacks(verbose_level);
>  		if (clear_ptypes) {
> @@ -3775,6 +3822,19 @@ main(int argc, char** argv)
>  		rte_exit(EXIT_FAILURE, "Start ports failed\n");
> 
>  	/* set all ports to promiscuous mode by default */
> +	if (hairpin_mode & 0x3) {
> +		RTE_ETH_FOREACH_DEV(port_id) {
> +			ret = rte_eth_hairpin_bind(port_id,
> RTE_MAX_ETHPORTS);
> +			if (ret != 0) {
> +				RTE_LOG(ERR, EAL, "Error during binding "
> +					"hairpin tx port %u: %s",
> +					port_id, rte_strerror(-ret));
> +				return -1;
> +			}
> +		}
> +	}
> +
> +	/* set all ports to promiscuous mode by default */
>  	RTE_ETH_FOREACH_DEV(port_id) {
>  		ret = rte_eth_promiscuous_enable(port_id);
>  		if (ret != 0)
> diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
> index c7e7e41..29ede20 100644
> --- a/app/test-pmd/testpmd.h
> +++ b/app/test-pmd/testpmd.h
> @@ -398,6 +398,8 @@ extern uint32_t param_total_num_mbufs;
> 
>  extern uint16_t stats_period;
> 
> +extern uint16_t hairpin_mode;
> +
>  #ifdef RTE_LIBRTE_LATENCY_STATS
>  extern uint8_t latencystats_enabled;
>  extern lcoreid_t latencystats_lcore_id;
> --
> 2.5.5


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

* Re: [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
                       ` (3 preceding siblings ...)
  2020-10-01  0:26     ` [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-04  9:45     ` Ori Kam
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
                       ` (2 subsequent siblings)
  7 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-04  9:45 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing

General comment I think in all of this patch set you are missing the 
documentation part., for example the testpmd doc.

Thanks,
Ori

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 1, 2020 3:26 AM
> Subject: [PATCH 0/4] introduce support for hairpin between two ports
> 
> This patch set will add the support for hairpin between two ports.
> In the meanwhile, the compatibility of the previous single port mode
> is taken into consideration and kept.
> 
> The patches contain the following changes:
> 1. new APIs to bind and unbind hairpin ports in manual binding mode.
> 2. new internal APIs for PMD to pass the queue information and
>    configure the queue pair.
> 3. new attribute members in the hairpin queue configuraiton structure
>    to specify the binding mode and enable explicit TX flow mode.
> 4. Testpmd support to configure the hairpin modes for two ports
>    hairpin verification.
> 
> Bing Zhao (4):
>   ethdev: add hairpin bind and unbind APIs
>   ethdev: add new attributes to hairpin config
>   ethdev: add APIs for hairpin queue operation
>   app/testpmd: change hairpin queues setup
> 
>  app/test-pmd/parameters.c                |  15 +++
>  app/test-pmd/testpmd.c                   |  68 ++++++++++++-
>  app/test-pmd/testpmd.h                   |   2 +
>  lib/librte_ethdev/rte_ethdev.c           | 162
> +++++++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev.h           |  69 +++++++++++++
>  lib/librte_ethdev/rte_ethdev_driver.h    | 160
> ++++++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev_version.map |   5 +
>  7 files changed, 477 insertions(+), 4 deletions(-)
> 
> --
> 2.5.5


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

* Re: [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
  2020-10-04  9:20       ` Ori Kam
@ 2020-10-07 11:21         ` Bing Zhao
  2020-10-07 11:42           ` Ori Kam
  0 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-07 11:21 UTC (permalink / raw)
  To: Ori Kam, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Ori,

> -----Original Message-----
> From: Ori Kam <orika@nvidia.com>
> Sent: Sunday, October 4, 2020 5:20 PM
> To: Bing Zhao <bingz@nvidia.com>; NBU-Contact-Thomas Monjalon
> <thomas@monjalon.net>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com
> Cc: dev@dpdk.org
> Subject: RE: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> 
> Hi Bing,
> 
> PSB,
> 
> Thanks,
> Ori
> > -----Original Message-----
> > From: Bing Zhao <bingz@nvidia.com>
> > Sent: Thursday, October 1, 2020 3:26 AM
> > Cc: dev@dpdk.org
> > Subject: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> >
> > In single port hairpin mode, all the hairpin TX and RX queues
> belong
> > to the same device. After the queues are set up properly, there is
> no
> > other dependency between the TX queue and its RX peer queue. The
> > binding process that connected the TX and RX queues together from
> > hardware level will be done automatically during the device start
> > procedure. Everything required is configured and initialized
> already
> > for the binding process.
> >
> > But in two ports hairpin mode, there will be some cross-
> dependences
> > between two different ports. Usually, the ports will be
> initialized
> > serially by the main thread but not in parallel. The earlier port
> will
> > not be able to enable the bind if the following peer port is not
> yet
> > configured with HW resources. What's more, if one port is detached
> /
> > attached dynamically, it would introduce more trouble for the
> hairpin
> > binding.
> >
> > To overcome these, new APIs for binding and unbinding are added.
> > During startup, only the hairpin TX and RX peer queues will be set
> up.
> > Nothing will be done when starting the device if the queues are
> > without auto-bind attribute. Only after the required ports pair
> > started, the `rte_eth_hairpin_bind()` API can be called to bind
> the
> > all TX queues of the egress port to the RX queues of the peer port.
> > Then the connection between the egress and ingress ports pair will
> be
> > established.
> >
> > The `rte_eth_hairpin_unbind()` API could be used to disconnect the
> > egress and the peer ingress ports. This should only be called
> before
> > the device is closed if needed. When doing the clean up, all the
> > egress and ingress pairs related to a single port should be taken
> into
> > consideration.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
> >  lib/librte_ethdev/rte_ethdev.c           | 107
> > +++++++++++++++++++++++++++++++
> >  lib/librte_ethdev/rte_ethdev.h           |  51 +++++++++++++++
> >  lib/librte_ethdev/rte_ethdev_driver.h    |  52 +++++++++++++++
> >  lib/librte_ethdev/rte_ethdev_version.map |   2 +
> >  4 files changed, 212 insertions(+)
> >
> > diff --git a/lib/librte_ethdev/rte_ethdev.c
> > b/lib/librte_ethdev/rte_ethdev.c index dfe5c1b..72f567b 100644
> > --- a/lib/librte_ethdev/rte_ethdev.c
> > +++ b/lib/librte_ethdev/rte_ethdev.c
> > @@ -2175,6 +2175,113 @@ rte_eth_tx_hairpin_queue_setup(uint16_t
> > port_id, uint16_t tx_queue_id,
> >  	return eth_err(port_id, ret);
> >  }
> >
> > +int
> > +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port) {
> > +	struct rte_eth_dev *dev;
> > +	struct rte_eth_dev *rdev;
> > +	uint16_t p;
> > +	uint16_t rp;
> > +	int ret = 0;
> > +
> > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> > +	dev = &rte_eth_devices[tx_port];
> > +	if (!dev->data->dev_started) {
> > +		RTE_ETHDEV_LOG(ERR, "TX port %d is not started",
> tx_port);
> > +		return -EBUSY;
> > +	}
> > +
> > +	/*
> > +	 * If the all the ports probed belong to two or more separate
> NICs, it
> > +	 * is recommended that each pair is bound independently but
> not in the
> > +	 * loop to bind all ports.
> > +	 */
> 
> I don't understand your comment.

I mean, in the following loops, if the application wants to bind one TX port to all the other RX ports, it would not be supported between different PMDs or different NICs. Then the roll-back actions will be done and no hairpin port peers will work successfully.

> 
> > +	if (rx_port == RTE_MAX_ETHPORTS) {
> 
> I think maybe this should be done in the tx queue. Since if the bind
> don't need some port why do we care if it is started?

Do you mean the checking in the RX ports loop below? At first, I intended to make sure this API would be called only after all the ports are started.
But yes, there is no need if some port is not being used.

> So either add a new function to get all peer ports from the tx port,
> or move this logic to the Target PMD.

There would be one more API to be introduced. To my understanding, it would be more appropriate if we add a new API here.
But it would keep RTE simpler if we move such logic into the PMD.

> 
> > +		RTE_ETH_FOREACH_DEV(p) {
> > +			rdev = &rte_eth_devices[p];
> > +			if (!rdev->data->dev_started) {
> > +				RTE_ETHDEV_LOG(ERR,
> > +					       "RX port %d is not started", p);
> > +				ret = -EBUSY;
> > +				goto unbind;
> > +			}
> > +			ret = (*dev->dev_ops->hairpin_bind)(dev, p);
> > +			if (ret) {
> > +				RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin
> > TX "
> > +					       "%d to RX %d", tx_port, p);
> > +				goto unbind;
> > +			}
> > +		}
> > +	} else {
> > +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> > +		rdev = &rte_eth_devices[rx_port];
> > +		if (!rdev->data->dev_started) {
> > +			RTE_ETHDEV_LOG(ERR,
> > +				       "RX port %d is not started", rx_port);
> > +			return -EBUSY;
> > +		}
> > +		ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
> > +		if (ret)
> > +			RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d
> "
> > +				       "to RX %d", tx_port, rx_port);
> > +	}
> > +
> > +	return ret;
> > +
> > +unbind:
> > +	/* Roll back the previous binding process. */
> > +	RTE_ETH_FOREACH_DEV(rp) {
> > +		if (rp < p)
> > +			(*dev->dev_ops->hairpin_unbind)(dev, rp);
> > +		else
> > +			break;
> > +	}
> > +	return ret;
> > +}
> > +
> > +int
> > +rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port) {
> > +	struct rte_eth_dev *dev;
> > +	struct rte_eth_dev *rdev;
> > +	uint16_t p;
> > +	int ret = 0;
> > +
> > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> > +	dev = &rte_eth_devices[tx_port];
> > +	if (!dev->data->dev_started) {
> > +		RTE_ETHDEV_LOG(ERR, "TX port %d is stopped", tx_port);
> > +		return -EBUSY;
> > +	}
> > +
> > +	if (rx_port == RTE_MAX_ETHPORTS) {
> > +		RTE_ETH_FOREACH_DEV(p) {
> > +			rdev = &rte_eth_devices[p];
> > +			if (!rdev->data->dev_started) {
> 
> This condition should never be true.
> First see my comment above about the list of devices, second port
> should fail to stop if it is bounded.

Thank, I will remove this check and make the logic the same as "bind" API.
When stopping the second port, PMD will check if any peer port is bound and return a failure then.

> 
> > +				RTE_ETHDEV_LOG(ERR, "RX port %d is
> > stopped", p);
> > +				ret = -EBUSY;
> > +				break;
> > +			}
> > +			ret = (*dev->dev_ops->hairpin_unbind)(dev, p);
> > +			if (ret) {
> > +				RTE_ETHDEV_LOG(ERR, "Failed to unbind
> > hairpin "
> > +					       "TX %d from RX %d", tx_port, p);
> > +				break;
> > +			}
> > +		}
> > +	} else {
> > +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> > +		rdev = &rte_eth_devices[rx_port];
> > +		if (!rdev->data->dev_started) {
> > +			RTE_ETHDEV_LOG(ERR, "RX port %d is stopped",
> > rx_port);
> > +			return -EBUSY;
> > +		}
> > +		ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> >  void
> >  rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t
> unsent,
> >  		void *userdata __rte_unused)
> > diff --git a/lib/librte_ethdev/rte_ethdev.h
> > b/lib/librte_ethdev/rte_ethdev.h index 645a186..c3fb684 100644
> > --- a/lib/librte_ethdev/rte_ethdev.h
> > +++ b/lib/librte_ethdev/rte_ethdev.h
> > @@ -2133,6 +2133,57 @@ int rte_eth_tx_hairpin_queue_setup
> >  	 const struct rte_eth_hairpin_conf *conf);
> >
> >  /**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice
> > + *
> > + * Bind all hairpin TX queues of one port to the RX queues of the
> peer port.
> > + * It is only allowed to call this API after all hairpin queues
> are
> > +configured
> > + * properly and the devices of TX and peer RX are in started
> state.
> > + *
> > + * @param tx_port
> > + *   The TX port identifier of the Ethernet device.
> > + * @param rx_port
> > + *   The peer RX port identifier of the Ethernet device.
> > + *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
> > + *   RX port ID could have the same value with TX port ID.
> > + *
> > + * @return
> > + *   - (0) if successful.
> > + *   - (-EINVAL) if bad parameter.
> > + *   - (-EBUSY) if device is not in started state.
> > + *   - (-ENOTSUP) if hardware doesn't support.
> > + *   - Others detailed errors from PMD drivers.
> > + */
> > +__rte_experimental
> > +int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice
> > + *
> > + * Unbind all hairpin TX queues of one port from the RX queues of
> the
> > + peer
> > port.
> > + * This should be called before closing the TX or RX devices
> > +(optional). After
> > + * unbind the hairpin ports pair, it is allowed to bind them
> again.
> > + * Changing queues configuration should be after stopping the
> device.
> > + *
> > + * @param tx_port
> > + *   The TX port identifier of the Ethernet device.
> > + * @param rx_port
> > + *   The peer RX port identifier of the Ethernet device.
> > + *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
> > + *   RX port ID could have the same value with TX port ID.
> > + *
> > + * @return
> > + *   - (0) if successful.
> > + *   - (-EINVAL) if bad parameter.
> > + *   - (-EBUSY) if device is in stopped state.
> > + *   - (-ENOTSUP) if hardware doesn't support.
> > + *   - Others detailed errors from PMD drivers.
> > + */
> > +__rte_experimental
> > +int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
> > +
> > +/**
> >   * Return the NUMA socket to which an Ethernet device is
> connected
> >   *
> >   * @param port_id
> > diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> > b/lib/librte_ethdev/rte_ethdev_driver.h
> > index 04ac8e9..910433f 100644
> > --- a/lib/librte_ethdev/rte_ethdev_driver.h
> > +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> > @@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
> >  	 const struct rte_eth_hairpin_conf *hairpin_conf);
> >
> >  /**
> > + * @internal
> > + * Bind all hairpin TX queues of one port to the RX queues of the
> peer port.
> > + *
> > + * @param dev
> > + *   ethdev handle of port.
> > + * @param rx_port
> > + *   the peer RX port.
> > + *
> > + * @return
> > + *   Negative errno value on error, 0 on success.
> > + *
> > + * @retval 0
> > + *   Success, bind successfully.
> > + * @retval -ENOTSUP
> > + *   Bind API is not supported.
> > + * @retval -EINVAL
> > + *   One of the parameters is invalid.
> > + * @retval -EBUSY
> > + *   Device is not started.
> > + */
> > +typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
> > +				uint16_t rx_port);
> > +
> > +/**
> > + * @internal
> > + * Unbind all hairpin TX queues of one port from the RX queues of
> the
> > +peer
> > port.
> > + *
> > + * @param dev
> > + *   ethdev handle of port.
> > + * @param rx_port
> > + *   the peer RX port.
> > + *
> > + * @return
> > + *   Negative errno value on error, 0 on success.
> > + *
> > + * @retval 0
> > + *   Success, bind successfully.
> > + * @retval -ENOTSUP
> > + *   Bind API is not supported.
> > + * @retval -EINVAL
> > + *   One of the parameters is invalid.
> > + * @retval -EBUSY
> > + *   Device is already stopped.
> > + */
> > +typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
> > +				  uint16_t rx_port);
> > +
> > +/**
> >   * @internal A structure containing the functions exported by an
> > Ethernet driver.
> >   */
> >  struct eth_dev_ops {
> > @@ -713,6 +761,10 @@ struct eth_dev_ops {
> >  	/**< Set up device RX hairpin queue. */
> >  	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
> >  	/**< Set up device TX hairpin queue. */
> > +	eth_hairpin_bind_t hairpin_bind;
> > +	/**< Bind all hairpin TX queues of device to the peer port RX
> queues. */
> > +	eth_hairpin_unbind_t hairpin_unbind;
> > +	/**< Unbind all hairpin TX queues from the peer port RX queues.
> */
> >  };
> >
> >  /**
> > diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> > b/lib/librte_ethdev/rte_ethdev_version.map
> > index c95ef51..18efe4e 100644
> > --- a/lib/librte_ethdev/rte_ethdev_version.map
> > +++ b/lib/librte_ethdev/rte_ethdev_version.map
> > @@ -227,6 +227,8 @@ EXPERIMENTAL {
> >  	rte_tm_wred_profile_delete;
> >
> >  	# added in 20.11
> > +	rte_eth_hairpin_bind;
> > +	rte_eth_hairpin_unbind;
> >  	rte_eth_link_speed_to_str;
> >  	rte_eth_link_to_str;
> >  };
> > --
> > 2.5.5

Thanks


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

* Re: [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config
  2020-10-04  9:22       ` Ori Kam
@ 2020-10-07 11:32         ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-07 11:32 UTC (permalink / raw)
  To: Ori Kam, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Ori,

> -----Original Message-----
> From: Ori Kam <orika@nvidia.com>
> Sent: Sunday, October 4, 2020 5:22 PM
> To: Bing Zhao <bingz@nvidia.com>; NBU-Contact-Thomas Monjalon
> <thomas@monjalon.net>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com
> Cc: dev@dpdk.org
> Subject: RE: [PATCH 2/4] ethdev: add new attributes to hairpin
> config
> 
> Hi Bing,
> 
> PSB,
> Best,
> Ori
> 
> > -----Original Message-----
> > From: Bing Zhao <bingz@nvidia.com>
> > Sent: Thursday, October 1, 2020 3:26 AM
> > Subject: [PATCH 2/4] ethdev: add new attributes to hairpin config
> >
> > To support two ports hairpin mode and keep the backward
> compatibility
> > for the application, two new attribute members of hairpin queue
> config
> > structure are added.
> >
> > `tx_explicit` means if the application itself will insert the TX
> part
> > flow rules. If not set, PMD will insert the rules implicitly.
> > `manual_bind` means if the hairpin TX queue and peer RX queue will
> be
> > bound automatically during device start stage.
> >
> > Different TX and RX queue pairs could have different values, but
> it is
> > highly recommend that all paired queues between one egress and its
> > peer ingress ports have the same values, in order not to bring any
> > chaos to the system. The actual support of these attribute
> parameters
> > will be checked and decided by the PMD driver.
> >
> > In a single port hairpin, if both are zero without any setting,
> the
> > behavior will remain the same as before. It means no bind API
> needs to
> > be called and no TX flow rules need to be inserted manually by the
> > application.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
> >  lib/librte_ethdev/rte_ethdev.h | 18 ++++++++++++++++++
> >  1 file changed, 18 insertions(+)
> >
> > diff --git a/lib/librte_ethdev/rte_ethdev.h
> > b/lib/librte_ethdev/rte_ethdev.h index c3fb684..0cabff0 100644
> > --- a/lib/librte_ethdev/rte_ethdev.h
> > +++ b/lib/librte_ethdev/rte_ethdev.h
> > @@ -1027,6 +1027,21 @@ struct rte_eth_hairpin_cap {
> >
> >  #define RTE_ETH_MAX_HAIRPIN_PEERS 32
> >
> > +/*
> > + * Hairpin queue attribute parameters.
> > + * Each TX queue and peer RX queue should have the same value.
> > + * Default value 0 is for backward-compatibility, the same
> behaviors
> > +should
> > + * remain if the value is not set (0).
> > + */
> > +/**< Hairpin queues will be bound automatically */
> > +#define RTE_ETH_HAIRPIN_BIND_AUTO		(0)
> > +/**< Hairpin queues will be bound manually with bind API */
> > +#define RTE_ETH_HAIRPIN_BIND_MANUAL		(1)
> > +/**< Hairpin TX part flow rule will be inserted implicitly by PMD
> */
> > +#define RTE_ETH_HAIRPIN_TXRULE_IMPLICIT		(0)
> > +/**< Hairpin TX part flow rule will be inserted explicitly by APP
> */
> > +#define RTE_ETH_HAIRPIN_TXRULE_EXPLICIT		(1)
> > +
> 
> Why do you need those defines if you are using bit fields?

I will remove this and add the description of the modes in the document.

> 
> >  /**
> >   * @warning
> >   * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice @@ -1046,6 +1061,9 @@ struct rte_eth_hairpin_peer {
> >   */
> >  struct rte_eth_hairpin_conf {
> >  	uint16_t peer_count; /**< The number of peers. */
> > +	uint32_t reserved : 30; /**< Reserved bits. */
> > +	uint32_t tx_explicit : 1; /**< Explicit TX flow rule mode. */
> > +	uint32_t manual_bind : 1; /**< Manually bind hairpin queues.
> */
> 
> Why not place the new bits at the end?

By using uint16_t bit fields, there will be some warnings by the compiler and it is not standard.
I prefer to change the "uint16_t peer_count" into "uint32_t peer_count:16" to use the gap before the next structure.
Or yes, I can move it to the end of this current structure.

> Also why do you place the reserved first?

Thanks, I will move it to the end of the u32 like other structure definitions.

> 
> >  	struct rte_eth_hairpin_peer
> peers[RTE_ETH_MAX_HAIRPIN_PEERS];  };
> >
> > --
> > 2.5.5

Thanks


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

* Re: [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation
  2020-10-04  9:34       ` Ori Kam
@ 2020-10-07 11:34         ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-07 11:34 UTC (permalink / raw)
  To: Ori Kam, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Ori,

> -----Original Message-----
> From: Ori Kam <orika@nvidia.com>
> Sent: Sunday, October 4, 2020 5:35 PM
> To: Bing Zhao <bingz@nvidia.com>; NBU-Contact-Thomas Monjalon
> <thomas@monjalon.net>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com
> Cc: dev@dpdk.org
> Subject: RE: [PATCH 3/4] ethdev: add APIs for hairpin queue
> operation
> 
> Hi Bing,
> 
> PSB
> Best,
> Ori
> 
> > -----Original Message-----
> > From: Bing Zhao <bingz@nvidia.com>
> > Sent: Thursday, October 1, 2020 3:26 AM
> > Subject: [PATCH 3/4] ethdev: add APIs for hairpin queue operation
> >
> > Every hairpin queue pair should be configured properly and the
> > connection between TX and RX queues should be established, before
> > hairpin function works. In single port hairpin mode, the queues of
> > each pair belong to the same device. It is easy to get the
> hardware
> > and software information of each queue and configure the hairpin
> > connection with such information. In two ports hairpin mode, it is
> not
> > easy or inappropriate to access one queue's information from
> another
> > device.
> >
> > Since hairpin is configured per queue pair, three new APIs are
> > introduced and they are internal for the PMD using.
> >
> > The peer update API helps to pass one queue's information to the
> peer
> > queue and get the peer's information back for the next step.
> > The peer bind API configures the current queue with the peer's
> > information. For each hairpin queue pair, this API may need to be
> > called twice to configure the TX, RX queues separately.
> > The peer unbind API resets the current queue configuraion and
> state to
> > disconnect it from the peer queue. Also, it may need to be called
> > twice to disconnect TX, RX queues from each other.
> >
> > Some parameter of the above APIs might not be mandatory, and it
> > depends on the PMD implementation.
> >
> > The structure of `rte_hairpin_peer_info` is only a declaration and
> the
> > actual members will be defined in each PMD when being used.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
> >  lib/librte_ethdev/rte_ethdev.c           |  55 ++++++++++++++++
> >  lib/librte_ethdev/rte_ethdev_driver.h    | 108
> > +++++++++++++++++++++++++++++++
> >  lib/librte_ethdev/rte_ethdev_version.map |   3 +
> >  3 files changed, 166 insertions(+)
> >
> > diff --git a/lib/librte_ethdev/rte_ethdev.c
> > b/lib/librte_ethdev/rte_ethdev.c index 72f567b..4bfc26e 100644
> > --- a/lib/librte_ethdev/rte_ethdev.c
> > +++ b/lib/librte_ethdev/rte_ethdev.c
> > @@ -5515,6 +5515,61 @@ handle_port_link_status(const char *cmd
> > __rte_unused,
> >  	return 0;
> >  }
> >
> > +int
> > +rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t
> > peer_queue,
> > +				  struct rte_hairpin_peer_info *cur_info,
> > +				  struct rte_hairpin_peer_info *peer_info,
> > +				  bool direction)
> > +{
> > +	struct rte_eth_dev *dev;
> > +
> > +	/* Current queue information is not mandatory. */
> > +	if (peer_info == NULL)
> > +		return -EINVAL;
> > +
> > +	/* No need to check the validity again. */
> > +	dev = &rte_eth_devices[peer_port];
> > +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> > >hairpin_queue_peer_update,
> > +				-ENOTSUP);
> > +
> > +	return (*dev->dev_ops->hairpin_queue_peer_update)(dev,
> > peer_queue,
> > +					cur_info, peer_info, direction); }
> > +
> > +int
> > +rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t
> cur_queue,
> > +				struct rte_hairpin_peer_info *peer_info,
> > +				bool direction)
> > +{
> > +	struct rte_eth_dev *dev;
> > +
> > +	if (peer_info == NULL)
> > +		return -EINVAL;
> > +
> > +	/* No need to check the validity again. */
> > +	dev = &rte_eth_devices[cur_port];
> > +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> > >hairpin_queue_peer_bind,
> > +				-ENOTSUP);
> > +
> > +	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
> > +							peer_info, direction);
> > +}
> > +
> > +int
> > +rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t
> cur_queue,
> > +				  bool direction)
> > +{
> > +	struct rte_eth_dev *dev;
> > +
> > +	/* No need to check the validity again. */
> > +	dev = &rte_eth_devices[cur_port];
> > +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> > >hairpin_queue_peer_bind,
> > +				-ENOTSUP);
> > +
> > +	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev,
> cur_queue,
> > +							  direction);
> > +}
> > +
> >  RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
> >
> >  RTE_INIT(ethdev_init_telemetry)
> > diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> > b/lib/librte_ethdev/rte_ethdev_driver.h
> > index 910433f..d759c58 100644
> > --- a/lib/librte_ethdev/rte_ethdev_driver.h
> > +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> > @@ -21,6 +21,9 @@
> >  extern "C" {
> >  #endif
> >
> > +/**< @internal Declaration of the hairpin peer queue information
> structure.
> > */
> > +struct rte_hairpin_peer_info;
> > +
> >  /*
> >   * Definitions of all functions exported by an Ethernet driver
> through the
> >   * generic structure of type *eth_dev_ops* supplied in the
> > *rte_eth_dev* @@ -622,6 +625,21 @@ typedef int
> > (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,  typedef int
> > (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
> >  				  uint16_t rx_port);
> >
> > +typedef int (*eth_hairpin_queue_peer_update_t)
> > +	(struct rte_eth_dev *dev, uint16_t peer_queue,
> > +	 struct rte_hairpin_peer_info *current_info,
> > +	 struct rte_hairpin_peer_info *peer_info, bool direction);
> /**<
> > +@internal Update and fetch peer queue information. */
> > +
> > +typedef int (*eth_hairpin_queue_peer_bind_t)
> > +	(struct rte_eth_dev *dev, uint16_t cur_queue,
> > +	 struct rte_hairpin_peer_info *peer_info, bool direction);
> /**<
> > +@internal Bind peer queue to the current queue with fetched
> > information. */
> > +
> > +typedef int (*eth_hairpin_queue_peer_unbind_t)
> > +	(struct rte_eth_dev *dev, uint16_t cur_queue, bool direction);
> /**<
> > +@internal Unbind peer queue from the current queue. */
> > +
> >  /**
> >   * @internal A structure containing the functions exported by an
> > Ethernet driver.
> >   */
> > @@ -765,6 +783,9 @@ struct eth_dev_ops {
> >  	/**< Bind all hairpin TX queues of device to the peer port RX
> queues. */
> >  	eth_hairpin_unbind_t hairpin_unbind;
> >  	/**< Unbind all hairpin TX queues from the peer port RX queues.
> */
> > +	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
> > +	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
> > +	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
> >  };
> >
> >  /**
> > @@ -1120,6 +1141,93 @@ __rte_internal
> >  int
> >  rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t
> > ethdev_uninit);
> >
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice
> > + *
> > + * @internal
> > + * Pass the current hairpin queue HW and/or HW information to the
> > + peer
> > queue
> 
> I don't think you need experimental tag when using internal.

OK, I will remove it.

> 
> > + * and fetch back the information of the peer queue.
> > + *
> > + * @param peer_port
> > + *  Peer port identifier of the Ethernet device.
> > + * @param peer_queue
> > + *  Peer queue index of the port.
> > + * @param cur_info
> > + *  Pointer to the current information structure.
> > + * @param peer_info
> > + *  Pointer to the peer information, output.
> > + * @param direction
> > + *  Direction to pass the information.
> > + *  true - pass TX queue information and get peer RX queue
> > +information
> > + *  false - pass RX queue information and get peer TX queue
> > +information
> > + *
> > + * @return
> > + *  Negative errno value on error, 0 on success.
> > + */
> > +__rte_internal
> > +int
> > +rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t
> > peer_queue,
> > +				  struct rte_hairpin_peer_info *cur_info,
> > +				  struct rte_hairpin_peer_info *peer_info,
> > +				  bool direction);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice
> > + *
> > + * @internal
> > + * Configure current hairpin queue with the peer information
> fetched
> > + to
> > create
> 
> Same comment as above.

I will remove it.

> 
> > + * the connection (bind) with peer queue in the specified
> direction.
> > + * This function might need to be called twice to fully create
> the connection.
> > + *
> > + * @param cur_port
> > + *  Current port identifier of the Ethernet device.
> > + * @param cur_queue
> > + *  Current queue index of the port.
> > + * @param peer_info
> > + *  Pointer to the peer information, input.
> > + * @param direction
> > + *  Direction to create the connection.
> > + *  true - bind current TX queue to peer RX queue
> > + *  false - bind current RX queue to peer TX queue
> > + *
> > + * @return
> > + *  Negative errno value on error, 0 on success.
> > + */
> > +__rte_internal
> > +int
> > +rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t
> cur_queue,
> > +				struct rte_hairpin_peer_info *peer_info,
> > +				bool direction);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > notice
> > + *
> > + * @internal
> > + * Reset the current queue state and configuration to disconnect
> > +(unbind) it
> > + * from the peer queue.
> > + * This function might need to be called twice to disconnect each
> other.
> > + *
> > + * @param cur_port
> > + *  Current port identifier of the Ethernet device.
> > + * @param cur_queue
> > + *  Current queue index of the port.
> > + * @param direction
> > + *  Direction to create the connection.
> > + *  true - unbind current TX queue from peer RX queue
> > + *  false - unbind current RX queue from peer TX queue
> > + *
> > + * @return
> > + *  Negative errno value on error, 0 on success.
> > + */
> > +__rte_internal
> > +int
> > +rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t
> cur_queue,
> > +				  bool direction);
> > +
> > +
> >  #ifdef __cplusplus
> >  }
> >  #endif
> > diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> > b/lib/librte_ethdev/rte_ethdev_version.map
> > index 18efe4e..d05cd97 100644
> > --- a/lib/librte_ethdev/rte_ethdev_version.map
> > +++ b/lib/librte_ethdev/rte_ethdev_version.map
> > @@ -250,6 +250,9 @@ INTERNAL {
> >  	rte_eth_devargs_parse;
> >  	rte_eth_dma_zone_free;
> >  	rte_eth_dma_zone_reserve;
> > +	rte_eth_hairpin_queue_peer_bind;
> > +	rte_eth_hairpin_queue_peer_unbind;
> > +	rte_eth_hairpin_queue_peer_update;
> >  	rte_eth_switch_domain_alloc;
> >  	rte_eth_switch_domain_free;
> >  	rte_flow_expand_rss;
> > --
> > 2.5.5

Thanks


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

* Re: [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup
  2020-10-04  9:39       ` Ori Kam
@ 2020-10-07 11:36         ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-07 11:36 UTC (permalink / raw)
  To: Ori Kam, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Ori,
Thanks for your comments.
I will update the commit logs as well as the documents.

BR. Bing

> -----Original Message-----
> From: Ori Kam <orika@nvidia.com>
> Sent: Sunday, October 4, 2020 5:40 PM
> To: Bing Zhao <bingz@nvidia.com>; NBU-Contact-Thomas Monjalon
> <thomas@monjalon.net>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com
> Cc: dev@dpdk.org
> Subject: RE: [PATCH 4/4] app/testpmd: change hairpin queues setup
> 
> Hi Bing,
> 
> PSB,
> 
> Best,
> Ori
> 
> > -----Original Message-----
> > From: Bing Zhao <bingz@nvidia.com>
> > Sent: Thursday, October 1, 2020 3:26 AM
> > Subject: [PATCH 4/4] app/testpmd: change hairpin queues setup
> >
> > A new parameter `hairpin-mode` is introduced to the testpmd
> command
> > line. Bitmask value is used to provide more flexible configuration.
> >
> > Bit 0 in the LSB indicates the hairpin will use the loop mode. The
> > previous port RX queue will be connected to the current port TX
> queue.
> > Bit 1 in the LSB indicates the hairpin will use pair port mode.
> The
> > even index port will be paired with the next odd index port. If
> the
> > total number of probed port is odd, then the last one will be
> paired
> > to itself.
> > If this byte is zero, then each port will be paired to itself.
> > Bit 0 takes a higher priority in the checking.
> >
> > Bit 4 in the second bytes indicate if the hairpin will use
> explicit TX
> > flow mode.
> >
> I think some basic example will be great here.
> 
> > If not set, default value zero will be used and the behavior will
> try
> > to get align with the previous single port mode. If the ports
> belong
> > to different vendors' NICs, it is suggested to use the `self`
> hairpin
> > mode only.
> >
> > Since hairpin configures the hardware resources, the port mask of
> > packets forwarding engine will not be used here.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
> >  app/test-pmd/parameters.c | 15 +++++++++++
> >  app/test-pmd/testpmd.c    | 68
> > ++++++++++++++++++++++++++++++++++++++++++++---
> >  app/test-pmd/testpmd.h    |  2 ++
> >  3 files changed, 81 insertions(+), 4 deletions(-)
> >
> > diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
> > index 1ead595..991029d 100644
> > --- a/app/test-pmd/parameters.c
> > +++ b/app/test-pmd/parameters.c
> > @@ -221,6 +221,9 @@ usage(char* progname)
> >  	       "enabled\n");
> >  	printf("  --record-core-cycles: enable measurement of CPU
> cycles.\n");
> >  	printf("  --record-burst-stats: enable display of RX and TX
> > bursts.\n");
> > +	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port
> mode.\n "
> > +	       "    0x10 - explicit tx rule, 0x02 - hairpin ports
> paired\n"
> > +	       "    0x01 - hairpin ports loop, 0x00 - hairpin port
> self\n");
> >  }
> >
> >  #ifdef RTE_LIBRTE_CMDLINE
> > @@ -644,6 +647,7 @@ launch_args_parse(int argc, char** argv)
> >  		{ "rxd",			1, 0, 0 },
> >  		{ "txd",			1, 0, 0 },
> >  		{ "hairpinq",			1, 0, 0 },
> > +		{ "hairpin-mode",		1, 0, 0 },
> >  		{ "burst",			1, 0, 0 },
> >  		{ "mbcache",			1, 0, 0 },
> >  		{ "txpt",			1, 0, 0 },
> > @@ -1111,6 +1115,17 @@ launch_args_parse(int argc, char** argv)
> >  				rte_exit(EXIT_FAILURE, "Either rx or tx
> queues should "
> >  						"be non-zero\n");
> >  			}
> > +			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode"))
> {
> > +				char *end = NULL;
> > +				unsigned int n;
> > +
> > +				errno = 0;
> > +				n = strtoul(optarg, &end, 0);
> > +				if (errno != 0 || end == optarg)
> > +					rte_exit(EXIT_FAILURE, "hairpin mode
> > invalid\n");
> > +				else
> > +					hairpin_mode = (uint16_t)n;
> > +			}
> >  			if (!strcmp(lgopts[opt_idx].name, "burst")) {
> >  				n = atoi(optarg);
> >  				if (n == 0) {
> > diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index
> > fe6450c..c604045 100644
> > --- a/app/test-pmd/testpmd.c
> > +++ b/app/test-pmd/testpmd.c
> > @@ -367,6 +367,9 @@ bool setup_on_probe_event = true;
> >  /* Clear ptypes on port initialization. */  uint8_t clear_ptypes
> =
> > true;
> >
> > +/* Hairpin ports configuration mode. */ uint16_t hairpin_mode;
> > +
> >  /* Pretty printing of ethdev events */  static const char * const
> > eth_event_desc[] = {
> >  	[RTE_ETH_EVENT_UNKNOWN] = "unknown", @@ -2345,7 +2348,7 @@
> > port_is_started(portid_t port_id)
> >
> >  /* Configure the Rx and Tx hairpin queues for the selected port.
> */
> > static int -setup_hairpin_queues(portid_t pi)
> > +setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
> >  {
> >  	queueid_t qi;
> >  	struct rte_eth_hairpin_conf hairpin_conf = { @@ -2354,10
> +2357,48 @@
> > setup_hairpin_queues(portid_t pi)
> >  	int i;
> >  	int diag;
> >  	struct rte_port *port = &ports[pi];
> > +	uint16_t peer_rx_port = pi;
> > +	uint16_t peer_tx_port = pi;
> > +	uint32_t manual = 1;
> > +	uint32_t tx_exp = hairpin_mode & 0x10;
> > +
> > +	if (!(hairpin_mode & 0xf)) {
> > +		peer_rx_port = pi;
> > +		peer_tx_port = pi;
> > +		manual = 0;
> > +	} else if (hairpin_mode & 0x1) {
> > +		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
> > +
> > RTE_ETH_DEV_NO_OWNER);
> > +		if (peer_tx_port >= RTE_MAX_ETHPORTS)
> > +			peer_tx_port = rte_eth_find_next_owned_by(0,
> > +						RTE_ETH_DEV_NO_OWNER);
> > +		if (p_pi != RTE_MAX_ETHPORTS) {
> > +			peer_rx_port = p_pi;
> > +		} else {
> > +			uint16_t next_pi;
> > +
> > +			RTE_ETH_FOREACH_DEV(next_pi)
> > +				peer_rx_port = next_pi;
> > +		}
> > +		manual = 1;
> > +	} else if (hairpin_mode & 0x2) {
> > +		if (cnt_pi & 0x1) {
> > +			peer_rx_port = p_pi;
> > +		} else {
> > +			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
> > +						RTE_ETH_DEV_NO_OWNER);
> > +			if (peer_rx_port >= RTE_MAX_ETHPORTS)
> > +				peer_rx_port = pi;
> > +		}
> > +		peer_tx_port = peer_rx_port;
> > +		manual = 1;
> > +	}
> >
> >  	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
> > -		hairpin_conf.peers[0].port = pi;
> > +		hairpin_conf.peers[0].port = peer_rx_port;
> >  		hairpin_conf.peers[0].queue = i + nb_rxq;
> > +		hairpin_conf.manual_bind = !!manual;
> > +		hairpin_conf.tx_explicit = !!tx_exp;
> >  		diag = rte_eth_tx_hairpin_queue_setup
> >  			(pi, qi, nb_txd, &hairpin_conf);
> >  		i++;
> > @@ -2377,8 +2418,10 @@ setup_hairpin_queues(portid_t pi)
> >  		return -1;
> >  	}
> >  	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
> > -		hairpin_conf.peers[0].port = pi;
> > +		hairpin_conf.peers[0].port = peer_tx_port;
> >  		hairpin_conf.peers[0].queue = i + nb_txq;
> > +		hairpin_conf.manual_bind = !!manual;
> > +		hairpin_conf.tx_explicit = !!tx_exp;
> >  		diag = rte_eth_rx_hairpin_queue_setup
> >  			(pi, qi, nb_rxd, &hairpin_conf);
> >  		i++;
> > @@ -2405,6 +2448,8 @@ start_port(portid_t pid)  {
> >  	int diag, need_check_link_status = -1;
> >  	portid_t pi;
> > +	portid_t p_pi = RTE_MAX_ETHPORTS;
> > +	uint16_t cnt_pi = 0;
> >  	queueid_t qi;
> >  	struct rte_port *port;
> >  	struct rte_ether_addr mac_addr;
> > @@ -2544,8 +2589,10 @@ start_port(portid_t pid)
> >  				return -1;
> >  			}
> >  			/* setup hairpin queues */
> > -			if (setup_hairpin_queues(pi) != 0)
> > +			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
> >  				return -1;
> > +			p_pi = pi;
> > +			cnt_pi++;
> >  		}
> >  		configure_rxtx_dump_callbacks(verbose_level);
> >  		if (clear_ptypes) {
> > @@ -3775,6 +3822,19 @@ main(int argc, char** argv)
> >  		rte_exit(EXIT_FAILURE, "Start ports failed\n");
> >
> >  	/* set all ports to promiscuous mode by default */
> > +	if (hairpin_mode & 0x3) {
> > +		RTE_ETH_FOREACH_DEV(port_id) {
> > +			ret = rte_eth_hairpin_bind(port_id,
> > RTE_MAX_ETHPORTS);
> > +			if (ret != 0) {
> > +				RTE_LOG(ERR, EAL, "Error during binding "
> > +					"hairpin tx port %u: %s",
> > +					port_id, rte_strerror(-ret));
> > +				return -1;
> > +			}
> > +		}
> > +	}
> > +
> > +	/* set all ports to promiscuous mode by default */
> >  	RTE_ETH_FOREACH_DEV(port_id) {
> >  		ret = rte_eth_promiscuous_enable(port_id);
> >  		if (ret != 0)
> > diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index
> > c7e7e41..29ede20 100644
> > --- a/app/test-pmd/testpmd.h
> > +++ b/app/test-pmd/testpmd.h
> > @@ -398,6 +398,8 @@ extern uint32_t param_total_num_mbufs;
> >
> >  extern uint16_t stats_period;
> >
> > +extern uint16_t hairpin_mode;
> > +
> >  #ifdef RTE_LIBRTE_LATENCY_STATS
> >  extern uint8_t latencystats_enabled;
> >  extern lcoreid_t latencystats_lcore_id;
> > --
> > 2.5.5


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

* Re: [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
  2020-10-07 11:21         ` Bing Zhao
@ 2020-10-07 11:42           ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-07 11:42 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Wednesday, October 7, 2020 2:22 PM
> Subject: RE: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> 
> Hi Ori,
> 
> > -----Original Message-----
> > From: Ori Kam <orika@nvidia.com>
> > Sent: Sunday, October 4, 2020 5:20 PM
> > Subject: RE: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> >
> > Hi Bing,
> >
> > PSB,
> >
> > Thanks,
> > Ori
> > > -----Original Message-----
> > > From: Bing Zhao <bingz@nvidia.com>
> > > Sent: Thursday, October 1, 2020 3:26 AM
> > > Cc: dev@dpdk.org
> > > Subject: [PATCH 1/4] ethdev: add hairpin bind and unbind APIs
> > >
> > > In single port hairpin mode, all the hairpin TX and RX queues
> > belong
> > > to the same device. After the queues are set up properly, there is
> > no
> > > other dependency between the TX queue and its RX peer queue. The
> > > binding process that connected the TX and RX queues together from
> > > hardware level will be done automatically during the device start
> > > procedure. Everything required is configured and initialized
> > already
> > > for the binding process.
> > >
> > > But in two ports hairpin mode, there will be some cross-
> > dependences
> > > between two different ports. Usually, the ports will be
> > initialized
> > > serially by the main thread but not in parallel. The earlier port
> > will
> > > not be able to enable the bind if the following peer port is not
> > yet
> > > configured with HW resources. What's more, if one port is detached
> > /
> > > attached dynamically, it would introduce more trouble for the
> > hairpin
> > > binding.
> > >
> > > To overcome these, new APIs for binding and unbinding are added.
> > > During startup, only the hairpin TX and RX peer queues will be set
> > up.
> > > Nothing will be done when starting the device if the queues are
> > > without auto-bind attribute. Only after the required ports pair
> > > started, the `rte_eth_hairpin_bind()` API can be called to bind
> > the
> > > all TX queues of the egress port to the RX queues of the peer port.
> > > Then the connection between the egress and ingress ports pair will
> > be
> > > established.
> > >
> > > The `rte_eth_hairpin_unbind()` API could be used to disconnect the
> > > egress and the peer ingress ports. This should only be called
> > before
> > > the device is closed if needed. When doing the clean up, all the
> > > egress and ingress pairs related to a single port should be taken
> > into
> > > consideration.
> > >
> > > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > > ---
> > >  lib/librte_ethdev/rte_ethdev.c           | 107
> > > +++++++++++++++++++++++++++++++
> > >  lib/librte_ethdev/rte_ethdev.h           |  51 +++++++++++++++
> > >  lib/librte_ethdev/rte_ethdev_driver.h    |  52 +++++++++++++++
> > >  lib/librte_ethdev/rte_ethdev_version.map |   2 +
> > >  4 files changed, 212 insertions(+)
> > >
> > > diff --git a/lib/librte_ethdev/rte_ethdev.c
> > > b/lib/librte_ethdev/rte_ethdev.c index dfe5c1b..72f567b 100644
> > > --- a/lib/librte_ethdev/rte_ethdev.c
> > > +++ b/lib/librte_ethdev/rte_ethdev.c
> > > @@ -2175,6 +2175,113 @@ rte_eth_tx_hairpin_queue_setup(uint16_t
> > > port_id, uint16_t tx_queue_id,
> > >  	return eth_err(port_id, ret);
> > >  }
> > >
> > > +int
> > > +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port) {
> > > +	struct rte_eth_dev *dev;
> > > +	struct rte_eth_dev *rdev;
> > > +	uint16_t p;
> > > +	uint16_t rp;
> > > +	int ret = 0;
> > > +
> > > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> > > +	dev = &rte_eth_devices[tx_port];
> > > +	if (!dev->data->dev_started) {
> > > +		RTE_ETHDEV_LOG(ERR, "TX port %d is not started",
> > tx_port);
> > > +		return -EBUSY;
> > > +	}
> > > +
> > > +	/*
> > > +	 * If the all the ports probed belong to two or more separate
> > NICs, it
> > > +	 * is recommended that each pair is bound independently but
> > not in the
> > > +	 * loop to bind all ports.
> > > +	 */
> >
> > I don't understand your comment.
> 
> I mean, in the following loops, if the application wants to bind one TX port to all
> the other RX ports, it would not be supported between different PMDs or
> different NICs. Then the roll-back actions will be done and no hairpin port peers
> will work successfully.
> 

O.K.

> >
> > > +	if (rx_port == RTE_MAX_ETHPORTS) {
> >
> > I think maybe this should be done in the tx queue. Since if the bind
> > don't need some port why do we care if it is started?
> 
> Do you mean the checking in the RX ports loop below? At first, I intended to
> make sure this API would be called only after all the ports are started.
> But yes, there is no need if some port is not being used.
> 
I mean move the check to the tx pmd, if for some reason (peer device not started)
then it should fail.

> > So either add a new function to get all peer ports from the tx port,
> > or move this logic to the Target PMD.
> 
> There would be one more API to be introduced. To my understanding, it would
> be more appropriate if we add a new API here.
> But it would keep RTE simpler if we move such logic into the PMD.
> 
I'm fine with moving the logic to the PMD as long as it can check the status.
which he can using the internal function right?

> >
> > > +		RTE_ETH_FOREACH_DEV(p) {
> > > +			rdev = &rte_eth_devices[p];
> > > +			if (!rdev->data->dev_started) {
> > > +				RTE_ETHDEV_LOG(ERR,
> > > +					       "RX port %d is not started", p);
> > > +				ret = -EBUSY;
> > > +				goto unbind;
> > > +			}
> > > +			ret = (*dev->dev_ops->hairpin_bind)(dev, p);
> > > +			if (ret) {
> > > +				RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin
> > > TX "
> > > +					       "%d to RX %d", tx_port, p);
> > > +				goto unbind;
> > > +			}
> > > +		}
> > > +	} else {
> > > +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> > > +		rdev = &rte_eth_devices[rx_port];
> > > +		if (!rdev->data->dev_started) {
> > > +			RTE_ETHDEV_LOG(ERR,
> > > +				       "RX port %d is not started", rx_port);
> > > +			return -EBUSY;
> > > +		}
> > > +		ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
> > > +		if (ret)
> > > +			RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d
> > "
> > > +				       "to RX %d", tx_port, rx_port);
> > > +	}
> > > +
> > > +	return ret;
> > > +
> > > +unbind:
> > > +	/* Roll back the previous binding process. */
> > > +	RTE_ETH_FOREACH_DEV(rp) {
> > > +		if (rp < p)
> > > +			(*dev->dev_ops->hairpin_unbind)(dev, rp);
> > > +		else
> > > +			break;
> > > +	}
> > > +	return ret;
> > > +}
> > > +
> > > +int
> > > +rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port) {
> > > +	struct rte_eth_dev *dev;
> > > +	struct rte_eth_dev *rdev;
> > > +	uint16_t p;
> > > +	int ret = 0;
> > > +
> > > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> > > +	dev = &rte_eth_devices[tx_port];
> > > +	if (!dev->data->dev_started) {
> > > +		RTE_ETHDEV_LOG(ERR, "TX port %d is stopped", tx_port);
> > > +		return -EBUSY;
> > > +	}
> > > +
> > > +	if (rx_port == RTE_MAX_ETHPORTS) {
> > > +		RTE_ETH_FOREACH_DEV(p) {
> > > +			rdev = &rte_eth_devices[p];
> > > +			if (!rdev->data->dev_started) {
> >
> > This condition should never be true.
> > First see my comment above about the list of devices, second port
> > should fail to stop if it is bounded.
> 
> Thank, I will remove this check and make the logic the same as "bind" API.
> When stopping the second port, PMD will check if any peer port is bound and
> return a failure then.
> 

Great we should also consider what happens in case of hot plug,
In this case the hot plug port should be able to unbind himself from
all ports.

> >
> > > +				RTE_ETHDEV_LOG(ERR, "RX port %d is
> > > stopped", p);
> > > +				ret = -EBUSY;
> > > +				break;
> > > +			}
> > > +			ret = (*dev->dev_ops->hairpin_unbind)(dev, p);
> > > +			if (ret) {
> > > +				RTE_ETHDEV_LOG(ERR, "Failed to unbind
> > > hairpin "
> > > +					       "TX %d from RX %d", tx_port, p);
> > > +				break;
> > > +			}
> > > +		}
> > > +	} else {
> > > +		RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port, -EINVAL);
> > > +		rdev = &rte_eth_devices[rx_port];
> > > +		if (!rdev->data->dev_started) {
> > > +			RTE_ETHDEV_LOG(ERR, "RX port %d is stopped",
> > > rx_port);
> > > +			return -EBUSY;
> > > +		}
> > > +		ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > > +
> > >  void
> > >  rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t
> > unsent,
> > >  		void *userdata __rte_unused)
> > > diff --git a/lib/librte_ethdev/rte_ethdev.h
> > > b/lib/librte_ethdev/rte_ethdev.h index 645a186..c3fb684 100644
> > > --- a/lib/librte_ethdev/rte_ethdev.h
> > > +++ b/lib/librte_ethdev/rte_ethdev.h
> > > @@ -2133,6 +2133,57 @@ int rte_eth_tx_hairpin_queue_setup
> > >  	 const struct rte_eth_hairpin_conf *conf);
> > >
> > >  /**
> > > + * @warning
> > > + * @b EXPERIMENTAL: this API may change, or be removed, without
> > prior
> > > notice
> > > + *
> > > + * Bind all hairpin TX queues of one port to the RX queues of the
> > peer port.
> > > + * It is only allowed to call this API after all hairpin queues
> > are
> > > +configured
> > > + * properly and the devices of TX and peer RX are in started
> > state.
> > > + *
> > > + * @param tx_port
> > > + *   The TX port identifier of the Ethernet device.
> > > + * @param rx_port
> > > + *   The peer RX port identifier of the Ethernet device.
> > > + *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
> > > + *   RX port ID could have the same value with TX port ID.
> > > + *
> > > + * @return
> > > + *   - (0) if successful.
> > > + *   - (-EINVAL) if bad parameter.
> > > + *   - (-EBUSY) if device is not in started state.
> > > + *   - (-ENOTSUP) if hardware doesn't support.
> > > + *   - Others detailed errors from PMD drivers.
> > > + */
> > > +__rte_experimental
> > > +int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
> > > +
> > > +/**
> > > + * @warning
> > > + * @b EXPERIMENTAL: this API may change, or be removed, without
> > prior
> > > notice
> > > + *
> > > + * Unbind all hairpin TX queues of one port from the RX queues of
> > the
> > > + peer
> > > port.
> > > + * This should be called before closing the TX or RX devices
> > > +(optional). After
> > > + * unbind the hairpin ports pair, it is allowed to bind them
> > again.
> > > + * Changing queues configuration should be after stopping the
> > device.
> > > + *
> > > + * @param tx_port
> > > + *   The TX port identifier of the Ethernet device.
> > > + * @param rx_port
> > > + *   The peer RX port identifier of the Ethernet device.
> > > + *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
> > > + *   RX port ID could have the same value with TX port ID.
> > > + *
> > > + * @return
> > > + *   - (0) if successful.
> > > + *   - (-EINVAL) if bad parameter.
> > > + *   - (-EBUSY) if device is in stopped state.
> > > + *   - (-ENOTSUP) if hardware doesn't support.
> > > + *   - Others detailed errors from PMD drivers.
> > > + */
> > > +__rte_experimental
> > > +int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
> > > +
> > > +/**
> > >   * Return the NUMA socket to which an Ethernet device is
> > connected
> > >   *
> > >   * @param port_id
> > > diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> > > b/lib/librte_ethdev/rte_ethdev_driver.h
> > > index 04ac8e9..910433f 100644
> > > --- a/lib/librte_ethdev/rte_ethdev_driver.h
> > > +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> > > @@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
> > >  	 const struct rte_eth_hairpin_conf *hairpin_conf);
> > >
> > >  /**
> > > + * @internal
> > > + * Bind all hairpin TX queues of one port to the RX queues of the
> > peer port.
> > > + *
> > > + * @param dev
> > > + *   ethdev handle of port.
> > > + * @param rx_port
> > > + *   the peer RX port.
> > > + *
> > > + * @return
> > > + *   Negative errno value on error, 0 on success.
> > > + *
> > > + * @retval 0
> > > + *   Success, bind successfully.
> > > + * @retval -ENOTSUP
> > > + *   Bind API is not supported.
> > > + * @retval -EINVAL
> > > + *   One of the parameters is invalid.
> > > + * @retval -EBUSY
> > > + *   Device is not started.
> > > + */
> > > +typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
> > > +				uint16_t rx_port);
> > > +
> > > +/**
> > > + * @internal
> > > + * Unbind all hairpin TX queues of one port from the RX queues of
> > the
> > > +peer
> > > port.
> > > + *
> > > + * @param dev
> > > + *   ethdev handle of port.
> > > + * @param rx_port
> > > + *   the peer RX port.
> > > + *
> > > + * @return
> > > + *   Negative errno value on error, 0 on success.
> > > + *
> > > + * @retval 0
> > > + *   Success, bind successfully.
> > > + * @retval -ENOTSUP
> > > + *   Bind API is not supported.
> > > + * @retval -EINVAL
> > > + *   One of the parameters is invalid.
> > > + * @retval -EBUSY
> > > + *   Device is already stopped.
> > > + */
> > > +typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
> > > +				  uint16_t rx_port);
> > > +
> > > +/**
> > >   * @internal A structure containing the functions exported by an
> > > Ethernet driver.
> > >   */
> > >  struct eth_dev_ops {
> > > @@ -713,6 +761,10 @@ struct eth_dev_ops {
> > >  	/**< Set up device RX hairpin queue. */
> > >  	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
> > >  	/**< Set up device TX hairpin queue. */
> > > +	eth_hairpin_bind_t hairpin_bind;
> > > +	/**< Bind all hairpin TX queues of device to the peer port RX
> > queues. */
> > > +	eth_hairpin_unbind_t hairpin_unbind;
> > > +	/**< Unbind all hairpin TX queues from the peer port RX queues.
> > */
> > >  };
> > >
> > >  /**
> > > diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> > > b/lib/librte_ethdev/rte_ethdev_version.map
> > > index c95ef51..18efe4e 100644
> > > --- a/lib/librte_ethdev/rte_ethdev_version.map
> > > +++ b/lib/librte_ethdev/rte_ethdev_version.map
> > > @@ -227,6 +227,8 @@ EXPERIMENTAL {
> > >  	rte_tm_wred_profile_delete;
> > >
> > >  	# added in 20.11
> > > +	rte_eth_hairpin_bind;
> > > +	rte_eth_hairpin_unbind;
> > >  	rte_eth_link_speed_to_str;
> > >  	rte_eth_link_to_str;
> > >  };
> > > --
> > > 2.5.5
> 
> Thanks


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

* [dpdk-dev] [PATCH v2 0/6] introduce support for hairpin between two ports
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
                       ` (4 preceding siblings ...)
  2020-10-04  9:45     ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Ori Kam
@ 2020-10-08  8:51     ` Bing Zhao
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
                         ` (6 more replies)
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
  7 siblings, 7 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new API to get the hairpin peer ports list.
3. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
4. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
5. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.
6. documents update.

---
v2:
1. add documents update
2. remove all peer ports logic from rte API
3. conf structure optimizing
4. new API to get the peer ports and testpmd change to support
   hot-plug / unplug case
---

Bing Zhao (6):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add API to get hairpin peer ports list
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup
  doc: update for two ports hairpin mode

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   | 125 ++++++++++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 doc/guides/prog_guide/rte_flow.rst       |   3 +
 doc/guides/rel_notes/release_20_11.rst   |   8 ++
 doc/guides/testpmd_app_ug/run_app.rst    |   8 ++
 lib/librte_ethdev/rte_ethdev.c           | 133 +++++++++++++++++++++-
 lib/librte_ethdev/rte_ethdev.h           |  83 +++++++++++++-
 lib/librte_ethdev/rte_ethdev_driver.h    | 184 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   6 +
 10 files changed, 558 insertions(+), 9 deletions(-)

-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:07         ` Ori Kam
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config Bing Zhao
                         ` (5 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration, especially in the hot unplug case.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v2: remove the all peer ports logic from rte API
---
 lib/librte_ethdev/rte_ethdev.c           | 46 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 51 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  2 ++
 4 files changed, 151 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 0f56541..85a19bd 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2162,6 +2162,52 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+			       "to RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is already stopped", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+			       "TX %d from RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index d2bf74f..6206643 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2128,6 +2128,57 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * It is only allowed to call this API after all hairpin queues are configured
+ * properly and the devices of TX and peer RX are in started state.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices (optional). After
+ * unbind the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index c3062c2..f8eb879 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 	 const struct rte_eth_hairpin_conf *hairpin_conf);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -713,6 +761,10 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index c95ef51..18efe4e 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 };
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:23         ` Ori Kam
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of hairpin queue
configuration structure are added.

`tx_explicit` means if the application itself will insert the TX part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommended that all paired queues between one egress and
its peer ingress ports have the same values, in order not to bring
any chaos to the system. The actual support of these attribute
parameters will be checked and decided by the PMD drivers.

In a single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means no bind API needs
to be called and no TX flow rules need to be inserted manually by
the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v2: optimize the structure and remove unused macros
---
 lib/librte_ethdev/rte_ethdev.c | 8 ++++----
 lib/librte_ethdev/rte_ethdev.h | 5 ++++-
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 85a19bd..a4adeff 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -1954,13 +1954,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Rx queue(=%u), 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",
+			"Invalid value for number of peers for Rx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
@@ -2125,13 +2125,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
 			conf->peer_count, cap.max_tx_2_rx);
 		return -EINVAL;
 	}
 	if (conf->peer_count == 0) {
 		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
+			"Invalid value for number of peers for Tx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 6206643..94a981c 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1045,7 +1045,10 @@ struct rte_eth_hairpin_peer {
  * A structure used to configure hairpin binding.
  */
 struct rte_eth_hairpin_conf {
-	uint16_t peer_count; /**< The number of peers. */
+	uint32_t peer_count:16; /**< The number of peers. */
+	uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
+	uint32_t manual_bind:1; /**< Manually bind hairpin queues. */
+	uint32_t reserved:14; /**< Reserved bits. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:40         ` Ori Kam
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
                         ` (3 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

After hairpin queues are configured, in general, the application will
maintain the ports topology and even the queues configuration for
hairpin. But sometimes it will not.

If there is no hot-plug, it is easy to bind and unbind hairpin among
all the ports. The application can just connect or disconnect the
hairpin egress ports to / from all the probed ingress ports. Then
all the connections could be handled properly.

But with hot-plug / hot-unplug, one port could be probed and removed
dynamically. With two ports hairpin, all the connections from and to
this port should be handled after start(bind) or before stop(unbind).
It is necessary to know the hairpin topology with this port.

This API will return the ports list with the actual peer ports number
after configuration. Either peer RX or TX ports will be gotten with
this function call.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.c           | 24 ++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 27 +++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 30 ++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  1 +
 4 files changed, 82 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index a4adeff..ea7892a 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2208,6 +2208,30 @@ struct rte_eth_dev *
 	return ret;
 }
 
+int
+rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+			       bool direction)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	if (!peer_ports)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+				-ENOTSUP);
+
+	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+						      direction);
+	if (ret < 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports",
+			       cur_port, direction ? "RX" : "TX");
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 94a981c..6680de2 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2134,6 +2134,33 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
  *
+ * Get all the hairpin peer RX / TX ports of the current port.
+ * The caller should ensure that the array is large enough to save the ports
+ * list.
+ *
+ * @param cur_port
+ *   The current port identifier of the Ethernet device.
+ * @param peer_ports
+ *   Pointer to the array to save the peer ports list *
+ * @param direction
+ *   Current port to peer port direction
+ *   true - current used as TX to get all peer RX ports.
+ *   false - current used as RX to get all peer TX ports.
+ *
+ * @return
+ *   - (0 or positive) actual peer ports number.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+				   bool direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  * It is only allowed to call this API after all hairpin queues are configured
  * properly and the devices of TX and peer RX are in started state.
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index f8eb879..ecca9d6 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -576,6 +576,34 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 
 /**
  * @internal
+ * Get all hairpin TX/RX peer ports of the current device, if any.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param peer_ports
+ *   array to save the ports list.
+ * @param direction
+ *   value to decide the current to peer direction
+ *   true - used as TX to get all peer RX ports.
+ *   false - used as RX to get all peer TX ports.
+ *
+ * @return
+ *   Negative errno value on error, 0 or positive on success.
+ *
+ * @retval 0
+ *   Success, no peer ports.
+ * @retval >0
+ *   Actual number of the peer ports.
+ * @retval -ENOTSUP
+ *   Get peer ports API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ */
+typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
+					uint16_t *peer_ports, bool direction);
+
+/**
+ * @internal
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  *
  * @param dev
@@ -761,6 +789,8 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	hairpin_get_peer_ports_t hairpin_get_peer_ports;
+	/**< Get hairpin peer ports list. */
 	eth_hairpin_bind_t hairpin_bind;
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 18efe4e..1019e28 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -228,6 +228,7 @@ EXPERIMENTAL {
 
 	# added in 20.11
 	rte_eth_hairpin_bind;
+	rte_eth_hairpin_get_peer_ports;
 	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
                         ` (2 preceding siblings ...)
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:44         ` Ori Kam
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup Bing Zhao
                         ` (2 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between TX and RX queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the TX, RX queues separately.
The peer unbind API resets the current queue configuration and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect TX, RX queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 lib/librte_ethdev/rte_ethdev.c           |  55 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 102 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 160 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index ea7892a..4a77497 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5459,6 +5459,61 @@ enum rte_eth_switch_domain_state {
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  bool direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_unbind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index ecca9d6..713b82d 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -650,6 +653,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, bool direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, bool direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, bool direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -795,6 +813,12 @@ struct eth_dev_ops {
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin TX queues from the peer port RX queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	/**< Pass the current queue info and get the peer queue info. */
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	/**< Set up the connection between the pair of hairpin queues. */
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
+	/**< Disconnect the hairpin queues of a pair from each other. */
 };
 
 /**
@@ -1150,6 +1174,84 @@ typedef int (*ethdev_bus_specific_init)(struct rte_eth_dev *ethdev,
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @internal
+ * Pass the current hairpin queue HW and/or SW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  true - pass TX queue information and get peer RX queue information
+ *  false - pass RX queue information and get peer TX queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  bool direction);
+
+/**
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connection.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  true - bind current TX queue to peer RX queue
+ *  false - bind current RX queue to peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				bool direction);
+
+/**
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to create the connection.
+ *  true - unbind current TX queue from peer RX queue
+ *  false - unbind current RX queue from peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  bool direction);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 1019e28..0742733 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -251,6 +251,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 	rte_flow_expand_rss;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
                         ` (3 preceding siblings ...)
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:45         ` Ori Kam
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode Bing Zhao
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide more flexible configuration.
This parameter should be used when `hairpinq` is specified in the
command line.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port RX queue will be connected to the current port TX
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of probed port is odd, then the last one will be paired
to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
TX flow mode.

e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"

If not set, default value zero will be used and the behavior will
try to get align with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v2: move the hairpin bind/unbind into start/stop to support hot-plug
    and hot-unplug
---
 app/test-pmd/parameters.c |  15 ++++++
 app/test-pmd/testpmd.c    | 125 ++++++++++++++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h    |   2 +
 3 files changed, 138 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 1ead595..991029d 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -221,6 +221,9 @@
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -644,6 +647,7 @@
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1111,6 +1115,17 @@
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index ccba71c..344de83 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ struct fwd_engine * fwd_engines[] = {
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ struct extmem_param {
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,49 @@ struct extmem_param {
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			/* Last port will be the peer RX port of the first. */
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2419,10 @@ struct extmem_param {
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2449,12 @@ struct extmem_param {
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	portid_t pl[RTE_MAX_ETHPORTS];
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	uint16_t cnt_pi = 0;
+	uint16_t cfg_pi = 0;
+	int peer_pi;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,7 +2594,7 @@ struct extmem_param {
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
@@ -2557,6 +2607,9 @@ struct extmem_param {
 				pi);
 		}
 
+		p_pi = pi;
+		cnt_pi++;
+
 		/* start port */
 		if (rte_eth_dev_start(pi) < 0) {
 			printf("Fail to start port %d\n", pi);
@@ -2581,6 +2634,8 @@ struct extmem_param {
 
 		/* at least one port started, need checking link status */
 		need_check_link_status = 1;
+
+		pl[cfg_pi++] = pi;
 	}
 
 	if (need_check_link_status == 1 && !no_link_check)
@@ -2588,6 +2643,50 @@ struct extmem_param {
 	else if (need_check_link_status == 0)
 		printf("Please stop the ports first\n");
 
+	if (hairpin_mode & 0xf) {
+		uint16_t i;
+		int j;
+
+		/* bind all started hairpin ports */
+		for (i = 0; i < cfg_pi; i++) {
+			pi = pl[i];
+			/* bind current TX to all peer RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi,
+								 peer_pl, 1);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       pi, peer_pl[j],
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+			/* bind all peer TX to current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi,
+								 peer_pl, 0);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       peer_pl[j], pi,
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+		}
+	}
+
 	printf("Done\n");
 	return 0;
 }
@@ -2598,6 +2697,8 @@ struct extmem_param {
 	portid_t pi;
 	struct rte_port *port;
 	int need_check_link_status = 0;
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	int peer_pi;
 
 	if (dcb_test) {
 		dcb_test = 0;
@@ -2628,6 +2729,22 @@ struct extmem_param {
 						RTE_PORT_HANDLING) == 0)
 			continue;
 
+		if (hairpin_mode & 0xf) {
+			int j;
+
+			rte_eth_hairpin_unbind(pi, RTE_MAX_ETHPORTS);
+			/* unbind all peer TX from current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi,
+								 peer_pl, 0);
+			if (peer_pi < 0)
+				continue;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				rte_eth_hairpin_unbind(peer_pl[j], pi);
+			}
+		}
+
 		rte_eth_dev_stop(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c7e7e41..29ede20 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -398,6 +398,8 @@ struct queue_stats_mappings {
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
                         ` (4 preceding siblings ...)
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-08  8:51       ` Bing Zhao
  2020-10-08  9:47         ` Ori Kam
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08  8:51 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In the release notes, 2 ports hairpin mode feature is added.

In rte flow part, one suggestion is added to mention that metadata
could be used to connect the hairpin RX and TX flows if the hairpin
is working in explicit TX flow rule mode.

In the testpmd command line, the new parameter to set hairpin working
mode is described.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
 doc/guides/prog_guide/rte_flow.rst     | 3 +++
 doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
 doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
 3 files changed, 19 insertions(+)

diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 119b128..bb54d67 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -2592,6 +2592,9 @@ set, unpredictable value will be seen depending on driver implementation. For
 loopback/hairpin packet, metadata set on Rx/Tx may or may not be propagated to
 the other path depending on HW capability.
 
+In hairpin case with TX explicit flow mode, metadata could (not mandatory) be
+used to connect the RX and TX flows if it can be propagated from RX to TX path.
+
 .. _table_rte_flow_action_set_meta:
 
 .. table:: SET_META
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 0b2a370..05ceea0 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -109,6 +109,10 @@ New Features
   * Extern objects and functions can be plugged into the pipeline.
   * Transaction-oriented table updates.
 
+* **Updated the ethdev library to support hairpin between two ports.**
+
+  New APIs are introduced to support binding / unbinding 2 ports hairpin.
+  Hairpin TX part flow rules can be inserted explicitly.
 
 Removed Items
 -------------
@@ -240,6 +244,10 @@ ABI Changes
 
   * ``ethdev`` internal functions are marked with ``__rte_internal`` tag.
 
+  * ``struct rte_eth_hairpin_conf`` has two new members:
+
+    * ``uint32_t tx_explicit:1;``
+    * ``uint32_t manual_bind:1;``
 
 Known Issues
 ------------
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index e2539f6..4e627c4 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -497,3 +497,11 @@ The command line options are:
 *   ``--record-burst-stats``
 
     Enable display of RX and TX burst stats.
+
+*   ``--hairpin-mode=0xXX``
+
+    Set the hairpin port mode with bitmask, only valid when hairpin queues number is set.
+    bit 4 - explicit TX flow rule
+    bit 1 - two hairpin ports paired
+    bit 0 - two hairpin ports loop
+    The default value is 0. Hairpin will use single port mode and implicit TX flow mode.
-- 
1.8.3.1


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

* Re: [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-08  9:07         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:07 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs
> 
> In single port hairpin mode, all the hairpin TX and RX queues belong
> to the same device. After the queues are set up properly, there is
> no other dependency between the TX queue and its RX peer queue. The
> binding process that connected the TX and RX queues together from
> hardware level will be done automatically during the device start
> procedure. Everything required is configured and initialized already
> for the binding process.
> 
> But in two ports hairpin mode, there will be some cross-dependences
> between two different ports. Usually, the ports will be initialized
> serially by the main thread but not in parallel. The earlier port
> will not be able to enable the bind if the following peer port is
> not yet configured with HW resources. What's more, if one port is
> detached / attached dynamically, it would introduce more trouble
> for the hairpin binding.
> 
> To overcome these, new APIs for binding and unbinding are added.
> During startup, only the hairpin TX and RX peer queues will be set
> up. Nothing will be done when starting the device if the queues are
> without auto-bind attribute. Only after the required ports pair
> started, the `rte_eth_hairpin_bind()` API can be called to bind the
> all TX queues of the egress port to the RX queues of the peer port.
> Then the connection between the egress and ingress ports pair will
> be established.
> 
> The `rte_eth_hairpin_unbind()` API could be used to disconnect the
> egress and the peer ingress ports. This should only be called before
> the device is closed if needed. When doing the clean up, all the
> egress and ingress pairs related to a single port should be taken
> into consideration, especially in the hot unplug case.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
> v2: remove the all peer ports logic from rte API
> ---

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori



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

* Re: [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-08  9:23         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:23 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 2/6] ethdev: add new attributes to hairpin config
> 
> To support two ports hairpin mode and keep the backward compatibility
> for the application, two new attribute members of hairpin queue
> configuration structure are added.
> 
> `tx_explicit` means if the application itself will insert the TX part
> flow rules. If not set, PMD will insert the rules implicitly.
> `manual_bind` means if the hairpin TX queue and peer RX queue will be
> bound automatically during device start stage.
> 
> Different TX and RX queue pairs could have different values, but it
> is highly recommended that all paired queues between one egress and
> its peer ingress ports have the same values, in order not to bring
> any chaos to the system. The actual support of these attribute
> parameters will be checked and decided by the PMD drivers.
> 
> In a single port hairpin, if both are zero without any setting, the
> behavior will remain the same as before. It means no bind API needs
> to be called and no TX flow rules need to be inserted manually by
> the application.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
> v2: optimize the structure and remove unused macros
> ---
>  lib/librte_ethdev/rte_ethdev.c | 8 ++++----
>  lib/librte_ethdev/rte_ethdev.h | 5 ++++-
>  2 files changed, 8 insertions(+), 5 deletions(-)
> 
> diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
> index 85a19bd..a4adeff 100644
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -1954,13 +1954,13 @@ struct rte_eth_dev *
>  	}
>  	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",
> +			"Invalid value for number of peers for Rx queue(=%u),
> 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",
> +			"Invalid value for number of peers for Rx queue(=%u),
> should be: > 0",
>  			conf->peer_count);
>  		return -EINVAL;
>  	}
> @@ -2125,13 +2125,13 @@ struct rte_eth_dev *
>  	}
>  	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",
> +			"Invalid value for number of peers for Tx queue(=%u),
> should be: <= %hu",
>  			conf->peer_count, cap.max_tx_2_rx);
>  		return -EINVAL;
>  	}
>  	if (conf->peer_count == 0) {
>  		RTE_ETHDEV_LOG(ERR,
> -			"Invalid value for number of peers for Tx queue(=%hu),
> should be: > 0",
> +			"Invalid value for number of peers for Tx queue(=%u),
> should be: > 0",
>  			conf->peer_count);
>  		return -EINVAL;
>  	}
> diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
> index 6206643..94a981c 100644
> --- a/lib/librte_ethdev/rte_ethdev.h
> +++ b/lib/librte_ethdev/rte_ethdev.h
> @@ -1045,7 +1045,10 @@ struct rte_eth_hairpin_peer {
>   * A structure used to configure hairpin binding.
>   */
>  struct rte_eth_hairpin_conf {
> -	uint16_t peer_count; /**< The number of peers. */
> +	uint32_t peer_count:16; /**< The number of peers. */
> +	uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
> +	uint32_t manual_bind:1; /**< Manually bind hairpin queues. */
> +	uint32_t reserved:14; /**< Reserved bits. */
>  	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
>  };
> 
> --
> 1.8.3.1

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori


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

* Re: [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-08  9:40         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:40 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

PSB,
Best,
Ori
> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list
> 
> After hairpin queues are configured, in general, the application will
> maintain the ports topology and even the queues configuration for
> hairpin. But sometimes it will not.
> 
> If there is no hot-plug, it is easy to bind and unbind hairpin among
> all the ports. The application can just connect or disconnect the
> hairpin egress ports to / from all the probed ingress ports. Then
> all the connections could be handled properly.
> 
> But with hot-plug / hot-unplug, one port could be probed and removed
> dynamically. With two ports hairpin, all the connections from and to
> this port should be handled after start(bind) or before stop(unbind).
> It is necessary to know the hairpin topology with this port.
> 
> This API will return the ports list with the actual peer ports number
> after configuration. Either peer RX or TX ports will be gotten with
> this function call.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  lib/librte_ethdev/rte_ethdev.c           | 24 ++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev.h           | 27 +++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev_driver.h    | 30
> ++++++++++++++++++++++++++++++
>  lib/librte_ethdev/rte_ethdev_version.map |  1 +
>  4 files changed, 82 insertions(+)
> 
> diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
> index a4adeff..ea7892a 100644
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -2208,6 +2208,30 @@ struct rte_eth_dev *
>  	return ret;
>  }
> 
> +int
> +rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
> +			       bool direction)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	if (!peer_ports)
> +		return -EINVAL;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);
> +	dev = &rte_eth_devices[cur_port];
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> >hairpin_get_peer_ports,
> +				-ENOTSUP);
> +
> +	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
> +						      direction);
> +	if (ret < 0)
> +		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s
> ports",
> +			       cur_port, direction ? "RX" : "TX");
> +
> +	return ret;
> +}
> +
>  void
>  rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
>  		void *userdata __rte_unused)
> diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
> index 94a981c..6680de2 100644
> --- a/lib/librte_ethdev/rte_ethdev.h
> +++ b/lib/librte_ethdev/rte_ethdev.h
> @@ -2134,6 +2134,33 @@ int rte_eth_tx_queue_setup(uint16_t port_id,
> uint16_t tx_queue_id,
>   * @warning
>   * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
>   *
> + * Get all the hairpin peer RX / TX ports of the current port.
> + * The caller should ensure that the array is large enough to save the ports
> + * list.
> + *
> + * @param cur_port
> + *   The current port identifier of the Ethernet device.
> + * @param peer_ports
> + *   Pointer to the array to save the peer ports list *
> + * @param direction
> + *   Current port to peer port direction
> + *   true - current used as TX to get all peer RX ports.
> + *   false - current used as RX to get all peer TX ports.
> + *
> + * @return
> + *   - (0 or positive) actual peer ports number.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.
> + */
> +__rte_experimental
> +int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
> +				   bool direction);
> +

I think direction should be changed to int, or change the name to ingress, also
I think we should add the peer_ports array size, so in case the application
didn't allocate enough space it will simply end in error.

> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior
> notice
> + *
>   * Bind all hairpin TX queues of one port to the RX queues of the peer port.
>   * It is only allowed to call this API after all hairpin queues are configured
>   * properly and the devices of TX and peer RX are in started state.
> diff --git a/lib/librte_ethdev/rte_ethdev_driver.h
> b/lib/librte_ethdev/rte_ethdev_driver.h
> index f8eb879..ecca9d6 100644
> --- a/lib/librte_ethdev/rte_ethdev_driver.h
> +++ b/lib/librte_ethdev/rte_ethdev_driver.h
> @@ -576,6 +576,34 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
> 
>  /**
>   * @internal
> + * Get all hairpin TX/RX peer ports of the current device, if any.
> + *
> + * @param dev
> + *   ethdev handle of port.
> + * @param peer_ports
> + *   array to save the ports list.
> + * @param direction
> + *   value to decide the current to peer direction
> + *   true - used as TX to get all peer RX ports.
> + *   false - used as RX to get all peer TX ports.
> + *
> + * @return
> + *   Negative errno value on error, 0 or positive on success.
> + *
> + * @retval 0
> + *   Success, no peer ports.
> + * @retval >0
> + *   Actual number of the peer ports.
> + * @retval -ENOTSUP
> + *   Get peer ports API is not supported.
> + * @retval -EINVAL
> + *   One of the parameters is invalid.
> + */
> +typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
> +					uint16_t *peer_ports, bool direction);
> +
> +/**
> + * @internal
>   * Bind all hairpin TX queues of one port to the RX queues of the peer port.
>   *
>   * @param dev
> @@ -761,6 +789,8 @@ struct eth_dev_ops {
>  	/**< Set up device RX hairpin queue. */
>  	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
>  	/**< Set up device TX hairpin queue. */
> +	hairpin_get_peer_ports_t hairpin_get_peer_ports;
> +	/**< Get hairpin peer ports list. */
>  	eth_hairpin_bind_t hairpin_bind;
>  	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
>  	eth_hairpin_unbind_t hairpin_unbind;
> diff --git a/lib/librte_ethdev/rte_ethdev_version.map
> b/lib/librte_ethdev/rte_ethdev_version.map
> index 18efe4e..1019e28 100644
> --- a/lib/librte_ethdev/rte_ethdev_version.map
> +++ b/lib/librte_ethdev/rte_ethdev_version.map
> @@ -228,6 +228,7 @@ EXPERIMENTAL {
> 
>  	# added in 20.11
>  	rte_eth_hairpin_bind;
> +	rte_eth_hairpin_get_peer_ports;
>  	rte_eth_hairpin_unbind;
>  	rte_eth_link_speed_to_str;
>  	rte_eth_link_to_str;
> --
> 1.8.3.1


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

* Re: [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-08  9:44         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:44 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation
> 
> Every hairpin queue pair should be configured properly and the
> connection between TX and RX queues should be established, before
> hairpin function works. In single port hairpin mode, the queues of
> each pair belong to the same device. It is easy to get the hardware
> and software information of each queue and configure the hairpin
> connection with such information. In two ports hairpin mode, it is
> not easy or inappropriate to access one queue's information from
> another device.
> 
> Since hairpin is configured per queue pair, three new APIs are
> introduced and they are internal for the PMD using.
> 
> The peer update API helps to pass one queue's information to the
> peer queue and get the peer's information back for the next step.
> The peer bind API configures the current queue with the peer's
> information. For each hairpin queue pair, this API may need to be
> called twice to configure the TX, RX queues separately.
> The peer unbind API resets the current queue configuration and state
> to disconnect it from the peer queue. Also, it may need to be called
> twice to disconnect TX, RX queues from each other.
> 
> Some parameter of the above APIs might not be mandatory, and it
> depends on the PMD implementation.
> 
> The structure of `rte_hairpin_peer_info` is only a declaration and
> the actual members will be defined in each PMD when being used.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---


Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori


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

* Re: [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-08  9:45         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:45 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 5/6] app/testpmd: change hairpin queues setup
> 
> A new parameter `hairpin-mode` is introduced to the testpmd command
> line. Bitmask value is used to provide more flexible configuration.
> This parameter should be used when `hairpinq` is specified in the
> command line.
> 
> Bit 0 in the LSB indicates the hairpin will use the loop mode. The
> previous port RX queue will be connected to the current port TX
> queue.
> Bit 1 in the LSB indicates the hairpin will use pair port mode. The
> even index port will be paired with the next odd index port. If the
> total number of probed port is odd, then the last one will be paired
> to itself.
> If this byte is zero, then each port will be paired to itself.
> Bit 0 takes a higher priority in the checking.
> 
> Bit 4 in the second bytes indicate if the hairpin will use explicit
> TX flow mode.
> 
> e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"
> 
> If not set, default value zero will be used and the behavior will
> try to get align with the previous single port mode. If the ports
> belong to different vendors' NICs, it is suggested to use the `self`
> hairpin mode only.
> 
> Since hairpin configures the hardware resources, the port mask of
> packets forwarding engine will not be used here.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
> v2: move the hairpin bind/unbind into start/stop to support hot-plug
>     and hot-unplug
> ---

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori

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

* Re: [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode Bing Zhao
@ 2020-10-08  9:47         ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08  9:47 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 11:52 AM
> Subject: [PATCH v2 6/6] doc: update for two ports hairpin mode
> 
> In the release notes, 2 ports hairpin mode feature is added.
> 
> In rte flow part, one suggestion is added to mention that metadata
> could be used to connect the hairpin RX and TX flows if the hairpin
> is working in explicit TX flow rule mode.
> 
> In the testpmd command line, the new parameter to set hairpin working
> mode is described.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
>  doc/guides/prog_guide/rte_flow.rst     | 3 +++
>  doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
>  doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
>  3 files changed, 19 insertions(+)
> 
> diff --git a/doc/guides/prog_guide/rte_flow.rst
> b/doc/guides/prog_guide/rte_flow.rst
> index 119b128..bb54d67 100644
> --- a/doc/guides/prog_guide/rte_flow.rst
> +++ b/doc/guides/prog_guide/rte_flow.rst
> @@ -2592,6 +2592,9 @@ set, unpredictable value will be seen depending on
> driver implementation. For
>  loopback/hairpin packet, metadata set on Rx/Tx may or may not be
> propagated to
>  the other path depending on HW capability.
> 
> +In hairpin case with TX explicit flow mode, metadata could (not mandatory) be
> +used to connect the RX and TX flows if it can be propagated from RX to TX
> path.
> +
>  .. _table_rte_flow_action_set_meta:
> 
>  .. table:: SET_META
> diff --git a/doc/guides/rel_notes/release_20_11.rst
> b/doc/guides/rel_notes/release_20_11.rst
> index 0b2a370..05ceea0 100644
> --- a/doc/guides/rel_notes/release_20_11.rst
> +++ b/doc/guides/rel_notes/release_20_11.rst
> @@ -109,6 +109,10 @@ New Features
>    * Extern objects and functions can be plugged into the pipeline.
>    * Transaction-oriented table updates.
> 
> +* **Updated the ethdev library to support hairpin between two ports.**
> +
> +  New APIs are introduced to support binding / unbinding 2 ports hairpin.
> +  Hairpin TX part flow rules can be inserted explicitly.
> 
>  Removed Items
>  -------------
> @@ -240,6 +244,10 @@ ABI Changes
> 
>    * ``ethdev`` internal functions are marked with ``__rte_internal`` tag.
> 
> +  * ``struct rte_eth_hairpin_conf`` has two new members:
> +
> +    * ``uint32_t tx_explicit:1;``
> +    * ``uint32_t manual_bind:1;``
> 
>  Known Issues
>  ------------
> diff --git a/doc/guides/testpmd_app_ug/run_app.rst
> b/doc/guides/testpmd_app_ug/run_app.rst
> index e2539f6..4e627c4 100644
> --- a/doc/guides/testpmd_app_ug/run_app.rst
> +++ b/doc/guides/testpmd_app_ug/run_app.rst
> @@ -497,3 +497,11 @@ The command line options are:
>  *   ``--record-burst-stats``
> 
>      Enable display of RX and TX burst stats.
> +
> +*   ``--hairpin-mode=0xXX``
> +
> +    Set the hairpin port mode with bitmask, only valid when hairpin queues
> number is set.
> +    bit 4 - explicit TX flow rule
> +    bit 1 - two hairpin ports paired
> +    bit 0 - two hairpin ports loop
> +    The default value is 0. Hairpin will use single port mode and implicit TX flow
> mode.
> --
> 1.8.3.1

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori

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

* [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
                         ` (5 preceding siblings ...)
  2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode Bing Zhao
@ 2020-10-08 12:05       ` Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
                           ` (6 more replies)
  6 siblings, 7 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new API to get the hairpin peer ports list.
3. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
4. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
5. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.
6. documents update.

---
v3:
1. add length to protect the pointer to the array from getting corrupted
2. change the direction from bool to unsigned int
v2:
1. add documents update
2. remove all peer ports logic from rte API
3. conf structure optimizing
4. new API to get the peer ports and testpmd change to support
   hot-plug / unplug case
---

Bing Zhao (6):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add API to get hairpin peer ports list
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup
  doc: update for two ports hairpin mode

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   | 125 ++++++++++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 doc/guides/prog_guide/rte_flow.rst       |   3 +
 doc/guides/rel_notes/release_20_11.rst   |   8 ++
 doc/guides/testpmd_app_ug/run_app.rst    |   8 ++
 lib/librte_ethdev/rte_ethdev.c           | 133 +++++++++++++++++++++-
 lib/librte_ethdev/rte_ethdev.h           |  85 +++++++++++++-
 lib/librte_ethdev/rte_ethdev_driver.h    | 187 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   6 +
 10 files changed, 563 insertions(+), 9 deletions(-)

-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-14 14:35           ` Thomas Monjalon
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config Bing Zhao
                           ` (5 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration, especially in the hot unplug case.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: remove the all peer ports logic from rte API
---
 lib/librte_ethdev/rte_ethdev.c           | 46 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 51 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  2 ++
 4 files changed, 151 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 0f56541..85a19bd 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2162,6 +2162,52 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+			       "to RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is already stopped", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+			       "TX %d from RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index d2bf74f..6206643 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2128,6 +2128,57 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * It is only allowed to call this API after all hairpin queues are configured
+ * properly and the devices of TX and peer RX are in started state.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices (optional). After
+ * unbind the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index c3062c2..f8eb879 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -575,6 +575,54 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 	 const struct rte_eth_hairpin_conf *hairpin_conf);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -713,6 +761,10 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index c95ef51..18efe4e 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 };
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-12 21:37           ` Thomas Monjalon
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
                           ` (4 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of hairpin queue
configuration structure are added.

`tx_explicit` means if the application itself will insert the TX part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommended that all paired queues between one egress and
its peer ingress ports have the same values, in order not to bring
any chaos to the system. The actual support of these attribute
parameters will be checked and decided by the PMD drivers.

In a single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means no bind API needs
to be called and no TX flow rules need to be inserted manually by
the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: optimize the structure and remove unused macros
---
 lib/librte_ethdev/rte_ethdev.c | 8 ++++----
 lib/librte_ethdev/rte_ethdev.h | 5 ++++-
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 85a19bd..a4adeff 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -1954,13 +1954,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Rx queue(=%u), 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",
+			"Invalid value for number of peers for Rx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
@@ -2125,13 +2125,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
 			conf->peer_count, cap.max_tx_2_rx);
 		return -EINVAL;
 	}
 	if (conf->peer_count == 0) {
 		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
+			"Invalid value for number of peers for Tx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 6206643..94a981c 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1045,7 +1045,10 @@ struct rte_eth_hairpin_peer {
  * A structure used to configure hairpin binding.
  */
 struct rte_eth_hairpin_conf {
-	uint16_t peer_count; /**< The number of peers. */
+	uint32_t peer_count:16; /**< The number of peers. */
+	uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
+	uint32_t manual_bind:1; /**< Manually bind hairpin queues. */
+	uint32_t reserved:14; /**< Reserved bits. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-08 12:31           ` Ori Kam
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
                           ` (3 subsequent siblings)
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

After hairpin queues are configured, in general, the application will
maintain the ports topology and even the queues configuration for
hairpin. But sometimes it will not.

If there is no hot-plug, it is easy to bind and unbind hairpin among
all the ports. The application can just connect or disconnect the
hairpin egress ports to / from all the probed ingress ports. Then
all the connections could be handled properly.

But with hot-plug / hot-unplug, one port could be probed and removed
dynamically. With two ports hairpin, all the connections from and to
this port should be handled after start(bind) or before stop(unbind).
It is necessary to know the hairpin topology with this port.

This API will return the ports list with the actual peer ports number
after configuration. Either peer RX or TX ports will be gotten with
this function call.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v3:
1. change the direction from bool to unsigned int type
2. add length to protect the array from getting corrupted
---
 lib/librte_ethdev/rte_ethdev.c           | 24 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 29 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 33 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  1 +
 4 files changed, 87 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index a4adeff..e656501 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2208,6 +2208,30 @@ struct rte_eth_dev *
 	return ret;
 }
 
+int
+rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+			       size_t len, uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	if (!peer_ports || !len)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+				-ENOTSUP);
+
+	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+						      len, direction);
+	if (ret < 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports",
+			       cur_port, direction ? "RX" : "TX");
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 94a981c..45ff8f2 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2134,6 +2134,35 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
  *
+ * Get all the hairpin peer RX / TX ports of the current port.
+ * The caller should ensure that the array is large enough to save the ports
+ * list.
+ *
+ * @param cur_port
+ *   The current port identifier of the Ethernet device.
+ * @param peer_ports
+ *   Pointer to the array to store the peer ports list.
+ * @param len
+ *   Length of the array to store the port identifiers.
+ * @param direction
+ *   Current port to peer port direction
+ *   positive - current used as TX to get all peer RX ports.
+ *   zero - current used as RX to get all peer TX ports.
+ *
+ * @return
+ *   - (0 or positive) actual peer ports number.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+				   size_t len, uint32_t direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  * It is only allowed to call this API after all hairpin queues are configured
  * properly and the devices of TX and peer RX are in started state.
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index f8eb879..827884c 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -576,6 +576,37 @@ typedef int (*eth_tx_hairpin_queue_setup_t)
 
 /**
  * @internal
+ * Get all hairpin TX/RX peer ports of the current device, if any.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param peer_ports
+ *   array to save the ports list.
+ * @param len
+ *   array length.
+ * @param direction
+ *   value to decide the current to peer direction
+ *   positive - used as TX to get all peer RX ports.
+ *   zero - used as RX to get all peer TX ports.
+ *
+ * @return
+ *   Negative errno value on error, 0 or positive on success.
+ *
+ * @retval 0
+ *   Success, no peer ports.
+ * @retval >0
+ *   Actual number of the peer ports.
+ * @retval -ENOTSUP
+ *   Get peer ports API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ */
+typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
+					uint16_t *peer_ports, size_t len,
+					uint32_t direction);
+
+/**
+ * @internal
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  *
  * @param dev
@@ -761,6 +792,8 @@ struct eth_dev_ops {
 	/**< Set up device RX hairpin queue. */
 	eth_tx_hairpin_queue_setup_t tx_hairpin_queue_setup;
 	/**< Set up device TX hairpin queue. */
+	hairpin_get_peer_ports_t hairpin_get_peer_ports;
+	/**< Get hairpin peer ports list. */
 	eth_hairpin_bind_t hairpin_bind;
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 18efe4e..1019e28 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -228,6 +228,7 @@ EXPERIMENTAL {
 
 	# added in 20.11
 	rte_eth_hairpin_bind;
+	rte_eth_hairpin_get_peer_ports;
 	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 4/6] ethdev: add APIs for hairpin queue operation
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
                           ` (2 preceding siblings ...)
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 5/6] app/testpmd: change hairpin queues setup Bing Zhao
                           ` (2 subsequent siblings)
  6 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between TX and RX queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the TX, RX queues separately.
The peer unbind API resets the current queue configuration and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect TX, RX queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v3: change the direction from bool to unsigned int type
---
 lib/librte_ethdev/rte_ethdev.c           |  55 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 102 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 160 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index e656501..b44bfe1 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5459,6 +5459,61 @@ enum rte_eth_switch_domain_state {
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_unbind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 827884c..0edaa53 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -653,6 +656,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -798,6 +816,12 @@ struct eth_dev_ops {
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin TX queues from the peer port RX queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	/**< Pass the current queue info and get the peer queue info. */
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	/**< Set up the connection between the pair of hairpin queues. */
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
+	/**< Disconnect the hairpin queues of a pair from each other. */
 };
 
 /**
@@ -1153,6 +1177,84 @@ typedef int (*ethdev_bus_specific_init)(struct rte_eth_dev *ethdev,
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @internal
+ * Pass the current hairpin queue HW and/or SW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  positive - pass TX queue information and get peer RX queue information
+ *  zero - pass RX queue information and get peer TX queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction);
+
+/**
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connection.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - bind current TX queue to peer RX queue
+ *  zero - bind current RX queue to peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction);
+
+/**
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - unbind current TX queue from peer RX queue
+ *  zero - unbind current RX queue from peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 1019e28..0742733 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -251,6 +251,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 	rte_flow_expand_rss;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 5/6] app/testpmd: change hairpin queues setup
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
                           ` (3 preceding siblings ...)
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode Bing Zhao
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
  6 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide more flexible configuration.
This parameter should be used when `hairpinq` is specified in the
command line.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port RX queue will be connected to the current port TX
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of probed port is odd, then the last one will be paired
to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
TX flow mode.

e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"

If not set, default value zero will be used and the behavior will
try to get align with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: move the hairpin bind/unbind into start/stop to support hot-plug
    and hot-unplug
---
 app/test-pmd/parameters.c |  15 ++++++
 app/test-pmd/testpmd.c    | 125 ++++++++++++++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h    |   2 +
 3 files changed, 138 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 1ead595..991029d 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -221,6 +221,9 @@
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -644,6 +647,7 @@
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1111,6 +1115,17 @@
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index ccba71c..baf21df 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ struct fwd_engine * fwd_engines[] = {
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ struct extmem_param {
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,49 @@ struct extmem_param {
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			/* Last port will be the peer RX port of the first. */
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2419,10 @@ struct extmem_param {
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2449,12 @@ struct extmem_param {
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	portid_t pl[RTE_MAX_ETHPORTS];
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	uint16_t cnt_pi = 0;
+	uint16_t cfg_pi = 0;
+	int peer_pi;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,7 +2594,7 @@ struct extmem_param {
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
@@ -2557,6 +2607,9 @@ struct extmem_param {
 				pi);
 		}
 
+		p_pi = pi;
+		cnt_pi++;
+
 		/* start port */
 		if (rte_eth_dev_start(pi) < 0) {
 			printf("Fail to start port %d\n", pi);
@@ -2581,6 +2634,8 @@ struct extmem_param {
 
 		/* at least one port started, need checking link status */
 		need_check_link_status = 1;
+
+		pl[cfg_pi++] = pi;
 	}
 
 	if (need_check_link_status == 1 && !no_link_check)
@@ -2588,6 +2643,50 @@ struct extmem_param {
 	else if (need_check_link_status == 0)
 		printf("Please stop the ports first\n");
 
+	if (hairpin_mode & 0xf) {
+		uint16_t i;
+		int j;
+
+		/* bind all started hairpin ports */
+		for (i = 0; i < cfg_pi; i++) {
+			pi = pl[i];
+			/* bind current TX to all peer RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 1);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       pi, peer_pl[j],
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+			/* bind all peer TX to current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       peer_pl[j], pi,
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+		}
+	}
+
 	printf("Done\n");
 	return 0;
 }
@@ -2598,6 +2697,8 @@ struct extmem_param {
 	portid_t pi;
 	struct rte_port *port;
 	int need_check_link_status = 0;
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	int peer_pi;
 
 	if (dcb_test) {
 		dcb_test = 0;
@@ -2628,6 +2729,22 @@ struct extmem_param {
 						RTE_PORT_HANDLING) == 0)
 			continue;
 
+		if (hairpin_mode & 0xf) {
+			int j;
+
+			rte_eth_hairpin_unbind(pi, RTE_MAX_ETHPORTS);
+			/* unbind all peer TX from current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				continue;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				rte_eth_hairpin_unbind(peer_pl[j], pi);
+			}
+		}
+
 		rte_eth_dev_stop(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c7e7e41..29ede20 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -398,6 +398,8 @@ struct queue_stats_mappings {
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
                           ` (4 preceding siblings ...)
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 5/6] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-08 12:05         ` Bing Zhao
  2020-10-12 21:30           ` Thomas Monjalon
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
  6 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-08 12:05 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In the release notes, 2 ports hairpin mode feature is added.

In rte flow part, one suggestion is added to mention that metadata
could be used to connect the hairpin RX and TX flows if the hairpin
is working in explicit TX flow rule mode.

In the testpmd command line, the new parameter to set hairpin working
mode is described.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
 doc/guides/prog_guide/rte_flow.rst     | 3 +++
 doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
 doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
 3 files changed, 19 insertions(+)

diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 119b128..bb54d67 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -2592,6 +2592,9 @@ set, unpredictable value will be seen depending on driver implementation. For
 loopback/hairpin packet, metadata set on Rx/Tx may or may not be propagated to
 the other path depending on HW capability.
 
+In hairpin case with TX explicit flow mode, metadata could (not mandatory) be
+used to connect the RX and TX flows if it can be propagated from RX to TX path.
+
 .. _table_rte_flow_action_set_meta:
 
 .. table:: SET_META
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 0b2a370..05ceea0 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -109,6 +109,10 @@ New Features
   * Extern objects and functions can be plugged into the pipeline.
   * Transaction-oriented table updates.
 
+* **Updated the ethdev library to support hairpin between two ports.**
+
+  New APIs are introduced to support binding / unbinding 2 ports hairpin.
+  Hairpin TX part flow rules can be inserted explicitly.
 
 Removed Items
 -------------
@@ -240,6 +244,10 @@ ABI Changes
 
   * ``ethdev`` internal functions are marked with ``__rte_internal`` tag.
 
+  * ``struct rte_eth_hairpin_conf`` has two new members:
+
+    * ``uint32_t tx_explicit:1;``
+    * ``uint32_t manual_bind:1;``
 
 Known Issues
 ------------
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index e2539f6..4e627c4 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -497,3 +497,11 @@ The command line options are:
 *   ``--record-burst-stats``
 
     Enable display of RX and TX burst stats.
+
+*   ``--hairpin-mode=0xXX``
+
+    Set the hairpin port mode with bitmask, only valid when hairpin queues number is set.
+    bit 4 - explicit TX flow rule
+    bit 1 - two hairpin ports paired
+    bit 0 - two hairpin ports loop
+    The default value is 0. Hairpin will use single port mode and implicit TX flow mode.
-- 
1.8.3.1


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

* Re: [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-08 12:31           ` Ori Kam
  0 siblings, 0 replies; 81+ messages in thread
From: Ori Kam @ 2020-10-08 12:31 UTC (permalink / raw)
  To: Bing Zhao, NBU-Contact-Thomas Monjalon, ferruh.yigit, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Bing,

> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2020 3:05 PM
> Subject: [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list
> 
> After hairpin queues are configured, in general, the application will
> maintain the ports topology and even the queues configuration for
> hairpin. But sometimes it will not.
> 
> If there is no hot-plug, it is easy to bind and unbind hairpin among
> all the ports. The application can just connect or disconnect the
> hairpin egress ports to / from all the probed ingress ports. Then
> all the connections could be handled properly.
> 
> But with hot-plug / hot-unplug, one port could be probed and removed
> dynamically. With two ports hairpin, all the connections from and to
> this port should be handled after start(bind) or before stop(unbind).
> It is necessary to know the hairpin topology with this port.
> 
> This API will return the ports list with the actual peer ports number
> after configuration. Either peer RX or TX ports will be gotten with
> this function call.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
> v3:
> 1. change the direction from bool to unsigned int type
> 2. add length to protect the array from getting corrupted
> ---

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori

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

* Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode Bing Zhao
@ 2020-10-12 21:30           ` Thomas Monjalon
  2020-10-13  1:13             ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-12 21:30 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

08/10/2020 14:05, Bing Zhao:
> In the release notes, 2 ports hairpin mode feature is added.
> 
> In rte flow part, one suggestion is added to mention that metadata
> could be used to connect the hairpin RX and TX flows if the hairpin
> is working in explicit TX flow rule mode.
> 
> In the testpmd command line, the new parameter to set hairpin working
> mode is described.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
> ---
>  doc/guides/prog_guide/rte_flow.rst     | 3 +++
>  doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
>  doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
>  3 files changed, 19 insertions(+)

Please squash doc changes in related code changes commits,
so both are atomic. Thanks




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

* Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-12 21:37           ` Thomas Monjalon
  2020-10-13 12:29             ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-12 21:37 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

08/10/2020 14:05, Bing Zhao:
>  struct rte_eth_hairpin_conf {
> -	uint16_t peer_count; /**< The number of peers. */
> +	uint32_t peer_count:16; /**< The number of peers. */

Why not keeping uint16_t?

> +	uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
> +	uint32_t manual_bind:1; /**< Manually bind hairpin queues. */

Please describe more the expectations of these bits:
What is changed at ethdev or PMD level?
What the application is supposed to do?
Why choosing one mode or the other?



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

* Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode
  2020-10-12 21:30           ` Thomas Monjalon
@ 2020-10-13  1:13             ` Bing Zhao
  2020-10-13  6:37               ` Thomas Monjalon
  0 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-13  1:13 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Tuesday, October 13, 2020 5:31 AM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports
> hairpin mode
> 
> External email: Use caution opening links or attachments
> 
> 
> 08/10/2020 14:05, Bing Zhao:
> > In the release notes, 2 ports hairpin mode feature is added.
> >
> > In rte flow part, one suggestion is added to mention that metadata
> > could be used to connect the hairpin RX and TX flows if the
> hairpin is
> > working in explicit TX flow rule mode.
> >
> > In the testpmd command line, the new parameter to set hairpin
> working
> > mode is described.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> > ---
> >  doc/guides/prog_guide/rte_flow.rst     | 3 +++
> >  doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
> > doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
> >  3 files changed, 19 insertions(+)
> 
> Please squash doc changes in related code changes commits, so both
> are atomic. Thanks

OK, I will rebase and squash them into other commits. So there is no
need to care about the check patches warning of the headline prefix,
am I right?

> 
> 

Thanks a lot

BR. Bing

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

* Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode
  2020-10-13  1:13             ` Bing Zhao
@ 2020-10-13  6:37               ` Thomas Monjalon
  2020-10-13  6:40                 ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-13  6:37 UTC (permalink / raw)
  To: Bing Zhao
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

13/10/2020 03:13, Bing Zhao:
> From: Thomas Monjalon <thomas@monjalon.net>
> > 08/10/2020 14:05, Bing Zhao:
> > > In the release notes, 2 ports hairpin mode feature is added.
> > >
> > > In rte flow part, one suggestion is added to mention that metadata
> > > could be used to connect the hairpin RX and TX flows if the
> > hairpin is
> > > working in explicit TX flow rule mode.
> > >
> > > In the testpmd command line, the new parameter to set hairpin
> > working
> > > mode is described.
> > >
> > > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > > Acked-by: Ori Kam <orika@nvidia.com>
> > > ---
> > >  doc/guides/prog_guide/rte_flow.rst     | 3 +++
> > >  doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
> > > doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
> > >  3 files changed, 19 insertions(+)
> > 
> > Please squash doc changes in related code changes commits, so both
> > are atomic. Thanks
> 
> OK, I will rebase and squash them into other commits. So there is no
> need to care about the check patches warning of the headline prefix,
> am I right?

Right
We should improve this script to be less strict.



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

* Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode
  2020-10-13  6:37               ` Thomas Monjalon
@ 2020-10-13  6:40                 ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13  6:40 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Tuesday, October 13, 2020 2:37 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v3 6/6] doc: update for two ports
> hairpin mode
> 
> External email: Use caution opening links or attachments
> 
> 
> 13/10/2020 03:13, Bing Zhao:
> > From: Thomas Monjalon <thomas@monjalon.net>
> > > 08/10/2020 14:05, Bing Zhao:
> > > > In the release notes, 2 ports hairpin mode feature is added.
> > > >
> > > > In rte flow part, one suggestion is added to mention that
> metadata
> > > > could be used to connect the hairpin RX and TX flows if the
> > > hairpin is
> > > > working in explicit TX flow rule mode.
> > > >
> > > > In the testpmd command line, the new parameter to set hairpin
> > > working
> > > > mode is described.
> > > >
> > > > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > > > Acked-by: Ori Kam <orika@nvidia.com>
> > > > ---
> > > >  doc/guides/prog_guide/rte_flow.rst     | 3 +++
> > > >  doc/guides/rel_notes/release_20_11.rst | 8 ++++++++
> > > > doc/guides/testpmd_app_ug/run_app.rst  | 8 ++++++++
> > > >  3 files changed, 19 insertions(+)
> > >
> > > Please squash doc changes in related code changes commits, so
> both
> > > are atomic. Thanks
> >
> > OK, I will rebase and squash them into other commits. So there is
> no
> > need to care about the check patches warning of the headline
> prefix,
> > am I right?
> 
> Right
> We should improve this script to be less strict.
> 

I see.

So many thanks


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

* Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config
  2020-10-12 21:37           ` Thomas Monjalon
@ 2020-10-13 12:29             ` Bing Zhao
  2020-10-13 12:41               ` Thomas Monjalon
  0 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 12:29 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,
PSB

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Tuesday, October 13, 2020 5:37 AM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to
> hairpin config
> 
> External email: Use caution opening links or attachments
> 
> 
> 08/10/2020 14:05, Bing Zhao:
> >  struct rte_eth_hairpin_conf {
> > -     uint16_t peer_count; /**< The number of peers. */
> > +     uint32_t peer_count:16; /**< The number of peers. */
> 
> Why not keeping uint16_t?

The inside structure has a multiple of 4B, and the peer_count now only takes about 2B. AFAIK, usually, the structure will have an aligned length/offset and there will be some padding between the 2B + (2B pad) + 4B * 32 or 2B + (2B +2B) * 32 + 2B, depending on the compiler.
I changed to bit fields of a u32 due to the two facts:
1. Using the 2B and keep the whole structure aligned. No waste except the reserved bits.
2. Only u32 with bit fields is standard.

> 
> > +     uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
> > +     uint32_t manual_bind:1; /**< Manually bind hairpin queues.
> */
> 
> Please describe more the expectations of these bits:
> What is changed at ethdev or PMD level?

In ethdev level, there is almost no change. This attribute will be passed to PMD directly through the function pointer.
In PMD level, these bits should be checked and better to be saved. And the attribute fields should be checked for per queue pair and all the queues, and each queue pair should have the same attributes configured to make the behavior aligned. But it depends on the PMD itself to decide if all the queue pairs between a port pair should have the same attributes, or even all the queues from / to same port of all hairpin port pairs.
If manual_bind is not set, then the PMD will try to bind the queues and enable hairpin during device start stage and there is no need to call the bind / unbind API.
If tx_explicit is set, the application should insert the RX flow rules and TX flow rules seperataly and connect the RX/TX logic connection together.

> What the application is supposed to do?

The application should specify the new two attributes during the queue setup. And also, it could leave it unset (0 by default) to keep the behavior compatible with the previous release.
If manual_bind is set, then it is the application's responsibility to call the bind / unbind API to enable / disable the hairpin between specific ports.
If tx_explicit is set, as described above, the application should maintain the flows logic to make hairpin work as expected, e.g., they can choose metadata (not the only method), in the RX flow, the metadata is set and in the TX flow, it is used for matching. Then even if the headers are changed with NAT action or encap, the hairpin function could work as expected.

> Why choosing one mode or the other?

If the application wants to have the full control of hairpin flows, it could chose the explicit TX flow mode.
If two or more ports involved into the hairpin, it is suggested to use the manual bind.
Please note, the actual supported attributes denpend on the PMD and HW.

> 

Thanks

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

* Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config
  2020-10-13 12:29             ` Bing Zhao
@ 2020-10-13 12:41               ` Thomas Monjalon
  2020-10-13 13:21                 ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-13 12:41 UTC (permalink / raw)
  To: Bing Zhao
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

13/10/2020 14:29, Bing Zhao:
> From: Thomas Monjalon <thomas@monjalon.net>
> > 08/10/2020 14:05, Bing Zhao:
> > >  struct rte_eth_hairpin_conf {
> > > -     uint16_t peer_count; /**< The number of peers. */
> > > +     uint32_t peer_count:16; /**< The number of peers. */
> > 
> > Why not keeping uint16_t?
> 
> The inside structure has a multiple of 4B, and the peer_count now only takes about 2B. AFAIK, usually, the structure will have an aligned length/offset and there will be some padding between the 2B + (2B pad) + 4B * 32 or 2B + (2B +2B) * 32 + 2B, depending on the compiler.
> I changed to bit fields of a u32 due to the two facts:
> 1. Using the 2B and keep the whole structure aligned. No waste except the reserved bits.
> 2. Only u32 with bit fields is standard.

Oh I see, this is because u16 bit fields are not standard?

> > > +     uint32_t tx_explicit:1; /**< Explicit TX flow rule mode. */
> > > +     uint32_t manual_bind:1; /**< Manually bind hairpin queues.
> > */
> > 
> > Please describe more the expectations of these bits:
> > What is changed at ethdev or PMD level?
> 
> In ethdev level, there is almost no change. This attribute will be passed to PMD directly through the function pointer.
> In PMD level, these bits should be checked and better to be saved. And the attribute fields should be checked for per queue pair and all the queues, and each queue pair should have the same attributes configured to make the behavior aligned. But it depends on the PMD itself to decide if all the queue pairs between a port pair should have the same attributes, or even all the queues from / to same port of all hairpin port pairs.
> If manual_bind is not set, then the PMD will try to bind the queues and enable hairpin during device start stage and there is no need to call the bind / unbind API.
> If tx_explicit is set, the application should insert the RX flow rules and TX flow rules seperataly and connect the RX/TX logic connection together.
> 
> > What the application is supposed to do?
> 
> The application should specify the new two attributes during the queue setup. And also, it could leave it unset (0 by default) to keep the behavior compatible with the previous release.
> If manual_bind is set, then it is the application's responsibility to call the bind / unbind API to enable / disable the hairpin between specific ports.
> If tx_explicit is set, as described above, the application should maintain the flows logic to make hairpin work as expected, e.g., they can choose metadata (not the only method), in the RX flow, the metadata is set and in the TX flow, it is used for matching. Then even if the headers are changed with NAT action or encap, the hairpin function could work as expected.
> 
> > Why choosing one mode or the other?
> 
> If the application wants to have the full control of hairpin flows, it could chose the explicit TX flow mode.
> If two or more ports involved into the hairpin, it is suggested to use the manual bind.
> Please note, the actual supported attributes denpend on the PMD and HW.

The application impact must be described shortly in doxygen please.




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

* Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config
  2020-10-13 12:41               ` Thomas Monjalon
@ 2020-10-13 13:21                 ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 13:21 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Tuesday, October 13, 2020 8:42 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to
> hairpin config
> 
> External email: Use caution opening links or attachments
> 
> 
> 13/10/2020 14:29, Bing Zhao:
> > From: Thomas Monjalon <thomas@monjalon.net>
> > > 08/10/2020 14:05, Bing Zhao:
> > > >  struct rte_eth_hairpin_conf {
> > > > -     uint16_t peer_count; /**< The number of peers. */
> > > > +     uint32_t peer_count:16; /**< The number of peers. */
> > >
> > > Why not keeping uint16_t?
> >
> > The inside structure has a multiple of 4B, and the peer_count now
> only takes about 2B. AFAIK, usually, the structure will have an
> aligned length/offset and there will be some padding between the 2B
> + (2B pad) + 4B * 32 or 2B + (2B +2B) * 32 + 2B, depending on the
> compiler.
> > I changed to bit fields of a u32 due to the two facts:
> > 1. Using the 2B and keep the whole structure aligned. No waste
> except the reserved bits.
> > 2. Only u32 with bit fields is standard.
> 
> Oh I see, this is because u16 bit fields are not standard?

In some compiler, it is supported. But in the old compiler, it might not and some warning will be raised.
" nonstandard extension used : bit-field types other than int "

"
The following properties of bit fields are implementation-defined:

Whether bit fields of type int are treated as signed or unsigned
Whether types other than int, signed int, unsigned int, and _Bool (since C99) are permitted
"
In C11, it should be supported already.

> 
> > > > +     uint32_t tx_explicit:1; /**< Explicit TX flow rule mode.
> */
> > > > +     uint32_t manual_bind:1; /**< Manually bind hairpin
> queues.
> > > */
> > >
> > > Please describe more the expectations of these bits:
> > > What is changed at ethdev or PMD level?
> >
> > In ethdev level, there is almost no change. This attribute will be
> passed to PMD directly through the function pointer.
> > In PMD level, these bits should be checked and better to be saved.
> And the attribute fields should be checked for per queue pair and
> all the queues, and each queue pair should have the same attributes
> configured to make the behavior aligned. But it depends on the PMD
> itself to decide if all the queue pairs between a port pair should
> have the same attributes, or even all the queues from / to same port
> of all hairpin port pairs.
> > If manual_bind is not set, then the PMD will try to bind the
> queues and enable hairpin during device start stage and there is no
> need to call the bind / unbind API.
> > If tx_explicit is set, the application should insert the RX flow
> rules and TX flow rules separately and connect the RX/TX logic
> connection together.
> >
> > > What the application is supposed to do?
> >
> > The application should specify the new two attributes during the
> queue setup. And also, it could leave it unset (0 by default) to
> keep the behavior compatible with the previous release.
> > If manual_bind is set, then it is the application's responsibility
> to call the bind / unbind API to enable / disable the hairpin
> between specific ports.
> > If tx_explicit is set, as described above, the application should
> maintain the flows logic to make hairpin work as expected, e.g.,
> they can choose metadata (not the only method), in the RX flow, the
> metadata is set and in the TX flow, it is used for matching. Then
> even if the headers are changed with NAT action or encap, the
> hairpin function could work as expected.
> >
> > > Why choosing one mode or the other?
> >
> > If the application wants to have the full control of hairpin flows,
> it could chose the explicit TX flow mode.
> > If two or more ports involved into the hairpin, it is suggested to
> use the manual bind.
> > Please note, the actual supported attributes denpend on the PMD
> and HW.
> 
> The application impact must be described shortly in doxygen please.
> 
> 

OK, sure. I added into the commit message. I will also describe it shortly in the doc.

Thanks

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

* [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports
  2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
                           ` (5 preceding siblings ...)
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode Bing Zhao
@ 2020-10-13 16:19         ` Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
                             ` (4 more replies)
  6 siblings, 5 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new API to get the hairpin peer ports list.
3. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
4. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
5. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.
6. documents update.

---
v4:
1. squash documents update into patch
2. add more description of the hairpin conf attributes
v3:
1. add length to protect the pointer to the array from getting corrupted
2. change the direction from bool to unsigned int
v2:
1. add documents update
2. remove all peer ports logic from rte API
3. conf structure optimizing
4. new API to get the peer ports and testpmd change to support
   hot-plug / unplug case
---

Bing Zhao (5):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add API to get hairpin peer ports list
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   | 125 ++++++++++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 doc/guides/prog_guide/rte_flow.rst       |   3 +
 doc/guides/rel_notes/release_20_11.rst   |  11 ++
 doc/guides/testpmd_app_ug/run_app.rst    |   8 ++
 lib/librte_ethdev/rte_ethdev.c           | 133 +++++++++++++++++++++-
 lib/librte_ethdev/rte_ethdev.h           | 107 +++++++++++++++++-
 lib/librte_ethdev/rte_ethdev_driver.h    | 187 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   6 +
 10 files changed, 588 insertions(+), 9 deletions(-)

-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-13 16:19           ` Bing Zhao
  2020-10-14 14:43             ` Thomas Monjalon
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 2/5] ethdev: add new attributes to hairpin config Bing Zhao
                             ` (3 subsequent siblings)
  4 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration, especially in the hot unplug case.
mode is described.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v4: squash release notes update
v2: remove the all peer ports logic from rte API
---
 doc/guides/rel_notes/release_20_11.rst   |  4 +++
 lib/librte_ethdev/rte_ethdev.c           | 46 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 51 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  2 ++
 5 files changed, 155 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index bcc0fc2..6b3d223 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -60,6 +60,10 @@ New Features
   Added the FEC API which provides functions for query FEC capabilities and
   current FEC mode from device. Also, API for configuring FEC mode is also provided.
 
+* **Updated the ethdev library to support hairpin between two ports.**
+
+  New APIs are introduced to support binding / unbinding 2 ports hairpin.
+
 * **Updated Broadcom bnxt driver.**
 
   Updated the Broadcom bnxt driver with new features and improvements, including:
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 892c246..b6371fb 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2162,6 +2162,52 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+			       "to RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is already stopped", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+			       "TX %d from RX %d (%d - all ports)", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 5bcfbb8..5106098 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2158,6 +2158,57 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * It is only allowed to call this API after all hairpin queues are configured
+ * properly and the devices of TX and peer RX are in started state.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices (optional). After
+ * unbind the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device.
+ *
+ * @param tx_port
+ *   The TX port identifier of the Ethernet device.
+ * @param rx_port
+ *   The peer RX port identifier of the Ethernet device.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value with TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 35cc4fb..26ded15 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -656,6 +656,54 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 typedef int (*eth_fec_set_t)(struct rte_eth_dev *dev, uint32_t fec_capa);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, unbind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -801,6 +849,10 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index f8a0945..1aee03a 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 	rte_eth_fec_get_capability;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v4 2/5] ethdev: add new attributes to hairpin config
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-13 16:19           ` Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
                             ` (2 subsequent siblings)
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of the hairpin queue
configuration structure will be added.

`tx_explicit` means if the application itself will insert the TX part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during the device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommended that all paired queues between one egress and
its peer ingress ports have the same values, in order not to bring
any chaos to the system. The actual support of these attribute
parameters will be checked and decided by the PMD drivers.

In the single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means that no bind API
needs to be called and no TX flow rules need to be inserted manually
by the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v4: squash document update and more info for the two new attributes
v2: optimize the structure and remove unused macros
---
 doc/guides/prog_guide/rte_flow.rst     |  3 +++
 doc/guides/rel_notes/release_20_11.rst |  6 ++++++
 lib/librte_ethdev/rte_ethdev.c         |  8 ++++----
 lib/librte_ethdev/rte_ethdev.h         | 27 ++++++++++++++++++++++++++-
 4 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 119b128..bb54d67 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -2592,6 +2592,9 @@ set, unpredictable value will be seen depending on driver implementation. For
 loopback/hairpin packet, metadata set on Rx/Tx may or may not be propagated to
 the other path depending on HW capability.
 
+In hairpin case with TX explicit flow mode, metadata could (not mandatory) be
+used to connect the RX and TX flows if it can be propagated from RX to TX path.
+
 .. _table_rte_flow_action_set_meta:
 
 .. table:: SET_META
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 6b3d223..a1e20a6 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -63,6 +63,7 @@ New Features
 * **Updated the ethdev library to support hairpin between two ports.**
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
+  Hairpin TX part flow rules can be inserted explicitly.
 
 * **Updated Broadcom bnxt driver.**
 
@@ -318,6 +319,11 @@ ABI Changes
 
   * ``ethdev`` internal functions are marked with ``__rte_internal`` tag.
 
+  * ``struct rte_eth_hairpin_conf`` has two new members:
+
+    * ``uint32_t tx_explicit:1;``
+    * ``uint32_t manual_bind:1;``
+
 
 Known Issues
 ------------
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index b6371fb..14b9f3a 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -1954,13 +1954,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Rx queue(=%u), 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",
+			"Invalid value for number of peers for Rx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
@@ -2125,13 +2125,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
 			conf->peer_count, cap.max_tx_2_rx);
 		return -EINVAL;
 	}
 	if (conf->peer_count == 0) {
 		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
+			"Invalid value for number of peers for Tx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 5106098..938df08 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1045,7 +1045,32 @@ struct rte_eth_hairpin_peer {
  * A structure used to configure hairpin binding.
  */
 struct rte_eth_hairpin_conf {
-	uint16_t peer_count; /**< The number of peers. */
+	uint32_t peer_count:16; /**< The number of peers. */
+
+	/**
+	 * Explicit TX flow rule mode. One hairpin pair of queues should have
+	 * the same attribute. The actual support depends on the PMD.
+	 *
+	 * - When set, the user should be responsible for inserting the hairpin
+	 *   TX part flows and removing them.
+	 * - When clear, the PMD will try to handle the TX part of the flows,
+	 *   e.g., by splitting one flow into two parts.
+	 */
+	uint32_t tx_explicit:1;
+
+	/**
+	 * Manually bind hairpin queues. One hairpin pair of queues should have
+	 * the same attribute. The actual support depends on the PMD.
+	 *
+	 * - When set, to enable hairpin, the user should call the hairpin bind
+	 *   API after all the queues are set up properly and the ports are
+	 *   started. Also, the hairpin unbind API should be called accordingly
+	 *   before stopping a port that with hairpin configured.
+	 * - When clear, the PMD will try to enable the hairpin with the queues
+	 *   configured automatically during port start.
+	 */
+	uint32_t manual_bind:1;
+	uint32_t reserved:14; /**< Reserved bits. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 2/5] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-13 16:19           ` Bing Zhao
  2020-10-14 15:02             ` Thomas Monjalon
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 5/5] app/testpmd: change hairpin queues setup Bing Zhao
  4 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

After hairpin queues are configured, in general, the application will
maintain the ports topology and even the queues configuration for
the hairpin. But sometimes it will not.

If there is no hot-plug, it is easy to bind and unbind hairpin among
all the ports. The application can just connect or disconnect the
hairpin egress ports to/from all the probed ingress ports. Then all
the connections could be handled properly.

But with hot-plug / hot-unplug, one port could be probed and removed
dynamically. With two ports hairpin, all the connections from and to
this port should be handled after start(bind) or before stop(unbind).
It is necessary to know the hairpin topology with this port.

This API will return the ports list with the actual peer ports number
after configuration. Either peer RX or TX ports will be gotten with
this function call.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v4: add release notes update
v3:
1. change the direction from bool to unsigned int type
2. add length to protect the array from getting corrupted
---
 doc/guides/rel_notes/release_20_11.rst   |  1 +
 lib/librte_ethdev/rte_ethdev.c           | 24 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 29 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 33 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  1 +
 5 files changed, 88 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index a1e20a6..f4f654f 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -64,6 +64,7 @@ New Features
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
   Hairpin TX part flow rules can be inserted explicitly.
+  New API is added to get the hairpin peer ports list.
 
 * **Updated Broadcom bnxt driver.**
 
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 14b9f3a..1cdfe4d 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2208,6 +2208,30 @@ struct rte_eth_dev *
 	return ret;
 }
 
+int
+rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+			       size_t len, uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	if (!peer_ports || !len)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+				-ENOTSUP);
+
+	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+						      len, direction);
+	if (ret < 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports",
+			       cur_port, direction ? "RX" : "TX");
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 938df08..13f6fc0 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2186,6 +2186,35 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
  *
+ * Get all the hairpin peer RX / TX ports of the current port.
+ * The caller should ensure that the array is large enough to save the ports
+ * list.
+ *
+ * @param cur_port
+ *   The current port identifier of the Ethernet device.
+ * @param peer_ports
+ *   Pointer to the array to store the peer ports list.
+ * @param len
+ *   Length of the array to store the port identifiers.
+ * @param direction
+ *   Current port to peer port direction
+ *   positive - current used as TX to get all peer RX ports.
+ *   zero - current used as RX to get all peer TX ports.
+ *
+ * @return
+ *   - (0 or positive) actual peer ports number.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
+				   size_t len, uint32_t direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  * It is only allowed to call this API after all hairpin queues are configured
  * properly and the devices of TX and peer RX are in started state.
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 26ded15..3c3c859 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -657,6 +657,37 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 
 /**
  * @internal
+ * Get all hairpin TX/RX peer ports of the current device, if any.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param peer_ports
+ *   array to save the ports list.
+ * @param len
+ *   array length.
+ * @param direction
+ *   value to decide the current to peer direction
+ *   positive - used as TX to get all peer RX ports.
+ *   zero - used as RX to get all peer TX ports.
+ *
+ * @return
+ *   Negative errno value on error, 0 or positive on success.
+ *
+ * @retval 0
+ *   Success, no peer ports.
+ * @retval >0
+ *   Actual number of the peer ports.
+ * @retval -ENOTSUP
+ *   Get peer ports API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ */
+typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
+					uint16_t *peer_ports, size_t len,
+					uint32_t direction);
+
+/**
+ * @internal
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  *
  * @param dev
@@ -849,6 +880,8 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	hairpin_get_peer_ports_t hairpin_get_peer_ports;
+	/**< Get hairpin peer ports list. */
 	eth_hairpin_bind_t hairpin_bind;
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 1aee03a..e644391 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -228,6 +228,7 @@ EXPERIMENTAL {
 
 	# added in 20.11
 	rte_eth_hairpin_bind;
+	rte_eth_hairpin_get_peer_ports;
 	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v4 4/5] ethdev: add APIs for hairpin queue operation
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
                             ` (2 preceding siblings ...)
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-13 16:19           ` Bing Zhao
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 5/5] app/testpmd: change hairpin queues setup Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between TX and RX queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the TX, RX queues separately.
The peer unbind API resets the current queue configuration and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect TX, RX queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v3: change the direction from bool to unsigned int type
---
 lib/librte_ethdev/rte_ethdev.c           |  55 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 102 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 160 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 1cdfe4d..c6e6850 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5564,6 +5564,61 @@ enum rte_eth_switch_domain_state {
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_unbind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 3c3c859..d54e605 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -734,6 +737,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -886,6 +904,12 @@ struct eth_dev_ops {
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin TX queues from the peer port RX queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	/**< Pass the current queue info and get the peer queue info. */
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	/**< Set up the connection between the pair of hairpin queues. */
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
+	/**< Disconnect the hairpin queues of a pair from each other. */
 };
 
 /**
@@ -1241,6 +1265,84 @@ typedef int (*ethdev_bus_specific_init)(struct rte_eth_dev *ethdev,
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @internal
+ * Pass the current hairpin queue HW and/or SW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  positive - pass TX queue information and get peer RX queue information
+ *  zero - pass RX queue information and get peer TX queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction);
+
+/**
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connection.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - bind current TX queue to peer RX queue
+ *  zero - bind current RX queue to peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction);
+
+/**
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - unbind current TX queue from peer RX queue
+ *  zero - unbind current RX queue from peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index e644391..7402e9a 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -254,6 +254,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 };
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v4 5/5] app/testpmd: change hairpin queues setup
  2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
                             ` (3 preceding siblings ...)
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-13 16:19           ` Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-13 16:19 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide a more flexible configuration.
This parameter should be used when `hairpinq` is specified in the
command line.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port RX queue will be connected to the current port TX
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of the probed ports is odd, then the last one will be
paired to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
TX flow mode.

e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"

If not set, default value zero will be used and the behavior will
try to get aligned with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v4: squash testpmd guide update
v2: move the hairpin bind/unbind into start/stop to support hot-plug
    and hot-unplug
---
 app/test-pmd/parameters.c             |  15 ++++
 app/test-pmd/testpmd.c                | 125 ++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h                |   2 +
 doc/guides/testpmd_app_ug/run_app.rst |   8 +++
 4 files changed, 146 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 15ce8c1..e231b46 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -222,6 +222,9 @@
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -645,6 +648,7 @@
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1113,6 +1117,17 @@
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index ccba71c..baf21df 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ struct fwd_engine * fwd_engines[] = {
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ struct extmem_param {
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,49 @@ struct extmem_param {
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			/* Last port will be the peer RX port of the first. */
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2419,10 @@ struct extmem_param {
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2449,12 @@ struct extmem_param {
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	portid_t pl[RTE_MAX_ETHPORTS];
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	uint16_t cnt_pi = 0;
+	uint16_t cfg_pi = 0;
+	int peer_pi;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,7 +2594,7 @@ struct extmem_param {
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
@@ -2557,6 +2607,9 @@ struct extmem_param {
 				pi);
 		}
 
+		p_pi = pi;
+		cnt_pi++;
+
 		/* start port */
 		if (rte_eth_dev_start(pi) < 0) {
 			printf("Fail to start port %d\n", pi);
@@ -2581,6 +2634,8 @@ struct extmem_param {
 
 		/* at least one port started, need checking link status */
 		need_check_link_status = 1;
+
+		pl[cfg_pi++] = pi;
 	}
 
 	if (need_check_link_status == 1 && !no_link_check)
@@ -2588,6 +2643,50 @@ struct extmem_param {
 	else if (need_check_link_status == 0)
 		printf("Please stop the ports first\n");
 
+	if (hairpin_mode & 0xf) {
+		uint16_t i;
+		int j;
+
+		/* bind all started hairpin ports */
+		for (i = 0; i < cfg_pi; i++) {
+			pi = pl[i];
+			/* bind current TX to all peer RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 1);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       pi, peer_pl[j],
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+			/* bind all peer TX to current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s",
+					       peer_pl[j], pi,
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+		}
+	}
+
 	printf("Done\n");
 	return 0;
 }
@@ -2598,6 +2697,8 @@ struct extmem_param {
 	portid_t pi;
 	struct rte_port *port;
 	int need_check_link_status = 0;
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	int peer_pi;
 
 	if (dcb_test) {
 		dcb_test = 0;
@@ -2628,6 +2729,22 @@ struct extmem_param {
 						RTE_PORT_HANDLING) == 0)
 			continue;
 
+		if (hairpin_mode & 0xf) {
+			int j;
+
+			rte_eth_hairpin_unbind(pi, RTE_MAX_ETHPORTS);
+			/* unbind all peer TX from current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				continue;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				rte_eth_hairpin_unbind(peer_pl[j], pi);
+			}
+		}
+
 		rte_eth_dev_stop(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9a29d7a..f2b604c 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -398,6 +398,8 @@ struct queue_stats_mappings {
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index ec085c2..b5f5b98 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -503,3 +503,11 @@ The command line options are:
 *   ``--record-burst-stats``
 
     Enable display of RX and TX burst stats.
+
+*   ``--hairpin-mode=0xXX``
+
+    Set the hairpin port mode with bitmask, only valid when hairpin queues number is set.
+    bit 4 - explicit TX flow rule
+    bit 1 - two hairpin ports paired
+    bit 0 - two hairpin ports loop
+    The default value is 0. Hairpin will use single port mode and implicit TX flow mode.
-- 
1.8.3.1


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

* Re: [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-14 14:35           ` Thomas Monjalon
  2020-10-15  2:56             ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-14 14:35 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

Hi,
Cosmetic comments below:

08/10/2020 14:05, Bing Zhao:
> +int
> +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);

It should be -ENODEV

> +	dev = &rte_eth_devices[tx_port];
> +	if (!dev->data->dev_started) {
> +		RTE_ETHDEV_LOG(ERR, "TX port %d is not started", tx_port);
> +		return -EBUSY;
> +	}
> +
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
> +	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
> +	if (ret)
> +		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
> +			       "to RX %d (%d - all ports)", tx_port,
> +			       rx_port, RTE_MAX_ETHPORTS);

Looks like \n is missing.

> +
> +	return ret;
> +}
> +
> +int
> +rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)

Same comments as for bind function.

[...]
>  /**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
> + *
> + * Bind all hairpin TX queues of one port to the RX queues of the peer port.
> + * It is only allowed to call this API after all hairpin queues are configured
> + * properly and the devices of TX and peer RX are in started state.

"call this API" -> "call this function"

"devices of TX" is a strange wording.
I would make it simpler:
"the devices of TX and peer RX" -> "the devices"

> + *
> + * @param tx_port
> + *   The TX port identifier of the Ethernet device.

A device does not have a TX port.
I think you mean "The identifier of the TX port."

> + * @param rx_port
> + *   The peer RX port identifier of the Ethernet device.

Remove "of the Ethernet device"

> + *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
> + *   RX port ID could have the same value with TX port ID.

"same value as"

> + *
> + * @return
> + *   - (0) if successful.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-EBUSY) if device is not in started state.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.

Please add ENODEV case, maybe instead of EINVAL.

> + */
> +__rte_experimental
> +int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
> + *
> + * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
> + * This should be called before closing the TX or RX devices (optional). After

Split the line after the end of sentence and start next one on a fresh line.

> + * unbind the hairpin ports pair, it is allowed to bind them again.

"unbind" -> "unbinding"

> + * Changing queues configuration should be after stopping the device.
> + *
> + * @param tx_port
> + *   The TX port identifier of the Ethernet device.
> + * @param rx_port
> + *   The peer RX port identifier of the Ethernet device.
> + *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
> + *   RX port ID could have the same value with TX port ID.
> + *
> + * @return
> + *   - (0) if successful.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-EBUSY) if device is in stopped state.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.

Same comments as for the bind function.

> + */
> +__rte_experimental
> +int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);




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

* Re: [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-14 14:43             ` Thomas Monjalon
  2020-10-15  2:59               ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-14 14:43 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

Sorry I messed up versions and did a review on the v3,
which is still relevant for v4. Please check:
http://mails.dpdk.org/archives/dev/2020-October/186614.html



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

* Re: [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list
  2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-14 15:02             ` Thomas Monjalon
  2020-10-15  4:03               ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-14 15:02 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

13/10/2020 18:19, Bing Zhao:
> --- a/lib/librte_ethdev/rte_ethdev.c
> +++ b/lib/librte_ethdev/rte_ethdev.c
> @@ -2208,6 +2208,30 @@ struct rte_eth_dev *
>  	return ret;
>  }
>  
> +int
> +rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
> +			       size_t len, uint32_t direction)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	if (!peer_ports || !len)
> +		return -EINVAL;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);

should be -ENODEV

> +	dev = &rte_eth_devices[cur_port];
> +	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
> +				-ENOTSUP);
> +
> +	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
> +						      len, direction);
> +	if (ret < 0)
> +		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports",
> +			       cur_port, direction ? "RX" : "TX");

Missing \n

> +
> +	return ret;
> +}
> --- a/lib/librte_ethdev/rte_ethdev.h
> +++ b/lib/librte_ethdev/rte_ethdev.h
>   * @warning
>   * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
>   *
> + * Get all the hairpin peer RX / TX ports of the current port.
> + * The caller should ensure that the array is large enough to save the ports
> + * list.
> + *
> + * @param cur_port
> + *   The current port identifier of the Ethernet device.

Why current? In general we use "port_id".

> + * @param peer_ports
> + *   Pointer to the array to store the peer ports list.
> + * @param len
> + *   Length of the array to store the port identifiers.
> + * @param direction
> + *   Current port to peer port direction
> + *   positive - current used as TX to get all peer RX ports.
> + *   zero - current used as RX to get all peer TX ports.
> + *
> + * @return
> + *   - (0 or positive) actual peer ports number.
> + *   - (-EINVAL) if bad parameter.
> + *   - (-ENOTSUP) if hardware doesn't support.
> + *   - Others detailed errors from PMD drivers.

Please add ENODEV

> + */
> +__rte_experimental
> +int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t *peer_ports,
> +				   size_t len, uint32_t direction);




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

* Re: [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-14 14:35           ` Thomas Monjalon
@ 2020-10-15  2:56             ` Bing Zhao
  2020-10-15  7:31               ` Thomas Monjalon
  0 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  2:56 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Wednesday, October 14, 2020 10:36 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and
> unbind APIs
> 
> External email: Use caution opening links or attachments
> 
> 
> Hi,
> Cosmetic comments below:
> 
> 08/10/2020 14:05, Bing Zhao:
> > +int
> > +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port) {
> > +     struct rte_eth_dev *dev;
> > +     int ret;
> > +
> > +     RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> 
> It should be -ENODEV

Got it, changed. BTW, I checked the ethdev and it seems some functions are using "EINVAL" and some are using "ENODEV". So should all of these functions use "ENODEV"?

> 
> > +     dev = &rte_eth_devices[tx_port];
> > +     if (!dev->data->dev_started) {
> > +             RTE_ETHDEV_LOG(ERR, "TX port %d is not started",
> tx_port);
> > +             return -EBUSY;
> > +     }
> > +
> > +     RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -
> ENOTSUP);
> > +     ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
> > +     if (ret)
> > +             RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
> > +                            "to RX %d (%d - all ports)", tx_port,
> > +                            rx_port, RTE_MAX_ETHPORTS);
> 
> Looks like \n is missing.

Fixed, thanks.

> 
> > +
> > +     return ret;
> > +}
> > +
> > +int
> > +rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
> 
> Same comments as for bind function.

Done.

> 
> [...]
> >  /**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > + notice
> > + *
> > + * Bind all hairpin TX queues of one port to the RX queues of the
> peer port.
> > + * It is only allowed to call this API after all hairpin queues
> are
> > + configured
> > + * properly and the devices of TX and peer RX are in started
> state.
> 
> "call this API" -> "call this function"

Done.

> 
> "devices of TX" is a strange wording.
> I would make it simpler:
> "the devices of TX and peer RX" -> "the devices"
> 
> > + *
> > + * @param tx_port
> > + *   The TX port identifier of the Ethernet device.
> 
> A device does not have a TX port.
> I think you mean "The identifier of the TX port."

Done.

> 
> > + * @param rx_port
> > + *   The peer RX port identifier of the Ethernet device.
> 
> Remove "of the Ethernet device"

Done.

> 
> > + *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
> > + *   RX port ID could have the same value with TX port ID.
> 
> "same value as"

Done.

> 
> > + *
> > + * @return
> > + *   - (0) if successful.
> > + *   - (-EINVAL) if bad parameter.
> > + *   - (-EBUSY) if device is not in started state.
> > + *   - (-ENOTSUP) if hardware doesn't support.
> > + *   - Others detailed errors from PMD drivers.
> 
> Please add ENODEV case, maybe instead of EINVAL.

Replaced. PMD may return -EINVAL but it is not necessary so I will not list it here anymore.

> 
> > + */
> > +__rte_experimental
> > +int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change, or be removed, without
> prior
> > +notice
> > + *
> > + * Unbind all hairpin TX queues of one port from the RX queues of
> the peer port.
> > + * This should be called before closing the TX or RX devices
> > +(optional). After
> 
> Split the line after the end of sentence and start next one on a
> fresh line.
> 

Done

> > + * unbind the hairpin ports pair, it is allowed to bind them
> again.
> 
> "unbind" -> "unbinding"

Done

> 
> > + * Changing queues configuration should be after stopping the
> device.
> > + *
> > + * @param tx_port
> > + *   The TX port identifier of the Ethernet device.
> > + * @param rx_port
> > + *   The peer RX port identifier of the Ethernet device.
> > + *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
> > + *   RX port ID could have the same value with TX port ID.
> > + *
> > + * @return
> > + *   - (0) if successful.
> > + *   - (-EINVAL) if bad parameter.
> > + *   - (-EBUSY) if device is in stopped state.
> > + *   - (-ENOTSUP) if hardware doesn't support.
> > + *   - Others detailed errors from PMD drivers.
> 
> Same comments as for the bind function.

Done.

> 
> > + */
> > +__rte_experimental
> > +int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
> 
> 

Thanks a lot


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

* Re: [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-14 14:43             ` Thomas Monjalon
@ 2020-10-15  2:59               ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  2:59 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

All your comments are confirmed and addressed.

Thanks

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Wednesday, October 14, 2020 10:43 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and
> unbind APIs
> 
> External email: Use caution opening links or attachments
> 
> 
> Sorry I messed up versions and did a review on the v3, which is
> still relevant for v4. Please check:
> https://nam11.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmai
> ls.dpdk.org%2Farchives%2Fdev%2F2020-
> October%2F186614.html&amp;data=02%7C01%7Cbingz%40nvidia.com%7C88b908
> cfd5f14532065908d8704f9e8c%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C
> 0%7C637382834554491809&amp;sdata=2Dm0m5vty8qm%2BgeDEBQUFi3JR7OjgUa8Y
> d%2FxQheqgOA%3D&amp;reserved=0
> 


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

* Re: [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list
  2020-10-14 15:02             ` Thomas Monjalon
@ 2020-10-15  4:03               ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  4:03 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Wednesday, October 14, 2020 11:02 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get
> hairpin peer ports list
> 
> External email: Use caution opening links or attachments
> 
> 
> 13/10/2020 18:19, Bing Zhao:
> > --- a/lib/librte_ethdev/rte_ethdev.c
> > +++ b/lib/librte_ethdev/rte_ethdev.c
> > @@ -2208,6 +2208,30 @@ struct rte_eth_dev *
> >       return ret;
> >  }
> >
> > +int
> > +rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t
> *peer_ports,
> > +                            size_t len, uint32_t direction) {
> > +     struct rte_eth_dev *dev;
> > +     int ret;
> > +
> > +     if (!peer_ports || !len)
> > +             return -EINVAL;
> > +
> > +     RTE_ETH_VALID_PORTID_OR_ERR_RET(cur_port, -EINVAL);
> 
> should be -ENODEV

Fixed

> 
> > +     dev = &rte_eth_devices[cur_port];
> > +     RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops-
> >hairpin_get_peer_ports,
> > +                             -ENOTSUP);
> > +
> > +     ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev,
> peer_ports,
> > +                                                   len,
> direction);
> > +     if (ret < 0)
> > +             RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin
> peer %s ports",
> > +                            cur_port, direction ? "RX" : "TX");
> 
> Missing \n

Thanks, done.

> 
> > +
> > +     return ret;
> > +}
> > --- a/lib/librte_ethdev/rte_ethdev.h
> > +++ b/lib/librte_ethdev/rte_ethdev.h
> >   * @warning
> >   * @b EXPERIMENTAL: this API may change, or be removed, without
> prior notice
> >   *
> > + * Get all the hairpin peer RX / TX ports of the current port.
> > + * The caller should ensure that the array is large enough to
> save
> > + the ports
> > + * list.
> > + *
> > + * @param cur_port
> > + *   The current port identifier of the Ethernet device.
> 
> Why current? In general we use "port_id".

Changed to port_id

> 
> > + * @param peer_ports
> > + *   Pointer to the array to store the peer ports list.
> > + * @param len
> > + *   Length of the array to store the port identifiers.
> > + * @param direction
> > + *   Current port to peer port direction
> > + *   positive - current used as TX to get all peer RX ports.
> > + *   zero - current used as RX to get all peer TX ports.
> > + *
> > + * @return
> > + *   - (0 or positive) actual peer ports number.
> > + *   - (-EINVAL) if bad parameter.
> > + *   - (-ENOTSUP) if hardware doesn't support.
> > + *   - Others detailed errors from PMD drivers.
> 
> Please add ENODEV

Done.

> 
> > + */
> > +__rte_experimental
> > +int rte_eth_hairpin_get_peer_ports(uint16_t cur_port, uint16_t
> *peer_ports,
> > +                                size_t len, uint32_t direction);
> 
> 

Thanks a lot~

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

* [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
                       ` (5 preceding siblings ...)
  2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
@ 2020-10-15  5:35     ` Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
                         ` (4 more replies)
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
  7 siblings, 5 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new API to get the hairpin peer ports list.
3. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
4. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
5. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.
6. documents update.

---
v5:
1. Change EINVAL to ENODEV if port id is invalid
2. Description fixes and other minor changes
v4:
1. squash documents update into patch
2. add more description of the hairpin conf attributes
v3:
1. add length to protect the pointer to the array from getting corrupted
2. change the direction from bool to unsigned int
v2:
1. add documents update
2. remove all peer ports logic from rte API
3. conf structure optimizing
4. new API to get the peer ports and testpmd change to support
   hot-plug / unplug case
---

Bing Zhao (5):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add API to get hairpin peer ports list
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   | 125 ++++++++++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 doc/guides/prog_guide/rte_flow.rst       |   3 +
 doc/guides/rel_notes/release_20_11.rst   |  11 ++
 doc/guides/testpmd_app_ug/run_app.rst    |   8 ++
 lib/librte_ethdev/rte_ethdev.c           | 133 +++++++++++++++++++++-
 lib/librte_ethdev/rte_ethdev.h           | 109 +++++++++++++++++-
 lib/librte_ethdev/rte_ethdev_driver.h    | 186 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   6 +
 10 files changed, 589 insertions(+), 9 deletions(-)

-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-15  5:35       ` Bing Zhao
  2020-10-15 10:34         ` Thomas Monjalon
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config Bing Zhao
                         ` (3 subsequent siblings)
  4 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin TX and RX queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the TX queue and its RX peer queue. The
binding process that connected the TX and RX queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin TX and RX peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all TX queues of the egress port to the RX queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration, especially in the hot unplug case.
mode is described.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v5:
  * Change EINVAL to ENODEV
  * add newline character in the end of log line
  * descriptions update
v4: squash release notes update
v2: remove the all peer ports logic from rte API
---
 doc/guides/rel_notes/release_20_11.rst   |  4 +++
 lib/librte_ethdev/rte_ethdev.c           | 46 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  2 ++
 5 files changed, 156 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 30db8f2..0a9ae54 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -67,6 +67,10 @@ New Features
   Added the FEC API which provides functions for query FEC capabilities and
   current FEC mode from device. Also, API for configuring FEC mode is also provided.
 
+* **Updated the ethdev library to support hairpin between two ports.**
+
+  New APIs are introduced to support binding / unbinding 2 ports hairpin.
+
 * **Updated Broadcom bnxt driver.**
 
   Updated the Broadcom bnxt driver with new features and improvements, including:
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 5b7979a..150c555 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2212,6 +2212,52 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is not started\n", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
+			       "to RX %d (%d - all ports)\n", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+	dev = &rte_eth_devices[tx_port];
+	if (!dev->data->dev_started) {
+		RTE_ETHDEV_LOG(ERR, "TX port %d is already stopped\n", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	if (ret)
+		RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin "
+			       "TX %d from RX %d (%d - all ports)\n", tx_port,
+			       rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index f4cc591..3bdb189 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2158,6 +2158,58 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ * It is only allowed to call this function after all hairpin queues are
+ * configured properly and the devices are in started state.
+ *
+ * @param tx_port
+ *   The identifier of the TX port.
+ * @param rx_port
+ *   The identifier of peer RX port.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   RX port ID could have the same value as TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if TX port ID is invalid.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ * This should be called before closing the TX or RX devices, if the bind
+ * function is called before.
+ * After unbinding the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device(s).
+ *
+ * @param tx_port
+ *   The identifier of the TX port.
+ * @param rx_port
+ *   The identifier of peer RX port.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   RX port ID could have the same value as TX port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if TX port ID is invalid.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 35cc4fb..26ded15 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -656,6 +656,54 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 typedef int (*eth_fec_set_t)(struct rte_eth_dev *dev, uint32_t fec_capa);
 
 /**
+ * @internal
+ * Bind all hairpin TX queues of one port to the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin TX queues of one port from the RX queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer RX port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, unbind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -801,6 +849,10 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin TX queues from the peer port RX queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index f8a0945..1aee03a 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 	rte_eth_fec_get_capability;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-15  5:35       ` Bing Zhao
  2020-10-15 10:46         ` Thomas Monjalon
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
                         ` (2 subsequent siblings)
  4 siblings, 1 reply; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of the hairpin queue
configuration structure will be added.

`tx_explicit` means if the application itself will insert the TX part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin TX queue and peer RX queue will be
bound automatically during the device start stage.

Different TX and RX queue pairs could have different values, but it
is highly recommended that all paired queues between one egress and
its peer ingress ports have the same values, in order not to bring
any chaos to the system. The actual support of these attribute
parameters will be checked and decided by the PMD drivers.

In the single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means that no bind API
needs to be called and no TX flow rules need to be inserted manually
by the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v4: squash document update and more info for the two new attributes
v2: optimize the structure and remove unused macros
---
 doc/guides/prog_guide/rte_flow.rst     |  3 +++
 doc/guides/rel_notes/release_20_11.rst |  6 ++++++
 lib/librte_ethdev/rte_ethdev.c         |  8 ++++----
 lib/librte_ethdev/rte_ethdev.h         | 27 ++++++++++++++++++++++++++-
 4 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index f26a6c2..c6f828a 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -2592,6 +2592,9 @@ set, unpredictable value will be seen depending on driver implementation. For
 loopback/hairpin packet, metadata set on Rx/Tx may or may not be propagated to
 the other path depending on HW capability.
 
+In hairpin case with TX explicit flow mode, metadata could (not mandatory) be
+used to connect the RX and TX flows if it can be propagated from RX to TX path.
+
 .. _table_rte_flow_action_set_meta:
 
 .. table:: SET_META
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 0a9ae54..2e7dc2d 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -70,6 +70,7 @@ New Features
 * **Updated the ethdev library to support hairpin between two ports.**
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
+  Hairpin TX part flow rules can be inserted explicitly.
 
 * **Updated Broadcom bnxt driver.**
 
@@ -355,6 +356,11 @@ ABI Changes
 
   * ``ethdev`` internal functions are marked with ``__rte_internal`` tag.
 
+  * ``struct rte_eth_hairpin_conf`` has two new members:
+
+    * ``uint32_t tx_explicit:1;``
+    * ``uint32_t manual_bind:1;``
+
 
 Known Issues
 ------------
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 150c555..3cde7a7 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2004,13 +2004,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Rx queue(=%u), 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",
+			"Invalid value for number of peers for Rx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
@@ -2175,13 +2175,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
 			conf->peer_count, cap.max_tx_2_rx);
 		return -EINVAL;
 	}
 	if (conf->peer_count == 0) {
 		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
+			"Invalid value for number of peers for Tx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 3bdb189..dabbbd4 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1045,7 +1045,32 @@ struct rte_eth_hairpin_peer {
  * A structure used to configure hairpin binding.
  */
 struct rte_eth_hairpin_conf {
-	uint16_t peer_count; /**< The number of peers. */
+	uint32_t peer_count:16; /**< The number of peers. */
+
+	/**
+	 * Explicit TX flow rule mode. One hairpin pair of queues should have
+	 * the same attribute. The actual support depends on the PMD.
+	 *
+	 * - When set, the user should be responsible for inserting the hairpin
+	 *   TX part flows and removing them.
+	 * - When clear, the PMD will try to handle the TX part of the flows,
+	 *   e.g., by splitting one flow into two parts.
+	 */
+	uint32_t tx_explicit:1;
+
+	/**
+	 * Manually bind hairpin queues. One hairpin pair of queues should have
+	 * the same attribute. The actual support depends on the PMD.
+	 *
+	 * - When set, to enable hairpin, the user should call the hairpin bind
+	 *   API after all the queues are set up properly and the ports are
+	 *   started. Also, the hairpin unbind API should be called accordingly
+	 *   before stopping a port that with hairpin configured.
+	 * - When clear, the PMD will try to enable the hairpin with the queues
+	 *   configured automatically during port start.
+	 */
+	uint32_t manual_bind:1;
+	uint32_t reserved:14; /**< Reserved bits. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v5 3/5] ethdev: add API to get hairpin peer ports list
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-15  5:35       ` Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 5/5] app/testpmd: change hairpin queues setup Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

After hairpin queues are configured, in general, the application will
maintain the ports topology and even the queues configuration for
the hairpin. But sometimes it will not.

If there is no hot-plug, it is easy to bind and unbind hairpin among
all the ports. The application can just connect or disconnect the
hairpin egress ports to/from all the probed ingress ports. Then all
the connections could be handled properly.

But with hot-plug / hot-unplug, one port could be probed and removed
dynamically. With two ports hairpin, all the connections from and to
this port should be handled after start(bind) or before stop(unbind).
It is necessary to know the hairpin topology with this port.

This function will return the ports list with the actual peer ports
number after configuration. Either peer RX or TX ports will be
gotten with this function call.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v5:
  * change EINVAL to ENODEV
  * add newline character
v4: add release notes update
v3:
  * change the direction from bool to unsigned int type
  * add length to protect the array from getting corrupted
---
 doc/guides/rel_notes/release_20_11.rst   |  1 +
 lib/librte_ethdev/rte_ethdev.c           | 24 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 30 +++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 33 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  1 +
 5 files changed, 89 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 2e7dc2d..0596c66 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -71,6 +71,7 @@ New Features
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
   Hairpin TX part flow rules can be inserted explicitly.
+  New API is added to get the hairpin peer ports list.
 
 * **Updated Broadcom bnxt driver.**
 
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 3cde7a7..a95d294 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2258,6 +2258,30 @@ struct rte_eth_dev *
 	return ret;
 }
 
+int
+rte_eth_hairpin_get_peer_ports(uint16_t port_id, uint16_t *peer_ports,
+			       size_t len, uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	if (!peer_ports || !len)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+				-ENOTSUP);
+
+	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+						      len, direction);
+	if (ret < 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports\n",
+			       port_id, direction ? "RX" : "TX");
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index dabbbd4..bf81dfe 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2186,6 +2186,36 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
  *
+ * Get all the hairpin peer RX / TX ports of the current port.
+ * The caller should ensure that the array is large enough to save the ports
+ * list.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param peer_ports
+ *   Pointer to the array to store the peer ports list.
+ * @param len
+ *   Length of the array to store the port identifiers.
+ * @param direction
+ *   Current port to peer port direction
+ *   positive - current used as TX to get all peer RX ports.
+ *   zero - current used as RX to get all peer TX ports.
+ *
+ * @return
+ *   - (0 or positive) actual peer ports number.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENODEV) if *port_id* invalid
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_get_peer_ports(uint16_t port_id, uint16_t *peer_ports,
+				   size_t len, uint32_t direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  * It is only allowed to call this function after all hairpin queues are
  * configured properly and the devices are in started state.
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 26ded15..3c3c859 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -657,6 +657,37 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 
 /**
  * @internal
+ * Get all hairpin TX/RX peer ports of the current device, if any.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param peer_ports
+ *   array to save the ports list.
+ * @param len
+ *   array length.
+ * @param direction
+ *   value to decide the current to peer direction
+ *   positive - used as TX to get all peer RX ports.
+ *   zero - used as RX to get all peer TX ports.
+ *
+ * @return
+ *   Negative errno value on error, 0 or positive on success.
+ *
+ * @retval 0
+ *   Success, no peer ports.
+ * @retval >0
+ *   Actual number of the peer ports.
+ * @retval -ENOTSUP
+ *   Get peer ports API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ */
+typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
+					uint16_t *peer_ports, size_t len,
+					uint32_t direction);
+
+/**
+ * @internal
  * Bind all hairpin TX queues of one port to the RX queues of the peer port.
  *
  * @param dev
@@ -849,6 +880,8 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	hairpin_get_peer_ports_t hairpin_get_peer_ports;
+	/**< Get hairpin peer ports list. */
 	eth_hairpin_bind_t hairpin_bind;
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 1aee03a..e644391 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -228,6 +228,7 @@ EXPERIMENTAL {
 
 	# added in 20.11
 	rte_eth_hairpin_bind;
+	rte_eth_hairpin_get_peer_ports;
 	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v5 4/5] ethdev: add APIs for hairpin queue operation
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
                         ` (2 preceding siblings ...)
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-15  5:35       ` Bing Zhao
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 5/5] app/testpmd: change hairpin queues setup Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between TX and RX queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the TX, RX queues separately.
The peer unbind API resets the current queue configuration and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect TX, RX queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v5: commnets update
v3: change the direction from bool to unsigned int type
---
 lib/librte_ethdev/rte_ethdev.c           |  55 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 101 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 159 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index a95d294..8358337 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5624,6 +5624,61 @@ enum rte_eth_switch_domain_state {
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_unbind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 3c3c859..1744d27 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -734,6 +737,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -886,6 +904,12 @@ struct eth_dev_ops {
 	/**< Bind all hairpin TX queues of device to the peer port RX queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin TX queues from the peer port RX queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	/**< Pass the current queue info and get the peer queue info. */
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	/**< Set up the connection between the pair of hairpin queues. */
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
+	/**< Disconnect the hairpin queues of a pair from each other. */
 };
 
 /**
@@ -1241,6 +1265,83 @@ typedef int (*ethdev_bus_specific_init)(struct rte_eth_dev *ethdev,
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @internal
+ * Pass the current hairpin queue HW and/or SW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  positive - pass TX queue information and get peer RX queue information
+ *  zero - pass RX queue information and get peer TX queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction);
+
+/**
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connections.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - bind current TX queue to peer RX queue
+ *  zero - bind current RX queue to peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction);
+
+/**
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to destroy the connection.
+ *  positive - unbind current TX queue from peer RX queue
+ *  zero - unbind current RX queue from peer TX queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index e644391..7402e9a 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -254,6 +254,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 };
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v5 5/5] app/testpmd: change hairpin queues setup
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
                         ` (3 preceding siblings ...)
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-15  5:35       ` Bing Zhao
  4 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15  5:35 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide a more flexible configuration.
This parameter should be used when `hairpinq` is specified in the
command line.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port RX queue will be connected to the current port TX
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of the probed ports is odd, then the last one will be
paired to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
TX flow mode.

e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"

If not set, default value zero will be used and the behavior will
try to get aligned with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v5: add newline character
v4: squash testpmd guide update
v2: move the hairpin bind/unbind into start/stop to support hot-plug
    and hot-unplug
---
 app/test-pmd/parameters.c             |  15 ++++
 app/test-pmd/testpmd.c                | 125 ++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h                |   2 +
 doc/guides/testpmd_app_ug/run_app.rst |   8 +++
 4 files changed, 146 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 15ce8c1..e231b46 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -222,6 +222,9 @@
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -645,6 +648,7 @@
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1113,6 +1117,17 @@
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index ccba71c..6e07162 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ struct fwd_engine * fwd_engines[] = {
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ struct extmem_param {
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,49 @@ struct extmem_param {
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			/* Last port will be the peer RX port of the first. */
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2419,10 @@ struct extmem_param {
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2449,12 @@ struct extmem_param {
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	portid_t pl[RTE_MAX_ETHPORTS];
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	uint16_t cnt_pi = 0;
+	uint16_t cfg_pi = 0;
+	int peer_pi;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,7 +2594,7 @@ struct extmem_param {
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
@@ -2557,6 +2607,9 @@ struct extmem_param {
 				pi);
 		}
 
+		p_pi = pi;
+		cnt_pi++;
+
 		/* start port */
 		if (rte_eth_dev_start(pi) < 0) {
 			printf("Fail to start port %d\n", pi);
@@ -2581,6 +2634,8 @@ struct extmem_param {
 
 		/* at least one port started, need checking link status */
 		need_check_link_status = 1;
+
+		pl[cfg_pi++] = pi;
 	}
 
 	if (need_check_link_status == 1 && !no_link_check)
@@ -2588,6 +2643,50 @@ struct extmem_param {
 	else if (need_check_link_status == 0)
 		printf("Please stop the ports first\n");
 
+	if (hairpin_mode & 0xf) {
+		uint16_t i;
+		int j;
+
+		/* bind all started hairpin ports */
+		for (i = 0; i < cfg_pi; i++) {
+			pi = pl[i];
+			/* bind current TX to all peer RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 1);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s\n",
+					       pi, peer_pl[j],
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+			/* bind all peer TX to current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+				if (diag < 0) {
+					printf("Error during binding "
+					       "hairpin tx port %u to %u: %s\n",
+					       peer_pl[j], pi,
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+		}
+	}
+
 	printf("Done\n");
 	return 0;
 }
@@ -2598,6 +2697,8 @@ struct extmem_param {
 	portid_t pi;
 	struct rte_port *port;
 	int need_check_link_status = 0;
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	int peer_pi;
 
 	if (dcb_test) {
 		dcb_test = 0;
@@ -2628,6 +2729,22 @@ struct extmem_param {
 						RTE_PORT_HANDLING) == 0)
 			continue;
 
+		if (hairpin_mode & 0xf) {
+			int j;
+
+			rte_eth_hairpin_unbind(pi, RTE_MAX_ETHPORTS);
+			/* unbind all peer TX from current RX */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				continue;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				rte_eth_hairpin_unbind(peer_pl[j], pi);
+			}
+		}
+
 		rte_eth_dev_stop(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9a29d7a..f2b604c 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -398,6 +398,8 @@ struct queue_stats_mappings {
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index ec085c2..b5f5b98 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -503,3 +503,11 @@ The command line options are:
 *   ``--record-burst-stats``
 
     Enable display of RX and TX burst stats.
+
+*   ``--hairpin-mode=0xXX``
+
+    Set the hairpin port mode with bitmask, only valid when hairpin queues number is set.
+    bit 4 - explicit TX flow rule
+    bit 1 - two hairpin ports paired
+    bit 0 - two hairpin ports loop
+    The default value is 0. Hairpin will use single port mode and implicit TX flow mode.
-- 
1.8.3.1


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

* Re: [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs
  2020-10-15  2:56             ` Bing Zhao
@ 2020-10-15  7:31               ` Thomas Monjalon
  0 siblings, 0 replies; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-15  7:31 UTC (permalink / raw)
  To: Bing Zhao
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

15/10/2020 04:56, Bing Zhao:
> From: Thomas Monjalon <thomas@monjalon.net>
> > 08/10/2020 14:05, Bing Zhao:
> > > +int
> > > +rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port) {
> > > +     struct rte_eth_dev *dev;
> > > +     int ret;
> > > +
> > > +     RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -EINVAL);
> > 
> > It should be -ENODEV
> 
> Got it, changed. BTW, I checked the ethdev and it seems some functions are using "EINVAL" and some are using "ENODEV". So should all of these functions use "ENODEV"?

Yes there is a patch pending to return ENODEV everywhere:
https://patches.dpdk.org/patch/80568/



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

* Re: [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-15 10:34         ` Thomas Monjalon
  2020-10-15 11:39           ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-15 10:34 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

15/10/2020 07:35, Bing Zhao:
> v5:
>   * Change EINVAL to ENODEV
>   * add newline character in the end of log line
>   * descriptions update

It looks good.
More minor coding style comments below. With those,
Acked-by: Thomas Monjalon <thomas@monjalon.net>

> +	if (ret)

Coding style recommends explicit comparison with == or !=

> +		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
> +			       "to RX %d (%d - all ports)\n", tx_port,
> +			       rx_port, RTE_MAX_ETHPORTS);

It is preferred not splitting the log lines,
or maybe only after a format specifier, so it can be grepped.
Here the space after %d would be better on the next line.

In general Rx/Tx is preferred over the full capital RX/TX version.

Thanks



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

* Re: [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config
  2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-15 10:46         ` Thomas Monjalon
  2020-10-15 13:45           ` Bing Zhao
  0 siblings, 1 reply; 81+ messages in thread
From: Thomas Monjalon @ 2020-10-15 10:46 UTC (permalink / raw)
  To: Bing Zhao
  Cc: orika, ferruh.yigit, arybchenko, mdr, nhorman, bernard.iremonger,
	beilei.xing, wenzhuo.lu, dev

15/10/2020 07:35, Bing Zhao:
> To support two ports hairpin mode and keep the backward compatibility
> for the application, two new attribute members of the hairpin queue
> configuration structure will be added.
> 
> `tx_explicit` means if the application itself will insert the TX part
> flow rules. If not set, PMD will insert the rules implicitly.
> `manual_bind` means if the hairpin TX queue and peer RX queue will be
> bound automatically during the device start stage.
> 
> Different TX and RX queue pairs could have different values, but it
> is highly recommended that all paired queues between one egress and
> its peer ingress ports have the same values, in order not to bring
> any chaos to the system. The actual support of these attribute
> parameters will be checked and decided by the PMD drivers.
> 
> In the single port hairpin, if both are zero without any setting, the
> behavior will remain the same as before. It means that no bind API
> needs to be called and no TX flow rules need to be inserted manually
> by the application.
> 
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
> ---
> v4: squash document update and more info for the two new attributes
> v2: optimize the structure and remove unused macros
> ---

Acked-by: Thomas Monjalon <thomas@monjalon.net>

Minor comments below.

>  struct rte_eth_hairpin_conf {
> -	uint16_t peer_count; /**< The number of peers. */
> +	uint32_t peer_count:16; /**< The number of peers. */
> +
> +	/**
> +	 * Explicit TX flow rule mode. One hairpin pair of queues should have
> +	 * the same attribute. The actual support depends on the PMD.

The second sentence should be on a separate line.

About the third sentence, implementation is always PMD-specific.
PMD will reject the not supported conf, as usual.
I think this comment is not needed in the API description.

> +	 *
> +	 * - When set, the user should be responsible for inserting the hairpin
> +	 *   TX part flows and removing them.
> +	 * - When clear, the PMD will try to handle the TX part of the flows,
> +	 *   e.g., by splitting one flow into two parts.
> +	 */
> +	uint32_t tx_explicit:1;
> +
> +	/**
> +	 * Manually bind hairpin queues. One hairpin pair of queues should have
> +	 * the same attribute. The actual support depends on the PMD.

Same here

> +	 *
> +	 * - When set, to enable hairpin, the user should call the hairpin bind
> +	 *   API after all the queues are set up properly and the ports are
> +	 *   started. Also, the hairpin unbind API should be called accordingly
> +	 *   before stopping a port that with hairpin configured.
> +	 * - When clear, the PMD will try to enable the hairpin with the queues
> +	 *   configured automatically during port start.
> +	 */
> +	uint32_t manual_bind:1;
> +	uint32_t reserved:14; /**< Reserved bits. */
>  	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
>  };




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

* Re: [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-15 10:34         ` Thomas Monjalon
@ 2020-10-15 11:39           ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 11:39 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Thursday, October 15, 2020 6:34 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs
> 
> External email: Use caution opening links or attachments
> 
> 
> 15/10/2020 07:35, Bing Zhao:
> > v5:
> >   * Change EINVAL to ENODEV
> >   * add newline character in the end of log line
> >   * descriptions update
> 
> It looks good.
> More minor coding style comments below. With those,
> Acked-by: Thomas Monjalon <thomas@monjalon.net>

Thanks for the review and comments

> 
> > +     if (ret)
> 
> Coding style recommends explicit comparison with == or !=

Done

> 
> > +             RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin TX %d "
> > +                            "to RX %d (%d - all ports)\n",
> tx_port,
> > +                            rx_port, RTE_MAX_ETHPORTS);
> 
> It is preferred not splitting the log lines, or maybe only after a
> format specifier, so it can be grepped.
> Here the space after %d would be better on the next line.
> 
> In general Rx/Tx is preferred over the full capital RX/TX version.

Done

> 
> Thanks
> 

BR. Bing


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

* [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports
  2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
                       ` (6 preceding siblings ...)
  2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-15 13:08     ` Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
                         ` (5 more replies)
  7 siblings, 6 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

The patches contain the following changes:
1. new APIs to bind and unbind hairpin ports in manual binding mode.
2. new API to get the hairpin peer ports list.
3. new internal APIs for PMD to pass the queue information and
   configure the queue pair.
4. new attribute members in the hairpin queue configuraiton structure
   to specify the binding mode and enable explicit TX flow mode.
5. Testpmd support to configure the hairpin modes for two ports
   hairpin verification.
6. documents update.

---
v6:
1. Coding style updates
2. Using "Rx" & "Tx" instead of the capital formats
v5:
1. Change EINVAL to ENODEV if port id is invalid
2. Description fixes and other minor changes
v4:
1. squash documents update into patch
2. add more description of the hairpin conf attributes
v3:
1. add length to protect the pointer to the array from getting corrupted
2. change the direction from bool to unsigned int
v2:
1. add documents update
2. remove all peer ports logic from rte API
3. conf structure optimizing
4. new API to get the peer ports and testpmd change to support
   hot-plug / unplug case
---

Bing Zhao (5):
  ethdev: add hairpin bind and unbind APIs
  ethdev: add new attributes to hairpin config
  ethdev: add API to get hairpin peer ports list
  ethdev: add APIs for hairpin queue operation
  app/testpmd: change hairpin queues setup

 app/test-pmd/parameters.c                |  15 +++
 app/test-pmd/testpmd.c                   | 125 ++++++++++++++++++++-
 app/test-pmd/testpmd.h                   |   2 +
 doc/guides/prog_guide/rte_flow.rst       |   3 +
 doc/guides/rel_notes/release_20_11.rst   |  12 ++
 doc/guides/testpmd_app_ug/run_app.rst    |   8 ++
 lib/librte_ethdev/rte_ethdev.c           | 133 +++++++++++++++++++++-
 lib/librte_ethdev/rte_ethdev.h           | 109 +++++++++++++++++-
 lib/librte_ethdev/rte_ethdev_driver.h    | 186 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   6 +
 10 files changed, 590 insertions(+), 9 deletions(-)

-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v6 1/5] ethdev: add hairpin bind and unbind APIs
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
@ 2020-10-15 13:08       ` Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 2/5] ethdev: add new attributes to hairpin config Bing Zhao
                         ` (4 subsequent siblings)
  5 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

In single port hairpin mode, all the hairpin Tx and Rx queues belong
to the same device. After the queues are set up properly, there is
no other dependency between the Tx queue and its Rx peer queue. The
binding process that connected the Tx and Rx queues together from
hardware level will be done automatically during the device start
procedure. Everything required is configured and initialized already
for the binding process.

But in two ports hairpin mode, there will be some cross-dependences
between two different ports. Usually, the ports will be initialized
serially by the main thread but not in parallel. The earlier port
will not be able to enable the bind if the following peer port is
not yet configured with HW resources. What's more, if one port is
detached / attached dynamically, it would introduce more trouble
for the hairpin binding.

To overcome these, new APIs for binding and unbinding are added.
During startup, only the hairpin Tx and Rx peer queues will be set
up. Nothing will be done when starting the device if the queues are
without auto-bind attribute. Only after the required ports pair
started, the `rte_eth_hairpin_bind()` API can be called to bind the
all Tx queues of the egress port to the Rx queues of the peer port.
Then the connection between the egress and ingress ports pair will
be established.

The `rte_eth_hairpin_unbind()` API could be used to disconnect the
egress and the peer ingress ports. This should only be called before
the device is closed if needed. When doing the clean up, all the
egress and ingress pairs related to a single port should be taken
into consideration, especially in the hot unplug case.
mode is described.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
Acked-by: Thomas Monjalon <thomas@monjalon.net>
---
v6:
  * Coding style updates
  * Using "Rx" & "Tx"
v5:
  * Change EINVAL to ENODEV
  * add newline character in the end of log line
  * descriptions update
v4: squash release notes update
v2: remove the all peer ports logic from rte API
---
 doc/guides/rel_notes/release_20_11.rst   |  4 +++
 lib/librte_ethdev/rte_ethdev.c           | 46 ++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 52 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  2 ++
 5 files changed, 156 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index f8686a5..02bf7ca 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -89,6 +89,10 @@ New Features
     as flow action.
   * Added new flow APIs to create/update/destroy/query shared action.
 
+* **Updated the ethdev library to support hairpin between two ports.**
+
+  New APIs are introduced to support binding / unbinding 2 ports hairpin.
+
 * **Updated Broadcom bnxt driver.**
 
   Updated the Broadcom bnxt driver with new features and improvements, including:
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index d9b82df..57cf4a7 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2211,6 +2211,52 @@ struct rte_eth_dev *
 	return eth_err(port_id, ret);
 }
 
+int
+rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+	dev = &rte_eth_devices[tx_port];
+	if (dev->data->dev_started == 0) {
+		RTE_ETHDEV_LOG(ERR, "Tx port %d is not started\n", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_bind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_bind)(dev, rx_port);
+	if (ret != 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to bind hairpin Tx %d"
+			       " to Rx %d (%d - all ports)\n",
+			       tx_port, rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
+int
+rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port, -ENODEV);
+	dev = &rte_eth_devices[tx_port];
+	if (dev->data->dev_started == 0) {
+		RTE_ETHDEV_LOG(ERR, "Tx port %d is already stopped\n", tx_port);
+		return -EBUSY;
+	}
+
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_unbind, -ENOTSUP);
+	ret = (*dev->dev_ops->hairpin_unbind)(dev, rx_port);
+	if (ret != 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to unbind hairpin Tx %d"
+			       " from Rx %d (%d - all ports)\n",
+			       tx_port, rx_port, RTE_MAX_ETHPORTS);
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index a61ca11..10eb626 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2158,6 +2158,58 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
 	 const struct rte_eth_hairpin_conf *conf);
 
 /**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Bind all hairpin Tx queues of one port to the Rx queues of the peer port.
+ * It is only allowed to call this function after all hairpin queues are
+ * configured properly and the devices are in started state.
+ *
+ * @param tx_port
+ *   The identifier of the Tx port.
+ * @param rx_port
+ *   The identifier of peer Rx port.
+ *   RTE_MAX_ETHPORTS is allowed for the traversal of all devices.
+ *   Rx port ID could have the same value as Tx port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if Tx port ID is invalid.
+ *   - (-EBUSY) if device is not in started state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_bind(uint16_t tx_port, uint16_t rx_port);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Unbind all hairpin Tx queues of one port from the Rx queues of the peer port.
+ * This should be called before closing the Tx or Rx devices, if the bind
+ * function is called before.
+ * After unbinding the hairpin ports pair, it is allowed to bind them again.
+ * Changing queues configuration should be after stopping the device(s).
+ *
+ * @param tx_port
+ *   The identifier of the Tx port.
+ * @param rx_port
+ *   The identifier of peer Rx port.
+ *   RTE_MAX_ETHPORTS is allowed for traversal of all devices.
+ *   Rx port ID could have the same value as Tx port ID.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if Tx port ID is invalid.
+ *   - (-EBUSY) if device is in stopped state.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_unbind(uint16_t tx_port, uint16_t rx_port);
+
+/**
  * Return the NUMA socket to which an Ethernet device is connected
  *
  * @param port_id
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 35cc4fb..9a65bd5 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -656,6 +656,54 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 typedef int (*eth_fec_set_t)(struct rte_eth_dev *dev, uint32_t fec_capa);
 
 /**
+ * @internal
+ * Bind all hairpin Tx queues of one port to the Rx queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer Rx port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, bind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is not started.
+ */
+typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
+				uint16_t rx_port);
+
+/**
+ * @internal
+ * Unbind all hairpin Tx queues of one port from the Rx queues of the peer port.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param rx_port
+ *   the peer Rx port.
+ *
+ * @return
+ *   Negative errno value on error, 0 on success.
+ *
+ * @retval 0
+ *   Success, unbind successfully.
+ * @retval -ENOTSUP
+ *   Bind API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ * @retval -EBUSY
+ *   Device is already stopped.
+ */
+typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
+				  uint16_t rx_port);
+
+/**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
 struct eth_dev_ops {
@@ -801,6 +849,10 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	eth_hairpin_bind_t hairpin_bind;
+	/**< Bind all hairpin Tx queues of device to the peer port Rx queues. */
+	eth_hairpin_unbind_t hairpin_unbind;
+	/**< Unbind all hairpin Tx queues from the peer port Rx queues. */
 };
 
 /**
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 31242da..d67c9dc 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -227,6 +227,8 @@ EXPERIMENTAL {
 	rte_tm_wred_profile_delete;
 
 	# added in 20.11
+	rte_eth_hairpin_bind;
+	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
 	rte_eth_fec_get_capability;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v6 2/5] ethdev: add new attributes to hairpin config
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
@ 2020-10-15 13:08       ` Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
                         ` (3 subsequent siblings)
  5 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

To support two ports hairpin mode and keep the backward compatibility
for the application, two new attribute members of the hairpin queue
configuration structure will be added.

`tx_explicit` means if the application itself will insert the Tx part
flow rules. If not set, PMD will insert the rules implicitly.
`manual_bind` means if the hairpin Tx queue and peer Rx queue will be
bound automatically during the device start stage.

Different Tx and Rx queue pairs could have different values, but it
is highly recommended that all paired queues between one egress and
its peer ingress ports have the same values, in order not to bring
any chaos to the system. The actual support of these attribute
parameters will be checked and decided by the PMD drivers.

In the single port hairpin, if both are zero without any setting, the
behavior will remain the same as before. It means that no bind API
needs to be called and no Tx flow rules need to be inserted manually
by the application.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
Acked-by: Thomas Monjalon <thomas@monjalon.net>
---
v6: Using unnecessary comment and using "Rx" & "Tx"
v4: squash document update and more info for the two new attributes
v2: optimize the structure and remove unused macros
---
 doc/guides/prog_guide/rte_flow.rst     |  3 +++
 doc/guides/rel_notes/release_20_11.rst |  7 +++++++
 lib/librte_ethdev/rte_ethdev.c         |  8 ++++----
 lib/librte_ethdev/rte_ethdev.h         | 27 ++++++++++++++++++++++++++-
 4 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 55497c9..3df005a 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -2618,6 +2618,9 @@ set, unpredictable value will be seen depending on driver implementation. For
 loopback/hairpin packet, metadata set on Rx/Tx may or may not be propagated to
 the other path depending on HW capability.
 
+In hairpin case with Tx explicit flow mode, metadata could (not mandatory) be
+used to connect the Rx and Tx flows if it can be propagated from Rx to Tx path.
+
 .. _table_rte_flow_action_set_meta:
 
 .. table:: SET_META
diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 02bf7ca..2f23e6f 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -92,6 +92,7 @@ New Features
 * **Updated the ethdev library to support hairpin between two ports.**
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
+  Hairpin Tx part flow rules can be inserted explicitly.
 
 * **Updated Broadcom bnxt driver.**
 
@@ -396,6 +397,12 @@ ABI Changes
     Applications should use the new values for identification of existing
     extensions in the packet header.
 
+  * ``struct rte_eth_hairpin_conf`` has two new members:
+
+    * ``uint32_t tx_explicit:1;``
+    * ``uint32_t manual_bind:1;``
+
+
 Known Issues
 ------------
 
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 57cf4a7..bcbee30 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2003,13 +2003,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Rx queue(=%u), 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",
+			"Invalid value for number of peers for Rx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
@@ -2174,13 +2174,13 @@ struct rte_eth_dev *
 	}
 	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",
+			"Invalid value for number of peers for Tx queue(=%u), should be: <= %hu",
 			conf->peer_count, cap.max_tx_2_rx);
 		return -EINVAL;
 	}
 	if (conf->peer_count == 0) {
 		RTE_ETHDEV_LOG(ERR,
-			"Invalid value for number of peers for Tx queue(=%hu), should be: > 0",
+			"Invalid value for number of peers for Tx queue(=%u), should be: > 0",
 			conf->peer_count);
 		return -EINVAL;
 	}
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 10eb626..a8e5cdc 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -1045,7 +1045,32 @@ struct rte_eth_hairpin_peer {
  * A structure used to configure hairpin binding.
  */
 struct rte_eth_hairpin_conf {
-	uint16_t peer_count; /**< The number of peers. */
+	uint32_t peer_count:16; /**< The number of peers. */
+
+	/**
+	 * Explicit Tx flow rule mode.
+	 * One hairpin pair of queues should have the same attribute.
+	 *
+	 * - When set, the user should be responsible for inserting the hairpin
+	 *   Tx part flows and removing them.
+	 * - When clear, the PMD will try to handle the Tx part of the flows,
+	 *   e.g., by splitting one flow into two parts.
+	 */
+	uint32_t tx_explicit:1;
+
+	/**
+	 * Manually bind hairpin queues.
+	 * One hairpin pair of queues should have the same attribute.
+	 *
+	 * - When set, to enable hairpin, the user should call the hairpin bind
+	 *   function after all the queues are set up properly and the ports are
+	 *   started. Also, the hairpin unbind function should be called
+	 *   accordingly before stopping a port that with hairpin configured.
+	 * - When clear, the PMD will try to enable the hairpin with the queues
+	 *   configured automatically during port start.
+	 */
+	uint32_t manual_bind:1;
+	uint32_t reserved:14; /**< Reserved bits. */
 	struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
 };
 
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v6 3/5] ethdev: add API to get hairpin peer ports list
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 2/5] ethdev: add new attributes to hairpin config Bing Zhao
@ 2020-10-15 13:08       ` Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
                         ` (2 subsequent siblings)
  5 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

After hairpin queues are configured, in general, the application will
maintain the ports topology and even the queues configuration for
the hairpin. But sometimes it will not.

If there is no hot-plug, it is easy to bind and unbind hairpin among
all the ports. The application can just connect or disconnect the
hairpin egress ports to/from all the probed ingress ports. Then all
the connections could be handled properly.

But with hot-plug / hot-unplug, one port could be probed and removed
dynamically. With two ports hairpin, all the connections from and to
this port should be handled after start(bind) or before stop(unbind).
It is necessary to know the hairpin topology with this port.

This function will return the ports list with the actual peer ports
number after configuration. Either peer Rx or Tx ports will be
gotten with this function call.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v6:
  * Coding style updates
  * Using "Rx" & "Tx"
v5:
  * change EINVAL to ENODEV
  * add newline character
v4: add release notes update
v3:
  * change the direction from bool to unsigned int type
  * add length to protect the array from getting corrupted
---
 doc/guides/rel_notes/release_20_11.rst   |  1 +
 lib/librte_ethdev/rte_ethdev.c           | 24 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h           | 30 +++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 33 ++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |  1 +
 5 files changed, 89 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 2f23e6f..7b8e3ee 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -93,6 +93,7 @@ New Features
 
   New APIs are introduced to support binding / unbinding 2 ports hairpin.
   Hairpin Tx part flow rules can be inserted explicitly.
+  New API is added to get the hairpin peer ports list.
 
 * **Updated Broadcom bnxt driver.**
 
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index bcbee30..80907b2 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2257,6 +2257,30 @@ struct rte_eth_dev *
 	return ret;
 }
 
+int
+rte_eth_hairpin_get_peer_ports(uint16_t port_id, uint16_t *peer_ports,
+			       size_t len, uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	if (peer_ports == NULL || len == 0)
+		return -EINVAL;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_get_peer_ports,
+				-ENOTSUP);
+
+	ret = (*dev->dev_ops->hairpin_get_peer_ports)(dev, peer_ports,
+						      len, direction);
+	if (ret < 0)
+		RTE_ETHDEV_LOG(ERR, "Failed to get %d hairpin peer %s ports\n",
+			       port_id, direction ? "Rx" : "Tx");
+
+	return ret;
+}
+
 void
 rte_eth_tx_buffer_drop_callback(struct rte_mbuf **pkts, uint16_t unsent,
 		void *userdata __rte_unused)
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index a8e5cdc..f02acdc 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2186,6 +2186,36 @@ int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
  *
+ * Get all the hairpin peer Rx / Tx ports of the current port.
+ * The caller should ensure that the array is large enough to save the ports
+ * list.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param peer_ports
+ *   Pointer to the array to store the peer ports list.
+ * @param len
+ *   Length of the array to store the port identifiers.
+ * @param direction
+ *   Current port to peer port direction
+ *   positive - current used as Tx to get all peer Rx ports.
+ *   zero - current used as Rx to get all peer Tx ports.
+ *
+ * @return
+ *   - (0 or positive) actual peer ports number.
+ *   - (-EINVAL) if bad parameter.
+ *   - (-ENODEV) if *port_id* invalid
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - Others detailed errors from PMD drivers.
+ */
+__rte_experimental
+int rte_eth_hairpin_get_peer_ports(uint16_t port_id, uint16_t *peer_ports,
+				   size_t len, uint32_t direction);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
  * Bind all hairpin Tx queues of one port to the Rx queues of the peer port.
  * It is only allowed to call this function after all hairpin queues are
  * configured properly and the devices are in started state.
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 9a65bd5..03e00d5 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -657,6 +657,37 @@ typedef int (*eth_fec_get_t)(struct rte_eth_dev *dev,
 
 /**
  * @internal
+ * Get all hairpin Tx/Rx peer ports of the current device, if any.
+ *
+ * @param dev
+ *   ethdev handle of port.
+ * @param peer_ports
+ *   array to save the ports list.
+ * @param len
+ *   array length.
+ * @param direction
+ *   value to decide the current to peer direction
+ *   positive - used as Tx to get all peer Rx ports.
+ *   zero - used as Rx to get all peer Tx ports.
+ *
+ * @return
+ *   Negative errno value on error, 0 or positive on success.
+ *
+ * @retval 0
+ *   Success, no peer ports.
+ * @retval >0
+ *   Actual number of the peer ports.
+ * @retval -ENOTSUP
+ *   Get peer ports API is not supported.
+ * @retval -EINVAL
+ *   One of the parameters is invalid.
+ */
+typedef int (*hairpin_get_peer_ports_t)(struct rte_eth_dev *dev,
+					uint16_t *peer_ports, size_t len,
+					uint32_t direction);
+
+/**
+ * @internal
  * Bind all hairpin Tx queues of one port to the Rx queues of the peer port.
  *
  * @param dev
@@ -849,6 +880,8 @@ struct eth_dev_ops {
 	/**< Get Forward Error Correction(FEC) mode. */
 	eth_fec_set_t fec_set;
 	/**< Set Forward Error Correction(FEC) mode. */
+	hairpin_get_peer_ports_t hairpin_get_peer_ports;
+	/**< Get hairpin peer ports list. */
 	eth_hairpin_bind_t hairpin_bind;
 	/**< Bind all hairpin Tx queues of device to the peer port Rx queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index d67c9dc..a1e6897 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -228,6 +228,7 @@ EXPERIMENTAL {
 
 	# added in 20.11
 	rte_eth_hairpin_bind;
+	rte_eth_hairpin_get_peer_ports;
 	rte_eth_hairpin_unbind;
 	rte_eth_link_speed_to_str;
 	rte_eth_link_to_str;
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v6 4/5] ethdev: add APIs for hairpin queue operation
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
                         ` (2 preceding siblings ...)
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
@ 2020-10-15 13:08       ` Bing Zhao
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 5/5] app/testpmd: change hairpin queues setup Bing Zhao
  2020-10-15 23:03       ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Ferruh Yigit
  5 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Every hairpin queue pair should be configured properly and the
connection between Tx and Rx queues should be established, before
hairpin function works. In single port hairpin mode, the queues of
each pair belong to the same device. It is easy to get the hardware
and software information of each queue and configure the hairpin
connection with such information. In two ports hairpin mode, it is
not easy or inappropriate to access one queue's information from
another device.

Since hairpin is configured per queue pair, three new APIs are
introduced and they are internal for the PMD using.

The peer update API helps to pass one queue's information to the
peer queue and get the peer's information back for the next step.
The peer bind API configures the current queue with the peer's
information. For each hairpin queue pair, this API may need to be
called twice to configure the Tx, Rx queues separately.
The peer unbind API resets the current queue configuration and state
to disconnect it from the peer queue. Also, it may need to be called
twice to disconnect Tx, Rx queues from each other.

Some parameter of the above APIs might not be mandatory, and it
depends on the PMD implementation.

The structure of `rte_hairpin_peer_info` is only a declaration and
the actual members will be defined in each PMD when being used.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v6: Using "Rx" & "Tx"
v5: commnets update
v3: change the direction from bool to unsigned int type
---
 lib/librte_ethdev/rte_ethdev.c           |  55 +++++++++++++++++
 lib/librte_ethdev/rte_ethdev_driver.h    | 101 +++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 3 files changed, 159 insertions(+)

diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 80907b2..a6d2579 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -5643,6 +5643,61 @@ enum rte_eth_switch_domain_state {
 	return 0;
 }
 
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* Current queue information is not mandatory. */
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[peer_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_update,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_update)(dev, peer_queue,
+					cur_info, peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	if (peer_info == NULL)
+		return -EINVAL;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_bind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_bind)(dev, cur_queue,
+							peer_info, direction);
+}
+
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction)
+{
+	struct rte_eth_dev *dev;
+
+	/* No need to check the validity again. */
+	dev = &rte_eth_devices[cur_port];
+	RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->hairpin_queue_peer_unbind,
+				-ENOTSUP);
+
+	return (*dev->dev_ops->hairpin_queue_peer_unbind)(dev, cur_queue,
+							  direction);
+}
+
 RTE_LOG_REGISTER(rte_eth_dev_logtype, lib.ethdev, INFO);
 
 RTE_INIT(ethdev_init_telemetry)
diff --git a/lib/librte_ethdev/rte_ethdev_driver.h b/lib/librte_ethdev/rte_ethdev_driver.h
index 03e00d5..23a1454 100644
--- a/lib/librte_ethdev/rte_ethdev_driver.h
+++ b/lib/librte_ethdev/rte_ethdev_driver.h
@@ -21,6 +21,9 @@
 extern "C" {
 #endif
 
+/**< @internal Declaration of the hairpin peer queue information structure. */
+struct rte_hairpin_peer_info;
+
 /*
  * Definitions of all functions exported by an Ethernet driver through the
  * generic structure of type *eth_dev_ops* supplied in the *rte_eth_dev*
@@ -734,6 +737,21 @@ typedef int (*eth_hairpin_bind_t)(struct rte_eth_dev *dev,
 typedef int (*eth_hairpin_unbind_t)(struct rte_eth_dev *dev,
 				  uint16_t rx_port);
 
+typedef int (*eth_hairpin_queue_peer_update_t)
+	(struct rte_eth_dev *dev, uint16_t peer_queue,
+	 struct rte_hairpin_peer_info *current_info,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Update and fetch peer queue information. */
+
+typedef int (*eth_hairpin_queue_peer_bind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue,
+	 struct rte_hairpin_peer_info *peer_info, uint32_t direction);
+/**< @internal Bind peer queue to the current queue with fetched information. */
+
+typedef int (*eth_hairpin_queue_peer_unbind_t)
+	(struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);
+/**< @internal Unbind peer queue from the current queue. */
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -886,6 +904,12 @@ struct eth_dev_ops {
 	/**< Bind all hairpin Tx queues of device to the peer port Rx queues. */
 	eth_hairpin_unbind_t hairpin_unbind;
 	/**< Unbind all hairpin Tx queues from the peer port Rx queues. */
+	eth_hairpin_queue_peer_update_t hairpin_queue_peer_update;
+	/**< Pass the current queue info and get the peer queue info. */
+	eth_hairpin_queue_peer_bind_t hairpin_queue_peer_bind;
+	/**< Set up the connection between the pair of hairpin queues. */
+	eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;
+	/**< Disconnect the hairpin queues of a pair from each other. */
 };
 
 /**
@@ -1241,6 +1265,83 @@ typedef int (*ethdev_bus_specific_init)(struct rte_eth_dev *ethdev,
 int
 rte_eth_dev_destroy(struct rte_eth_dev *ethdev, ethdev_uninit_t ethdev_uninit);
 
+/**
+ * @internal
+ * Pass the current hairpin queue HW and/or SW information to the peer queue
+ * and fetch back the information of the peer queue.
+ *
+ * @param peer_port
+ *  Peer port identifier of the Ethernet device.
+ * @param peer_queue
+ *  Peer queue index of the port.
+ * @param cur_info
+ *  Pointer to the current information structure.
+ * @param peer_info
+ *  Pointer to the peer information, output.
+ * @param direction
+ *  Direction to pass the information.
+ *  positive - pass Tx queue information and get peer Rx queue information
+ *  zero - pass Rx queue information and get peer Tx queue information
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_update(uint16_t peer_port, uint16_t peer_queue,
+				  struct rte_hairpin_peer_info *cur_info,
+				  struct rte_hairpin_peer_info *peer_info,
+				  uint32_t direction);
+
+/**
+ * @internal
+ * Configure current hairpin queue with the peer information fetched to create
+ * the connection (bind) with peer queue in the specified direction.
+ * This function might need to be called twice to fully create the connections.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param peer_info
+ *  Pointer to the peer information, input.
+ * @param direction
+ *  Direction to create the connection.
+ *  positive - bind current Tx queue to peer Rx queue
+ *  zero - bind current Rx queue to peer Tx queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_bind(uint16_t cur_port, uint16_t cur_queue,
+				struct rte_hairpin_peer_info *peer_info,
+				uint32_t direction);
+
+/**
+ * @internal
+ * Reset the current queue state and configuration to disconnect (unbind) it
+ * from the peer queue.
+ * This function might need to be called twice to disconnect each other.
+ *
+ * @param cur_port
+ *  Current port identifier of the Ethernet device.
+ * @param cur_queue
+ *  Current queue index of the port.
+ * @param direction
+ *  Direction to destroy the connection.
+ *  positive - unbind current Tx queue from peer Rx queue
+ *  zero - unbind current Rx queue from peer Tx queue
+ *
+ * @return
+ *  Negative errno value on error, 0 on success.
+ */
+__rte_internal
+int
+rte_eth_hairpin_queue_peer_unbind(uint16_t cur_port, uint16_t cur_queue,
+				  uint32_t direction);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index a1e6897..f64c379 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -258,6 +258,9 @@ INTERNAL {
 	rte_eth_devargs_parse;
 	rte_eth_dma_zone_free;
 	rte_eth_dma_zone_reserve;
+	rte_eth_hairpin_queue_peer_bind;
+	rte_eth_hairpin_queue_peer_unbind;
+	rte_eth_hairpin_queue_peer_update;
 	rte_eth_switch_domain_alloc;
 	rte_eth_switch_domain_free;
 };
-- 
1.8.3.1


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

* [dpdk-dev] [PATCH v6 5/5] app/testpmd: change hairpin queues setup
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
                         ` (3 preceding siblings ...)
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
@ 2020-10-15 13:08       ` Bing Zhao
  2020-10-15 23:03       ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Ferruh Yigit
  5 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:08 UTC (permalink / raw)
  To: thomas, orika, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

A new parameter `hairpin-mode` is introduced to the testpmd command
line. Bitmask value is used to provide a more flexible configuration.
This parameter should be used when `hairpinq` is specified in the
command line.

Bit 0 in the LSB indicates the hairpin will use the loop mode. The
previous port Rx queue will be connected to the current port Tx
queue.
Bit 1 in the LSB indicates the hairpin will use pair port mode. The
even index port will be paired with the next odd index port. If the
total number of the probed ports is odd, then the last one will be
paired to itself.
If this byte is zero, then each port will be paired to itself.
Bit 0 takes a higher priority in the checking.

Bit 4 in the second bytes indicate if the hairpin will use explicit
Tx flow mode.

e.g. in the command line, "--hairpinq=2 --hairpin-mode=0x11"

If not set, default value zero will be used and the behavior will
try to get aligned with the previous single port mode. If the ports
belong to different vendors' NICs, it is suggested to use the `self`
hairpin mode only.

Since hairpin configures the hardware resources, the port mask of
packets forwarding engine will not be used here.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v6: using "Rx" & "Tx"
v5: add newline character
v4: squash testpmd guide update
v2: move the hairpin bind/unbind into start/stop to support hot-plug
    and hot-unplug
---
 app/test-pmd/parameters.c             |  15 ++++
 app/test-pmd/testpmd.c                | 125 ++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h                |   2 +
 doc/guides/testpmd_app_ug/run_app.rst |   8 +++
 4 files changed, 146 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 15ce8c1..a391f87 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -222,6 +222,9 @@
 	       "enabled\n");
 	printf("  --record-core-cycles: enable measurement of CPU cycles.\n");
 	printf("  --record-burst-stats: enable display of RX and TX bursts.\n");
+	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n "
+	       "    0x10 - explicit Tx rule, 0x02 - hairpin ports paired\n"
+	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
 }
 
 #ifdef RTE_LIBRTE_CMDLINE
@@ -645,6 +648,7 @@
 		{ "rxd",			1, 0, 0 },
 		{ "txd",			1, 0, 0 },
 		{ "hairpinq",			1, 0, 0 },
+		{ "hairpin-mode",		1, 0, 0 },
 		{ "burst",			1, 0, 0 },
 		{ "mbcache",			1, 0, 0 },
 		{ "txpt",			1, 0, 0 },
@@ -1113,6 +1117,17 @@
 				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
 						"be non-zero\n");
 			}
+			if (!strcmp(lgopts[opt_idx].name, "hairpin-mode")) {
+				char *end = NULL;
+				unsigned int n;
+
+				errno = 0;
+				n = strtoul(optarg, &end, 0);
+				if (errno != 0 || end == optarg)
+					rte_exit(EXIT_FAILURE, "hairpin mode invalid\n");
+				else
+					hairpin_mode = (uint16_t)n;
+			}
 			if (!strcmp(lgopts[opt_idx].name, "burst")) {
 				n = atoi(optarg);
 				if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index ccba71c..6caba60 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -367,6 +367,9 @@ struct fwd_engine * fwd_engines[] = {
 /* Clear ptypes on port initialization. */
 uint8_t clear_ptypes = true;
 
+/* Hairpin ports configuration mode. */
+uint16_t hairpin_mode;
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
 	[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2345,7 +2348,7 @@ struct extmem_param {
 
 /* Configure the Rx and Tx hairpin queues for the selected port. */
 static int
-setup_hairpin_queues(portid_t pi)
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
 {
 	queueid_t qi;
 	struct rte_eth_hairpin_conf hairpin_conf = {
@@ -2354,10 +2357,49 @@ struct extmem_param {
 	int i;
 	int diag;
 	struct rte_port *port = &ports[pi];
+	uint16_t peer_rx_port = pi;
+	uint16_t peer_tx_port = pi;
+	uint32_t manual = 1;
+	uint32_t tx_exp = hairpin_mode & 0x10;
+
+	if (!(hairpin_mode & 0xf)) {
+		peer_rx_port = pi;
+		peer_tx_port = pi;
+		manual = 0;
+	} else if (hairpin_mode & 0x1) {
+		peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+						       RTE_ETH_DEV_NO_OWNER);
+		if (peer_tx_port >= RTE_MAX_ETHPORTS)
+			peer_tx_port = rte_eth_find_next_owned_by(0,
+						RTE_ETH_DEV_NO_OWNER);
+		if (p_pi != RTE_MAX_ETHPORTS) {
+			peer_rx_port = p_pi;
+		} else {
+			uint16_t next_pi;
+
+			/* Last port will be the peer RX port of the first. */
+			RTE_ETH_FOREACH_DEV(next_pi)
+				peer_rx_port = next_pi;
+		}
+		manual = 1;
+	} else if (hairpin_mode & 0x2) {
+		if (cnt_pi & 0x1) {
+			peer_rx_port = p_pi;
+		} else {
+			peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+						RTE_ETH_DEV_NO_OWNER);
+			if (peer_rx_port >= RTE_MAX_ETHPORTS)
+				peer_rx_port = pi;
+		}
+		peer_tx_port = peer_rx_port;
+		manual = 1;
+	}
 
 	for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_rx_port;
 		hairpin_conf.peers[0].queue = i + nb_rxq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_tx_hairpin_queue_setup
 			(pi, qi, nb_txd, &hairpin_conf);
 		i++;
@@ -2377,8 +2419,10 @@ struct extmem_param {
 		return -1;
 	}
 	for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
-		hairpin_conf.peers[0].port = pi;
+		hairpin_conf.peers[0].port = peer_tx_port;
 		hairpin_conf.peers[0].queue = i + nb_txq;
+		hairpin_conf.manual_bind = !!manual;
+		hairpin_conf.tx_explicit = !!tx_exp;
 		diag = rte_eth_rx_hairpin_queue_setup
 			(pi, qi, nb_rxd, &hairpin_conf);
 		i++;
@@ -2405,6 +2449,12 @@ struct extmem_param {
 {
 	int diag, need_check_link_status = -1;
 	portid_t pi;
+	portid_t p_pi = RTE_MAX_ETHPORTS;
+	portid_t pl[RTE_MAX_ETHPORTS];
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	uint16_t cnt_pi = 0;
+	uint16_t cfg_pi = 0;
+	int peer_pi;
 	queueid_t qi;
 	struct rte_port *port;
 	struct rte_ether_addr mac_addr;
@@ -2544,7 +2594,7 @@ struct extmem_param {
 				return -1;
 			}
 			/* setup hairpin queues */
-			if (setup_hairpin_queues(pi) != 0)
+			if (setup_hairpin_queues(pi, p_pi, cnt_pi) != 0)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
@@ -2557,6 +2607,9 @@ struct extmem_param {
 				pi);
 		}
 
+		p_pi = pi;
+		cnt_pi++;
+
 		/* start port */
 		if (rte_eth_dev_start(pi) < 0) {
 			printf("Fail to start port %d\n", pi);
@@ -2581,6 +2634,8 @@ struct extmem_param {
 
 		/* at least one port started, need checking link status */
 		need_check_link_status = 1;
+
+		pl[cfg_pi++] = pi;
 	}
 
 	if (need_check_link_status == 1 && !no_link_check)
@@ -2588,6 +2643,50 @@ struct extmem_param {
 	else if (need_check_link_status == 0)
 		printf("Please stop the ports first\n");
 
+	if (hairpin_mode & 0xf) {
+		uint16_t i;
+		int j;
+
+		/* bind all started hairpin ports */
+		for (i = 0; i < cfg_pi; i++) {
+			pi = pl[i];
+			/* bind current Tx to all peer Rx */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 1);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+				if (diag < 0) {
+					printf("Error during binding hairpin"
+					       " Tx port %u to %u: %s\n",
+					       pi, peer_pl[j],
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+			/* bind all peer Tx to current Rx */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				return peer_pi;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+				if (diag < 0) {
+					printf("Error during binding hairpin"
+					       " Tx port %u to %u: %s\n",
+					       peer_pl[j], pi,
+					       rte_strerror(-diag));
+					return -1;
+				}
+			}
+		}
+	}
+
 	printf("Done\n");
 	return 0;
 }
@@ -2598,6 +2697,8 @@ struct extmem_param {
 	portid_t pi;
 	struct rte_port *port;
 	int need_check_link_status = 0;
+	portid_t peer_pl[RTE_MAX_ETHPORTS];
+	int peer_pi;
 
 	if (dcb_test) {
 		dcb_test = 0;
@@ -2628,6 +2729,22 @@ struct extmem_param {
 						RTE_PORT_HANDLING) == 0)
 			continue;
 
+		if (hairpin_mode & 0xf) {
+			int j;
+
+			rte_eth_hairpin_unbind(pi, RTE_MAX_ETHPORTS);
+			/* unbind all peer Tx from current Rx */
+			peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+							RTE_MAX_ETHPORTS, 0);
+			if (peer_pi < 0)
+				continue;
+			for (j = 0; j < peer_pi; j++) {
+				if (!port_is_started(peer_pl[j]))
+					continue;
+				rte_eth_hairpin_unbind(peer_pl[j], pi);
+			}
+		}
+
 		rte_eth_dev_stop(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index a4dfd4f..f8b0a35 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -408,6 +408,8 @@ struct queue_stats_mappings {
 
 extern uint16_t stats_period;
 
+extern uint16_t hairpin_mode;
+
 #ifdef RTE_LIBRTE_LATENCY_STATS
 extern uint8_t latencystats_enabled;
 extern lcoreid_t latencystats_lcore_id;
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index ec085c2..d3ee59d 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -503,3 +503,11 @@ The command line options are:
 *   ``--record-burst-stats``
 
     Enable display of RX and TX burst stats.
+
+*   ``--hairpin-mode=0xXX``
+
+    Set the hairpin port mode with bitmask, only valid when hairpin queues number is set.
+    bit 4 - explicit Tx flow rule
+    bit 1 - two hairpin ports paired
+    bit 0 - two hairpin ports loop
+    The default value is 0. Hairpin will use single port mode and implicit Tx flow mode.
-- 
1.8.3.1


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

* Re: [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config
  2020-10-15 10:46         ` Thomas Monjalon
@ 2020-10-15 13:45           ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-15 13:45 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon
  Cc: Ori Kam, ferruh.yigit, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu, dev

Hi Thomas,

All comments are addressed and thanks a lot for the reviewing.

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Thursday, October 15, 2020 6:46 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Ori Kam <orika@nvidia.com>; ferruh.yigit@intel.com;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com; dev@dpdk.org
> Subject: Re: [PATCH v5 2/5] ethdev: add new attributes to hairpin
> config
> 
> External email: Use caution opening links or attachments
> 
> 
> 15/10/2020 07:35, Bing Zhao:
> > To support two ports hairpin mode and keep the backward
> compatibility
> > for the application, two new attribute members of the hairpin
> queue
> > configuration structure will be added.
> >
> > `tx_explicit` means if the application itself will insert the TX
> part
> > flow rules. If not set, PMD will insert the rules implicitly.
> > `manual_bind` means if the hairpin TX queue and peer RX queue will
> be
> > bound automatically during the device start stage.
> >
> > Different TX and RX queue pairs could have different values, but
> it is
> > highly recommended that all paired queues between one egress and
> its
> > peer ingress ports have the same values, in order not to bring any
> > chaos to the system. The actual support of these attribute
> parameters
> > will be checked and decided by the PMD drivers.
> >
> > In the single port hairpin, if both are zero without any setting,
> the
> > behavior will remain the same as before. It means that no bind API
> > needs to be called and no TX flow rules need to be inserted
> manually
> > by the application.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> > ---
> > v4: squash document update and more info for the two new
> attributes
> > v2: optimize the structure and remove unused macros
> > ---
> 
> Acked-by: Thomas Monjalon <thomas@monjalon.net>
> 
> Minor comments below.
> 
> >  struct rte_eth_hairpin_conf {
> > -     uint16_t peer_count; /**< The number of peers. */
> > +     uint32_t peer_count:16; /**< The number of peers. */
> > +
> > +     /**
> > +      * Explicit TX flow rule mode. One hairpin pair of queues
> should have
> > +      * the same attribute. The actual support depends on the PMD.
> 
> The second sentence should be on a separate line.
> 
> About the third sentence, implementation is always PMD-specific.
> PMD will reject the not supported conf, as usual.
> I think this comment is not needed in the API description.
> 
> > +      *
> > +      * - When set, the user should be responsible for inserting
> the hairpin
> > +      *   TX part flows and removing them.
> > +      * - When clear, the PMD will try to handle the TX part of
> the flows,
> > +      *   e.g., by splitting one flow into two parts.
> > +      */
> > +     uint32_t tx_explicit:1;
> > +
> > +     /**
> > +      * Manually bind hairpin queues. One hairpin pair of queues
> should have
> > +      * the same attribute. The actual support depends on the PMD.
> 
> Same here
> 
> > +      *
> > +      * - When set, to enable hairpin, the user should call the
> hairpin bind
> > +      *   API after all the queues are set up properly and the
> ports are
> > +      *   started. Also, the hairpin unbind API should be called
> accordingly
> > +      *   before stopping a port that with hairpin configured.
> > +      * - When clear, the PMD will try to enable the hairpin with
> the queues
> > +      *   configured automatically during port start.
> > +      */
> > +     uint32_t manual_bind:1;
> > +     uint32_t reserved:14; /**< Reserved bits. */
> >       struct rte_eth_hairpin_peer peers[RTE_ETH_MAX_HAIRPIN_PEERS];
> > };
> 
> 


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

* Re: [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports
  2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
                         ` (4 preceding siblings ...)
  2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 5/5] app/testpmd: change hairpin queues setup Bing Zhao
@ 2020-10-15 23:03       ` Ferruh Yigit
  2020-10-16  1:34         ` Bing Zhao
  5 siblings, 1 reply; 81+ messages in thread
From: Ferruh Yigit @ 2020-10-15 23:03 UTC (permalink / raw)
  To: Bing Zhao, thomas, orika, arybchenko, mdr, nhorman,
	bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

On 10/15/2020 2:08 PM, Bing Zhao wrote:
> The patches contain the following changes:
> 1. new APIs to bind and unbind hairpin ports in manual binding mode.
> 2. new API to get the hairpin peer ports list.
> 3. new internal APIs for PMD to pass the queue information and
>     configure the queue pair.
> 4. new attribute members in the hairpin queue configuraiton structure
>     to specify the binding mode and enable explicit TX flow mode.
> 5. Testpmd support to configure the hairpin modes for two ports
>     hairpin verification.
> 6. documents update.
> 
> ---
> v6:
> 1. Coding style updates
> 2. Using "Rx" & "Tx" instead of the capital formats
> v5:
> 1. Change EINVAL to ENODEV if port id is invalid
> 2. Description fixes and other minor changes
> v4:
> 1. squash documents update into patch
> 2. add more description of the hairpin conf attributes
> v3:
> 1. add length to protect the pointer to the array from getting corrupted
> 2. change the direction from bool to unsigned int
> v2:
> 1. add documents update
> 2. remove all peer ports logic from rte API
> 3. conf structure optimizing
> 4. new API to get the peer ports and testpmd change to support
>     hot-plug / unplug case
> ---
> 
> Bing Zhao (5):
>    ethdev: add hairpin bind and unbind APIs
>    ethdev: add new attributes to hairpin config
>    ethdev: add API to get hairpin peer ports list
>    ethdev: add APIs for hairpin queue operation
>    app/testpmd: change hairpin queues setup
> 

Series applied to dpdk-next-net/main, thanks.


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

* Re: [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports
  2020-10-15 23:03       ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Ferruh Yigit
@ 2020-10-16  1:34         ` Bing Zhao
  0 siblings, 0 replies; 81+ messages in thread
From: Bing Zhao @ 2020-10-16  1:34 UTC (permalink / raw)
  To: Ferruh Yigit, NBU-Contact-Thomas Monjalon, Ori Kam, arybchenko,
	mdr, nhorman, bernard.iremonger, beilei.xing, wenzhuo.lu
  Cc: dev

Hi Ferruh,

> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@intel.com>
> Sent: Friday, October 16, 2020 7:03 AM
> To: Bing Zhao <bingz@nvidia.com>; NBU-Contact-Thomas Monjalon
> <thomas@monjalon.net>; Ori Kam <orika@nvidia.com>;
> arybchenko@solarflare.com; mdr@ashroe.eu; nhorman@tuxdriver.com;
> bernard.iremonger@intel.com; beilei.xing@intel.com;
> wenzhuo.lu@intel.com
> Cc: dev@dpdk.org
> Subject: Re: [PATCH v6 0/5] introduce support for hairpin between
> two ports
> 
> External email: Use caution opening links or attachments
> 
> 
> On 10/15/2020 2:08 PM, Bing Zhao wrote:
> > The patches contain the following changes:
> > 1. new APIs to bind and unbind hairpin ports in manual binding
> mode.
> > 2. new API to get the hairpin peer ports list.
> > 3. new internal APIs for PMD to pass the queue information and
> >     configure the queue pair.
> > 4. new attribute members in the hairpin queue configuraiton
> structure
> >     to specify the binding mode and enable explicit TX flow mode.
> > 5. Testpmd support to configure the hairpin modes for two ports
> >     hairpin verification.
> > 6. documents update.
> >
> > ---
> > v6:
> > 1. Coding style updates
> > 2. Using "Rx" & "Tx" instead of the capital formats
> > v5:
> > 1. Change EINVAL to ENODEV if port id is invalid 2. Description
> fixes
> > and other minor changes
> > v4:
> > 1. squash documents update into patch
> > 2. add more description of the hairpin conf attributes
> > v3:
> > 1. add length to protect the pointer to the array from getting
> > corrupted 2. change the direction from bool to unsigned int
> > v2:
> > 1. add documents update
> > 2. remove all peer ports logic from rte API 3. conf structure
> > optimizing 4. new API to get the peer ports and testpmd change to
> > support
> >     hot-plug / unplug case
> > ---
> >
> > Bing Zhao (5):
> >    ethdev: add hairpin bind and unbind APIs
> >    ethdev: add new attributes to hairpin config
> >    ethdev: add API to get hairpin peer ports list
> >    ethdev: add APIs for hairpin queue operation
> >    app/testpmd: change hairpin queues setup
> >
> 
> Series applied to dpdk-next-net/main, thanks.


Many thanks for your help.

BR. Bing

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

end of thread, other threads:[~2020-10-16  1:34 UTC | newest]

Thread overview: 81+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-11  4:51 [dpdk-dev] [RFC] introduce support for hairpin between two ports Bing Zhao
2020-09-13 15:48 ` [dpdk-dev] [RFC PATCH v2 0/4] " Bing Zhao
2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 1/4] ethdev: add support for flow item transmit queue Bing Zhao
2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 2/4] testpmd: add item transmit queue in flow CLI Bing Zhao
2020-09-13 15:48   ` [dpdk-dev] [RFC PATCH v2 3/4] ethdev: add hairpin bind APIs Bing Zhao
2020-09-13 15:49   ` [dpdk-dev] [RFC PATCH v2 4/4] ethdev: add new attributes to hairpin queues config Bing Zhao
2020-10-01  0:25   ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Bing Zhao
2020-10-01  0:25     ` [dpdk-dev] [PATCH 1/4] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-04  9:20       ` Ori Kam
2020-10-07 11:21         ` Bing Zhao
2020-10-07 11:42           ` Ori Kam
2020-10-01  0:26     ` [dpdk-dev] [PATCH 2/4] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-04  9:22       ` Ori Kam
2020-10-07 11:32         ` Bing Zhao
2020-10-01  0:26     ` [dpdk-dev] [PATCH 3/4] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-04  9:34       ` Ori Kam
2020-10-07 11:34         ` Bing Zhao
2020-10-01  0:26     ` [dpdk-dev] [PATCH 4/4] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-04  9:39       ` Ori Kam
2020-10-07 11:36         ` Bing Zhao
2020-10-04  9:45     ` [dpdk-dev] [PATCH 0/4] introduce support for hairpin between two ports Ori Kam
2020-10-08  8:51     ` [dpdk-dev] [PATCH v2 0/6] " Bing Zhao
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-08  9:07         ` Ori Kam
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 2/6] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-08  9:23         ` Ori Kam
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
2020-10-08  9:40         ` Ori Kam
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-08  9:44         ` Ori Kam
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 5/6] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-08  9:45         ` Ori Kam
2020-10-08  8:51       ` [dpdk-dev] [PATCH v2 6/6] doc: update for two ports hairpin mode Bing Zhao
2020-10-08  9:47         ` Ori Kam
2020-10-08 12:05       ` [dpdk-dev] [PATCH v3 0/6] introduce support for hairpin between two ports Bing Zhao
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 1/6] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-14 14:35           ` Thomas Monjalon
2020-10-15  2:56             ` Bing Zhao
2020-10-15  7:31               ` Thomas Monjalon
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 2/6] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-12 21:37           ` Thomas Monjalon
2020-10-13 12:29             ` Bing Zhao
2020-10-13 12:41               ` Thomas Monjalon
2020-10-13 13:21                 ` Bing Zhao
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 3/6] ethdev: add API to get hairpin peer ports list Bing Zhao
2020-10-08 12:31           ` Ori Kam
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 4/6] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 5/6] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-08 12:05         ` [dpdk-dev] [PATCH v3 6/6] doc: update for two ports hairpin mode Bing Zhao
2020-10-12 21:30           ` Thomas Monjalon
2020-10-13  1:13             ` Bing Zhao
2020-10-13  6:37               ` Thomas Monjalon
2020-10-13  6:40                 ` Bing Zhao
2020-10-13 16:19         ` [dpdk-dev] [PATCH v4 0/5] introduce support for hairpin between two ports Bing Zhao
2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-14 14:43             ` Thomas Monjalon
2020-10-15  2:59               ` Bing Zhao
2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 2/5] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
2020-10-14 15:02             ` Thomas Monjalon
2020-10-15  4:03               ` Bing Zhao
2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-13 16:19           ` [dpdk-dev] [PATCH v4 5/5] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-15  5:35     ` [dpdk-dev] [PATCH v5 0/5] introduce support for hairpin between two ports Bing Zhao
2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-15 10:34         ` Thomas Monjalon
2020-10-15 11:39           ` Bing Zhao
2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 2/5] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-15 10:46         ` Thomas Monjalon
2020-10-15 13:45           ` Bing Zhao
2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-15  5:35       ` [dpdk-dev] [PATCH v5 5/5] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-15 13:08     ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Bing Zhao
2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 1/5] ethdev: add hairpin bind and unbind APIs Bing Zhao
2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 2/5] ethdev: add new attributes to hairpin config Bing Zhao
2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 3/5] ethdev: add API to get hairpin peer ports list Bing Zhao
2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 4/5] ethdev: add APIs for hairpin queue operation Bing Zhao
2020-10-15 13:08       ` [dpdk-dev] [PATCH v6 5/5] app/testpmd: change hairpin queues setup Bing Zhao
2020-10-15 23:03       ` [dpdk-dev] [PATCH v6 0/5] introduce support for hairpin between two ports Ferruh Yigit
2020-10-16  1:34         ` Bing Zhao

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