<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;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri",sans-serif;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></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"><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 9:03 PM<br>
<b>To:</b> Feifei Wang <Feifei.Wang2@arm.com>; Yuying Zhang <yuying.zhang@intel.com>; Beilei Xing <beilei.xing@intel.com><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 2/4] net/i40e: implement 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"> <o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>Define specific function implementation for i40e driver.<br>
Currently, mbufs recycle mode can support 128bit<br>
vector path and avx2 path. And can be enabled both in<br>
fast free and no fast free mode.<br>
<br>
Suggested-by: Honnappa Nagarahalli <<a href="mailto:honnappa.nagarahalli@arm.com">honnappa.nagarahalli@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>
 drivers/net/i40e/i40e_ethdev.c | 1 +<br>
 drivers/net/i40e/i40e_ethdev.h | 2 +<br>
 .../net/i40e/i40e_recycle_mbufs_vec_common.c | 140 ++++++++++++++++++<br>
 drivers/net/i40e/i40e_rxtx.c | 32 ++++<br>
 drivers/net/i40e/i40e_rxtx.h | 4 +<br>
 drivers/net/i40e/meson.build | 2 +<br>
 6 files changed, 181 insertions(+)<br>
 create mode 100644 drivers/net/i40e/i40e_recycle_mbufs_vec_common.c<br>
<br>
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c<br>
index f9d8f9791f..d4eecd16cf 100644<br>
--- a/drivers/net/i40e/i40e_ethdev.c<br>
+++ b/drivers/net/i40e/i40e_ethdev.c<br>
@@ -496,6 +496,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {<br>
         .flow_ops_get = i40e_dev_flow_ops_get,<br>
         .rxq_info_get = i40e_rxq_info_get,<br>
         .txq_info_get = i40e_txq_info_get,<br>
+ .recycle_rxq_info_get = i40e_recycle_rxq_info_get,<br>
         .rx_burst_mode_get = i40e_rx_burst_mode_get,<br>
         .tx_burst_mode_get = i40e_tx_burst_mode_get,<br>
         .timesync_enable = i40e_timesync_enable,<br>
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h<br>
index 9b806d130e..b5b2d6cf2b 100644<br>
--- a/drivers/net/i40e/i40e_ethdev.h<br>
+++ b/drivers/net/i40e/i40e_ethdev.h<br>
@@ -1355,6 +1355,8 @@ void i40e_rxq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
         struct rte_eth_rxq_info *qinfo);<br>
 void i40e_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
         struct rte_eth_txq_info *qinfo);<br>
+void i40e_recycle_rxq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info);<br>
 int i40e_rx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
                            struct rte_eth_burst_mode *mode);<br>
 int i40e_tx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
