From a6bd4911ad93f8ab8a378591b3a86f82223695eb Mon Sep 17 00:00:00 2001 From: Viacheslav Ovsiienko Date: Sun, 21 Jul 2019 14:24:53 +0000 Subject: [PATCH] net/mlx5: remove Tx implementation This patch removes the existing Tx datapath code as preparation step before introducing the new implementation. The following entities are being removed: - deprecated devargs support - tx_burst() routines - related PRM definitions - SQ configuration code - Tx routine selection code - incompatible Tx completion code The following devargs are deprecated and ignored: - "txq_inline" is going to be converted to "txq_inline_max" for compatibility issue - "tx_vec_en" - "txqs_max_vec" - "txq_mpw_hdr_dseg_en" - "txq_max_inline_len" is going to be converted to "txq_inline_mpw" for compatibility issue The deprecated devarg keys are recognized by PMD and ignored/converted to the new ones in order not to block device probing. Signed-off-by: Viacheslav Ovsiienko Acked-by: Yongseok Koh --- doc/guides/nics/mlx5.rst | 34 +- drivers/net/mlx5/mlx5.c | 39 +- drivers/net/mlx5/mlx5.h | 5 - drivers/net/mlx5/mlx5_defs.h | 16 - drivers/net/mlx5/mlx5_ethdev.c | 58 - drivers/net/mlx5/mlx5_prm.h | 77 -- drivers/net/mlx5/mlx5_rxtx.c | 1434 +------------------------ drivers/net/mlx5/mlx5_rxtx.h | 273 ----- drivers/net/mlx5/mlx5_rxtx_vec.c | 175 --- drivers/net/mlx5/mlx5_rxtx_vec_neon.h | 289 ----- drivers/net/mlx5/mlx5_rxtx_vec_sse.h | 284 ----- drivers/net/mlx5/mlx5_txq.c | 110 +- 12 files changed, 65 insertions(+), 2729 deletions(-) diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst index 16aa390380..5cf1e76261 100644 --- a/doc/guides/nics/mlx5.rst +++ b/doc/guides/nics/mlx5.rst @@ -350,13 +350,8 @@ Run-time configuration - ``txq_inline`` parameter [int] - Amount of data to be inlined during TX operations. Improves latency. - Can improve PPS performance when PCI back pressure is detected and may be - useful for scenarios involving heavy traffic on many queues. - - Because additional software logic is necessary to handle this mode, this - option should be used with care, as it can lower performance when back - pressure is not expected. + Amount of data to be inlined during TX operations. This parameter is + deprecated and ignored, kept for compatibility issue. - ``txqs_min_inline`` parameter [int] @@ -378,16 +373,8 @@ Run-time configuration - ``txqs_max_vec`` parameter [int] Enable vectorized Tx only when the number of TX queues is less than or - equal to this value. Effective only when ``tx_vec_en`` is enabled. - - On ConnectX-5: - - - Set to 8 by default on ARMv8. - - Set to 4 by default otherwise. - - On BlueField - - - Set to 16 by default. + equal to this value. This parameter is deprecated and ignored, kept + for compatibility issue to not prevent driver from probing. - ``txq_mpw_en`` parameter [int] @@ -418,7 +405,8 @@ Run-time configuration - ``txq_mpw_hdr_dseg_en`` parameter [int] A nonzero value enables including two pointers in the first block of TX - descriptor. This can be used to lessen CPU load for memory copy. + descriptor. The parameter is deprecated and ignored, kept for compatibility + issue. Effective only when Enhanced MPS is supported. Disabled by default. @@ -427,14 +415,14 @@ Run-time configuration Maximum size of packet to be inlined. This limits the size of packet to be inlined. If the size of a packet is larger than configured value, the packet isn't inlined even though there's enough space remained in the - descriptor. Instead, the packet is included with pointer. - - Effective only when Enhanced MPS is supported. The default value is 256. + descriptor. Instead, the packet is included with pointer. This parameter + is deprecated. - ``tx_vec_en`` parameter [int] - A nonzero value enables Tx vector on ConnectX-5, ConnectX-6 and BlueField NICs if the number of - global Tx queues on the port is less than ``txqs_max_vec``. + A nonzero value enables Tx vector on ConnectX-5, ConnectX-6 and BlueField + NICs if the number of global Tx queues on the port is less than + ``txqs_max_vec``. The parameter is deprecated and ignored. This option cannot be used with certain offloads such as ``DEV_TX_OFFLOAD_TCP_TSO, DEV_TX_OFFLOAD_VXLAN_TNL_TSO, DEV_TX_OFFLOAD_GRE_TNL_TSO, DEV_TX_OFFLOAD_VLAN_INSERT``. diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c index 7e0df5183e..5b11b20fb9 100644 --- a/drivers/net/mlx5/mlx5.c +++ b/drivers/net/mlx5/mlx5.c @@ -69,7 +69,7 @@ /* Device parameter to set the minimum number of Rx queues to enable MPRQ. */ #define MLX5_RXQS_MIN_MPRQ "rxqs_min_mprq" -/* Device parameter to configure inline send. */ +/* Device parameter to configure inline send. Deprecated, ignored.*/ #define MLX5_TXQ_INLINE "txq_inline" /* @@ -80,20 +80,29 @@ /* * Device parameter to configure the number of TX queues threshold for - * enabling vectorized Tx. + * enabling vectorized Tx, deprecated, ignored (no vectorized Tx routines). */ #define MLX5_TXQS_MAX_VEC "txqs_max_vec" /* Device parameter to enable multi-packet send WQEs. */ #define MLX5_TXQ_MPW_EN "txq_mpw_en" -/* Device parameter to include 2 dsegs in the title WQEBB. */ +/* + * Device parameter to include 2 dsegs in the title WQEBB. + * Deprecated, ignored. + */ #define MLX5_TXQ_MPW_HDR_DSEG_EN "txq_mpw_hdr_dseg_en" -/* Device parameter to limit the size of inlining packet. */ +/* + * Device parameter to limit the size of inlining packet. + * Deprecated, ignored. + */ #define MLX5_TXQ_MAX_INLINE_LEN "txq_max_inline_len" -/* Device parameter to enable hardware Tx vector. */ +/* + * Device parameter to enable hardware Tx vector. + * Deprecated, ignored (no vectorized Tx routines anymore). + */ #define MLX5_TX_VEC_EN "tx_vec_en" /* Device parameter to enable hardware Rx vector. */ @@ -997,19 +1006,19 @@ mlx5_args_check(const char *key, const char *val, void *opaque) } else if (strcmp(MLX5_RXQS_MIN_MPRQ, key) == 0) { config->mprq.min_rxqs_num = tmp; } else if (strcmp(MLX5_TXQ_INLINE, key) == 0) { - config->txq_inline = tmp; + DRV_LOG(WARNING, "%s: deprecated parameter, ignored", key); } else if (strcmp(MLX5_TXQS_MIN_INLINE, key) == 0) { config->txqs_inline = tmp; } else if (strcmp(MLX5_TXQS_MAX_VEC, key) == 0) { - config->txqs_vec = tmp; + DRV_LOG(WARNING, "%s: deprecated parameter, ignored", key); } else if (strcmp(MLX5_TXQ_MPW_EN, key) == 0) { config->mps = !!tmp; } else if (strcmp(MLX5_TXQ_MPW_HDR_DSEG_EN, key) == 0) { - config->mpw_hdr_dseg = !!tmp; + DRV_LOG(WARNING, "%s: deprecated parameter, ignored", key); } else if (strcmp(MLX5_TXQ_MAX_INLINE_LEN, key) == 0) { - config->inline_max_packet_sz = tmp; + DRV_LOG(WARNING, "%s: deprecated parameter, ignored", key); } else if (strcmp(MLX5_TX_VEC_EN, key) == 0) { - config->tx_vec_en = !!tmp; + DRV_LOG(WARNING, "%s: deprecated parameter, ignored", key); } else if (strcmp(MLX5_RX_VEC_EN, key) == 0) { config->rx_vec_en = !!tmp; } else if (strcmp(MLX5_L3_VXLAN_EN, key) == 0) { @@ -2016,12 +2025,8 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, dev_config = (struct mlx5_dev_config){ .hw_padding = 0, .mps = MLX5_ARG_UNSET, - .tx_vec_en = 1, .rx_vec_en = 1, - .txq_inline = MLX5_ARG_UNSET, .txqs_inline = MLX5_ARG_UNSET, - .txqs_vec = MLX5_ARG_UNSET, - .inline_max_packet_sz = MLX5_ARG_UNSET, .vf_nl_en = 1, .mr_ext_memseg_en = 1, .mprq = { @@ -2034,9 +2039,6 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, }; /* Device specific configuration. */ switch (pci_dev->id.device_id) { - case PCI_DEVICE_ID_MELLANOX_CONNECTX5BF: - dev_config.txqs_vec = MLX5_VPMD_MAX_TXQS_BLUEFIELD; - break; case PCI_DEVICE_ID_MELLANOX_CONNECTX4VF: case PCI_DEVICE_ID_MELLANOX_CONNECTX4LXVF: case PCI_DEVICE_ID_MELLANOX_CONNECTX5VF: @@ -2046,9 +2048,6 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, default: break; } - /* Set architecture-dependent default value if unset. */ - if (dev_config.txqs_vec == MLX5_ARG_UNSET) - dev_config.txqs_vec = MLX5_VPMD_MAX_TXQS; for (i = 0; i != ns; ++i) { uint32_t restore; diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index 6230371356..354f6bc8a1 100644 --- a/drivers/net/mlx5/mlx5.h +++ b/drivers/net/mlx5/mlx5.h @@ -198,9 +198,7 @@ struct mlx5_dev_config { unsigned int cqe_comp:1; /* CQE compression is enabled. */ unsigned int cqe_pad:1; /* CQE padding is enabled. */ unsigned int tso:1; /* Whether TSO is supported. */ - unsigned int tx_vec_en:1; /* Tx vector is enabled. */ unsigned int rx_vec_en:1; /* Rx vector is enabled. */ - unsigned int mpw_hdr_dseg:1; /* Enable DSEGs in the title WQEBB. */ unsigned int mr_ext_memseg_en:1; /* Whether memseg should be extended for MR creation. */ unsigned int l3_vxlan_en:1; /* Enable L3 VXLAN flow creation. */ @@ -224,10 +222,7 @@ struct mlx5_dev_config { unsigned int tso_max_payload_sz; /* Maximum TCP payload for TSO. */ unsigned int ind_table_max_size; /* Maximum indirection table size. */ unsigned int max_dump_files_num; /* Maximum dump files per queue. */ - int txq_inline; /* Maximum packet size for inlining. */ int txqs_inline; /* Queue number threshold for inlining. */ - int txqs_vec; /* Queue number threshold for vectorized Tx. */ - int inline_max_packet_sz; /* Max packet size for inlining. */ struct mlx5_hca_attr hca_attr; /* HCA attributes. */ }; diff --git a/drivers/net/mlx5/mlx5_defs.h b/drivers/net/mlx5/mlx5_defs.h index 13801a5c2d..68613043dd 100644 --- a/drivers/net/mlx5/mlx5_defs.h +++ b/drivers/net/mlx5/mlx5_defs.h @@ -60,15 +60,6 @@ /* Maximum Packet headers size (L2+L3+L4) for TSO. */ #define MLX5_MAX_TSO_HEADER 192 -/* Default maximum number of Tx queues for vectorized Tx. */ -#if defined(RTE_ARCH_ARM64) -#define MLX5_VPMD_MAX_TXQS 8 -#define MLX5_VPMD_MAX_TXQS_BLUEFIELD 16 -#else -#define MLX5_VPMD_MAX_TXQS 4 -#define MLX5_VPMD_MAX_TXQS_BLUEFIELD MLX5_VPMD_MAX_TXQS -#endif - /* Threshold of buffer replenishment for vectorized Rx. */ #define MLX5_VPMD_RXQ_RPLNSH_THRESH(n) \ (RTE_MIN(MLX5_VPMD_RX_MAX_BURST, (unsigned int)(n) >> 2)) @@ -76,13 +67,6 @@ /* Maximum size of burst for vectorized Rx. */ #define MLX5_VPMD_RX_MAX_BURST 64U -/* - * Maximum size of burst for vectorized Tx. This is related to the maximum size - * of Enhanced MPW (eMPW) WQE as vectorized Tx is supported with eMPW. - * Careful when changing, large value can cause WQE DS to overlap. - */ -#define MLX5_VPMD_TX_MAX_BURST 32U - /* Number of packets vectorized Rx can simultaneously process in a loop. */ #define MLX5_VPMD_DESCS_PER_LOOP 4 diff --git a/drivers/net/mlx5/mlx5_ethdev.c b/drivers/net/mlx5/mlx5_ethdev.c index f9826c92b8..738d540f5a 100644 --- a/drivers/net/mlx5/mlx5_ethdev.c +++ b/drivers/net/mlx5/mlx5_ethdev.c @@ -1652,64 +1652,6 @@ mlx5_set_link_up(struct rte_eth_dev *dev) return mlx5_set_flags(dev, ~IFF_UP, IFF_UP); } -/** - * Configure the TX function to use. - * - * @param dev - * Pointer to private data structure. - * - * @return - * Pointer to selected Tx burst function. - */ -eth_tx_burst_t -mlx5_select_tx_function(struct rte_eth_dev *dev) -{ - struct mlx5_priv *priv = dev->data->dev_private; - eth_tx_burst_t tx_pkt_burst = mlx5_tx_burst; - struct mlx5_dev_config *config = &priv->config; - uint64_t tx_offloads = dev->data->dev_conf.txmode.offloads; - int tso = !!(tx_offloads & (DEV_TX_OFFLOAD_TCP_TSO | - DEV_TX_OFFLOAD_VXLAN_TNL_TSO | - DEV_TX_OFFLOAD_GRE_TNL_TSO | - DEV_TX_OFFLOAD_IP_TNL_TSO | - DEV_TX_OFFLOAD_UDP_TNL_TSO)); - int swp = !!(tx_offloads & (DEV_TX_OFFLOAD_IP_TNL_TSO | - DEV_TX_OFFLOAD_UDP_TNL_TSO | - DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM)); - int vlan_insert = !!(tx_offloads & DEV_TX_OFFLOAD_VLAN_INSERT); - - assert(priv != NULL); - /* Select appropriate TX function. */ - if (vlan_insert || tso || swp) - return tx_pkt_burst; - if (config->mps == MLX5_MPW_ENHANCED) { - if (mlx5_check_vec_tx_support(dev) > 0) { - if (mlx5_check_raw_vec_tx_support(dev) > 0) - tx_pkt_burst = mlx5_tx_burst_raw_vec; - else - tx_pkt_burst = mlx5_tx_burst_vec; - DRV_LOG(DEBUG, - "port %u selected enhanced MPW Tx vectorized" - " function", - dev->data->port_id); - } else { - tx_pkt_burst = mlx5_tx_burst_empw; - DRV_LOG(DEBUG, - "port %u selected enhanced MPW Tx function", - dev->data->port_id); - } - } else if (config->mps && (config->txq_inline > 0)) { - tx_pkt_burst = mlx5_tx_burst_mpw_inline; - DRV_LOG(DEBUG, "port %u selected MPW inline Tx function", - dev->data->port_id); - } else if (config->mps) { - tx_pkt_burst = mlx5_tx_burst_mpw; - DRV_LOG(DEBUG, "port %u selected MPW Tx function", - dev->data->port_id); - } - return tx_pkt_burst; -} - /** * Configure the RX function to use. * diff --git a/drivers/net/mlx5/mlx5_prm.h b/drivers/net/mlx5/mlx5_prm.h index 95ff29a08b..dfd93174fa 100644 --- a/drivers/net/mlx5/mlx5_prm.h +++ b/drivers/net/mlx5/mlx5_prm.h @@ -39,32 +39,12 @@ /* Invalidate a CQE. */ #define MLX5_CQE_INVALIDATE (MLX5_CQE_INVALID << 4) -/* Maximum number of packets a multi-packet WQE can handle. */ -#define MLX5_MPW_DSEG_MAX 5 - /* WQE DWORD size */ #define MLX5_WQE_DWORD_SIZE 16 /* WQE size */ #define MLX5_WQE_SIZE (4 * MLX5_WQE_DWORD_SIZE) -/* Max size of a WQE session. */ -#define MLX5_WQE_SIZE_MAX 960U - -/* Compute the number of DS. */ -#define MLX5_WQE_DS(n) \ - (((n) + MLX5_WQE_DWORD_SIZE - 1) / MLX5_WQE_DWORD_SIZE) - -/* Room for inline data in multi-packet WQE. */ -#define MLX5_MWQE64_INL_DATA 28 - -/* Default minimum number of Tx queues for inlining packets. */ -#define MLX5_EMPW_MIN_TXQS 8 - -/* Default max packet length to be inlined. */ -#define MLX5_EMPW_MAX_INLINE_LEN (4U * MLX5_WQE_SIZE) - - #define MLX5_OPC_MOD_ENHANCED_MPSW 0 #define MLX5_OPCODE_ENHANCED_MPSW 0x29 @@ -164,47 +144,11 @@ enum mlx5_completion_mode { MLX5_COMP_CQE_AND_EQE = 0x3, }; -/* Subset of struct mlx5_wqe_eth_seg. */ -struct mlx5_wqe_eth_seg_small { - uint32_t rsvd0; - uint8_t cs_flags; - uint8_t rsvd1; - uint16_t mss; - uint32_t flow_table_metadata; - uint16_t inline_hdr_sz; - uint8_t inline_hdr[2]; -} __rte_aligned(MLX5_WQE_DWORD_SIZE); - -struct mlx5_wqe_inl_small { - uint32_t byte_cnt; - uint8_t raw; -} __rte_aligned(MLX5_WQE_DWORD_SIZE); - -struct mlx5_wqe_ctrl { - uint32_t ctrl0; - uint32_t ctrl1; - uint32_t ctrl2; - uint32_t ctrl3; -} __rte_aligned(MLX5_WQE_DWORD_SIZE); - /* Small common part of the WQE. */ struct mlx5_wqe { uint32_t ctrl[4]; - struct mlx5_wqe_eth_seg_small eseg; -}; - -/* Vectorize WQE header. */ -struct mlx5_wqe_v { - rte_v128u32_t ctrl; - rte_v128u32_t eseg; }; -/* WQE. */ -struct mlx5_wqe64 { - struct mlx5_wqe hdr; - uint8_t raw[32]; -} __rte_aligned(MLX5_WQE_SIZE); - /* MPW mode. */ enum mlx5_mpw_mode { MLX5_MPW_DISABLED, @@ -212,27 +156,6 @@ enum mlx5_mpw_mode { MLX5_MPW_ENHANCED, /* Enhanced Multi-Packet Send WQE, a.k.a MPWv2. */ }; -/* MPW session status. */ -enum mlx5_mpw_state { - MLX5_MPW_STATE_OPENED, - MLX5_MPW_INL_STATE_OPENED, - MLX5_MPW_ENHANCED_STATE_OPENED, - MLX5_MPW_STATE_CLOSED, -}; - -/* MPW session descriptor. */ -struct mlx5_mpw { - enum mlx5_mpw_state state; - unsigned int pkts_n; - unsigned int len; - unsigned int total_len; - volatile struct mlx5_wqe *wqe; - union { - volatile struct mlx5_wqe_data_seg *dseg[MLX5_MPW_DSEG_MAX]; - volatile uint8_t *raw; - } data; -}; - /* WQE for Multi-Packet RQ. */ struct mlx5_wqe_mprq { struct mlx5_wqe_srq_next_seg next_seg; diff --git a/drivers/net/mlx5/mlx5_rxtx.c b/drivers/net/mlx5/mlx5_rxtx.c index c1dc8c4e17..f2d69187d6 100644 --- a/drivers/net/mlx5/mlx5_rxtx.c +++ b/drivers/net/mlx5/mlx5_rxtx.c @@ -287,140 +287,6 @@ mlx5_set_swp_types_table(void) } } -/** - * Return the size of tailroom of WQ. - * - * @param txq - * Pointer to TX queue structure. - * @param addr - * Pointer to tail of WQ. - * - * @return - * Size of tailroom. - */ -static inline size_t -tx_mlx5_wq_tailroom(struct mlx5_txq_data *txq, void *addr) -{ - size_t tailroom; - tailroom = (uintptr_t)(txq->wqes) + - (1 << txq->wqe_n) * MLX5_WQE_SIZE - - (uintptr_t)addr; - return tailroom; -} - -/** - * Copy data to tailroom of circular queue. - * - * @param dst - * Pointer to destination. - * @param src - * Pointer to source. - * @param n - * Number of bytes to copy. - * @param base - * Pointer to head of queue. - * @param tailroom - * Size of tailroom from dst. - * - * @return - * Pointer after copied data. - */ -static inline void * -mlx5_copy_to_wq(void *dst, const void *src, size_t n, - void *base, size_t tailroom) -{ - void *ret; - - if (n > tailroom) { - rte_memcpy(dst, src, tailroom); - rte_memcpy(base, (void *)((uintptr_t)src + tailroom), - n - tailroom); - ret = (uint8_t *)base + n - tailroom; - } else { - rte_memcpy(dst, src, n); - ret = (n == tailroom) ? base : (uint8_t *)dst + n; - } - return ret; -} - -/** - * Inline TSO headers into WQE. - * - * @return - * 0 on success, negative errno value on failure. - */ -static int -inline_tso(struct mlx5_txq_data *txq, struct rte_mbuf *buf, - uint32_t *length, - uintptr_t *addr, - uint16_t *pkt_inline_sz, - uint8_t **raw, - uint16_t *max_wqe, - uint16_t *tso_segsz, - uint16_t *tso_header_sz) -{ - uintptr_t end = (uintptr_t)(((uintptr_t)txq->wqes) + - (1 << txq->wqe_n) * MLX5_WQE_SIZE); - unsigned int copy_b; - uint8_t vlan_sz = (buf->ol_flags & PKT_TX_VLAN_PKT) ? 4 : 0; - const uint8_t tunneled = txq->tunnel_en && (buf->ol_flags & - PKT_TX_TUNNEL_MASK); - uint16_t n_wqe; - - *tso_segsz = buf->tso_segsz; - *tso_header_sz = buf->l2_len + vlan_sz + buf->l3_len + buf->l4_len; - if (unlikely(*tso_segsz == 0 || *tso_header_sz == 0)) { - txq->stats.oerrors++; - return -EINVAL; - } - if (tunneled) - *tso_header_sz += buf->outer_l2_len + buf->outer_l3_len; - /* First seg must contain all TSO headers. */ - if (unlikely(*tso_header_sz > MLX5_MAX_TSO_HEADER) || - *tso_header_sz > DATA_LEN(buf)) { - txq->stats.oerrors++; - return -EINVAL; - } - copy_b = *tso_header_sz - *pkt_inline_sz; - if (!copy_b || ((end - (uintptr_t)*raw) < copy_b)) - return -EAGAIN; - n_wqe = (MLX5_WQE_DS(copy_b) - 1 + 3) / 4; - if (unlikely(*max_wqe < n_wqe)) - return -EINVAL; - *max_wqe -= n_wqe; - rte_memcpy((void *)*raw, (void *)*addr, copy_b); - *length -= copy_b; - *addr += copy_b; - copy_b = MLX5_WQE_DS(copy_b) * MLX5_WQE_DWORD_SIZE; - *pkt_inline_sz += copy_b; - *raw += copy_b; - return 0; -} - -/** - * DPDK callback to check the status of a tx descriptor. - * - * @param tx_queue - * The tx queue. - * @param[in] offset - * The index of the descriptor in the ring. - * - * @return - * The status of the tx descriptor. - */ -int -mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset) -{ - struct mlx5_txq_data *txq = tx_queue; - uint16_t used; - - mlx5_tx_complete(txq); - used = txq->elts_head - txq->elts_tail; - if (offset < used) - return RTE_ETH_TX_DESC_FULL; - return RTE_ETH_TX_DESC_DONE; -} - /** * Internal function to compute the number of used descriptors in an RX queue * @@ -655,7 +521,7 @@ mlx5_tx_error_cqe_handle(struct mlx5_txq_data *txq, (1 << txq->cqe_n)); mlx5_dump_debug_information(name, "MLX5 Error SQ:", (const void *)((uintptr_t) - tx_mlx5_wqe(txq, 0)), + txq->wqes), MLX5_WQE_SIZE * (1 << txq->wqe_n)); txq_ctrl->dump_file_n++; @@ -682,1247 +548,6 @@ mlx5_tx_error_cqe_handle(struct mlx5_txq_data *txq, return txq->elts_tail; } -/** - * DPDK callback for TX. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - unsigned int i = 0; - unsigned int j = 0; - unsigned int k = 0; - uint16_t max_elts; - uint16_t max_wqe; - unsigned int comp; - volatile struct mlx5_wqe_ctrl *last_wqe = NULL; - unsigned int segs_n = 0; - const unsigned int max_inline = txq->max_inline; - uint64_t addr_64; - - if (unlikely(!pkts_n)) - return 0; - /* Prefetch first packet cacheline. */ - rte_prefetch0(*pkts); - /* Start processing. */ - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - if (unlikely(!max_wqe)) - return 0; - do { - struct rte_mbuf *buf = *pkts; /* First_seg. */ - uint8_t *raw; - volatile struct mlx5_wqe_v *wqe = NULL; - volatile rte_v128u32_t *dseg = NULL; - uint32_t length; - unsigned int ds = 0; - unsigned int sg = 0; /* counter of additional segs attached. */ - uintptr_t addr; - uint16_t pkt_inline_sz = MLX5_WQE_DWORD_SIZE + 2; - uint16_t tso_header_sz = 0; - uint16_t ehdr; - uint8_t cs_flags; - uint8_t tso = txq->tso_en && (buf->ol_flags & PKT_TX_TCP_SEG); - uint32_t swp_offsets = 0; - uint8_t swp_types = 0; - rte_be32_t metadata; - uint16_t tso_segsz = 0; -#ifdef MLX5_PMD_SOFT_COUNTERS - uint32_t total_length = 0; -#endif - int ret; - - segs_n = buf->nb_segs; - /* - * Make sure there is enough room to store this packet and - * that one ring entry remains unused. - */ - assert(segs_n); - if (max_elts < segs_n) - break; - max_elts -= segs_n; - sg = --segs_n; - if (unlikely(--max_wqe == 0)) - break; - wqe = (volatile struct mlx5_wqe_v *) - tx_mlx5_wqe(txq, txq->wqe_ci); - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1)); - if (pkts_n - i > 1) - rte_prefetch0(*(pkts + 1)); - addr = rte_pktmbuf_mtod(buf, uintptr_t); - length = DATA_LEN(buf); - ehdr = (((uint8_t *)addr)[1] << 8) | - ((uint8_t *)addr)[0]; -#ifdef MLX5_PMD_SOFT_COUNTERS - total_length = length; -#endif - if (length < (MLX5_WQE_DWORD_SIZE + 2)) { - txq->stats.oerrors++; - break; - } - /* Update element. */ - (*txq->elts)[elts_head & elts_m] = buf; - /* Prefetch next buffer data. */ - if (pkts_n - i > 1) - rte_prefetch0( - rte_pktmbuf_mtod(*(pkts + 1), volatile void *)); - cs_flags = txq_ol_cksum_to_cs(buf); - txq_mbuf_to_swp(txq, buf, (uint8_t *)&swp_offsets, &swp_types); - raw = ((uint8_t *)(uintptr_t)wqe) + 2 * MLX5_WQE_DWORD_SIZE; - /* Copy metadata from mbuf if valid */ - metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata : - 0; - /* Replace the Ethernet type by the VLAN if necessary. */ - if (buf->ol_flags & PKT_TX_VLAN_PKT) { - uint32_t vlan = rte_cpu_to_be_32(0x81000000 | - buf->vlan_tci); - unsigned int len = 2 * RTE_ETHER_ADDR_LEN - 2; - - addr += 2; - length -= 2; - /* Copy Destination and source mac address. */ - memcpy((uint8_t *)raw, ((uint8_t *)addr), len); - /* Copy VLAN. */ - memcpy((uint8_t *)raw + len, &vlan, sizeof(vlan)); - /* Copy missing two bytes to end the DSeg. */ - memcpy((uint8_t *)raw + len + sizeof(vlan), - ((uint8_t *)addr) + len, 2); - addr += len + 2; - length -= (len + 2); - } else { - memcpy((uint8_t *)raw, ((uint8_t *)addr) + 2, - MLX5_WQE_DWORD_SIZE); - length -= pkt_inline_sz; - addr += pkt_inline_sz; - } - raw += MLX5_WQE_DWORD_SIZE; - if (tso) { - ret = inline_tso(txq, buf, &length, - &addr, &pkt_inline_sz, - &raw, &max_wqe, - &tso_segsz, &tso_header_sz); - if (ret == -EINVAL) { - break; - } else if (ret == -EAGAIN) { - /* NOP WQE. */ - wqe->ctrl = (rte_v128u32_t){ - rte_cpu_to_be_32(txq->wqe_ci << 8), - rte_cpu_to_be_32(txq->qp_num_8s | 1), - rte_cpu_to_be_32 - (MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET), - 0, - }; - ds = 1; -#ifdef MLX5_PMD_SOFT_COUNTERS - total_length = 0; -#endif - k++; - goto next_wqe; - } - } - /* Inline if enough room. */ - if (max_inline || tso) { - uint32_t inl = 0; - uintptr_t end = (uintptr_t) - (((uintptr_t)txq->wqes) + - (1 << txq->wqe_n) * MLX5_WQE_SIZE); - unsigned int inline_room = max_inline * - RTE_CACHE_LINE_SIZE - - (pkt_inline_sz - 2) - - !!tso * sizeof(inl); - uintptr_t addr_end; - unsigned int copy_b; - -pkt_inline: - addr_end = RTE_ALIGN_FLOOR(addr + inline_room, - RTE_CACHE_LINE_SIZE); - copy_b = (addr_end > addr) ? - RTE_MIN((addr_end - addr), length) : 0; - if (copy_b && ((end - (uintptr_t)raw) > - (copy_b + sizeof(inl)))) { - /* - * One Dseg remains in the current WQE. To - * keep the computation positive, it is - * removed after the bytes to Dseg conversion. - */ - uint16_t n = (MLX5_WQE_DS(copy_b) - 1 + 3) / 4; - - if (unlikely(max_wqe < n)) - break; - max_wqe -= n; - if (tso) { - assert(inl == 0); - inl = rte_cpu_to_be_32(copy_b | - MLX5_INLINE_SEG); - rte_memcpy((void *)raw, - (void *)&inl, sizeof(inl)); - raw += sizeof(inl); - pkt_inline_sz += sizeof(inl); - } - rte_memcpy((void *)raw, (void *)addr, copy_b); - addr += copy_b; - length -= copy_b; - pkt_inline_sz += copy_b; - } - /* - * 2 DWORDs consumed by the WQE header + ETH segment + - * the size of the inline part of the packet. - */ - ds = 2 + MLX5_WQE_DS(pkt_inline_sz - 2); - if (length > 0) { - if (ds % (MLX5_WQE_SIZE / - MLX5_WQE_DWORD_SIZE) == 0) { - if (unlikely(--max_wqe == 0)) - break; - dseg = (volatile rte_v128u32_t *) - tx_mlx5_wqe(txq, txq->wqe_ci + - ds / 4); - } else { - dseg = (volatile rte_v128u32_t *) - ((uintptr_t)wqe + - (ds * MLX5_WQE_DWORD_SIZE)); - } - goto use_dseg; - } else if (!segs_n) { - goto next_pkt; - } else { - /* - * Further inline the next segment only for - * non-TSO packets. - */ - if (!tso) { - raw += copy_b; - inline_room -= copy_b; - } else { - inline_room = 0; - } - /* Move to the next segment. */ - --segs_n; - buf = buf->next; - assert(buf); - addr = rte_pktmbuf_mtod(buf, uintptr_t); - length = DATA_LEN(buf); -#ifdef MLX5_PMD_SOFT_COUNTERS - total_length += length; -#endif - (*txq->elts)[++elts_head & elts_m] = buf; - goto pkt_inline; - } - } else { - /* - * No inline has been done in the packet, only the - * Ethernet Header as been stored. - */ - dseg = (volatile rte_v128u32_t *) - ((uintptr_t)wqe + (3 * MLX5_WQE_DWORD_SIZE)); - ds = 3; -use_dseg: - /* Add the remaining packet as a simple ds. */ - addr_64 = rte_cpu_to_be_64(addr); - *dseg = (rte_v128u32_t){ - rte_cpu_to_be_32(length), - mlx5_tx_mb2mr(txq, buf), - addr_64, - addr_64 >> 32, - }; - ++ds; - if (!segs_n) - goto next_pkt; - } -next_seg: - assert(buf); - assert(ds); - assert(wqe); - /* - * Spill on next WQE when the current one does not have - * enough room left. Size of WQE must a be a multiple - * of data segment size. - */ - assert(!(MLX5_WQE_SIZE % MLX5_WQE_DWORD_SIZE)); - if (!(ds % (MLX5_WQE_SIZE / MLX5_WQE_DWORD_SIZE))) { - if (unlikely(--max_wqe == 0)) - break; - dseg = (volatile rte_v128u32_t *) - tx_mlx5_wqe(txq, txq->wqe_ci + ds / 4); - rte_prefetch0(tx_mlx5_wqe(txq, - txq->wqe_ci + ds / 4 + 1)); - } else { - ++dseg; - } - ++ds; - buf = buf->next; - assert(buf); - length = DATA_LEN(buf); -#ifdef MLX5_PMD_SOFT_COUNTERS - total_length += length; -#endif - /* Store segment information. */ - addr_64 = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf, uintptr_t)); - *dseg = (rte_v128u32_t){ - rte_cpu_to_be_32(length), - mlx5_tx_mb2mr(txq, buf), - addr_64, - addr_64 >> 32, - }; - (*txq->elts)[++elts_head & elts_m] = buf; - if (--segs_n) - goto next_seg; -next_pkt: - if (ds > MLX5_DSEG_MAX) { - txq->stats.oerrors++; - break; - } - ++elts_head; - ++pkts; - ++i; - j += sg; - /* Initialize known and common part of the WQE structure. */ - if (tso) { - wqe->ctrl = (rte_v128u32_t){ - rte_cpu_to_be_32((txq->wqe_ci << 8) | - MLX5_OPCODE_TSO), - rte_cpu_to_be_32(txq->qp_num_8s | ds), - rte_cpu_to_be_32(MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET), - 0, - }; - wqe->eseg = (rte_v128u32_t){ - swp_offsets, - cs_flags | (swp_types << 8) | - (rte_cpu_to_be_16(tso_segsz) << 16), - metadata, - (ehdr << 16) | rte_cpu_to_be_16(tso_header_sz), - }; - } else { - wqe->ctrl = (rte_v128u32_t){ - rte_cpu_to_be_32((txq->wqe_ci << 8) | - MLX5_OPCODE_SEND), - rte_cpu_to_be_32(txq->qp_num_8s | ds), - rte_cpu_to_be_32(MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET), - 0, - }; - wqe->eseg = (rte_v128u32_t){ - swp_offsets, - cs_flags | (swp_types << 8), - metadata, - (ehdr << 16) | rte_cpu_to_be_16(pkt_inline_sz), - }; - } -next_wqe: - txq->wqe_ci += (ds + 3) / 4; - /* Save the last successful WQE for completion request */ - last_wqe = (volatile struct mlx5_wqe_ctrl *)wqe; -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent bytes counter. */ - txq->stats.obytes += total_length; -#endif - } while (i < pkts_n); - /* Take a shortcut if nothing must be sent. */ - if (unlikely((i + k) == 0)) - return 0; - txq->elts_head += (i + j); - /* Check whether completion threshold has been reached. */ - comp = txq->elts_comp + i + j + k; - if (comp >= MLX5_TX_COMP_THRESH) { - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request completion on last WQE. */ - last_wqe->ctrl2 = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - /* Save elts_head in unused "immediate" field of WQE. */ - last_wqe->ctrl3 = txq->elts_head; - txq->elts_comp = 0; - } else { - txq->elts_comp = comp; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent packets counter. */ - txq->stats.opackets += i; -#endif - /* Ring QP doorbell. */ - mlx5_tx_dbrec(txq, (volatile struct mlx5_wqe *)last_wqe); - return i; -} - -/** - * Open a MPW session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - * @param length - * Packet length. - */ -static inline void -mlx5_mpw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, uint32_t length) -{ - uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1); - volatile struct mlx5_wqe_data_seg (*dseg)[MLX5_MPW_DSEG_MAX] = - (volatile struct mlx5_wqe_data_seg (*)[]) - tx_mlx5_wqe(txq, idx + 1); - - mpw->state = MLX5_MPW_STATE_OPENED; - mpw->pkts_n = 0; - mpw->len = length; - mpw->total_len = 0; - mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx); - mpw->wqe->eseg.mss = rte_cpu_to_be_16(length); - mpw->wqe->eseg.inline_hdr_sz = 0; - mpw->wqe->eseg.rsvd0 = 0; - mpw->wqe->eseg.rsvd1 = 0; - mpw->wqe->eseg.flow_table_metadata = 0; - mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) | - (txq->wqe_ci << 8) | - MLX5_OPCODE_TSO); - mpw->wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET); - mpw->wqe->ctrl[3] = 0; - mpw->data.dseg[0] = (volatile struct mlx5_wqe_data_seg *) - (((uintptr_t)mpw->wqe) + (2 * MLX5_WQE_DWORD_SIZE)); - mpw->data.dseg[1] = (volatile struct mlx5_wqe_data_seg *) - (((uintptr_t)mpw->wqe) + (3 * MLX5_WQE_DWORD_SIZE)); - mpw->data.dseg[2] = &(*dseg)[0]; - mpw->data.dseg[3] = &(*dseg)[1]; - mpw->data.dseg[4] = &(*dseg)[2]; -} - -/** - * Close a MPW session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - */ -static inline void -mlx5_mpw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw) -{ - unsigned int num = mpw->pkts_n; - - /* - * Store size in multiple of 16 bytes. Control and Ethernet segments - * count as 2. - */ - mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s | (2 + num)); - mpw->state = MLX5_MPW_STATE_CLOSED; - if (num < 3) - ++txq->wqe_ci; - else - txq->wqe_ci += 2; - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci)); - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1)); -} - -/** - * DPDK callback for TX with MPW support. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - unsigned int i = 0; - unsigned int j = 0; - uint16_t max_elts; - uint16_t max_wqe; - unsigned int comp; - struct mlx5_mpw mpw = { - .state = MLX5_MPW_STATE_CLOSED, - }; - - if (unlikely(!pkts_n)) - return 0; - /* Prefetch first packet cacheline. */ - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci)); - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1)); - /* Start processing. */ - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - if (unlikely(!max_wqe)) - return 0; - do { - struct rte_mbuf *buf = *(pkts++); - uint32_t length; - unsigned int segs_n = buf->nb_segs; - uint32_t cs_flags; - rte_be32_t metadata; - - /* - * Make sure there is enough room to store this packet and - * that one ring entry remains unused. - */ - assert(segs_n); - if (max_elts < segs_n) - break; - /* Do not bother with large packets MPW cannot handle. */ - if (segs_n > MLX5_MPW_DSEG_MAX) { - txq->stats.oerrors++; - break; - } - max_elts -= segs_n; - --pkts_n; - cs_flags = txq_ol_cksum_to_cs(buf); - /* Copy metadata from mbuf if valid */ - metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata : - 0; - /* Retrieve packet information. */ - length = PKT_LEN(buf); - assert(length); - /* Start new session if packet differs. */ - if ((mpw.state == MLX5_MPW_STATE_OPENED) && - ((mpw.len != length) || - (segs_n != 1) || - (mpw.wqe->eseg.flow_table_metadata != metadata) || - (mpw.wqe->eseg.cs_flags != cs_flags))) - mlx5_mpw_close(txq, &mpw); - if (mpw.state == MLX5_MPW_STATE_CLOSED) { - /* - * Multi-Packet WQE consumes at most two WQE. - * mlx5_mpw_new() expects to be able to use such - * resources. - */ - if (unlikely(max_wqe < 2)) - break; - max_wqe -= 2; - mlx5_mpw_new(txq, &mpw, length); - mpw.wqe->eseg.cs_flags = cs_flags; - mpw.wqe->eseg.flow_table_metadata = metadata; - } - /* Multi-segment packets must be alone in their MPW. */ - assert((segs_n == 1) || (mpw.pkts_n == 0)); -#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG) - length = 0; -#endif - do { - volatile struct mlx5_wqe_data_seg *dseg; - uintptr_t addr; - - assert(buf); - (*txq->elts)[elts_head++ & elts_m] = buf; - dseg = mpw.data.dseg[mpw.pkts_n]; - addr = rte_pktmbuf_mtod(buf, uintptr_t); - *dseg = (struct mlx5_wqe_data_seg){ - .byte_count = rte_cpu_to_be_32(DATA_LEN(buf)), - .lkey = mlx5_tx_mb2mr(txq, buf), - .addr = rte_cpu_to_be_64(addr), - }; -#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG) - length += DATA_LEN(buf); -#endif - buf = buf->next; - ++mpw.pkts_n; - ++j; - } while (--segs_n); - assert(length == mpw.len); - if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) - mlx5_mpw_close(txq, &mpw); -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent bytes counter. */ - txq->stats.obytes += length; -#endif - ++i; - } while (pkts_n); - /* Take a shortcut if nothing must be sent. */ - if (unlikely(i == 0)) - return 0; - /* Check whether completion threshold has been reached. */ - /* "j" includes both packets and segments. */ - comp = txq->elts_comp + j; - if (comp >= MLX5_TX_COMP_THRESH) { - volatile struct mlx5_wqe *wqe = mpw.wqe; - - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request completion on last WQE. */ - wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - /* Save elts_head in unused "immediate" field of WQE. */ - wqe->ctrl[3] = elts_head; - txq->elts_comp = 0; - } else { - txq->elts_comp = comp; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent packets counter. */ - txq->stats.opackets += i; -#endif - /* Ring QP doorbell. */ - if (mpw.state == MLX5_MPW_STATE_OPENED) - mlx5_mpw_close(txq, &mpw); - mlx5_tx_dbrec(txq, mpw.wqe); - txq->elts_head = elts_head; - return i; -} - -/** - * Open a MPW inline session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - * @param length - * Packet length. - */ -static inline void -mlx5_mpw_inline_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, - uint32_t length) -{ - uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1); - struct mlx5_wqe_inl_small *inl; - - mpw->state = MLX5_MPW_INL_STATE_OPENED; - mpw->pkts_n = 0; - mpw->len = length; - mpw->total_len = 0; - mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx); - mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) | - (txq->wqe_ci << 8) | - MLX5_OPCODE_TSO); - mpw->wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET); - mpw->wqe->ctrl[3] = 0; - mpw->wqe->eseg.mss = rte_cpu_to_be_16(length); - mpw->wqe->eseg.inline_hdr_sz = 0; - mpw->wqe->eseg.cs_flags = 0; - mpw->wqe->eseg.rsvd0 = 0; - mpw->wqe->eseg.rsvd1 = 0; - mpw->wqe->eseg.flow_table_metadata = 0; - inl = (struct mlx5_wqe_inl_small *) - (((uintptr_t)mpw->wqe) + 2 * MLX5_WQE_DWORD_SIZE); - mpw->data.raw = (uint8_t *)&inl->raw; -} - -/** - * Close a MPW inline session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - */ -static inline void -mlx5_mpw_inline_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw) -{ - unsigned int size; - struct mlx5_wqe_inl_small *inl = (struct mlx5_wqe_inl_small *) - (((uintptr_t)mpw->wqe) + (2 * MLX5_WQE_DWORD_SIZE)); - - size = MLX5_WQE_SIZE - MLX5_MWQE64_INL_DATA + mpw->total_len; - /* - * Store size in multiple of 16 bytes. Control and Ethernet segments - * count as 2. - */ - mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s | - MLX5_WQE_DS(size)); - mpw->state = MLX5_MPW_STATE_CLOSED; - inl->byte_cnt = rte_cpu_to_be_32(mpw->total_len | MLX5_INLINE_SEG); - txq->wqe_ci += (size + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE; -} - -/** - * DPDK callback for TX with MPW inline support. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - unsigned int i = 0; - unsigned int j = 0; - uint16_t max_elts; - uint16_t max_wqe; - unsigned int comp; - unsigned int inline_room = txq->max_inline * RTE_CACHE_LINE_SIZE; - struct mlx5_mpw mpw = { - .state = MLX5_MPW_STATE_CLOSED, - }; - /* - * Compute the maximum number of WQE which can be consumed by inline - * code. - * - 2 DSEG for: - * - 1 control segment, - * - 1 Ethernet segment, - * - N Dseg from the inline request. - */ - const unsigned int wqe_inl_n = - ((2 * MLX5_WQE_DWORD_SIZE + - txq->max_inline * RTE_CACHE_LINE_SIZE) + - RTE_CACHE_LINE_SIZE - 1) / RTE_CACHE_LINE_SIZE; - - if (unlikely(!pkts_n)) - return 0; - /* Prefetch first packet cacheline. */ - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci)); - rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1)); - /* Start processing. */ - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - do { - struct rte_mbuf *buf = *(pkts++); - uintptr_t addr; - uint32_t length; - unsigned int segs_n = buf->nb_segs; - uint8_t cs_flags; - rte_be32_t metadata; - - /* - * Make sure there is enough room to store this packet and - * that one ring entry remains unused. - */ - assert(segs_n); - if (max_elts < segs_n) - break; - /* Do not bother with large packets MPW cannot handle. */ - if (segs_n > MLX5_MPW_DSEG_MAX) { - txq->stats.oerrors++; - break; - } - max_elts -= segs_n; - --pkts_n; - /* - * Compute max_wqe in case less WQE were consumed in previous - * iteration. - */ - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - cs_flags = txq_ol_cksum_to_cs(buf); - /* Copy metadata from mbuf if valid */ - metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata : - 0; - /* Retrieve packet information. */ - length = PKT_LEN(buf); - /* Start new session if packet differs. */ - if (mpw.state == MLX5_MPW_STATE_OPENED) { - if ((mpw.len != length) || - (segs_n != 1) || - (mpw.wqe->eseg.flow_table_metadata != metadata) || - (mpw.wqe->eseg.cs_flags != cs_flags)) - mlx5_mpw_close(txq, &mpw); - } else if (mpw.state == MLX5_MPW_INL_STATE_OPENED) { - if ((mpw.len != length) || - (segs_n != 1) || - (length > inline_room) || - (mpw.wqe->eseg.flow_table_metadata != metadata) || - (mpw.wqe->eseg.cs_flags != cs_flags)) { - mlx5_mpw_inline_close(txq, &mpw); - inline_room = - txq->max_inline * RTE_CACHE_LINE_SIZE; - } - } - if (mpw.state == MLX5_MPW_STATE_CLOSED) { - if ((segs_n != 1) || - (length > inline_room)) { - /* - * Multi-Packet WQE consumes at most two WQE. - * mlx5_mpw_new() expects to be able to use - * such resources. - */ - if (unlikely(max_wqe < 2)) - break; - max_wqe -= 2; - mlx5_mpw_new(txq, &mpw, length); - mpw.wqe->eseg.cs_flags = cs_flags; - mpw.wqe->eseg.flow_table_metadata = metadata; - } else { - if (unlikely(max_wqe < wqe_inl_n)) - break; - max_wqe -= wqe_inl_n; - mlx5_mpw_inline_new(txq, &mpw, length); - mpw.wqe->eseg.cs_flags = cs_flags; - mpw.wqe->eseg.flow_table_metadata = metadata; - } - } - /* Multi-segment packets must be alone in their MPW. */ - assert((segs_n == 1) || (mpw.pkts_n == 0)); - if (mpw.state == MLX5_MPW_STATE_OPENED) { - assert(inline_room == - txq->max_inline * RTE_CACHE_LINE_SIZE); -#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG) - length = 0; -#endif - do { - volatile struct mlx5_wqe_data_seg *dseg; - - assert(buf); - (*txq->elts)[elts_head++ & elts_m] = buf; - dseg = mpw.data.dseg[mpw.pkts_n]; - addr = rte_pktmbuf_mtod(buf, uintptr_t); - *dseg = (struct mlx5_wqe_data_seg){ - .byte_count = - rte_cpu_to_be_32(DATA_LEN(buf)), - .lkey = mlx5_tx_mb2mr(txq, buf), - .addr = rte_cpu_to_be_64(addr), - }; -#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG) - length += DATA_LEN(buf); -#endif - buf = buf->next; - ++mpw.pkts_n; - ++j; - } while (--segs_n); - assert(length == mpw.len); - if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) - mlx5_mpw_close(txq, &mpw); - } else { - unsigned int max; - - assert(mpw.state == MLX5_MPW_INL_STATE_OPENED); - assert(length <= inline_room); - assert(length == DATA_LEN(buf)); - addr = rte_pktmbuf_mtod(buf, uintptr_t); - (*txq->elts)[elts_head++ & elts_m] = buf; - /* Maximum number of bytes before wrapping. */ - max = ((((uintptr_t)(txq->wqes)) + - (1 << txq->wqe_n) * - MLX5_WQE_SIZE) - - (uintptr_t)mpw.data.raw); - if (length > max) { - rte_memcpy((void *)(uintptr_t)mpw.data.raw, - (void *)addr, - max); - mpw.data.raw = (volatile void *)txq->wqes; - rte_memcpy((void *)(uintptr_t)mpw.data.raw, - (void *)(addr + max), - length - max); - mpw.data.raw += length - max; - } else { - rte_memcpy((void *)(uintptr_t)mpw.data.raw, - (void *)addr, - length); - - if (length == max) - mpw.data.raw = - (volatile void *)txq->wqes; - else - mpw.data.raw += length; - } - ++mpw.pkts_n; - mpw.total_len += length; - ++j; - if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) { - mlx5_mpw_inline_close(txq, &mpw); - inline_room = - txq->max_inline * RTE_CACHE_LINE_SIZE; - } else { - inline_room -= length; - } - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent bytes counter. */ - txq->stats.obytes += length; -#endif - ++i; - } while (pkts_n); - /* Take a shortcut if nothing must be sent. */ - if (unlikely(i == 0)) - return 0; - /* Check whether completion threshold has been reached. */ - /* "j" includes both packets and segments. */ - comp = txq->elts_comp + j; - if (comp >= MLX5_TX_COMP_THRESH) { - volatile struct mlx5_wqe *wqe = mpw.wqe; - - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request completion on last WQE. */ - wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - /* Save elts_head in unused "immediate" field of WQE. */ - wqe->ctrl[3] = elts_head; - txq->elts_comp = 0; - } else { - txq->elts_comp = comp; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent packets counter. */ - txq->stats.opackets += i; -#endif - /* Ring QP doorbell. */ - if (mpw.state == MLX5_MPW_INL_STATE_OPENED) - mlx5_mpw_inline_close(txq, &mpw); - else if (mpw.state == MLX5_MPW_STATE_OPENED) - mlx5_mpw_close(txq, &mpw); - mlx5_tx_dbrec(txq, mpw.wqe); - txq->elts_head = elts_head; - return i; -} - -/** - * Open an Enhanced MPW session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - * @param length - * Packet length. - */ -static inline void -mlx5_empw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, int padding) -{ - uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1); - - mpw->state = MLX5_MPW_ENHANCED_STATE_OPENED; - mpw->pkts_n = 0; - mpw->total_len = sizeof(struct mlx5_wqe); - mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx); - mpw->wqe->ctrl[0] = - rte_cpu_to_be_32((MLX5_OPC_MOD_ENHANCED_MPSW << 24) | - (txq->wqe_ci << 8) | - MLX5_OPCODE_ENHANCED_MPSW); - mpw->wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ONLY_FIRST_ERR << - MLX5_COMP_MODE_OFFSET); - mpw->wqe->ctrl[3] = 0; - memset((void *)(uintptr_t)&mpw->wqe->eseg, 0, MLX5_WQE_DWORD_SIZE); - if (unlikely(padding)) { - uintptr_t addr = (uintptr_t)(mpw->wqe + 1); - - /* Pad the first 2 DWORDs with zero-length inline header. */ - *(volatile uint32_t *)addr = rte_cpu_to_be_32(MLX5_INLINE_SEG); - *(volatile uint32_t *)(addr + MLX5_WQE_DWORD_SIZE) = - rte_cpu_to_be_32(MLX5_INLINE_SEG); - mpw->total_len += 2 * MLX5_WQE_DWORD_SIZE; - /* Start from the next WQEBB. */ - mpw->data.raw = (volatile void *)(tx_mlx5_wqe(txq, idx + 1)); - } else { - mpw->data.raw = (volatile void *)(mpw->wqe + 1); - } -} - -/** - * Close an Enhanced MPW session. - * - * @param txq - * Pointer to TX queue structure. - * @param mpw - * Pointer to MPW session structure. - * - * @return - * Number of consumed WQEs. - */ -static inline uint16_t -mlx5_empw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw) -{ - uint16_t ret; - - /* Store size in multiple of 16 bytes. Control and Ethernet segments - * count as 2. - */ - mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s | - MLX5_WQE_DS(mpw->total_len)); - mpw->state = MLX5_MPW_STATE_CLOSED; - ret = (mpw->total_len + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE; - txq->wqe_ci += ret; - return ret; -} - -/** - * TX with Enhanced MPW support. - * - * @param txq - * Pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -static inline uint16_t -txq_burst_empw(struct mlx5_txq_data *txq, struct rte_mbuf **pkts, - uint16_t pkts_n) -{ - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - unsigned int i = 0; - unsigned int j = 0; - uint16_t max_elts; - uint16_t max_wqe; - unsigned int max_inline = txq->max_inline * RTE_CACHE_LINE_SIZE; - unsigned int mpw_room = 0; - unsigned int inl_pad = 0; - uint32_t inl_hdr; - uint64_t addr_64; - struct mlx5_mpw mpw = { - .state = MLX5_MPW_STATE_CLOSED, - }; - - if (unlikely(!pkts_n)) - return 0; - /* Start processing. */ - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - if (unlikely(!max_wqe)) - return 0; - do { - struct rte_mbuf *buf = *(pkts++); - uintptr_t addr; - unsigned int do_inline = 0; /* Whether inline is possible. */ - uint32_t length; - uint8_t cs_flags; - rte_be32_t metadata; - - /* Multi-segmented packet is handled in slow-path outside. */ - assert(NB_SEGS(buf) == 1); - /* Make sure there is enough room to store this packet. */ - if (max_elts - j == 0) - break; - cs_flags = txq_ol_cksum_to_cs(buf); - /* Copy metadata from mbuf if valid */ - metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata : - 0; - /* Retrieve packet information. */ - length = PKT_LEN(buf); - /* Start new session if: - * - multi-segment packet - * - no space left even for a dseg - * - next packet can be inlined with a new WQE - * - cs_flag differs - */ - if (mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED) { - if ((inl_pad + sizeof(struct mlx5_wqe_data_seg) > - mpw_room) || - (length <= txq->inline_max_packet_sz && - inl_pad + sizeof(inl_hdr) + length > - mpw_room) || - (mpw.wqe->eseg.flow_table_metadata != metadata) || - (mpw.wqe->eseg.cs_flags != cs_flags)) - max_wqe -= mlx5_empw_close(txq, &mpw); - } - if (unlikely(mpw.state == MLX5_MPW_STATE_CLOSED)) { - /* In Enhanced MPW, inline as much as the budget is - * allowed. The remaining space is to be filled with - * dsegs. If the title WQEBB isn't padded, it will have - * 2 dsegs there. - */ - mpw_room = RTE_MIN(MLX5_WQE_SIZE_MAX, - (max_inline ? max_inline : - pkts_n * MLX5_WQE_DWORD_SIZE) + - MLX5_WQE_SIZE); - if (unlikely(max_wqe * MLX5_WQE_SIZE < mpw_room)) - break; - /* Don't pad the title WQEBB to not waste WQ. */ - mlx5_empw_new(txq, &mpw, 0); - mpw_room -= mpw.total_len; - inl_pad = 0; - do_inline = length <= txq->inline_max_packet_sz && - sizeof(inl_hdr) + length <= mpw_room && - !txq->mpw_hdr_dseg; - mpw.wqe->eseg.cs_flags = cs_flags; - mpw.wqe->eseg.flow_table_metadata = metadata; - } else { - /* Evaluate whether the next packet can be inlined. - * Inlininig is possible when: - * - length is less than configured value - * - length fits for remaining space - * - not required to fill the title WQEBB with dsegs - */ - do_inline = - length <= txq->inline_max_packet_sz && - inl_pad + sizeof(inl_hdr) + length <= - mpw_room && - (!txq->mpw_hdr_dseg || - mpw.total_len >= MLX5_WQE_SIZE); - } - if (max_inline && do_inline) { - /* Inline packet into WQE. */ - unsigned int max; - - assert(mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED); - assert(length == DATA_LEN(buf)); - inl_hdr = rte_cpu_to_be_32(length | MLX5_INLINE_SEG); - addr = rte_pktmbuf_mtod(buf, uintptr_t); - mpw.data.raw = (volatile void *) - ((uintptr_t)mpw.data.raw + inl_pad); - max = tx_mlx5_wq_tailroom(txq, - (void *)(uintptr_t)mpw.data.raw); - /* Copy inline header. */ - mpw.data.raw = (volatile void *) - mlx5_copy_to_wq( - (void *)(uintptr_t)mpw.data.raw, - &inl_hdr, - sizeof(inl_hdr), - (void *)(uintptr_t)txq->wqes, - max); - max = tx_mlx5_wq_tailroom(txq, - (void *)(uintptr_t)mpw.data.raw); - /* Copy packet data. */ - mpw.data.raw = (volatile void *) - mlx5_copy_to_wq( - (void *)(uintptr_t)mpw.data.raw, - (void *)addr, - length, - (void *)(uintptr_t)txq->wqes, - max); - ++mpw.pkts_n; - mpw.total_len += (inl_pad + sizeof(inl_hdr) + length); - /* No need to get completion as the entire packet is - * copied to WQ. Free the buf right away. - */ - rte_pktmbuf_free_seg(buf); - mpw_room -= (inl_pad + sizeof(inl_hdr) + length); - /* Add pad in the next packet if any. */ - inl_pad = (((uintptr_t)mpw.data.raw + - (MLX5_WQE_DWORD_SIZE - 1)) & - ~(MLX5_WQE_DWORD_SIZE - 1)) - - (uintptr_t)mpw.data.raw; - } else { - /* No inline. Load a dseg of packet pointer. */ - volatile rte_v128u32_t *dseg; - - assert(mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED); - assert((inl_pad + sizeof(*dseg)) <= mpw_room); - assert(length == DATA_LEN(buf)); - if (!tx_mlx5_wq_tailroom(txq, - (void *)((uintptr_t)mpw.data.raw - + inl_pad))) - dseg = (volatile void *)txq->wqes; - else - dseg = (volatile void *) - ((uintptr_t)mpw.data.raw + - inl_pad); - (*txq->elts)[elts_head++ & elts_m] = buf; - addr_64 = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf, - uintptr_t)); - *dseg = (rte_v128u32_t) { - rte_cpu_to_be_32(length), - mlx5_tx_mb2mr(txq, buf), - addr_64, - addr_64 >> 32, - }; - mpw.data.raw = (volatile void *)(dseg + 1); - mpw.total_len += (inl_pad + sizeof(*dseg)); - ++j; - ++mpw.pkts_n; - mpw_room -= (inl_pad + sizeof(*dseg)); - inl_pad = 0; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent bytes counter. */ - txq->stats.obytes += length; -#endif - ++i; - } while (i < pkts_n); - /* Take a shortcut if nothing must be sent. */ - if (unlikely(i == 0)) - return 0; - /* Check whether completion threshold has been reached. */ - if (txq->elts_comp + j >= MLX5_TX_COMP_THRESH || - (uint16_t)(txq->wqe_ci - txq->mpw_comp) >= - (1 << txq->wqe_n) / MLX5_TX_COMP_THRESH_INLINE_DIV) { - volatile struct mlx5_wqe *wqe = mpw.wqe; - - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request completion on last WQE. */ - wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - /* Save elts_head in unused "immediate" field of WQE. */ - wqe->ctrl[3] = elts_head; - txq->elts_comp = 0; - txq->mpw_comp = txq->wqe_ci; - } else { - txq->elts_comp += j; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - /* Increment sent packets counter. */ - txq->stats.opackets += i; -#endif - if (mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED) - mlx5_empw_close(txq, &mpw); - /* Ring QP doorbell. */ - mlx5_tx_dbrec(txq, mpw.wqe); - txq->elts_head = elts_head; - return i; -} - -/** - * DPDK callback for TX with Enhanced MPW support. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t nb_tx = 0; - - while (pkts_n > nb_tx) { - uint16_t n; - uint16_t ret; - - n = txq_count_contig_multi_seg(&pkts[nb_tx], pkts_n - nb_tx); - if (n) { - ret = mlx5_tx_burst(dpdk_txq, &pkts[nb_tx], n); - if (!ret) - break; - nb_tx += ret; - } - n = txq_count_contig_single_seg(&pkts[nb_tx], pkts_n - nb_tx); - if (n) { - ret = txq_burst_empw(txq, &pkts[nb_tx], n); - if (!ret) - break; - nb_tx += ret; - } - } - return nb_tx; -} - /** * Translate RX completion flags to packet type. * @@ -2866,22 +1491,6 @@ removed_rx_burst(void *dpdk_txq __rte_unused, * (e.g. mlx5_rxtx_vec_sse.c for x86). */ -__rte_weak uint16_t -mlx5_tx_burst_raw_vec(void *dpdk_txq __rte_unused, - struct rte_mbuf **pkts __rte_unused, - uint16_t pkts_n __rte_unused) -{ - return 0; -} - -__rte_weak uint16_t -mlx5_tx_burst_vec(void *dpdk_txq __rte_unused, - struct rte_mbuf **pkts __rte_unused, - uint16_t pkts_n __rte_unused) -{ - return 0; -} - __rte_weak uint16_t mlx5_rx_burst_vec(void *dpdk_txq __rte_unused, struct rte_mbuf **pkts __rte_unused, @@ -2891,25 +1500,50 @@ mlx5_rx_burst_vec(void *dpdk_txq __rte_unused, } __rte_weak int -mlx5_check_raw_vec_tx_support(struct rte_eth_dev *dev __rte_unused) +mlx5_rxq_check_vec_support(struct mlx5_rxq_data *rxq __rte_unused) { return -ENOTSUP; } __rte_weak int -mlx5_check_vec_tx_support(struct rte_eth_dev *dev __rte_unused) +mlx5_check_vec_rx_support(struct rte_eth_dev *dev __rte_unused) { return -ENOTSUP; } -__rte_weak int -mlx5_rxq_check_vec_support(struct mlx5_rxq_data *rxq __rte_unused) +/** + * DPDK callback to check the status of a tx descriptor. + * + * @param tx_queue + * The tx queue. + * @param[in] offset + * The index of the descriptor in the ring. + * + * @return + * The status of the tx descriptor. + */ +int +mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset) { - return -ENOTSUP; + (void)tx_queue; + (void)offset; + return RTE_ETH_TX_DESC_FULL; } -__rte_weak int -mlx5_check_vec_rx_support(struct rte_eth_dev *dev __rte_unused) +/** + * Configure the TX function to use. + * + * @param dev + * Pointer to private data structure. + * + * @return + * Pointer to selected Tx burst function. + */ +eth_tx_burst_t +mlx5_select_tx_function(struct rte_eth_dev *dev) { - return -ENOTSUP; + (void)dev; + return removed_tx_burst; } + + diff --git a/drivers/net/mlx5/mlx5_rxtx.h b/drivers/net/mlx5/mlx5_rxtx.h index 3d79c18b60..acde09d4d0 100644 --- a/drivers/net/mlx5/mlx5_rxtx.h +++ b/drivers/net/mlx5/mlx5_rxtx.h @@ -329,14 +329,6 @@ extern uint8_t mlx5_swp_types_table[]; void mlx5_set_ptype_table(void); void mlx5_set_cksum_table(void); void mlx5_set_swp_types_table(void); -uint16_t mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); -uint16_t mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); -uint16_t mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); -uint16_t mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); __rte_noinline uint16_t mlx5_tx_error_cqe_handle(struct mlx5_txq_data *txq, volatile struct mlx5_err_cqe *err_cqe); uint16_t mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n); @@ -360,14 +352,8 @@ int mlx5_queue_state_modify_primary(struct rte_eth_dev *dev, const struct mlx5_mp_arg_queue_state_modify *sm); /* Vectorized version of mlx5_rxtx.c */ -int mlx5_check_raw_vec_tx_support(struct rte_eth_dev *dev); -int mlx5_check_vec_tx_support(struct rte_eth_dev *dev); int mlx5_rxq_check_vec_support(struct mlx5_rxq_data *rxq_data); int mlx5_check_vec_rx_support(struct rte_eth_dev *dev); -uint16_t mlx5_tx_burst_raw_vec(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); -uint16_t mlx5_tx_burst_vec(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n); uint16_t mlx5_rx_burst_vec(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n); @@ -477,122 +463,6 @@ check_cqe(volatile struct mlx5_cqe *cqe, const uint16_t cqes_n, return MLX5_CQE_STATUS_SW_OWN; } -/** - * Return the address of the WQE. - * - * @param txq - * Pointer to TX queue structure. - * @param wqe_ci - * WQE consumer index. - * - * @return - * WQE address. - */ -static inline uintptr_t * -tx_mlx5_wqe(struct mlx5_txq_data *txq, uint16_t ci) -{ - ci &= ((1 << txq->wqe_n) - 1); - return (uintptr_t *)((uintptr_t)txq->wqes + ci * MLX5_WQE_SIZE); -} - -/** - * Handle the next CQE. - * - * @param txq - * Pointer to TX queue structure. - * - * @return - * The last Tx buffer element to free. - */ -static __rte_always_inline uint16_t -mlx5_tx_cqe_handle(struct mlx5_txq_data *txq) -{ - const unsigned int cqe_n = 1 << txq->cqe_n; - const unsigned int cqe_cnt = cqe_n - 1; - uint16_t last_elts; - union { - volatile struct mlx5_cqe *cqe; - volatile struct mlx5_err_cqe *err_cqe; - } u = { - .cqe = &(*txq->cqes)[txq->cq_ci & cqe_cnt], - }; - int ret = check_cqe(u.cqe, cqe_n, txq->cq_ci); - - if (unlikely(ret != MLX5_CQE_STATUS_SW_OWN)) { - if (unlikely(ret == MLX5_CQE_STATUS_ERR)) - last_elts = mlx5_tx_error_cqe_handle(txq, u.err_cqe); - else - /* Do not release buffers. */ - return txq->elts_tail; - } else { - uint16_t new_wqe_pi = rte_be_to_cpu_16(u.cqe->wqe_counter); - volatile struct mlx5_wqe_ctrl *ctrl = - (volatile struct mlx5_wqe_ctrl *) - tx_mlx5_wqe(txq, new_wqe_pi); - - /* Release completion burst buffers. */ - last_elts = ctrl->ctrl3; - txq->wqe_pi = new_wqe_pi; - txq->cq_ci++; - } - rte_compiler_barrier(); - *txq->cq_db = rte_cpu_to_be_32(txq->cq_ci); - return last_elts; -} - -/** - * Manage TX completions. - * - * When sending a burst, mlx5_tx_burst() posts several WRs. - * - * @param txq - * Pointer to TX queue structure. - */ -static __rte_always_inline void -mlx5_tx_complete(struct mlx5_txq_data *txq) -{ - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - uint16_t elts_free = txq->elts_tail; - uint16_t elts_tail; - struct rte_mbuf *m, *free[elts_n]; - struct rte_mempool *pool = NULL; - unsigned int blk_n = 0; - - elts_tail = mlx5_tx_cqe_handle(txq); - assert((elts_tail & elts_m) < (1 << txq->wqe_n)); - /* Free buffers. */ - while (elts_free != elts_tail) { - m = rte_pktmbuf_prefree_seg((*txq->elts)[elts_free++ & elts_m]); - if (likely(m != NULL)) { - if (likely(m->pool == pool)) { - free[blk_n++] = m; - } else { - if (likely(pool != NULL)) - rte_mempool_put_bulk(pool, - (void *)free, - blk_n); - free[0] = m; - pool = m->pool; - blk_n = 1; - } - } - } - if (blk_n) - rte_mempool_put_bulk(pool, (void *)free, blk_n); -#ifndef NDEBUG - elts_free = txq->elts_tail; - /* Poisoning. */ - while (elts_free != elts_tail) { - memset(&(*txq->elts)[elts_free & elts_m], - 0x66, - sizeof((*txq->elts)[elts_free & elts_m])); - ++elts_free; - } -#endif - txq->elts_tail = elts_tail; -} - /** * Get Memory Pool (MP) from mbuf. If mbuf is indirect, the pool from which the * cloned mbuf is allocated is returned instead. @@ -710,147 +580,4 @@ mlx5_tx_dbrec(struct mlx5_txq_data *txq, volatile struct mlx5_wqe *wqe) mlx5_tx_dbrec_cond_wmb(txq, wqe, 1); } -/** - * Convert mbuf to Verb SWP. - * - * @param txq_data - * Pointer to the Tx queue. - * @param buf - * Pointer to the mbuf. - * @param offsets - * Pointer to the SWP header offsets. - * @param swp_types - * Pointer to the SWP header types. - */ -static __rte_always_inline void -txq_mbuf_to_swp(struct mlx5_txq_data *txq, struct rte_mbuf *buf, - uint8_t *offsets, uint8_t *swp_types) -{ - const uint64_t vlan = buf->ol_flags & PKT_TX_VLAN_PKT; - const uint64_t tunnel = buf->ol_flags & PKT_TX_TUNNEL_MASK; - const uint64_t tso = buf->ol_flags & PKT_TX_TCP_SEG; - const uint64_t csum_flags = buf->ol_flags & PKT_TX_L4_MASK; - const uint64_t inner_ip = - buf->ol_flags & (PKT_TX_IPV4 | PKT_TX_IPV6); - const uint64_t ol_flags_mask = PKT_TX_L4_MASK | PKT_TX_IPV6 | - PKT_TX_OUTER_IPV6; - uint16_t idx; - uint16_t off; - - if (likely(!txq->swp_en || (tunnel != PKT_TX_TUNNEL_UDP && - tunnel != PKT_TX_TUNNEL_IP))) - return; - /* - * The index should have: - * bit[0:1] = PKT_TX_L4_MASK - * bit[4] = PKT_TX_IPV6 - * bit[8] = PKT_TX_OUTER_IPV6 - * bit[9] = PKT_TX_OUTER_UDP - */ - idx = (buf->ol_flags & ol_flags_mask) >> 52; - if (tunnel == PKT_TX_TUNNEL_UDP) - idx |= 1 << 9; - *swp_types = mlx5_swp_types_table[idx]; - /* - * Set offsets for SW parser. Since ConnectX-5, SW parser just - * complements HW parser. SW parser starts to engage only if HW parser - * can't reach a header. For the older devices, HW parser will not kick - * in if any of SWP offsets is set. Therefore, all of the L3 offsets - * should be set regardless of HW offload. - */ - off = buf->outer_l2_len + (vlan ? sizeof(struct rte_vlan_hdr) : 0); - offsets[1] = off >> 1; /* Outer L3 offset. */ - off += buf->outer_l3_len; - if (tunnel == PKT_TX_TUNNEL_UDP) - offsets[0] = off >> 1; /* Outer L4 offset. */ - if (inner_ip) { - off += buf->l2_len; - offsets[3] = off >> 1; /* Inner L3 offset. */ - if (csum_flags == PKT_TX_TCP_CKSUM || tso || - csum_flags == PKT_TX_UDP_CKSUM) { - off += buf->l3_len; - offsets[2] = off >> 1; /* Inner L4 offset. */ - } - } -} - -/** - * Convert the Checksum offloads to Verbs. - * - * @param buf - * Pointer to the mbuf. - * - * @return - * Converted checksum flags. - */ -static __rte_always_inline uint8_t -txq_ol_cksum_to_cs(struct rte_mbuf *buf) -{ - uint32_t idx; - uint8_t is_tunnel = !!(buf->ol_flags & PKT_TX_TUNNEL_MASK); - const uint64_t ol_flags_mask = PKT_TX_TCP_SEG | PKT_TX_L4_MASK | - PKT_TX_IP_CKSUM | PKT_TX_OUTER_IP_CKSUM; - - /* - * The index should have: - * bit[0] = PKT_TX_TCP_SEG - * bit[2:3] = PKT_TX_UDP_CKSUM, PKT_TX_TCP_CKSUM - * bit[4] = PKT_TX_IP_CKSUM - * bit[8] = PKT_TX_OUTER_IP_CKSUM - * bit[9] = tunnel - */ - idx = ((buf->ol_flags & ol_flags_mask) >> 50) | (!!is_tunnel << 9); - return mlx5_cksum_table[idx]; -} - -/** - * Count the number of contiguous single segment packets. - * - * @param pkts - * Pointer to array of packets. - * @param pkts_n - * Number of packets. - * - * @return - * Number of contiguous single segment packets. - */ -static __rte_always_inline unsigned int -txq_count_contig_single_seg(struct rte_mbuf **pkts, uint16_t pkts_n) -{ - unsigned int pos; - - if (!pkts_n) - return 0; - /* Count the number of contiguous single segment packets. */ - for (pos = 0; pos < pkts_n; ++pos) - if (NB_SEGS(pkts[pos]) > 1) - break; - return pos; -} - -/** - * Count the number of contiguous multi-segment packets. - * - * @param pkts - * Pointer to array of packets. - * @param pkts_n - * Number of packets. - * - * @return - * Number of contiguous multi-segment packets. - */ -static __rte_always_inline unsigned int -txq_count_contig_multi_seg(struct rte_mbuf **pkts, uint16_t pkts_n) -{ - unsigned int pos; - - if (!pkts_n) - return 0; - /* Count the number of contiguous multi-segment packets. */ - for (pos = 0; pos < pkts_n; ++pos) - if (NB_SEGS(pkts[pos]) == 1) - break; - return pos; -} - #endif /* RTE_PMD_MLX5_RXTX_H_ */ diff --git a/drivers/net/mlx5/mlx5_rxtx_vec.c b/drivers/net/mlx5/mlx5_rxtx_vec.c index 073044f6d1..f6ec8289e6 100644 --- a/drivers/net/mlx5/mlx5_rxtx_vec.c +++ b/drivers/net/mlx5/mlx5_rxtx_vec.c @@ -39,138 +39,6 @@ #error "This should not be compiled if SIMD instructions are not supported." #endif -/** - * Count the number of packets having same ol_flags and same metadata (if - * PKT_TX_METADATA is set in ol_flags), and calculate cs_flags. - * - * @param pkts - * Pointer to array of packets. - * @param pkts_n - * Number of packets. - * @param cs_flags - * Pointer of flags to be returned. - * @param metadata - * Pointer of metadata to be returned. - * @param txq_offloads - * Offloads enabled on Tx queue - * - * @return - * Number of packets having same ol_flags and metadata, if relevant. - */ -static inline unsigned int -txq_calc_offload(struct rte_mbuf **pkts, uint16_t pkts_n, uint8_t *cs_flags, - rte_be32_t *metadata, const uint64_t txq_offloads) -{ - unsigned int pos; - const uint64_t cksum_ol_mask = - PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | - PKT_TX_UDP_CKSUM | PKT_TX_TUNNEL_GRE | - PKT_TX_TUNNEL_VXLAN | PKT_TX_OUTER_IP_CKSUM; - rte_be32_t p0_metadata, pn_metadata; - - if (!pkts_n) - return 0; - p0_metadata = pkts[0]->ol_flags & PKT_TX_METADATA ? - pkts[0]->tx_metadata : 0; - /* Count the number of packets having same offload parameters. */ - for (pos = 1; pos < pkts_n; ++pos) { - /* Check if packet has same checksum flags. */ - if ((txq_offloads & MLX5_VEC_TX_CKSUM_OFFLOAD_CAP) && - ((pkts[pos]->ol_flags ^ pkts[0]->ol_flags) & cksum_ol_mask)) - break; - /* Check if packet has same metadata. */ - if (txq_offloads & DEV_TX_OFFLOAD_MATCH_METADATA) { - pn_metadata = pkts[pos]->ol_flags & PKT_TX_METADATA ? - pkts[pos]->tx_metadata : 0; - if (pn_metadata != p0_metadata) - break; - } - } - *cs_flags = txq_ol_cksum_to_cs(pkts[0]); - *metadata = p0_metadata; - return pos; -} - -/** - * DPDK callback for vectorized TX. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst_raw_vec(void *dpdk_txq, struct rte_mbuf **pkts, - uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t nb_tx = 0; - - while (pkts_n > nb_tx) { - uint16_t n; - uint16_t ret; - - n = RTE_MIN((uint16_t)(pkts_n - nb_tx), MLX5_VPMD_TX_MAX_BURST); - ret = txq_burst_v(txq, &pkts[nb_tx], n, 0, 0); - nb_tx += ret; - if (!ret) - break; - } - return nb_tx; -} - -/** - * DPDK callback for vectorized TX with multi-seg packets and offload. - * - * @param dpdk_txq - * Generic pointer to TX queue structure. - * @param[in] pkts - * Packets to transmit. - * @param pkts_n - * Number of packets in array. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -uint16_t -mlx5_tx_burst_vec(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n) -{ - struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq; - uint16_t nb_tx = 0; - - while (pkts_n > nb_tx) { - uint8_t cs_flags = 0; - uint16_t n; - uint16_t ret; - rte_be32_t metadata = 0; - - /* Transmit multi-seg packets in the head of pkts list. */ - if ((txq->offloads & DEV_TX_OFFLOAD_MULTI_SEGS) && - NB_SEGS(pkts[nb_tx]) > 1) - nb_tx += txq_scatter_v(txq, - &pkts[nb_tx], - pkts_n - nb_tx); - n = RTE_MIN((uint16_t)(pkts_n - nb_tx), MLX5_VPMD_TX_MAX_BURST); - if (txq->offloads & DEV_TX_OFFLOAD_MULTI_SEGS) - n = txq_count_contig_single_seg(&pkts[nb_tx], n); - if (txq->offloads & (MLX5_VEC_TX_CKSUM_OFFLOAD_CAP | - DEV_TX_OFFLOAD_MATCH_METADATA)) - n = txq_calc_offload(&pkts[nb_tx], n, - &cs_flags, &metadata, - txq->offloads); - ret = txq_burst_v(txq, &pkts[nb_tx], n, cs_flags, metadata); - nb_tx += ret; - if (!ret) - break; - } - return nb_tx; -} - /** * Skip error packets. * @@ -242,49 +110,6 @@ mlx5_rx_burst_vec(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n) return nb_rx; } -/** - * Check Tx queue flags are set for raw vectorized Tx. - * - * @param dev - * Pointer to Ethernet device. - * - * @return - * 1 if supported, negative errno value if not. - */ -int __attribute__((cold)) -mlx5_check_raw_vec_tx_support(struct rte_eth_dev *dev) -{ - uint64_t offloads = dev->data->dev_conf.txmode.offloads; - - /* Doesn't support any offload. */ - if (offloads) - return -ENOTSUP; - return 1; -} - -/** - * Check a device can support vectorized TX. - * - * @param dev - * Pointer to Ethernet device. - * - * @return - * 1 if supported, negative errno value if not. - */ -int __attribute__((cold)) -mlx5_check_vec_tx_support(struct rte_eth_dev *dev) -{ - struct mlx5_priv *priv = dev->data->dev_private; - uint64_t offloads = dev->data->dev_conf.txmode.offloads; - - if (!priv->config.tx_vec_en || - priv->txqs_n > (unsigned int)priv->config.txqs_vec || - priv->config.mps != MLX5_MPW_ENHANCED || - offloads & ~MLX5_VEC_TX_OFFLOAD_CAP) - return -ENOTSUP; - return 1; -} - /** * Check a RX queue can support vectorized RX. * diff --git a/drivers/net/mlx5/mlx5_rxtx_vec_neon.h b/drivers/net/mlx5/mlx5_rxtx_vec_neon.h index 1c7e3b444a..99302869a7 100644 --- a/drivers/net/mlx5/mlx5_rxtx_vec_neon.h +++ b/drivers/net/mlx5/mlx5_rxtx_vec_neon.h @@ -26,295 +26,6 @@ #pragma GCC diagnostic ignored "-Wcast-qual" -/** - * Fill in buffer descriptors in a multi-packet send descriptor. - * - * @param txq - * Pointer to TX queue structure. - * @param dseg - * Pointer to buffer descriptor to be written. - * @param pkts - * Pointer to array of packets to be sent. - * @param n - * Number of packets to be filled. - */ -static inline void -txq_wr_dseg_v(struct mlx5_txq_data *txq, uint8_t *dseg, - struct rte_mbuf **pkts, unsigned int n) -{ - unsigned int pos; - uintptr_t addr; - const uint8x16_t dseg_shuf_m = { - 3, 2, 1, 0, /* length, bswap32 */ - 4, 5, 6, 7, /* lkey */ - 15, 14, 13, 12, /* addr, bswap64 */ - 11, 10, 9, 8 - }; -#ifdef MLX5_PMD_SOFT_COUNTERS - uint32_t tx_byte = 0; -#endif - - for (pos = 0; pos < n; ++pos, dseg += MLX5_WQE_DWORD_SIZE) { - uint8x16_t desc; - struct rte_mbuf *pkt = pkts[pos]; - - addr = rte_pktmbuf_mtod(pkt, uintptr_t); - desc = vreinterpretq_u8_u32((uint32x4_t) { - DATA_LEN(pkt), - mlx5_tx_mb2mr(txq, pkt), - addr, - addr >> 32 }); - desc = vqtbl1q_u8(desc, dseg_shuf_m); - vst1q_u8(dseg, desc); -#ifdef MLX5_PMD_SOFT_COUNTERS - tx_byte += DATA_LEN(pkt); -#endif - } -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.obytes += tx_byte; -#endif -} - -/** - * Send multi-segmented packets until it encounters a single segment packet in - * the pkts list. - * - * @param txq - * Pointer to TX queue structure. - * @param pkts - * Pointer to array of packets to be sent. - * @param pkts_n - * Number of packets to be sent. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -static uint16_t -txq_scatter_v(struct mlx5_txq_data *txq, struct rte_mbuf **pkts, - uint16_t pkts_n) -{ - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - const uint16_t wq_n = 1 << txq->wqe_n; - const uint16_t wq_mask = wq_n - 1; - const unsigned int nb_dword_per_wqebb = - MLX5_WQE_SIZE / MLX5_WQE_DWORD_SIZE; - const unsigned int nb_dword_in_hdr = - sizeof(struct mlx5_wqe) / MLX5_WQE_DWORD_SIZE; - unsigned int n; - volatile struct mlx5_wqe *wqe = NULL; - bool metadata_ol = - txq->offloads & DEV_TX_OFFLOAD_MATCH_METADATA ? true : false; - - assert(elts_n > pkts_n); - mlx5_tx_complete(txq); - if (unlikely(!pkts_n)) - return 0; - for (n = 0; n < pkts_n; ++n) { - struct rte_mbuf *buf = pkts[n]; - unsigned int segs_n = buf->nb_segs; - unsigned int ds = nb_dword_in_hdr; - unsigned int len = PKT_LEN(buf); - uint16_t wqe_ci = txq->wqe_ci; - const uint8x16_t ctrl_shuf_m = { - 3, 2, 1, 0, /* bswap32 */ - 7, 6, 5, 4, /* bswap32 */ - 11, 10, 9, 8, /* bswap32 */ - 12, 13, 14, 15 - }; - uint8_t cs_flags; - uint16_t max_elts; - uint16_t max_wqe; - uint8x16_t *t_wqe; - uint8_t *dseg; - uint8x16_t ctrl; - rte_be32_t metadata = - metadata_ol && (buf->ol_flags & PKT_TX_METADATA) ? - buf->tx_metadata : 0; - - assert(segs_n); - max_elts = elts_n - (elts_head - txq->elts_tail); - max_wqe = wq_n - (txq->wqe_ci - txq->wqe_pi); - /* - * A MPW session consumes 2 WQEs at most to - * include MLX5_MPW_DSEG_MAX pointers. - */ - if (segs_n == 1 || - max_elts < segs_n || max_wqe < 2) - break; - wqe = &((volatile struct mlx5_wqe64 *) - txq->wqes)[wqe_ci & wq_mask].hdr; - cs_flags = txq_ol_cksum_to_cs(buf); - /* Title WQEBB pointer. */ - t_wqe = (uint8x16_t *)wqe; - dseg = (uint8_t *)(wqe + 1); - do { - if (!(ds++ % nb_dword_per_wqebb)) { - dseg = (uint8_t *) - &((volatile struct mlx5_wqe64 *) - txq->wqes)[++wqe_ci & wq_mask]; - } - txq_wr_dseg_v(txq, dseg, &buf, 1); - dseg += MLX5_WQE_DWORD_SIZE; - (*txq->elts)[elts_head++ & elts_m] = buf; - buf = buf->next; - } while (--segs_n); - ++wqe_ci; - /* Fill CTRL in the header. */ - ctrl = vreinterpretq_u8_u32((uint32x4_t) { - MLX5_OPC_MOD_MPW << 24 | - txq->wqe_ci << 8 | MLX5_OPCODE_TSO, - txq->qp_num_8s | ds, 4, 0}); - ctrl = vqtbl1q_u8(ctrl, ctrl_shuf_m); - vst1q_u8((void *)t_wqe, ctrl); - /* Fill ESEG in the header. */ - vst1q_u32((void *)(t_wqe + 1), - ((uint32x4_t){ 0, - rte_cpu_to_be_16(len) << 16 | cs_flags, - metadata, 0 })); - txq->wqe_ci = wqe_ci; - } - if (!n) - return 0; - txq->elts_comp += (uint16_t)(elts_head - txq->elts_head); - txq->elts_head = elts_head; - if (txq->elts_comp >= MLX5_TX_COMP_THRESH) { - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - wqe->ctrl[3] = txq->elts_head; - txq->elts_comp = 0; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.opackets += n; -#endif - mlx5_tx_dbrec(txq, wqe); - return n; -} - -/** - * Send burst of packets with Enhanced MPW. If it encounters a multi-seg packet, - * it returns to make it processed by txq_scatter_v(). All the packets in - * the pkts list should be single segment packets having same offload flags. - * This must be checked by txq_count_contig_single_seg() and txq_calc_offload(). - * - * @param txq - * Pointer to TX queue structure. - * @param pkts - * Pointer to array of packets to be sent. - * @param pkts_n - * Number of packets to be sent (<= MLX5_VPMD_TX_MAX_BURST). - * @param cs_flags - * Checksum offload flags to be written in the descriptor. - * @param metadata - * Metadata value to be written in the descriptor. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -static inline uint16_t -txq_burst_v(struct mlx5_txq_data *txq, struct rte_mbuf **pkts, uint16_t pkts_n, - uint8_t cs_flags, rte_be32_t metadata) -{ - struct rte_mbuf **elts; - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - const unsigned int nb_dword_per_wqebb = - MLX5_WQE_SIZE / MLX5_WQE_DWORD_SIZE; - const unsigned int nb_dword_in_hdr = - sizeof(struct mlx5_wqe) / MLX5_WQE_DWORD_SIZE; - unsigned int n = 0; - unsigned int pos; - uint16_t max_elts; - uint16_t max_wqe; - uint32_t comp_req; - const uint16_t wq_n = 1 << txq->wqe_n; - const uint16_t wq_mask = wq_n - 1; - uint16_t wq_idx = txq->wqe_ci & wq_mask; - volatile struct mlx5_wqe64 *wq = - &((volatile struct mlx5_wqe64 *)txq->wqes)[wq_idx]; - volatile struct mlx5_wqe *wqe = (volatile struct mlx5_wqe *)wq; - const uint8x16_t ctrl_shuf_m = { - 3, 2, 1, 0, /* bswap32 */ - 7, 6, 5, 4, /* bswap32 */ - 11, 10, 9, 8, /* bswap32 */ - 12, 13, 14, 15 - }; - uint8x16_t *t_wqe; - uint8_t *dseg; - uint8x16_t ctrl; - - /* Make sure all packets can fit into a single WQE. */ - assert(elts_n > pkts_n); - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - pkts_n = RTE_MIN((unsigned int)RTE_MIN(pkts_n, max_wqe), max_elts); - if (unlikely(!pkts_n)) - return 0; - elts = &(*txq->elts)[elts_head & elts_m]; - /* Loop for available tailroom first. */ - n = RTE_MIN(elts_n - (elts_head & elts_m), pkts_n); - for (pos = 0; pos < (n & -2); pos += 2) - vst1q_u64((void *)&elts[pos], vld1q_u64((void *)&pkts[pos])); - if (n & 1) - elts[pos] = pkts[pos]; - /* Check if it crosses the end of the queue. */ - if (unlikely(n < pkts_n)) { - elts = &(*txq->elts)[0]; - for (pos = 0; pos < pkts_n - n; ++pos) - elts[pos] = pkts[n + pos]; - } - txq->elts_head += pkts_n; - /* Save title WQEBB pointer. */ - t_wqe = (uint8x16_t *)wqe; - dseg = (uint8_t *)(wqe + 1); - /* Calculate the number of entries to the end. */ - n = RTE_MIN( - (wq_n - wq_idx) * nb_dword_per_wqebb - nb_dword_in_hdr, - pkts_n); - /* Fill DSEGs. */ - txq_wr_dseg_v(txq, dseg, pkts, n); - /* Check if it crosses the end of the queue. */ - if (n < pkts_n) { - dseg = (uint8_t *)txq->wqes; - txq_wr_dseg_v(txq, dseg, &pkts[n], pkts_n - n); - } - if (txq->elts_comp + pkts_n < MLX5_TX_COMP_THRESH) { - txq->elts_comp += pkts_n; - comp_req = MLX5_COMP_ONLY_FIRST_ERR << MLX5_COMP_MODE_OFFSET; - } else { - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request a completion. */ - txq->elts_comp = 0; - comp_req = MLX5_COMP_ALWAYS << MLX5_COMP_MODE_OFFSET; - } - /* Fill CTRL in the header. */ - ctrl = vreinterpretq_u8_u32((uint32x4_t) { - MLX5_OPC_MOD_ENHANCED_MPSW << 24 | - txq->wqe_ci << 8 | MLX5_OPCODE_ENHANCED_MPSW, - txq->qp_num_8s | (pkts_n + 2), - comp_req, - txq->elts_head }); - ctrl = vqtbl1q_u8(ctrl, ctrl_shuf_m); - vst1q_u8((void *)t_wqe, ctrl); - /* Fill ESEG in the header. */ - vst1q_u32((void *)(t_wqe + 1), - ((uint32x4_t) { 0, cs_flags, metadata, 0 })); -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.opackets += pkts_n; -#endif - txq->wqe_ci += (nb_dword_in_hdr + pkts_n + (nb_dword_per_wqebb - 1)) / - nb_dword_per_wqebb; - /* Ring QP doorbell. */ - mlx5_tx_dbrec_cond_wmb(txq, wqe, pkts_n < MLX5_VPMD_TX_MAX_BURST); - return pkts_n; -} - /** * Store free buffers to RX SW ring. * diff --git a/drivers/net/mlx5/mlx5_rxtx_vec_sse.h b/drivers/net/mlx5/mlx5_rxtx_vec_sse.h index 503ca0f6ad..7bd254f225 100644 --- a/drivers/net/mlx5/mlx5_rxtx_vec_sse.h +++ b/drivers/net/mlx5/mlx5_rxtx_vec_sse.h @@ -28,290 +28,6 @@ #pragma GCC diagnostic ignored "-Wcast-qual" #endif -/** - * Fill in buffer descriptors in a multi-packet send descriptor. - * - * @param txq - * Pointer to TX queue structure. - * @param dseg - * Pointer to buffer descriptor to be written. - * @param pkts - * Pointer to array of packets to be sent. - * @param n - * Number of packets to be filled. - */ -static inline void -txq_wr_dseg_v(struct mlx5_txq_data *txq, __m128i *dseg, - struct rte_mbuf **pkts, unsigned int n) -{ - unsigned int pos; - uintptr_t addr; - const __m128i shuf_mask_dseg = - _mm_set_epi8(8, 9, 10, 11, /* addr, bswap64 */ - 12, 13, 14, 15, - 7, 6, 5, 4, /* lkey */ - 0, 1, 2, 3 /* length, bswap32 */); -#ifdef MLX5_PMD_SOFT_COUNTERS - uint32_t tx_byte = 0; -#endif - - for (pos = 0; pos < n; ++pos, ++dseg) { - __m128i desc; - struct rte_mbuf *pkt = pkts[pos]; - - addr = rte_pktmbuf_mtod(pkt, uintptr_t); - desc = _mm_set_epi32(addr >> 32, - addr, - mlx5_tx_mb2mr(txq, pkt), - DATA_LEN(pkt)); - desc = _mm_shuffle_epi8(desc, shuf_mask_dseg); - _mm_store_si128(dseg, desc); -#ifdef MLX5_PMD_SOFT_COUNTERS - tx_byte += DATA_LEN(pkt); -#endif - } -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.obytes += tx_byte; -#endif -} - -/** - * Send multi-segmented packets until it encounters a single segment packet in - * the pkts list. - * - * @param txq - * Pointer to TX queue structure. - * @param pkts - * Pointer to array of packets to be sent. - * @param pkts_n - * Number of packets to be sent. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -static uint16_t -txq_scatter_v(struct mlx5_txq_data *txq, struct rte_mbuf **pkts, - uint16_t pkts_n) -{ - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - const uint16_t wq_n = 1 << txq->wqe_n; - const uint16_t wq_mask = wq_n - 1; - const unsigned int nb_dword_per_wqebb = - MLX5_WQE_SIZE / MLX5_WQE_DWORD_SIZE; - const unsigned int nb_dword_in_hdr = - sizeof(struct mlx5_wqe) / MLX5_WQE_DWORD_SIZE; - unsigned int n; - volatile struct mlx5_wqe *wqe = NULL; - bool metadata_ol = - txq->offloads & DEV_TX_OFFLOAD_MATCH_METADATA ? true : false; - - assert(elts_n > pkts_n); - mlx5_tx_complete(txq); - if (unlikely(!pkts_n)) - return 0; - for (n = 0; n < pkts_n; ++n) { - struct rte_mbuf *buf = pkts[n]; - unsigned int segs_n = buf->nb_segs; - unsigned int ds = nb_dword_in_hdr; - unsigned int len = PKT_LEN(buf); - uint16_t wqe_ci = txq->wqe_ci; - const __m128i shuf_mask_ctrl = - _mm_set_epi8(15, 14, 13, 12, - 8, 9, 10, 11, /* bswap32 */ - 4, 5, 6, 7, /* bswap32 */ - 0, 1, 2, 3 /* bswap32 */); - uint8_t cs_flags; - uint16_t max_elts; - uint16_t max_wqe; - __m128i *t_wqe, *dseg; - __m128i ctrl; - rte_be32_t metadata = - metadata_ol && (buf->ol_flags & PKT_TX_METADATA) ? - buf->tx_metadata : 0; - - assert(segs_n); - max_elts = elts_n - (elts_head - txq->elts_tail); - max_wqe = wq_n - (txq->wqe_ci - txq->wqe_pi); - /* - * A MPW session consumes 2 WQEs at most to - * include MLX5_MPW_DSEG_MAX pointers. - */ - if (segs_n == 1 || - max_elts < segs_n || max_wqe < 2) - break; - if (segs_n > MLX5_MPW_DSEG_MAX) { - txq->stats.oerrors++; - break; - } - wqe = &((volatile struct mlx5_wqe64 *) - txq->wqes)[wqe_ci & wq_mask].hdr; - cs_flags = txq_ol_cksum_to_cs(buf); - /* Title WQEBB pointer. */ - t_wqe = (__m128i *)wqe; - dseg = (__m128i *)(wqe + 1); - do { - if (!(ds++ % nb_dword_per_wqebb)) { - dseg = (__m128i *) - &((volatile struct mlx5_wqe64 *) - txq->wqes)[++wqe_ci & wq_mask]; - } - txq_wr_dseg_v(txq, dseg++, &buf, 1); - (*txq->elts)[elts_head++ & elts_m] = buf; - buf = buf->next; - } while (--segs_n); - ++wqe_ci; - /* Fill CTRL in the header. */ - ctrl = _mm_set_epi32(0, 4, txq->qp_num_8s | ds, - MLX5_OPC_MOD_MPW << 24 | - txq->wqe_ci << 8 | MLX5_OPCODE_TSO); - ctrl = _mm_shuffle_epi8(ctrl, shuf_mask_ctrl); - _mm_store_si128(t_wqe, ctrl); - /* Fill ESEG in the header. */ - _mm_store_si128(t_wqe + 1, - _mm_set_epi32(0, metadata, - (rte_cpu_to_be_16(len) << 16) | - cs_flags, 0)); - txq->wqe_ci = wqe_ci; - } - if (!n) - return 0; - txq->elts_comp += (uint16_t)(elts_head - txq->elts_head); - txq->elts_head = elts_head; - if (txq->elts_comp >= MLX5_TX_COMP_THRESH) { - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - wqe->ctrl[2] = rte_cpu_to_be_32(MLX5_COMP_ALWAYS << - MLX5_COMP_MODE_OFFSET); - wqe->ctrl[3] = txq->elts_head; - txq->elts_comp = 0; - } -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.opackets += n; -#endif - mlx5_tx_dbrec(txq, wqe); - return n; -} - -/** - * Send burst of packets with Enhanced MPW. If it encounters a multi-seg packet, - * it returns to make it processed by txq_scatter_v(). All the packets in - * the pkts list should be single segment packets having same offload flags. - * This must be checked by txq_count_contig_single_seg() and txq_calc_offload(). - * - * @param txq - * Pointer to TX queue structure. - * @param pkts - * Pointer to array of packets to be sent. - * @param pkts_n - * Number of packets to be sent (<= MLX5_VPMD_TX_MAX_BURST). - * @param cs_flags - * Checksum offload flags to be written in the descriptor. - * @param metadata - * Metadata value to be written in the descriptor. - * - * @return - * Number of packets successfully transmitted (<= pkts_n). - */ -static inline uint16_t -txq_burst_v(struct mlx5_txq_data *txq, struct rte_mbuf **pkts, uint16_t pkts_n, - uint8_t cs_flags, rte_be32_t metadata) -{ - struct rte_mbuf **elts; - uint16_t elts_head = txq->elts_head; - const uint16_t elts_n = 1 << txq->elts_n; - const uint16_t elts_m = elts_n - 1; - const unsigned int nb_dword_per_wqebb = - MLX5_WQE_SIZE / MLX5_WQE_DWORD_SIZE; - const unsigned int nb_dword_in_hdr = - sizeof(struct mlx5_wqe) / MLX5_WQE_DWORD_SIZE; - unsigned int n = 0; - unsigned int pos; - uint16_t max_elts; - uint16_t max_wqe; - uint32_t comp_req; - const uint16_t wq_n = 1 << txq->wqe_n; - const uint16_t wq_mask = wq_n - 1; - uint16_t wq_idx = txq->wqe_ci & wq_mask; - volatile struct mlx5_wqe64 *wq = - &((volatile struct mlx5_wqe64 *)txq->wqes)[wq_idx]; - volatile struct mlx5_wqe *wqe = (volatile struct mlx5_wqe *)wq; - const __m128i shuf_mask_ctrl = - _mm_set_epi8(15, 14, 13, 12, - 8, 9, 10, 11, /* bswap32 */ - 4, 5, 6, 7, /* bswap32 */ - 0, 1, 2, 3 /* bswap32 */); - __m128i *t_wqe, *dseg; - __m128i ctrl; - - /* Make sure all packets can fit into a single WQE. */ - assert(elts_n > pkts_n); - mlx5_tx_complete(txq); - max_elts = (elts_n - (elts_head - txq->elts_tail)); - max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi); - pkts_n = RTE_MIN((unsigned int)RTE_MIN(pkts_n, max_wqe), max_elts); - assert(pkts_n <= MLX5_DSEG_MAX - nb_dword_in_hdr); - if (unlikely(!pkts_n)) - return 0; - elts = &(*txq->elts)[elts_head & elts_m]; - /* Loop for available tailroom first. */ - n = RTE_MIN(elts_n - (elts_head & elts_m), pkts_n); - for (pos = 0; pos < (n & -2); pos += 2) - _mm_storeu_si128((__m128i *)&elts[pos], - _mm_loadu_si128((__m128i *)&pkts[pos])); - if (n & 1) - elts[pos] = pkts[pos]; - /* Check if it crosses the end of the queue. */ - if (unlikely(n < pkts_n)) { - elts = &(*txq->elts)[0]; - for (pos = 0; pos < pkts_n - n; ++pos) - elts[pos] = pkts[n + pos]; - } - txq->elts_head += pkts_n; - /* Save title WQEBB pointer. */ - t_wqe = (__m128i *)wqe; - dseg = (__m128i *)(wqe + 1); - /* Calculate the number of entries to the end. */ - n = RTE_MIN( - (wq_n - wq_idx) * nb_dword_per_wqebb - nb_dword_in_hdr, - pkts_n); - /* Fill DSEGs. */ - txq_wr_dseg_v(txq, dseg, pkts, n); - /* Check if it crosses the end of the queue. */ - if (n < pkts_n) { - dseg = (__m128i *)txq->wqes; - txq_wr_dseg_v(txq, dseg, &pkts[n], pkts_n - n); - } - if (txq->elts_comp + pkts_n < MLX5_TX_COMP_THRESH) { - txq->elts_comp += pkts_n; - comp_req = MLX5_COMP_ONLY_FIRST_ERR << MLX5_COMP_MODE_OFFSET; - } else { - /* A CQE slot must always be available. */ - assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci)); - /* Request a completion. */ - txq->elts_comp = 0; - comp_req = MLX5_COMP_ALWAYS << MLX5_COMP_MODE_OFFSET; - } - /* Fill CTRL in the header. */ - ctrl = _mm_set_epi32(txq->elts_head, comp_req, - txq->qp_num_8s | (pkts_n + 2), - MLX5_OPC_MOD_ENHANCED_MPSW << 24 | - txq->wqe_ci << 8 | MLX5_OPCODE_ENHANCED_MPSW); - ctrl = _mm_shuffle_epi8(ctrl, shuf_mask_ctrl); - _mm_store_si128(t_wqe, ctrl); - /* Fill ESEG in the header. */ - _mm_store_si128(t_wqe + 1, _mm_set_epi32(0, metadata, cs_flags, 0)); -#ifdef MLX5_PMD_SOFT_COUNTERS - txq->stats.opackets += pkts_n; -#endif - txq->wqe_ci += (nb_dword_in_hdr + pkts_n + (nb_dword_per_wqebb - 1)) / - nb_dword_per_wqebb; - /* Ring QP doorbell. */ - mlx5_tx_dbrec_cond_wmb(txq, wqe, pkts_n < MLX5_VPMD_TX_MAX_BURST); - return pkts_n; -} - /** * Store free buffers to RX SW ring. * diff --git a/drivers/net/mlx5/mlx5_txq.c b/drivers/net/mlx5/mlx5_txq.c index 82493d7cb2..55892e281d 100644 --- a/drivers/net/mlx5/mlx5_txq.c +++ b/drivers/net/mlx5/mlx5_txq.c @@ -364,25 +364,6 @@ error: return -rte_errno; } -/** - * Check if the burst function is using eMPW. - * - * @param tx_pkt_burst - * Tx burst function pointer. - * - * @return - * 1 if the burst function is using eMPW, 0 otherwise. - */ -static int -is_empw_burst_func(eth_tx_burst_t tx_pkt_burst) -{ - if (tx_pkt_burst == mlx5_tx_burst_raw_vec || - tx_pkt_burst == mlx5_tx_burst_vec || - tx_pkt_burst == mlx5_tx_burst_empw) - return 1; - return 0; -} - /** * Create the Tx queue Verbs object. * @@ -414,7 +395,6 @@ mlx5_txq_ibv_new(struct rte_eth_dev *dev, uint16_t idx) struct mlx5dv_cq cq_info; struct mlx5dv_obj obj; const int desc = 1 << txq_data->elts_n; - eth_tx_burst_t tx_pkt_burst = mlx5_select_tx_function(dev); int ret = 0; assert(txq_data); @@ -432,8 +412,6 @@ mlx5_txq_ibv_new(struct rte_eth_dev *dev, uint16_t idx) .comp_mask = 0, }; cqe_n = desc / MLX5_TX_COMP_THRESH + 1; - if (is_empw_burst_func(tx_pkt_burst)) - cqe_n += MLX5_TX_COMP_THRESH_INLINE_DIV; tmpl.cq = mlx5_glue->create_cq(priv->sh->ctx, cqe_n, NULL, NULL, 0); if (tmpl.cq == NULL) { DRV_LOG(ERR, "port %u Tx queue %u CQ creation failure", @@ -698,93 +676,7 @@ txq_calc_wqebb_cnt(struct mlx5_txq_ctrl *txq_ctrl) static void txq_set_params(struct mlx5_txq_ctrl *txq_ctrl) { - struct mlx5_priv *priv = txq_ctrl->priv; - struct mlx5_dev_config *config = &priv->config; - const unsigned int max_tso_inline = - ((MLX5_MAX_TSO_HEADER + (RTE_CACHE_LINE_SIZE - 1)) / - RTE_CACHE_LINE_SIZE); - unsigned int txq_inline; - unsigned int txqs_inline; - unsigned int inline_max_packet_sz; - eth_tx_burst_t tx_pkt_burst = - mlx5_select_tx_function(ETH_DEV(priv)); - int is_empw_func = is_empw_burst_func(tx_pkt_burst); - int tso = !!(txq_ctrl->txq.offloads & (DEV_TX_OFFLOAD_TCP_TSO | - DEV_TX_OFFLOAD_VXLAN_TNL_TSO | - DEV_TX_OFFLOAD_GRE_TNL_TSO | - DEV_TX_OFFLOAD_IP_TNL_TSO | - DEV_TX_OFFLOAD_UDP_TNL_TSO)); - - txq_inline = (config->txq_inline == MLX5_ARG_UNSET) ? - 0 : config->txq_inline; - txqs_inline = (config->txqs_inline == MLX5_ARG_UNSET) ? - 0 : config->txqs_inline; - inline_max_packet_sz = - (config->inline_max_packet_sz == MLX5_ARG_UNSET) ? - 0 : config->inline_max_packet_sz; - if (is_empw_func) { - if (config->txq_inline == MLX5_ARG_UNSET) - txq_inline = MLX5_WQE_SIZE_MAX - MLX5_WQE_SIZE; - if (config->txqs_inline == MLX5_ARG_UNSET) - txqs_inline = MLX5_EMPW_MIN_TXQS; - if (config->inline_max_packet_sz == MLX5_ARG_UNSET) - inline_max_packet_sz = MLX5_EMPW_MAX_INLINE_LEN; - txq_ctrl->txq.mpw_hdr_dseg = config->mpw_hdr_dseg; - txq_ctrl->txq.inline_max_packet_sz = inline_max_packet_sz; - } - if (txq_inline && priv->txqs_n >= txqs_inline) { - unsigned int ds_cnt; - - txq_ctrl->txq.max_inline = - ((txq_inline + (RTE_CACHE_LINE_SIZE - 1)) / - RTE_CACHE_LINE_SIZE); - if (is_empw_func) { - /* To minimize the size of data set, avoid requesting - * too large WQ. - */ - txq_ctrl->max_inline_data = - ((RTE_MIN(txq_inline, - inline_max_packet_sz) + - (RTE_CACHE_LINE_SIZE - 1)) / - RTE_CACHE_LINE_SIZE) * RTE_CACHE_LINE_SIZE; - } else { - txq_ctrl->max_inline_data = - txq_ctrl->txq.max_inline * RTE_CACHE_LINE_SIZE; - } - /* - * Check if the inline size is too large in a way which - * can make the WQE DS to overflow. - * Considering in calculation: - * WQE CTRL (1 DS) - * WQE ETH (1 DS) - * Inline part (N DS) - */ - ds_cnt = 2 + (txq_ctrl->txq.max_inline / MLX5_WQE_DWORD_SIZE); - if (ds_cnt > MLX5_DSEG_MAX) { - unsigned int max_inline = (MLX5_DSEG_MAX - 2) * - MLX5_WQE_DWORD_SIZE; - - max_inline = max_inline - (max_inline % - RTE_CACHE_LINE_SIZE); - DRV_LOG(WARNING, - "port %u txq inline is too large (%d) setting" - " it to the maximum possible: %d\n", - PORT_ID(priv), txq_inline, max_inline); - txq_ctrl->txq.max_inline = max_inline / - RTE_CACHE_LINE_SIZE; - } - } - if (tso) { - txq_ctrl->max_tso_header = max_tso_inline * RTE_CACHE_LINE_SIZE; - txq_ctrl->txq.max_inline = RTE_MAX(txq_ctrl->txq.max_inline, - max_tso_inline); - txq_ctrl->txq.tso_en = 1; - } - txq_ctrl->txq.tunnel_en = config->tunnel_en | config->swp; - txq_ctrl->txq.swp_en = ((DEV_TX_OFFLOAD_IP_TNL_TSO | - DEV_TX_OFFLOAD_UDP_TNL_TSO | - DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM) & - txq_ctrl->txq.offloads) && config->swp; + (void)txq_ctrl; } /** -- 2.20.1