<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:DengXian;
        panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:"\@DengXian";
        panose-1:2 1 6 0 3 1 1 1 1 1;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
        {mso-style-priority:34;
        margin-top:0in;
        margin-right:0in;
        margin-bottom:0in;
        margin-left:.5in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;
        font-family:"Calibri",sans-serif;
        mso-ligatures:none;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:760026406;
        mso-list-template-ids:63088652;}
@list l0:level1
        {mso-level-start-at:2;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l1
        {mso-list-id:1296521465;
        mso-list-template-ids:659834528;}
@list l2
        {mso-list-id:1459491666;
        mso-list-type:hybrid;
        mso-list-template-ids:-782183046 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l2:level1
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level3
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l2:level4
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level6
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l2:level7
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l2:level9
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="blue" vlink="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">Thanks for the comments.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="border:none;border-left:solid blue 1.5pt;padding:0in 0in 0in 4.0pt">
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> Константин Ананьев <konstantin.v.ananyev@yandex.ru>
<br>
<b>Sent:</b> Monday, June 5, 2023 8:54 PM<br>
<b>To:</b> Feifei Wang <Feifei.Wang2@arm.com>; thomas@monjalon.net; Ferruh Yigit <ferruh.yigit@amd.com>; Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru><br>
<b>Cc:</b> dev@dpdk.org; nd <nd@arm.com>; Honnappa Nagarahalli <Honnappa.Nagarahalli@arm.com>; Ruifeng Wang <Ruifeng.Wang@arm.com><br>
<b>Subject:</b> Re: [PATCH v6 1/4] ethdev: add API for mbufs recycle mode<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Hi Feifei,<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">few more comments from me, see below.<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>Add 'rte_eth_recycle_rx_queue_info_get' and 'rte_eth_recycle_mbufs'<br>
APIs to recycle used mbufs from a transmit queue of an Ethernet device,<br>
and move these mbufs into a mbuf ring for a receive queue of an Ethernet<br>
device. This can bypass mempool 'put/get' operations hence saving CPU<br>
cycles.<br>
<br>
For each recycling mbufs, the rte_eth_recycle_mbufs() function performs<br>
the following operations:<br>
- Copy used *rte_mbuf* buffer pointers from Tx mbuf ring into Rx mbuf<br>
ring.<br>
- Replenish the Rx descriptors with the recycling *rte_mbuf* mbufs freed<br>
from the Tx mbuf ring.<br>
<br>
Suggested-by: Honnappa Nagarahalli <<a href="mailto:honnappa.nagarahalli@arm.com">honnappa.nagarahalli@arm.com</a>><br>
Suggested-by: Ruifeng Wang <<a href="mailto:ruifeng.wang@arm.com">ruifeng.wang@arm.com</a>><br>
Signed-off-by: Feifei Wang <<a href="mailto:feifei.wang2@arm.com">feifei.wang2@arm.com</a>><br>
Reviewed-by: Ruifeng Wang <<a href="mailto:ruifeng.wang@arm.com">ruifeng.wang@arm.com</a>><br>
Reviewed-by: Honnappa Nagarahalli <<a href="mailto:honnappa.nagarahalli@arm.com">honnappa.nagarahalli@arm.com</a>><br>
---<br>
 doc/guides/rel_notes/release_23_07.rst | 7 +<br>
 lib/ethdev/ethdev_driver.h | 10 ++<br>
 lib/ethdev/ethdev_private.c | 2 +<br>
 lib/ethdev/rte_ethdev.c | 31 +++++<br>
 lib/ethdev/rte_ethdev.h | 182 +++++++++++++++++++++++++<br>
 lib/ethdev/rte_ethdev_core.h | 15 +-<br>
 lib/ethdev/version.map | 4 +<br>
 7 files changed, 249 insertions(+), 2 deletions(-)<br>
<br>
diff --git a/doc/guides/rel_notes/release_23_07.rst b/doc/guides/rel_notes/release_23_07.rst<br>
index a9b1293689..f279036cb9 100644<br>
--- a/doc/guides/rel_notes/release_23_07.rst<br>
+++ b/doc/guides/rel_notes/release_23_07.rst<br>
@@ -55,6 +55,13 @@ New Features<br>
      Also, make sure to start the actual text at the margin.<br>
      =======================================================<br>
 <br>
+* **Add mbufs recycling support. **<br>
+ Added ``rte_eth_recycle_rx_queue_info_get`` and ``rte_eth_recycle_mbufs``<br>
+ APIs which allow the user to copy used mbufs from the Tx mbuf ring<br>
+ into the Rx mbuf ring. This feature supports the case that the Rx Ethernet<br>
+ device is different from the Tx Ethernet device with respective driver<br>
+ callback functions in ``rte_eth_recycle_mbufs``.<br>
+<br>
 <br>
 Removed Items<br>
 -------------<br>
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h<br>
index 2c9d615fb5..c6723d5277 100644<br>
--- a/lib/ethdev/ethdev_driver.h<br>
+++ b/lib/ethdev/ethdev_driver.h<br>
@@ -59,6 +59,10 @@ struct rte_eth_dev {<br>
         eth_rx_descriptor_status_t rx_descriptor_status;<br>
         /** Check the status of a Tx descriptor */<br>
         eth_tx_descriptor_status_t tx_descriptor_status;<br>
+ /** Pointer to PMD transmit mbufs reuse function */<br>
+ eth_recycle_tx_mbufs_reuse_t recycle_tx_mbufs_reuse;<br>
+ /** Pointer to PMD receive descriptors refill function */<br>
+ eth_recycle_rx_descriptors_refill_t recycle_rx_descriptors_refill;<br>
 <br>
         /**<br>
          * Device data that is shared between primary and secondary processes<br>
@@ -504,6 +508,10 @@ typedef void (*eth_rxq_info_get_t)(struct rte_eth_dev *dev,<br>
 typedef void (*eth_txq_info_get_t)(struct rte_eth_dev *dev,<br>
         uint16_t tx_queue_id, struct rte_eth_txq_info *qinfo);<br>
 <br>
+typedef void (*eth_recycle_rxq_info_get_t)(struct rte_eth_dev *dev,<br>
+ uint16_t rx_queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info);<br>
+<br>
 typedef int (*eth_burst_mode_get_t)(struct rte_eth_dev *dev,<br>
         uint16_t queue_id, struct rte_eth_burst_mode *mode);<br>
 <br>
@@ -1247,6 +1255,8 @@ struct eth_dev_ops {<br>
         eth_rxq_info_get_t rxq_info_get;<br>
         /** Retrieve Tx queue information */<br>
         eth_txq_info_get_t txq_info_get;<br>
+ /** Retrieve mbufs recycle Rx queue information */<br>
+ eth_recycle_rxq_info_get_t recycle_rxq_info_get;<br>
         eth_burst_mode_get_t rx_burst_mode_get; /**< Get Rx burst mode */<br>
         eth_burst_mode_get_t tx_burst_mode_get; /**< Get Tx burst mode */<br>
         eth_fw_version_get_t fw_version_get; /**< Get firmware version */<br>
diff --git a/lib/ethdev/ethdev_private.c b/lib/ethdev/ethdev_private.c<br>
index 14ec8c6ccf..f8ab64f195 100644<br>
--- a/lib/ethdev/ethdev_private.c<br>
+++ b/lib/ethdev/ethdev_private.c<br>
@@ -277,6 +277,8 @@ eth_dev_fp_ops_setup(struct rte_eth_fp_ops *fpo,<br>
         fpo->rx_queue_count = dev->rx_queue_count;<br>
         fpo->rx_descriptor_status = dev->rx_descriptor_status;<br>
         fpo->tx_descriptor_status = dev->tx_descriptor_status;<br>
+ fpo->recycle_tx_mbufs_reuse = dev->recycle_tx_mbufs_reuse;<br>
+ fpo->recycle_rx_descriptors_refill = dev->recycle_rx_descriptors_refill;<br>
 <br>
         fpo->rxq.data = dev->data->rx_queues;<br>
         fpo->rxq.clbk = (void **)(uintptr_t)dev->post_rx_burst_cbs;<br>
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c<br>
index 4d03255683..7c27dcfea4 100644<br>
--- a/lib/ethdev/rte_ethdev.c<br>
+++ b/lib/ethdev/rte_ethdev.c<br>
@@ -5784,6 +5784,37 @@ rte_eth_tx_queue_info_get(uint16_t port_id, uint16_t queue_id,<br>
         return 0;<br>
 }<br>
 <br>
+int<br>
+rte_eth_recycle_rx_queue_info_get(uint16_t port_id, uint16_t queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info)<br>
+{<br>
+ struct rte_eth_dev *dev;<br>
+<br>
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);<br>
+ dev = &rte_eth_devices[port_id];<br>
+<br>
+ if (queue_id >= dev->data->nb_rx_queues) {<br>
+ RTE_ETHDEV_LOG(ERR, "Invalid Rx queue_id=%u\n", queue_id);<br>
+ return -EINVAL;<br>
+ }<br>
+<br>
+ if (dev->data->rx_queues == NULL ||<br>
+ dev->data->rx_queues[queue_id] == NULL) {<br>
+ RTE_ETHDEV_LOG(ERR,<br>
+ "Rx queue %"PRIu16" of device with port_id=%"<br>
+ PRIu16" has not been setup\n",<br>
+ queue_id, port_id);<br>
+ return -EINVAL;<br>
+ }<br>
+<br>
+ if (*dev->dev_ops->recycle_rxq_info_get == NULL)<br>
+ return -ENOTSUP;<br>
+<br>
+ dev->dev_ops->recycle_rxq_info_get(dev, queue_id, recycle_rxq_info);<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
 int<br>
 rte_eth_rx_burst_mode_get(uint16_t port_id, uint16_t queue_id,<br>
                           struct rte_eth_burst_mode *mode)<br>
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h<br>
index 99fe9e238b..7434aa2483 100644<br>
--- a/lib/ethdev/rte_ethdev.h<br>
+++ b/lib/ethdev/rte_ethdev.h<br>
@@ -1820,6 +1820,30 @@ struct rte_eth_txq_info {<br>
         uint8_t queue_state; /**< one of RTE_ETH_QUEUE_STATE_*. */<br>
 } __rte_cache_min_aligned;<br>
 <br>
+/**<br>
+ * @warning<br>
+ * @b EXPERIMENTAL: this structure may change without prior notice.<br>
+ *<br>
+ * Ethernet device Rx queue information structure for recycling mbufs.<br>
+ * Used to retrieve Rx queue information when Tx queue reusing mbufs and moving<br>
+ * them into Rx mbuf ring.<br>
+ */<br>
+struct rte_eth_recycle_rxq_info {<br>
+ struct rte_mbuf **mbuf_ring; /**< mbuf ring of Rx queue. */<br>
+ struct rte_mempool *mp; /**< mempool of Rx queue. */<br>
+ uint16_t *refill_head; /**< head of Rx queue refilling mbufs. */<br>
+ uint16_t *receive_tail; /**< tail of Rx queue receiving pkts. */<br>
+ uint16_t mbuf_ring_size; /**< configured number of mbuf ring size. */<br>
+ /**<br>
+ * Requirement on mbuf refilling batch size of Rx mbuf ring.<br>
+ * For some PMD drivers, the number of Rx mbuf ring refilling mbufs<br>
+ * should be aligned with mbuf ring size, in order to simplify<br>
+ * ring wrapping around.<br>
+ * Value 0 means that PMD drivers have no requirement for this.<br>
+ */<br>
+ uint16_t refill_requirement;<br>
+} __rte_cache_min_aligned;<br>
+<br>
 /* Generic Burst mode flag definition, values can be ORed. */<br>
 <br>
 /**<br>
@@ -4809,6 +4833,31 @@ int rte_eth_rx_queue_info_get(uint16_t port_id, uint16_t queue_id,<br>
 int rte_eth_tx_queue_info_get(uint16_t port_id, uint16_t queue_id,<br>
         struct rte_eth_txq_info *qinfo);<br>
 <br>
+/**<br>
+ * @warning<br>
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice<br>
+ *<br>
+ * Retrieve information about given ports's Rx queue for recycling mbufs.<br>
+ *<br>
+ * @param port_id<br>
+ * The port identifier of the Ethernet device.<br>
+ * @param queue_id<br>
+ * The Rx queue on the Ethernet devicefor which information<br>
+ * will be retrieved.<br>
+ * @param recycle_rxq_info<br>
+ * A pointer to a structure of type *rte_eth_recycle_rxq_info* to be filled.<br>
+ *<br>
+ * @return<br>
+ * - 0: Success<br>
+ * - -ENODEV: If *port_id* is invalid.<br>
+ * - -ENOTSUP: routine is not supported by the device PMD.<br>
+ * - -EINVAL: The queue_id is out of range.<br>
+ */<br>
+__rte_experimental<br>
+int rte_eth_recycle_rx_queue_info_get(uint16_t port_id,<br>
+ uint16_t queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info);<br>
+<br>
 /**<br>
  * Retrieve information about the Rx packet burst mode.<br>
  *<br>
@@ -6483,6 +6532,139 @@ rte_eth_tx_buffer(uint16_t port_id, uint16_t queue_id,<br>
         return rte_eth_tx_buffer_flush(port_id, queue_id, buffer);<br>
 }<br>
 <br>
+/**<br>
+ * @warning<br>
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice<br>
+ *<br>
+ * Recycle used mbufs from a transmit queue of an Ethernet device, and move<br>
+ * these mbufs into a mbuf ring for a receive queue of an Ethernet device.<br>
+ * This can bypass mempool path to save CPU cycles.<br>
+ *<br>
+ * The rte_eth_recycle_mbufs() function loops, with rte_eth_rx_burst() and<br>
+ * rte_eth_tx_burst() functions, freeing Tx used mbufs and replenishing Rx<br>
+ * descriptors. The number of recycling mbufs depends on the request of Rx mbuf<br>
+ * ring, with the constraint of enough used mbufs from Tx mbuf ring.<br>
+ *<br>
+ * For each recycling mbufs, the rte_eth_recycle_mbufs() function performs the<br>
+ * following operations:<br>
+ *<br>
+ * - Copy used *rte_mbuf* buffer pointers from Tx mbuf ring into Rx mbuf ring.<br>
+ *<br>
+ * - Replenish the Rx descriptors with the recycling *rte_mbuf* mbufs freed<br>
+ * from the Tx mbuf ring.<br>
+ *<br>
+ * This function spilts Rx and Tx path with different callback functions. The<br>
+ * callback function recycle_tx_mbufs_reuse is for Tx driver. The callback<br>
+ * function recycle_rx_descriptors_refill is for Rx driver. rte_eth_recycle_mbufs()<br>
+ * can support the case that Rx Ethernet device is different from Tx Ethernet device.<br>
+ *<br>
+ * It is the responsibility of users to select the Rx/Tx queue pair to recycle<br>
+ * mbufs. Before call this function, users must call rte_eth_recycle_rxq_info_get<br>
+ * function to retrieve selected Rx queue information.<br>
+ * @see rte_eth_recycle_rxq_info_get, struct rte_eth_recycle_rxq_info<br>
+ *<br>
+ * Currently, the rte_eth_recycle_mbufs() function can only support one-time pairing<br>
+ * between the receive queue and transmit queue. Do not pair one receive queue with<br>
+ * multiple transmit queues or pair one transmit queue with multiple receive queues,<br>
+ * in order to avoid memory error rewriting.<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal">Probably I am missing something, but why it is not possible to do something like that:<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<div>
<div>
<p class="MsoNormal"><span lang="FR">rte_eth_recycle_mbufs(rx_port_id=X, rx_queue_id=Y, tx_port_id=N, tx_queue_id=M, ...);<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span lang="FR">....<o:p></o:p></span></p>
</div>
<div>
<div>
<p class="MsoNormal"><span lang="FR">rte_eth_recycle_mbufs(rx_port_id=X, rx_queue_id=Y, tx_port_id=N, tx_queue_id=K, ...);<o:p></o:p></span></p>
</div>
</div>
</div>
<div>
<p class="MsoNormal"><span lang="FR"> <o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal">I.E. feed rx queue from 2 tx queues?<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Two problems for this:<o:p></o:p></p>
<ol style="margin-top:0in" start="1" type="1">
<li class="MsoListParagraph" style="margin-left:0in;mso-list:l2 level1 lfo3">If we have 2 tx queues for rx, the thread should make the extra judgement to<o:p></o:p></li></ol>
<p class="MsoListParagraph">decide which one to choose in the driver layer.<o:p></o:p></p>
<p class="MsoListParagraph">On the other hand, current mechanism can support users to switch 1 txq to another timely<o:p></o:p></p>
<p class="MsoListParagraph">in the application layer. If user want to choose another txq, he just need to change the txq_queue_id parameter<o:p></o:p></p>
<p class="MsoListParagraph">in the API.<o:p></o:p></p>
<ol style="margin-top:0in" start="2" type="1">
<li class="MsoListParagraph" style="margin-left:0in;mso-list:l2 level1 lfo3">If you want one rxq to support two txq at the same time, this needs to add spinlock on guard variable to<o:p></o:p></li></ol>
<p class="MsoListParagraph">avoid multi-thread conflict. Spinlock will decrease the data-path performance greatly.  Thus, we do not consider<o:p></o:p></p>
<p class="MsoListParagraph">1 rxq mapping multiple txqs here.<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>+ *<br>
+ * @param rx_port_id<br>
+ * Port identifying the receive side.<br>
+ * @param rx_queue_id<br>
+ * The index of the receive queue identifying the receive side.<br>
+ * The value must be in the range [0, nb_rx_queue - 1] previously supplied<br>
+ * to rte_eth_dev_configure().<br>
+ * @param tx_port_id<br>
+ * Port identifying the transmit side.<br>
+ * @param tx_queue_id<br>
+ * The index of the transmit queue identifying the transmit side.<br>
+ * The value must be in the range [0, nb_tx_queue - 1] previously supplied<br>
+ * to rte_eth_dev_configure().<br>
+ * @param recycle_rxq_info<br>
+ * A pointer to a structure of type *rte_eth_recycle_rxq_info* which contains<br>
+ * the information of the Rx queue mbuf ring.<br>
+ * @return<br>
+ * The number of recycling mbufs.<br>
+ */<br>
+__rte_experimental<br>
+static inline uint16_t<br>
+rte_eth_recycle_mbufs(uint16_t rx_port_id, uint16_t rx_queue_id,<br>
+ uint16_t tx_port_id, uint16_t tx_queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info)<br>
+{<br>
+ struct rte_eth_fp_ops *p;<br>
+ void *qd;<br>
+ uint16_t nb_mbufs;<br>
+<br>
+#ifdef RTE_ETHDEV_DEBUG_TX<br>
+ if (tx_port_id >= RTE_MAX_ETHPORTS ||<br>
+ tx_queue_id >= RTE_MAX_QUEUES_PER_PORT) {<br>
+ RTE_ETHDEV_LOG(ERR,<br>
+ "Invalid tx_port_id=%u or tx_queue_id=%u\n",<br>
+ tx_port_id, tx_queue_id);<br>
+ return 0;<br>
+ }<br>
+#endif<br>
+<br>
+ /* fetch pointer to queue data */<br>
+ p = &rte_eth_fp_ops[tx_port_id];<br>
+ qd = p->txq.data[tx_queue_id];<br>
+<br>
+#ifdef RTE_ETHDEV_DEBUG_TX<br>
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(tx_port_id, 0);<br>
+<br>
+ if (qd == NULL) {<br>
+ RTE_ETHDEV_LOG(ERR, "Invalid Tx queue_id=%u for port_id=%u\n",<br>
+ tx_queue_id, tx_port_id);<br>
+ return 0;<br>
+ }<br>
+#endif<br>
+ if (p->recycle_tx_mbufs_reuse == NULL)<br>
+ return 0;<br>
+<br>
+ /* Copy used *rte_mbuf* buffer pointers from Tx mbuf ring<br>
+ * into Rx mbuf ring.<br>
+ */<br>
+ nb_mbufs = p->recycle_tx_mbufs_reuse(qd, recycle_rxq_info);<br>
+<br>
+ /* If no recycling mbufs, return 0. */<br>
+ if (nb_mbufs == 0)<br>
+ return 0;<br>
+<br>
+#ifdef RTE_ETHDEV_DEBUG_RX<br>
+ if (rx_port_id >= RTE_MAX_ETHPORTS ||<br>
+ rx_queue_id >= RTE_MAX_QUEUES_PER_PORT) {<br>
+ RTE_ETHDEV_LOG(ERR, "Invalid rx_port_id=%u or rx_queue_id=%u\n",<br>
+ rx_port_id, rx_queue_id);<br>
+ return 0;<br>
+ }<br>
+#endif<br>
+<br>
+ /* fetch pointer to queue data */<br>
+ p = &rte_eth_fp_ops[rx_port_id];<br>
+ qd = p->rxq.data[rx_queue_id];<br>
+<br>
+#ifdef RTE_ETHDEV_DEBUG_RX<br>
+ RTE_ETH_VALID_PORTID_OR_ERR_RET(rx_port_id, 0);<br>
+<br>
+ if (qd == NULL) {<br>
+ RTE_ETHDEV_LOG(ERR, "Invalid Rx queue_id=%u for port_id=%u\n",<br>
+ rx_queue_id, rx_port_id);<br>
+ return 0;<br>
+ }<br>
+#endif<br>
+<br>
+ if (p->recycle_rx_descriptors_refill == NULL)<br>
+ return 0;<br>
+<br>
+ /* Replenish the Rx descriptors with the recycling<br>
+ * into Rx mbuf ring.<br>
+ */<br>
+ p->recycle_rx_descriptors_refill(qd, nb_mbufs);<br>
+<br>
+ return nb_mbufs;<br>
+}<br>
+<br>
 /**<br>
  * @warning<br>
  * @b EXPERIMENTAL: this API may change without prior notice<br>
diff --git a/lib/ethdev/rte_ethdev_core.h b/lib/ethdev/rte_ethdev_core.h<br>
index dcf8adab92..a2e6ea6b6c 100644<br>
--- a/lib/ethdev/rte_ethdev_core.h<br>
+++ b/lib/ethdev/rte_ethdev_core.h<br>
@@ -56,6 +56,13 @@ typedef int (*eth_rx_descriptor_status_t)(void *rxq, uint16_t offset);<br>
 /** @internal Check the status of a Tx descriptor */<br>
 typedef int (*eth_tx_descriptor_status_t)(void *txq, uint16_t offset);<br>
 <br>
+/** @internal Copy used mbufs from Tx mbuf ring into Rx mbuf ring */<br>
+typedef uint16_t (*eth_recycle_tx_mbufs_reuse_t)(void *txq,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info);<br>
+<br>
+/** @internal Refill Rx descriptors with the recycling mbufs */<br>
+typedef void (*eth_recycle_rx_descriptors_refill_t)(void *rxq, uint16_t nb);<br>
+<br>
 /**<br>
  * @internal<br>
  * Structure used to hold opaque pointers to internal ethdev Rx/Tx<br>
@@ -90,9 +97,11 @@ struct rte_eth_fp_ops {<br>
         eth_rx_queue_count_t rx_queue_count;<br>
         /** Check the status of a Rx descriptor. */<br>
         eth_rx_descriptor_status_t rx_descriptor_status;<br>
+ /** Refill Rx descriptors with the recycling mbufs. <span lang="FR">*/<br>
+ eth_recycle_rx_descriptors_refill_t recycle_rx_descriptors_refill;<o:p></o:p></span></p>
</blockquote>
<div>
<p class="MsoNormal">I am afraid we can't put new fields here without ABI breakage.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Agree<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">It has to be below rxq.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Now thinking about current layout probably not the best one,<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">and when introducing this struct, I should probably put rxq either<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">on the top of the struct, or on the next cache line.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">But such change is not possible right now anyway.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Same story for txq.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Thus we should rearrange the structure like below<span lang="ZH-CN" style="font-family:DengXian">:</span><o:p></o:p></p>
<p class="MsoNormal" style="margin-left:4.7pt">struct rte_eth_fp_ops {<o:p></o:p></p>
<p class="MsoNormal" style="margin-left:20.45pt"><span lang="FR">    struct rte_ethdev_qdata rxq;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin-left:9.95pt"><span lang="FR">         </span>eth_rx_burst_t rx_pkt_burst;<o:p></o:p></p>
<p class="MsoNormal" style="margin-left:9.95pt"><span lang="FR">         eth_rx_queue_count_t rx_queue_count;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin-left:9.95pt">         eth_rx_descriptor_status_t rx_descriptor_status;<o:p></o:p></p>
<p class="MsoNormal" style="margin-left:15.2pt"><span lang="FR">       eth_recycle_rx_descriptors_refill_t recycle_rx_descriptors_refill;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="FR">              </span>uintptr_t reserved1[2];<o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>         /** Rx queues data. */<br>
         struct rte_ethdev_qdata rxq;<br>
- uintptr_t reserved1[3];<br>
+ uintptr_t reserved1[2];<br>
         /**@}*/<br>
 <br>
         /**@{*/<br>
@@ -106,9 +115,11 @@ struct rte_eth_fp_ops {<br>
         eth_tx_prep_t tx_pkt_prepare;<br>
         /** Check the status of a Tx descriptor. */<br>
         eth_tx_descriptor_status_t tx_descriptor_status;<br>
+ /** Copy used mbufs from Tx mbuf ring into Rx. <span lang="FR">*/<br>
+ eth_recycle_tx_mbufs_reuse_t recycle_tx_mbufs_reuse;<br>
         /** Tx queues data. */<br>
         struct rte_ethdev_qdata txq;<br>
- uintptr_t reserved2[3];<br>
+ uintptr_t reserved2[2];<br>
         /**@}*/<br>
 <br>
 } __rte_cache_aligned;<br>
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map<br>
index 357d1a88c0..45c417f6bd 100644<br>
--- a/lib/ethdev/version.map<br>
+++ b/lib/ethdev/version.map<br>
@@ -299,6 +299,10 @@ EXPERIMENTAL {<br>
         rte_flow_action_handle_query_update;<br>
         rte_flow_async_action_handle_query_update;<br>
         rte_flow_async_create_by_index;<br>
+<br>
+ # added in 23.07<br>
+ rte_eth_recycle_mbufs;<br>
+ rte_eth_recycle_rx_queue_info_get;<br>
 };<br>
 <br>
 INTERNAL {<o:p></o:p></span></p>
<p class="MsoNormal">--<br>
2.25.1<br>
 <o:p></o:p></p>
</blockquote>
</div>
</div>
</body>
</html>