diff --git a/drivers/net/i40e/i40e_recycle_mbufs_vec_common.c b/drivers/net/i40e/i40e_recycle_mbufs_vec_common.c<br>
new file mode 100644<br>
index 0000000000..08d708fd7d<br>
--- /dev/null<br>
+++ b/drivers/net/i40e/i40e_recycle_mbufs_vec_common.c<br>
@@ -0,0 +1,140 @@<br>
+/* SPDX-License-Identifier: BSD-3-Clause<br>
+ * Copyright (c) 2023 Arm Limited.<br>
+ */<br>
+<br>
+#include <stdint.h><br>
+#include <ethdev_driver.h><br>
+<br>
+#include "base/i40e_prototype.h"<br>
+#include "base/i40e_type.h"<br>
+#include "i40e_ethdev.h"<br>
+#include "i40e_rxtx.h"<br>
+<br>
+#pragma GCC diagnostic ignored "-Wcast-qual"<br>
+<br>
+void<br>
+i40e_recycle_rx_descriptors_refill_vec(void *rx_queue, uint16_t nb_mbufs)<br>
+{<br>
+ struct i40e_rx_queue *rxq = rx_queue;<br>
+ struct i40e_rx_entry *rxep;<br>
+ volatile union i40e_rx_desc *rxdp;<br>
+ uint16_t rx_id;<br>
+ uint64_t paddr;<br>
+ uint64_t dma_addr;<br>
+ uint16_t i;<br>
+<br>
+ rxdp = rxq->rx_ring + rxq->rxrearm_start;<br>
+ rxep = &rxq->sw_ring[rxq->rxrearm_start];<br>
+<br>
+ for (i = 0; i < nb_mbufs; i++) {<br>
+ /* Initialize rxdp descs. */<br>
+ paddr = (rxep[i].mbuf)->buf_iova + RTE_PKTMBUF_HEADROOM;<br>
+ dma_addr = rte_cpu_to_le_64(paddr);<br>
+ /* flush desc with pa dma_addr */<br>
+ rxdp[i].read.hdr_addr = 0;<br>
+ rxdp[i].read.pkt_addr = dma_addr;<br>
+ }<br>
+<br>
+ /* Update the descriptor initializer index */<br>
+ rxq->rxrearm_start += nb_mbufs;<br>
+ rx_id = rxq->rxrearm_start - 1;<br>
+<br>
+ if (unlikely(rxq->rxrearm_start >= rxq->nb_rx_desc)) {<br>
+ rxq->rxrearm_start = 0;<br>
+ rx_id = rxq->nb_rx_desc - 1;<br>
+ }<br>
+<br>
+ rxq->rxrearm_nb -= nb_mbufs;<br>
+<br>
+ rte_io_wmb();<br>
+ /* Update the tail pointer on the NIC */<br>
+ I40E_PCI_REG_WRITE_RELAXED(rxq->qrx_tail, rx_id);<br>
+}<br>
+<br>
+uint16_t<br>
+i40e_recycle_tx_mbufs_reuse_vec(void *tx_queue,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info)<br>
+{<br>
+ struct i40e_tx_queue *txq = tx_queue;<br>
+ struct i40e_tx_entry *txep;<br>
+ struct rte_mbuf **rxep;<br>
+ struct rte_mbuf *m[RTE_I40E_TX_MAX_FREE_BUF_SZ];<br>
+ int i, j, n;<br>
+ uint16_t avail = 0;<br>
+ uint16_t mbuf_ring_size = recycle_rxq_info->mbuf_ring_size;<br>
+ uint16_t mask = recycle_rxq_info->mbuf_ring_size - 1;<br>
+ uint16_t refill_requirement = recycle_rxq_info->refill_requirement;<br>
+ uint16_t refill_head = *recycle_rxq_info->refill_head;<br>
+ uint16_t receive_tail = *recycle_rxq_info->receive_tail;<br>
+<br>
+ /* Get available recycling Rx buffers. */<br>
+ avail = (mbuf_ring_size - (refill_head - receive_tail)) & mask;<br>
+<br>
+ /* Check Tx free thresh and Rx available space. */<br>
+ if (txq->nb_tx_free > txq->tx_free_thresh || avail <= txq->tx_rs_thresh)<br>
+ return 0;<br>
+<br>
+ /* check DD bits on threshold descriptor */<br>
+ if ((txq->tx_ring[txq->tx_next_dd].cmd_type_offset_bsz &<br>
+ rte_cpu_to_le_64(I40E_TXD_QW1_DTYPE_MASK)) !=<br>
+ rte_cpu_to_le_64(I40E_TX_DESC_DTYPE_DESC_DONE))<br>
+ return 0;<br>
+<br>
+ n = txq->tx_rs_thresh;<br>
+<br>
+ /* Mbufs recycle mode can only support no ring buffer wrapping around.<br>
+ * Two case for this:<br>
+ *<br>
+ * case 1: The refill head of Rx buffer ring needs to be aligned with<br>
+ * mbuf ring size. In this case, the number of Tx freeing buffers<br>
+ * should be equal to refill_requirement.<br>
+ *<br>
+ * case 2: The refill head of Rx ring buffer does not need to be aligned<br>
+ * with mbuf ring size. In this case, the update of refill head can not<br>
+ * exceed the Rx mbuf ring size.<br>
+ */<br>
+ if (refill_requirement != n ||<br>
+ (!refill_requirement && (refill_head + n > mbuf_ring_size)))<br>
+ return 0;<br>
+<br>
+ /* First buffer to free from S/W ring is at index<br>
+ * tx_next_dd - (tx_rs_thresh-1).<br>
+ */<br>
+ txep = &txq->sw_ring[txq->tx_next_dd - (n - 1)];<br>
+ rxep = recycle_rxq_info->mbuf_ring;<br>
+ rxep += refill_head;<br>
+<br>
+ if (txq->offloads & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) {<br>
+ /* Directly put mbufs from Tx to Rx. */<br>
+ for (i = 0; i < n; i++, rxep++, txep++)<br>
+ *rxep = txep[0].mbuf;<br>
+ } else {<br>
+ for (i = 0, j = 0; i < n; i++) {<br>
+ /* Avoid txq contains buffers from expected mempool. */<br>
+ if (unlikely(recycle_rxq_info->mp<br>
+ != txep[i].mbuf->pool))<br>
+ return 0;<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal">I don't think that it is possible to simply return 0 here:<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">we might already have some mbufs inside rxep[], so we probably need<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">to return that number (j).<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">No, here is just pre-free, not actually put mbufs into rxeq.<o:p></o:p></p>
<p class="MsoNormal">After run out of the loop, we call rte_memcpy to actually copy<o:p></o:p></p>
<p class="MsoNormal">mbufs into rxep.<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>+<br>
+ m[j] = rte_pktmbuf_prefree_seg(txep[i].mbuf);<br>
+<br>
+ /* In case 1, each of Tx buffers should be the<br>
+ * last reference.<br>
+ */<br>
+ if (unlikely(m[j] == NULL && refill_requirement))<br>
+ return 0;<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal"> <o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">same here, we can't simply return 0, it will introduce mbuf leakage.<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>+ /* In case 2, the number of valid Tx free<br>
+ * buffers should be recorded.<br>
+ */<br>
+ j++;<br>
+ }<br>
+ rte_memcpy(rxep, m, sizeof(void *) * j);<o:p></o:p></p>
</blockquote>
<div>
<p class="MsoNormal">Wonder why do you need intermediate buffer for released mbufs?<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Why can't just:<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">...<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">m = rte_pktmbuf_prefree_seg(txep[i].mbuf);<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">...<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">rxep[j++] = m;<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">?<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Might save you few extra cycles.<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal">Sometimes ‘rte_pktmbuf_prefree_seg’ can return NULL due to<o:p></o:p></p>
<p class="MsoNormal">mbuf->refcnt > 1. So we should firstly ensure all ‘m’ are valid and<o:p></o:p></p>
<p class="MsoNormal">then copy them into rxep.<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p>+ }<br>
+<br>
+ /* Update counters for Tx. */<br>
+ txq->nb_tx_free = (uint16_t)(txq->nb_tx_free + txq->tx_rs_thresh);<br>
+ txq->tx_next_dd = (uint16_t)(txq->tx_next_dd + txq->tx_rs_thresh);<br>
+ if (txq->tx_next_dd >= txq->nb_tx_desc)<br>
+ txq->tx_next_dd = (uint16_t)(txq->tx_rs_thresh - 1);<br>
+<br>
+ return n;<br>
+}<br>
diff --git a/drivers/net/i40e/i40e_rxtx.c b/drivers/net/i40e/i40e_rxtx.c<br>
index 788ffb51c2..53cf787f04 100644<br>
--- a/drivers/net/i40e/i40e_rxtx.c<br>
+++ b/drivers/net/i40e/i40e_rxtx.c<br>
@@ -3197,6 +3197,30 @@ i40e_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
         qinfo->conf.offloads = txq->offloads;<br>
 }<br>
 <br>
