<div> </div><div> </div><div> </div><blockquote><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" rel="noopener noreferrer">honnappa.nagarahalli@arm.com</a>><br />Signed-off-by: Feifei Wang <<a href="mailto:feifei.wang2@arm.com" rel="noopener noreferrer">feifei.wang2@arm.com</a>><br />Reviewed-by: Ruifeng Wang <<a href="mailto:ruifeng.wang@arm.com" rel="noopener noreferrer">ruifeng.wang@arm.com</a>><br />Reviewed-by: Honnappa Nagarahalli <<a href="mailto:honnappa.nagarahalli@arm.com" rel="noopener noreferrer">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;</p></blockquote><div>I don't think that it is possible to simply return 0 here:</div><div>we might already have some mbufs inside rxep[], so we probably need</div><div>to return that number (j).</div><div> </div><blockquote><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;</p></blockquote><div> </div><div>same here, we can't simply return 0, it will introduce mbuf leakage.</div><blockquote><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);</p></blockquote><div>Wonder why do you need intermediate buffer for released mbufs?</div><div>Why can't just:</div><div>...</div><div>m = rte_pktmbuf_prefree_seg(txep[i].mbuf);</div><div>...</div><div>rxep[j++] = m;</div><div>?</div><div>Might save you few extra cycles.</div><div> </div><blockquote><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 /> </p>--<br />2.25.1<br /> </blockquote>