+void<br>
+i40e_recycle_rxq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info)<br>
+{<br>
+ struct i40e_rx_queue *rxq;<br>
+ struct i40e_adapter *ad =<br>
+ I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);<br>
+<br>
+ rxq = dev->data->rx_queues[queue_id];<br>
+<br>
+ recycle_rxq_info->mbuf_ring = (void *)rxq->sw_ring;<br>
+ recycle_rxq_info->mp = rxq->mp;<br>
+ recycle_rxq_info->mbuf_ring_size = rxq->nb_rx_desc;<br>
+ recycle_rxq_info->receive_tail = &rxq->rx_tail;<br>
+<br>
+ if (ad->rx_vec_allowed) {<br>
+ recycle_rxq_info->refill_requirement = RTE_I40E_RXQ_REARM_THRESH;<br>
+ recycle_rxq_info->refill_head = &rxq->rxrearm_start;<br>
+ } else {<br>
+ recycle_rxq_info->refill_requirement = rxq->rx_free_thresh;<br>
+ recycle_rxq_info->refill_head = &rxq->rx_free_trigger;<br>
+ }<br>
+}<br>
+<br>
 #ifdef RTE_ARCH_X86<br>
 static inline bool<br>
 get_avx_supported(bool request_avx512)<br>
@@ -3291,6 +3315,8 @@ i40e_set_rx_function(struct rte_eth_dev *dev)<br>
                                 dev->rx_pkt_burst = ad->rx_use_avx2 ?<br>
                                         i40e_recv_scattered_pkts_vec_avx2 :<br>
                                         i40e_recv_scattered_pkts_vec;<br>
+ dev->recycle_rx_descriptors_refill =<br>
+ i40e_recycle_rx_descriptors_refill_vec;<br>
                         }<br>
                 } else {<br>
                         if (ad->rx_use_avx512) {<br>
@@ -3309,9 +3335,12 @@ i40e_set_rx_function(struct rte_eth_dev *dev)<br>
                                 dev->rx_pkt_burst = ad->rx_use_avx2 ?<br>
                                         i40e_recv_pkts_vec_avx2 :<br>
                                         i40e_recv_pkts_vec;<br>
+ dev->recycle_rx_descriptors_refill =<br>
+ i40e_recycle_rx_descriptors_refill_vec;<br>
                         }<br>
                 }<br>
 #else /* RTE_ARCH_X86 */<br>
+ dev->recycle_rx_descriptors_refill = i40e_recycle_rx_descriptors_refill_vec;<br>
                 if (dev->data->scattered_rx) {<br>
                         PMD_INIT_LOG(DEBUG,<br>
                                      "Using Vector Scattered Rx (port %d).",<br>
@@ -3479,15 +3508,18 @@ i40e_set_tx_function(struct rte_eth_dev *dev)<br>
                                 dev->tx_pkt_burst = ad->tx_use_avx2 ?<br>
                                                     i40e_xmit_pkts_vec_avx2 :<br>
                                                     i40e_xmit_pkts_vec;<br>
+ dev->recycle_tx_mbufs_reuse = i40e_recycle_tx_mbufs_reuse_vec;<br>
                         }<br>
 #else /* RTE_ARCH_X86 */<br>
                         PMD_INIT_LOG(DEBUG, "Using Vector Tx (port %d).",<br>
                                      dev->data->port_id);<br>
                         dev->tx_pkt_burst = i40e_xmit_pkts_vec;<br>
+ dev->recycle_tx_mbufs_reuse = i40e_recycle_tx_mbufs_reuse_vec;<br>
 #endif /* RTE_ARCH_X86 */<br>
                 } else {<br>
                         PMD_INIT_LOG(DEBUG, "Simple tx finally be used.");<br>
                         dev->tx_pkt_burst = i40e_xmit_pkts_simple;<br>
+ dev->recycle_tx_mbufs_reuse = i40e_recycle_tx_mbufs_reuse_vec;<br>
                 }<br>
                 dev->tx_pkt_prepare = i40e_simple_prep_pkts;<br>
         } else {<br>
diff --git a/drivers/net/i40e/i40e_rxtx.h b/drivers/net/i40e/i40e_rxtx.h<br>
index 5e6eecc501..ed8921ddc0 100644<br>
--- a/drivers/net/i40e/i40e_rxtx.h<br>
+++ b/drivers/net/i40e/i40e_rxtx.h<br>
@@ -233,6 +233,10 @@ uint32_t i40e_dev_rx_queue_count(void *rx_queue);<br>
 int i40e_dev_rx_descriptor_status(void *rx_queue, uint16_t offset);<br>
 int i40e_dev_tx_descriptor_status(void *tx_queue, uint16_t offset);<br>
 <br>
+uint16_t i40e_recycle_tx_mbufs_reuse_vec(void *tx_queue,<br>
+ struct rte_eth_recycle_rxq_info *recycle_rxq_info);<br>
+void i40e_recycle_rx_descriptors_refill_vec(void *rx_queue, uint16_t nb_mbufs);<br>
+<br>
 uint16_t i40e_recv_pkts_vec(void *rx_queue, struct rte_mbuf **rx_pkts,<br>
                             uint16_t nb_pkts);<br>
 uint16_t i40e_recv_scattered_pkts_vec(void *rx_queue,<br>
diff --git a/drivers/net/i40e/meson.build b/drivers/net/i40e/meson.build<br>
index 8e53b87a65..58eb627abc 100644<br>
--- a/drivers/net/i40e/meson.build<br>
+++ b/drivers/net/i40e/meson.build<br>
@@ -42,6 +42,8 @@ testpmd_sources = files('i40e_testpmd.c')<br>
 deps += ['hash']<br>
 includes += include_directories('base')<br>
 <br>
+sources += files('i40e_recycle_mbufs_vec_common.c')<br>
+<br>
 if arch_subdir == 'x86'<br>
     sources += files('i40e_rxtx_vec_sse.c')<br>
 <o:p></o:p></p>
<p class="MsoNormal">--<br>
2.25.1<br>
 <o:p></o:p></p>
</blockquote>
</div>
</div>
</body>
</html>