2018-01-29 13:11:30 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
* Copyright 2015 6WIND S.A.
|
2018-03-20 19:20:35 +00:00
|
|
|
* Copyright 2015 Mellanox Technologies, Ltd
|
2015-10-30 18:52:31 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
/* Verbs header. */
|
|
|
|
/* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
|
|
|
|
#ifdef PEDANTIC
|
2016-09-19 14:36:54 +00:00
|
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
2015-10-30 18:52:31 +00:00
|
|
|
#endif
|
|
|
|
#include <infiniband/verbs.h>
|
2017-09-26 15:38:24 +00:00
|
|
|
#include <infiniband/mlx5dv.h>
|
2015-10-30 18:52:31 +00:00
|
|
|
#ifdef PEDANTIC
|
2016-09-19 14:36:54 +00:00
|
|
|
#pragma GCC diagnostic error "-Wpedantic"
|
2015-10-30 18:52:31 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <rte_mbuf.h>
|
|
|
|
#include <rte_mempool.h>
|
|
|
|
#include <rte_prefetch.h>
|
|
|
|
#include <rte_common.h>
|
|
|
|
#include <rte_branch_prediction.h>
|
2016-06-24 13:17:52 +00:00
|
|
|
#include <rte_ether.h>
|
2015-10-30 18:52:31 +00:00
|
|
|
|
|
|
|
#include "mlx5.h"
|
|
|
|
#include "mlx5_utils.h"
|
|
|
|
#include "mlx5_rxtx.h"
|
2016-03-03 14:26:44 +00:00
|
|
|
#include "mlx5_autoconf.h"
|
2015-10-30 18:52:31 +00:00
|
|
|
#include "mlx5_defs.h"
|
2016-06-24 13:17:52 +00:00
|
|
|
#include "mlx5_prm.h"
|
|
|
|
|
2017-05-13 09:27:25 +00:00
|
|
|
static __rte_always_inline uint32_t
|
2018-04-23 12:33:03 +00:00
|
|
|
rxq_cq_to_pkt_type(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe);
|
2016-11-24 16:03:33 +00:00
|
|
|
|
2017-05-13 09:27:25 +00:00
|
|
|
static __rte_always_inline int
|
2017-10-09 14:44:39 +00:00
|
|
|
mlx5_rx_poll_len(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe,
|
2017-05-13 09:27:25 +00:00
|
|
|
uint16_t cqe_cnt, uint32_t *rss_hash);
|
2016-11-24 16:03:33 +00:00
|
|
|
|
2017-05-13 09:27:25 +00:00
|
|
|
static __rte_always_inline uint32_t
|
2018-04-23 12:33:04 +00:00
|
|
|
rxq_cq_to_ol_flags(volatile struct mlx5_cqe *cqe);
|
2016-11-24 16:03:33 +00:00
|
|
|
|
2018-05-09 11:13:48 +00:00
|
|
|
static __rte_always_inline void
|
|
|
|
rxq_cq_to_mbuf(struct mlx5_rxq_data *rxq, struct rte_mbuf *pkt,
|
|
|
|
volatile struct mlx5_cqe *cqe, uint32_t rss_hash_res);
|
|
|
|
|
2018-05-09 11:13:50 +00:00
|
|
|
static __rte_always_inline void
|
|
|
|
mprq_buf_replace(struct mlx5_rxq_data *rxq, uint16_t rq_idx);
|
|
|
|
|
2017-07-26 19:29:33 +00:00
|
|
|
uint32_t mlx5_ptype_table[] __rte_cache_aligned = {
|
|
|
|
[0xff] = RTE_PTYPE_ALL_MASK, /* Last entry for errored packet. */
|
2017-07-06 18:41:10 +00:00
|
|
|
};
|
2016-11-24 16:03:31 +00:00
|
|
|
|
2018-04-08 12:41:20 +00:00
|
|
|
uint8_t mlx5_cksum_table[1 << 10] __rte_cache_aligned;
|
|
|
|
uint8_t mlx5_swp_types_table[1 << 10] __rte_cache_aligned;
|
|
|
|
|
2017-07-26 19:29:33 +00:00
|
|
|
/**
|
|
|
|
* Build a table to translate Rx completion flags to packet type.
|
|
|
|
*
|
|
|
|
* @note: fix mlx5_dev_supported_ptypes_get() if any change here.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mlx5_set_ptype_table(void)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
uint32_t (*p)[RTE_DIM(mlx5_ptype_table)] = &mlx5_ptype_table;
|
|
|
|
|
2017-08-02 17:25:54 +00:00
|
|
|
/* Last entry must not be overwritten, reserved for errored packet. */
|
|
|
|
for (i = 0; i < RTE_DIM(mlx5_ptype_table) - 1; ++i)
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[i] = RTE_PTYPE_UNKNOWN;
|
|
|
|
/*
|
|
|
|
* The index to the array should have:
|
|
|
|
* bit[1:0] = l3_hdr_type
|
|
|
|
* bit[4:2] = l4_hdr_type
|
|
|
|
* bit[5] = ip_frag
|
|
|
|
* bit[6] = tunneled
|
|
|
|
* bit[7] = outer_l3_type
|
|
|
|
*/
|
2017-10-24 06:16:09 +00:00
|
|
|
/* L2 */
|
|
|
|
(*p)[0x00] = RTE_PTYPE_L2_ETHER;
|
2017-07-26 19:29:33 +00:00
|
|
|
/* L3 */
|
|
|
|
(*p)[0x01] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_NONFRAG;
|
|
|
|
(*p)[0x02] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_NONFRAG;
|
|
|
|
/* Fragmented */
|
|
|
|
(*p)[0x21] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_FRAG;
|
|
|
|
(*p)[0x22] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_FRAG;
|
|
|
|
/* TCP */
|
|
|
|
(*p)[0x05] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x06] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
2018-03-30 05:13:38 +00:00
|
|
|
(*p)[0x0d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x0e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x11] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x12] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
/* UDP */
|
|
|
|
(*p)[0x09] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_UDP;
|
|
|
|
(*p)[0x0a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_UDP;
|
|
|
|
/* Repeat with outer_l3_type being set. Just in case. */
|
|
|
|
(*p)[0x81] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_NONFRAG;
|
|
|
|
(*p)[0x82] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_NONFRAG;
|
|
|
|
(*p)[0xa1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_FRAG;
|
|
|
|
(*p)[0xa2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_FRAG;
|
|
|
|
(*p)[0x85] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x86] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
2018-03-30 05:13:38 +00:00
|
|
|
(*p)[0x8d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x8e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x91] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
|
|
|
(*p)[0x92] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0x89] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_UDP;
|
|
|
|
(*p)[0x8a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_L4_UDP;
|
|
|
|
/* Tunneled - L3 */
|
2018-04-23 12:33:03 +00:00
|
|
|
(*p)[0x40] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0x41] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_NONFRAG;
|
|
|
|
(*p)[0x42] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_NONFRAG;
|
2018-04-23 12:33:03 +00:00
|
|
|
(*p)[0xc0] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0xc1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_NONFRAG;
|
|
|
|
(*p)[0xc2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_NONFRAG;
|
|
|
|
/* Tunneled - Fragmented */
|
|
|
|
(*p)[0x61] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_FRAG;
|
|
|
|
(*p)[0x62] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_FRAG;
|
|
|
|
(*p)[0xe1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_FRAG;
|
|
|
|
(*p)[0xe2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_FRAG;
|
|
|
|
/* Tunneled - TCP */
|
|
|
|
(*p)[0x45] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0x46] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2018-03-30 05:13:38 +00:00
|
|
|
(*p)[0x4d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0x4e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0x51] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0x52] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0xc5] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0xc6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2018-03-30 05:13:38 +00:00
|
|
|
(*p)[0xcd] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0xce] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0xd1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
|
|
|
(*p)[0xd2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L4_TCP;
|
2017-07-26 19:29:33 +00:00
|
|
|
/* Tunneled - UDP */
|
|
|
|
(*p)[0x49] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_UDP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0x4a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_UDP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0xc9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_UDP;
|
2017-07-26 19:29:33 +00:00
|
|
|
(*p)[0xca] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
|
|
|
|
RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
|
2017-11-07 19:04:49 +00:00
|
|
|
RTE_PTYPE_INNER_L4_UDP;
|
2017-07-26 19:29:33 +00:00
|
|
|
}
|
|
|
|
|
2018-04-08 12:41:20 +00:00
|
|
|
/**
|
|
|
|
* Build a table to translate packet to checksum type of Verbs.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mlx5_set_cksum_table(void)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
uint8_t v;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
for (i = 0; i < RTE_DIM(mlx5_cksum_table); ++i) {
|
|
|
|
v = 0;
|
|
|
|
if (i & (1 << 9)) {
|
|
|
|
/* Tunneled packet. */
|
|
|
|
if (i & (1 << 8)) /* Outer IP. */
|
|
|
|
v |= MLX5_ETH_WQE_L3_CSUM;
|
|
|
|
if (i & (1 << 4)) /* Inner IP. */
|
|
|
|
v |= MLX5_ETH_WQE_L3_INNER_CSUM;
|
|
|
|
if (i & (3 << 2 | 1 << 0)) /* L4 or TSO. */
|
|
|
|
v |= MLX5_ETH_WQE_L4_INNER_CSUM;
|
|
|
|
} else {
|
|
|
|
/* No tunnel. */
|
|
|
|
if (i & (1 << 4)) /* IP. */
|
|
|
|
v |= MLX5_ETH_WQE_L3_CSUM;
|
|
|
|
if (i & (3 << 2 | 1 << 0)) /* L4 or TSO. */
|
|
|
|
v |= MLX5_ETH_WQE_L4_CSUM;
|
|
|
|
}
|
|
|
|
mlx5_cksum_table[i] = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build a table to translate packet type of mbuf to SWP type of Verbs.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mlx5_set_swp_types_table(void)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
uint8_t v;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
for (i = 0; i < RTE_DIM(mlx5_swp_types_table); ++i) {
|
|
|
|
v = 0;
|
|
|
|
if (i & (1 << 8))
|
|
|
|
v |= MLX5_ETH_WQE_L3_OUTER_IPV6;
|
|
|
|
if (i & (1 << 9))
|
|
|
|
v |= MLX5_ETH_WQE_L4_OUTER_UDP;
|
|
|
|
if (i & (1 << 4))
|
|
|
|
v |= MLX5_ETH_WQE_L3_INNER_IPV6;
|
|
|
|
if ((i & 3) == (PKT_TX_UDP_CKSUM >> 52))
|
|
|
|
v |= MLX5_ETH_WQE_L4_INNER_UDP;
|
|
|
|
mlx5_swp_types_table[i] = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-15 23:55:44 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2017-10-09 14:44:40 +00:00
|
|
|
tx_mlx5_wq_tailroom(struct mlx5_txq_data *txq, void *addr)
|
2017-03-15 23:55:44 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-08 12:41:19 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
2018-04-08 12:41:20 +00:00
|
|
|
const uint8_t tunneled = txq->tunnel_en && (buf->ol_flags &
|
|
|
|
PKT_TX_TUNNEL_MASK);
|
2018-04-08 12:41:19 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-04-08 12:41:20 +00:00
|
|
|
if (tunneled)
|
2018-04-08 12:41:19 +00:00
|
|
|
*tso_header_sz += buf->outer_l2_len + buf->outer_l3_len;
|
2018-04-08 12:41:20 +00:00
|
|
|
/* First seg must contain all TSO headers. */
|
|
|
|
if (unlikely(*tso_header_sz > MLX5_MAX_TSO_HEADER) ||
|
|
|
|
*tso_header_sz > DATA_LEN(buf)) {
|
2018-04-08 12:41:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-03-29 08:36:32 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2017-10-09 14:44:40 +00:00
|
|
|
struct mlx5_txq_data *txq = tx_queue;
|
2017-07-06 18:41:06 +00:00
|
|
|
uint16_t used;
|
2017-03-29 08:36:32 +00:00
|
|
|
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_complete(txq);
|
2017-07-06 18:41:06 +00:00
|
|
|
used = txq->elts_head - txq->elts_tail;
|
2017-03-29 08:36:32 +00:00
|
|
|
if (offset < used)
|
|
|
|
return RTE_ETH_TX_DESC_FULL;
|
|
|
|
return RTE_ETH_TX_DESC_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DPDK callback to check the status of a rx descriptor.
|
|
|
|
*
|
|
|
|
* @param rx_queue
|
|
|
|
* The rx queue.
|
|
|
|
* @param[in] offset
|
|
|
|
* The index of the descriptor in the ring.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* The status of the tx descriptor.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
|
|
|
|
{
|
2017-10-09 14:44:39 +00:00
|
|
|
struct mlx5_rxq_data *rxq = rx_queue;
|
2017-03-29 08:36:32 +00:00
|
|
|
struct rxq_zip *zip = &rxq->zip;
|
|
|
|
volatile struct mlx5_cqe *cqe;
|
|
|
|
const unsigned int cqe_n = (1 << rxq->cqe_n);
|
|
|
|
const unsigned int cqe_cnt = cqe_n - 1;
|
|
|
|
unsigned int cq_ci;
|
|
|
|
unsigned int used;
|
|
|
|
|
|
|
|
/* if we are processing a compressed cqe */
|
|
|
|
if (zip->ai) {
|
|
|
|
used = zip->cqe_cnt - zip->ca;
|
|
|
|
cq_ci = zip->cq_ci;
|
|
|
|
} else {
|
|
|
|
used = 0;
|
|
|
|
cq_ci = rxq->cq_ci;
|
|
|
|
}
|
|
|
|
cqe = &(*rxq->cqes)[cq_ci & cqe_cnt];
|
|
|
|
while (check_cqe(cqe, cqe_n, cq_ci) == 0) {
|
|
|
|
int8_t op_own;
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
op_own = cqe->op_own;
|
|
|
|
if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED)
|
2017-09-17 10:42:02 +00:00
|
|
|
n = rte_be_to_cpu_32(cqe->byte_cnt);
|
2017-03-29 08:36:32 +00:00
|
|
|
else
|
|
|
|
n = 1;
|
|
|
|
cq_ci += n;
|
|
|
|
used += n;
|
|
|
|
cqe = &(*rxq->cqes)[cq_ci & cqe_cnt];
|
|
|
|
}
|
|
|
|
used = RTE_MIN(used, (1U << rxq->elts_n) - 1);
|
|
|
|
if (offset < used)
|
|
|
|
return RTE_ETH_RX_DESC_DONE;
|
|
|
|
return RTE_ETH_RX_DESC_AVAIL;
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:52:31 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2017-10-09 14:44:40 +00:00
|
|
|
struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
|
2016-06-24 13:17:53 +00:00
|
|
|
uint16_t elts_head = txq->elts_head;
|
2017-07-06 18:41:06 +00:00
|
|
|
const uint16_t elts_n = 1 << txq->elts_n;
|
|
|
|
const uint16_t elts_m = elts_n - 1;
|
2016-06-24 13:17:59 +00:00
|
|
|
unsigned int i = 0;
|
2016-06-24 13:18:00 +00:00
|
|
|
unsigned int j = 0;
|
2017-03-02 09:01:31 +00:00
|
|
|
unsigned int k = 0;
|
2017-07-06 18:41:06 +00:00
|
|
|
uint16_t max_elts;
|
2017-02-02 10:34:12 +00:00
|
|
|
uint16_t max_wqe;
|
2016-06-24 13:17:55 +00:00
|
|
|
unsigned int comp;
|
2017-05-09 20:49:30 +00:00
|
|
|
volatile struct mlx5_wqe_ctrl *last_wqe = NULL;
|
2016-09-20 08:53:51 +00:00
|
|
|
unsigned int segs_n = 0;
|
2017-11-23 09:22:36 +00:00
|
|
|
const unsigned int max_inline = txq->max_inline;
|
2015-10-30 18:52:31 +00:00
|
|
|
|
2016-06-24 13:17:53 +00:00
|
|
|
if (unlikely(!pkts_n))
|
|
|
|
return 0;
|
2016-03-03 14:27:11 +00:00
|
|
|
/* Prefetch first packet cacheline. */
|
2016-06-24 13:17:59 +00:00
|
|
|
rte_prefetch0(*pkts);
|
2016-06-24 13:17:53 +00:00
|
|
|
/* Start processing. */
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_complete(txq);
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts = (elts_n - (elts_head - txq->elts_tail));
|
2017-12-27 03:55:45 +00:00
|
|
|
/* A CQE slot must always be available. */
|
|
|
|
assert((1u << txq->cqe_n) - (txq->cq_pi - txq->cq_ci));
|
2017-02-02 10:34:12 +00:00
|
|
|
max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi);
|
|
|
|
if (unlikely(!max_wqe))
|
|
|
|
return 0;
|
2016-06-24 13:17:59 +00:00
|
|
|
do {
|
2018-04-08 12:41:20 +00:00
|
|
|
struct rte_mbuf *buf = *pkts; /* First_seg. */
|
2017-11-23 09:22:35 +00:00
|
|
|
uint8_t *raw;
|
|
|
|
volatile struct mlx5_wqe_v *wqe = NULL;
|
2016-11-24 16:03:32 +00:00
|
|
|
volatile rte_v128u32_t *dseg = NULL;
|
2016-06-24 13:17:42 +00:00
|
|
|
uint32_t length;
|
2016-09-20 08:53:46 +00:00
|
|
|
unsigned int ds = 0;
|
2017-05-09 20:49:30 +00:00
|
|
|
unsigned int sg = 0; /* counter of additional segs attached. */
|
2016-09-20 08:53:51 +00:00
|
|
|
uintptr_t addr;
|
2017-02-02 10:34:11 +00:00
|
|
|
uint16_t pkt_inline_sz = MLX5_WQE_DWORD_SIZE + 2;
|
2017-03-02 09:01:31 +00:00
|
|
|
uint16_t tso_header_sz = 0;
|
2016-11-24 16:03:35 +00:00
|
|
|
uint16_t ehdr;
|
2017-11-20 15:35:47 +00:00
|
|
|
uint8_t cs_flags;
|
2018-04-08 12:41:20 +00:00
|
|
|
uint8_t tso = txq->tso_en && (buf->ol_flags & PKT_TX_TCP_SEG);
|
|
|
|
uint8_t is_vlan = !!(buf->ol_flags & PKT_TX_VLAN_PKT);
|
|
|
|
uint32_t swp_offsets = 0;
|
|
|
|
uint8_t swp_types = 0;
|
2017-06-20 05:24:47 +00:00
|
|
|
uint16_t tso_segsz = 0;
|
2016-09-20 08:53:51 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
uint32_t total_length = 0;
|
|
|
|
#endif
|
2018-04-08 12:41:19 +00:00
|
|
|
int ret;
|
2015-10-30 18:52:31 +00:00
|
|
|
|
2016-09-20 08:53:51 +00:00
|
|
|
segs_n = buf->nb_segs;
|
2016-06-24 13:17:59 +00:00
|
|
|
/*
|
|
|
|
* Make sure there is enough room to store this packet and
|
|
|
|
* that one ring entry remains unused.
|
|
|
|
*/
|
2016-06-24 13:18:00 +00:00
|
|
|
assert(segs_n);
|
2017-07-06 18:41:06 +00:00
|
|
|
if (max_elts < segs_n)
|
2016-06-24 13:17:59 +00:00
|
|
|
break;
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts -= segs_n;
|
2017-12-27 03:55:43 +00:00
|
|
|
sg = --segs_n;
|
2017-02-02 10:34:12 +00:00
|
|
|
if (unlikely(--max_wqe == 0))
|
|
|
|
break;
|
2016-11-24 16:03:32 +00:00
|
|
|
wqe = (volatile struct mlx5_wqe_v *)
|
2016-11-24 16:03:31 +00:00
|
|
|
tx_mlx5_wqe(txq, txq->wqe_ci);
|
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1));
|
2017-05-09 20:49:31 +00:00
|
|
|
if (pkts_n - i > 1)
|
|
|
|
rte_prefetch0(*(pkts + 1));
|
2016-09-20 08:53:51 +00:00
|
|
|
addr = rte_pktmbuf_mtod(buf, uintptr_t);
|
2016-06-24 13:17:42 +00:00
|
|
|
length = DATA_LEN(buf);
|
2016-11-24 16:03:35 +00:00
|
|
|
ehdr = (((uint8_t *)addr)[1] << 8) |
|
|
|
|
((uint8_t *)addr)[0];
|
2016-09-20 08:53:51 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
total_length = length;
|
|
|
|
#endif
|
2017-09-14 10:50:38 +00:00
|
|
|
if (length < (MLX5_WQE_DWORD_SIZE + 2)) {
|
|
|
|
txq->stats.oerrors++;
|
2017-03-29 07:51:39 +00:00
|
|
|
break;
|
2017-09-14 10:50:38 +00:00
|
|
|
}
|
2016-06-24 13:17:42 +00:00
|
|
|
/* Update element. */
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[elts_head & elts_m] = buf;
|
2016-06-24 13:17:42 +00:00
|
|
|
/* Prefetch next buffer data. */
|
2017-05-09 20:49:31 +00:00
|
|
|
if (pkts_n - i > 1)
|
|
|
|
rte_prefetch0(
|
|
|
|
rte_pktmbuf_mtod(*(pkts + 1), volatile void *));
|
2018-04-08 12:41:20 +00:00
|
|
|
cs_flags = txq_ol_cksum_to_cs(buf);
|
|
|
|
txq_mbuf_to_swp(txq, buf, tso, is_vlan,
|
|
|
|
(uint8_t *)&swp_offsets, &swp_types);
|
2016-11-24 16:03:30 +00:00
|
|
|
raw = ((uint8_t *)(uintptr_t)wqe) + 2 * MLX5_WQE_DWORD_SIZE;
|
2016-09-20 08:53:51 +00:00
|
|
|
/* Replace the Ethernet type by the VLAN if necessary. */
|
2018-04-08 12:41:20 +00:00
|
|
|
if (is_vlan) {
|
2017-09-17 10:42:02 +00:00
|
|
|
uint32_t vlan = rte_cpu_to_be_32(0x81000000 |
|
|
|
|
buf->vlan_tci);
|
2017-02-02 10:34:11 +00:00
|
|
|
unsigned int len = 2 * 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;
|
2016-09-20 08:53:51 +00:00
|
|
|
}
|
2017-08-31 16:27:06 +00:00
|
|
|
raw += MLX5_WQE_DWORD_SIZE;
|
2017-12-27 03:55:44 +00:00
|
|
|
if (tso) {
|
2018-04-08 12:41:20 +00:00
|
|
|
ret = inline_tso(txq, buf, &length,
|
2018-04-08 12:41:19 +00:00
|
|
|
&addr, &pkt_inline_sz,
|
|
|
|
&raw, &max_wqe,
|
|
|
|
&tso_segsz, &tso_header_sz);
|
|
|
|
if (ret == -EINVAL) {
|
2017-12-27 03:55:44 +00:00
|
|
|
break;
|
2018-04-08 12:41:19 +00:00
|
|
|
} else if (ret == -EAGAIN) {
|
2017-12-27 03:55:44 +00:00
|
|
|
/* 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),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
};
|
|
|
|
ds = 1;
|
2017-11-14 13:52:44 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
2017-12-27 03:55:44 +00:00
|
|
|
total_length = 0;
|
2017-11-14 13:52:44 +00:00
|
|
|
#endif
|
2017-12-27 03:55:44 +00:00
|
|
|
k++;
|
|
|
|
goto next_wqe;
|
2017-03-02 09:01:31 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-20 08:53:51 +00:00
|
|
|
/* Inline if enough room. */
|
2017-11-23 09:22:36 +00:00
|
|
|
if (max_inline || tso) {
|
2017-12-27 03:55:43 +00:00
|
|
|
uint32_t inl = 0;
|
2016-11-24 16:03:31 +00:00
|
|
|
uintptr_t end = (uintptr_t)
|
|
|
|
(((uintptr_t)txq->wqes) +
|
|
|
|
(1 << txq->wqe_n) * MLX5_WQE_SIZE);
|
2017-04-13 17:46:51 +00:00
|
|
|
unsigned int inline_room = max_inline *
|
|
|
|
RTE_CACHE_LINE_SIZE -
|
2017-08-31 16:27:06 +00:00
|
|
|
(pkt_inline_sz - 2) -
|
|
|
|
!!tso * sizeof(inl);
|
2017-12-27 03:55:43 +00:00
|
|
|
uintptr_t addr_end;
|
|
|
|
unsigned int copy_b;
|
2016-09-20 08:53:51 +00:00
|
|
|
|
2017-12-27 03:55:43 +00:00
|
|
|
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;
|
2017-02-02 10:34:13 +00:00
|
|
|
if (copy_b && ((end - (uintptr_t)raw) > copy_b)) {
|
2017-02-02 10:34:12 +00:00
|
|
|
/*
|
|
|
|
* One Dseg remains in the current WQE. To
|
|
|
|
* keep the computation positive, it is
|
|
|
|
* removed after the bytes to Dseg conversion.
|
|
|
|
*/
|
2017-02-02 10:34:13 +00:00
|
|
|
uint16_t n = (MLX5_WQE_DS(copy_b) - 1 + 3) / 4;
|
|
|
|
|
2017-02-02 10:34:12 +00:00
|
|
|
if (unlikely(max_wqe < n))
|
|
|
|
break;
|
|
|
|
max_wqe -= n;
|
2018-05-11 17:39:13 +00:00
|
|
|
if (tso) {
|
|
|
|
assert(inl == 0);
|
2017-10-25 23:30:40 +00:00
|
|
|
inl = rte_cpu_to_be_32(copy_b |
|
|
|
|
MLX5_INLINE_SEG);
|
2017-03-02 09:01:31 +00:00
|
|
|
rte_memcpy((void *)raw,
|
|
|
|
(void *)&inl, sizeof(inl));
|
|
|
|
raw += sizeof(inl);
|
|
|
|
pkt_inline_sz += sizeof(inl);
|
|
|
|
}
|
2016-09-20 08:53:51 +00:00
|
|
|
rte_memcpy((void *)raw, (void *)addr, copy_b);
|
|
|
|
addr += copy_b;
|
|
|
|
length -= copy_b;
|
|
|
|
pkt_inline_sz += copy_b;
|
|
|
|
}
|
|
|
|
/*
|
2016-12-26 15:28:36 +00:00
|
|
|
* 2 DWORDs consumed by the WQE header + ETH segment +
|
2016-09-20 08:53:51 +00:00
|
|
|
* the size of the inline part of the packet.
|
|
|
|
*/
|
|
|
|
ds = 2 + MLX5_WQE_DS(pkt_inline_sz - 2);
|
|
|
|
if (length > 0) {
|
2017-02-02 10:34:12 +00:00
|
|
|
if (ds % (MLX5_WQE_SIZE /
|
|
|
|
MLX5_WQE_DWORD_SIZE) == 0) {
|
|
|
|
if (unlikely(--max_wqe == 0))
|
|
|
|
break;
|
2016-11-24 16:03:32 +00:00
|
|
|
dseg = (volatile rte_v128u32_t *)
|
2017-02-02 10:34:12 +00:00
|
|
|
tx_mlx5_wqe(txq, txq->wqe_ci +
|
|
|
|
ds / 4);
|
|
|
|
} else {
|
|
|
|
dseg = (volatile rte_v128u32_t *)
|
|
|
|
((uintptr_t)wqe +
|
|
|
|
(ds * MLX5_WQE_DWORD_SIZE));
|
|
|
|
}
|
2016-09-20 08:53:51 +00:00
|
|
|
goto use_dseg;
|
|
|
|
} else if (!segs_n) {
|
|
|
|
goto next_pkt;
|
|
|
|
} else {
|
2018-05-11 17:39:13 +00:00
|
|
|
/*
|
|
|
|
* 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. */
|
2017-12-27 03:55:43 +00:00
|
|
|
--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;
|
2016-09-20 08:53:51 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-06-24 13:18:00 +00:00
|
|
|
/*
|
2016-09-20 08:53:51 +00:00
|
|
|
* No inline has been done in the packet, only the
|
|
|
|
* Ethernet Header as been stored.
|
2016-06-24 13:18:00 +00:00
|
|
|
*/
|
2016-11-24 16:03:32 +00:00
|
|
|
dseg = (volatile rte_v128u32_t *)
|
2016-09-20 08:53:51 +00:00
|
|
|
((uintptr_t)wqe + (3 * MLX5_WQE_DWORD_SIZE));
|
|
|
|
ds = 3;
|
|
|
|
use_dseg:
|
|
|
|
/* Add the remaining packet as a simple ds. */
|
2017-11-23 09:22:34 +00:00
|
|
|
addr = rte_cpu_to_be_64(addr);
|
2016-11-24 16:03:32 +00:00
|
|
|
*dseg = (rte_v128u32_t){
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32(length),
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_mb2mr(txq, buf),
|
2017-11-23 09:22:34 +00:00
|
|
|
addr,
|
|
|
|
addr >> 32,
|
2016-09-20 08:53:51 +00:00
|
|
|
};
|
2016-06-24 13:18:00 +00:00
|
|
|
++ds;
|
2016-09-20 08:53:51 +00:00
|
|
|
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))) {
|
2017-02-02 10:34:12 +00:00
|
|
|
if (unlikely(--max_wqe == 0))
|
|
|
|
break;
|
2016-11-24 16:03:32 +00:00
|
|
|
dseg = (volatile rte_v128u32_t *)
|
2017-02-02 10:34:12 +00:00
|
|
|
tx_mlx5_wqe(txq, txq->wqe_ci + ds / 4);
|
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq,
|
|
|
|
txq->wqe_ci + ds / 4 + 1));
|
2016-09-20 08:53:51 +00:00
|
|
|
} else {
|
|
|
|
++dseg;
|
|
|
|
}
|
|
|
|
++ds;
|
|
|
|
buf = buf->next;
|
|
|
|
assert(buf);
|
|
|
|
length = DATA_LEN(buf);
|
2016-06-24 13:18:00 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
2016-09-20 08:53:51 +00:00
|
|
|
total_length += length;
|
2016-06-24 13:18:00 +00:00
|
|
|
#endif
|
2016-09-20 08:53:51 +00:00
|
|
|
/* Store segment information. */
|
2017-11-23 09:22:34 +00:00
|
|
|
addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf, uintptr_t));
|
2016-11-24 16:03:32 +00:00
|
|
|
*dseg = (rte_v128u32_t){
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32(length),
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_mb2mr(txq, buf),
|
2017-11-23 09:22:34 +00:00
|
|
|
addr,
|
|
|
|
addr >> 32,
|
2016-09-20 08:53:51 +00:00
|
|
|
};
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[++elts_head & elts_m] = buf;
|
2017-12-27 03:55:43 +00:00
|
|
|
if (--segs_n)
|
2016-09-20 08:53:51 +00:00
|
|
|
goto next_seg;
|
|
|
|
next_pkt:
|
2017-09-14 10:50:39 +00:00
|
|
|
if (ds > MLX5_DSEG_MAX) {
|
|
|
|
txq->stats.oerrors++;
|
|
|
|
break;
|
|
|
|
}
|
2017-07-06 18:41:06 +00:00
|
|
|
++elts_head;
|
2017-05-09 20:49:31 +00:00
|
|
|
++pkts;
|
2016-09-20 08:53:51 +00:00
|
|
|
++i;
|
2017-12-27 03:55:43 +00:00
|
|
|
j += sg;
|
2016-11-24 16:03:30 +00:00
|
|
|
/* Initialize known and common part of the WQE structure. */
|
2017-03-02 09:01:31 +00:00
|
|
|
if (tso) {
|
|
|
|
wqe->ctrl = (rte_v128u32_t){
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32((txq->wqe_ci << 8) |
|
|
|
|
MLX5_OPCODE_TSO),
|
|
|
|
rte_cpu_to_be_32(txq->qp_num_8s | ds),
|
2017-03-02 09:01:31 +00:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
};
|
|
|
|
wqe->eseg = (rte_v128u32_t){
|
2018-04-08 12:41:20 +00:00
|
|
|
swp_offsets,
|
|
|
|
cs_flags | (swp_types << 8) |
|
|
|
|
(rte_cpu_to_be_16(tso_segsz) << 16),
|
2017-03-02 09:01:31 +00:00
|
|
|
0,
|
2017-09-17 10:42:02 +00:00
|
|
|
(ehdr << 16) | rte_cpu_to_be_16(tso_header_sz),
|
2017-03-02 09:01:31 +00:00
|
|
|
};
|
|
|
|
} else {
|
|
|
|
wqe->ctrl = (rte_v128u32_t){
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32((txq->wqe_ci << 8) |
|
|
|
|
MLX5_OPCODE_SEND),
|
|
|
|
rte_cpu_to_be_32(txq->qp_num_8s | ds),
|
2017-03-02 09:01:31 +00:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
};
|
|
|
|
wqe->eseg = (rte_v128u32_t){
|
2018-04-08 12:41:20 +00:00
|
|
|
swp_offsets,
|
|
|
|
cs_flags | (swp_types << 8),
|
2017-03-02 09:01:31 +00:00
|
|
|
0,
|
2017-09-17 10:42:02 +00:00
|
|
|
(ehdr << 16) | rte_cpu_to_be_16(pkt_inline_sz),
|
2017-03-02 09:01:31 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
next_wqe:
|
2016-09-20 08:53:51 +00:00
|
|
|
txq->wqe_ci += (ds + 3) / 4;
|
2017-05-09 20:49:30 +00:00
|
|
|
/* Save the last successful WQE for completion request */
|
|
|
|
last_wqe = (volatile struct mlx5_wqe_ctrl *)wqe;
|
2015-10-30 18:52:36 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment sent bytes counter. */
|
2016-09-20 08:53:51 +00:00
|
|
|
txq->stats.obytes += total_length;
|
2015-10-30 18:52:36 +00:00
|
|
|
#endif
|
2017-05-09 20:49:31 +00:00
|
|
|
} while (i < pkts_n);
|
2015-10-30 18:52:31 +00:00
|
|
|
/* Take a shortcut if nothing must be sent. */
|
2017-03-02 09:01:31 +00:00
|
|
|
if (unlikely((i + k) == 0))
|
2015-10-30 18:52:31 +00:00
|
|
|
return 0;
|
2017-07-06 18:41:06 +00:00
|
|
|
txq->elts_head += (i + j);
|
2016-06-24 13:17:55 +00:00
|
|
|
/* Check whether completion threshold has been reached. */
|
2017-03-02 09:01:31 +00:00
|
|
|
comp = txq->elts_comp + i + j + k;
|
2016-06-24 13:17:55 +00:00
|
|
|
if (comp >= MLX5_TX_COMP_THRESH) {
|
|
|
|
/* Request completion on last WQE. */
|
2017-09-17 10:42:02 +00:00
|
|
|
last_wqe->ctrl2 = rte_cpu_to_be_32(8);
|
2016-06-24 13:17:55 +00:00
|
|
|
/* Save elts_head in unused "immediate" field of WQE. */
|
2017-05-09 20:49:30 +00:00
|
|
|
last_wqe->ctrl3 = txq->elts_head;
|
2016-06-24 13:17:55 +00:00
|
|
|
txq->elts_comp = 0;
|
2017-12-27 03:55:45 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
++txq->cq_pi;
|
|
|
|
#endif
|
2016-06-24 13:17:55 +00:00
|
|
|
} else {
|
|
|
|
txq->elts_comp = comp;
|
|
|
|
}
|
2015-10-30 18:52:36 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment sent packets counter. */
|
|
|
|
txq->stats.opackets += i;
|
|
|
|
#endif
|
2015-10-30 18:52:31 +00:00
|
|
|
/* Ring QP doorbell. */
|
2017-05-09 20:49:30 +00:00
|
|
|
mlx5_tx_dbrec(txq, (volatile struct mlx5_wqe *)last_wqe);
|
2015-10-30 18:52:31 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2016-06-24 13:17:57 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_mpw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, uint32_t length)
|
2016-06-24 13:17:57 +00:00
|
|
|
{
|
2016-09-20 08:53:50 +00:00
|
|
|
uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1);
|
2016-06-24 13:17:57 +00:00
|
|
|
volatile struct mlx5_wqe_data_seg (*dseg)[MLX5_MPW_DSEG_MAX] =
|
|
|
|
(volatile struct mlx5_wqe_data_seg (*)[])
|
2016-11-24 16:03:31 +00:00
|
|
|
tx_mlx5_wqe(txq, idx + 1);
|
2016-06-24 13:17:57 +00:00
|
|
|
|
|
|
|
mpw->state = MLX5_MPW_STATE_OPENED;
|
|
|
|
mpw->pkts_n = 0;
|
|
|
|
mpw->len = length;
|
|
|
|
mpw->total_len = 0;
|
2016-11-24 16:03:31 +00:00
|
|
|
mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx);
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->eseg.mss = rte_cpu_to_be_16(length);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw->wqe->eseg.inline_hdr_sz = 0;
|
|
|
|
mpw->wqe->eseg.rsvd0 = 0;
|
|
|
|
mpw->wqe->eseg.rsvd1 = 0;
|
|
|
|
mpw->wqe->eseg.rsvd2 = 0;
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) |
|
|
|
|
(txq->wqe_ci << 8) |
|
|
|
|
MLX5_OPCODE_TSO);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw->wqe->ctrl[2] = 0;
|
|
|
|
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));
|
2016-06-24 13:17:57 +00:00
|
|
|
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
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_mpw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
|
2016-06-24 13:17:57 +00:00
|
|
|
{
|
|
|
|
unsigned int num = mpw->pkts_n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store size in multiple of 16 bytes. Control and Ethernet segments
|
|
|
|
* count as 2.
|
|
|
|
*/
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s | (2 + num));
|
2016-06-24 13:17:57 +00:00
|
|
|
mpw->state = MLX5_MPW_STATE_CLOSED;
|
|
|
|
if (num < 3)
|
|
|
|
++txq->wqe_ci;
|
|
|
|
else
|
|
|
|
txq->wqe_ci += 2;
|
2016-11-24 16:03:31 +00:00
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci));
|
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1));
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2017-10-09 14:44:40 +00:00
|
|
|
struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
|
2016-06-24 13:17:57 +00:00
|
|
|
uint16_t elts_head = txq->elts_head;
|
2017-07-06 18:41:06 +00:00
|
|
|
const uint16_t elts_n = 1 << txq->elts_n;
|
|
|
|
const uint16_t elts_m = elts_n - 1;
|
2016-06-24 13:17:59 +00:00
|
|
|
unsigned int i = 0;
|
2016-06-24 13:18:00 +00:00
|
|
|
unsigned int j = 0;
|
2017-07-06 18:41:06 +00:00
|
|
|
uint16_t max_elts;
|
2017-02-02 10:34:12 +00:00
|
|
|
uint16_t max_wqe;
|
2016-06-24 13:17:57 +00:00
|
|
|
unsigned int comp;
|
|
|
|
struct mlx5_mpw mpw = {
|
|
|
|
.state = MLX5_MPW_STATE_CLOSED,
|
|
|
|
};
|
|
|
|
|
2016-06-24 13:17:59 +00:00
|
|
|
if (unlikely(!pkts_n))
|
|
|
|
return 0;
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Prefetch first packet cacheline. */
|
2016-11-24 16:03:31 +00:00
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci));
|
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1));
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Start processing. */
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_complete(txq);
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts = (elts_n - (elts_head - txq->elts_tail));
|
2017-12-27 03:55:45 +00:00
|
|
|
/* A CQE slot must always be available. */
|
|
|
|
assert((1u << txq->cqe_n) - (txq->cq_pi - txq->cq_ci));
|
2017-02-02 10:34:12 +00:00
|
|
|
max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi);
|
|
|
|
if (unlikely(!max_wqe))
|
|
|
|
return 0;
|
2016-06-24 13:17:59 +00:00
|
|
|
do {
|
2016-06-24 13:18:00 +00:00
|
|
|
struct rte_mbuf *buf = *(pkts++);
|
2016-06-24 13:17:57 +00:00
|
|
|
uint32_t length;
|
2016-06-24 13:18:00 +00:00
|
|
|
unsigned int segs_n = buf->nb_segs;
|
2017-11-20 15:35:47 +00:00
|
|
|
uint32_t cs_flags;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
2016-06-24 13:17:59 +00:00
|
|
|
/*
|
|
|
|
* Make sure there is enough room to store this packet and
|
|
|
|
* that one ring entry remains unused.
|
|
|
|
*/
|
2016-06-24 13:18:00 +00:00
|
|
|
assert(segs_n);
|
2017-07-06 18:41:06 +00:00
|
|
|
if (max_elts < segs_n)
|
2016-06-24 13:18:00 +00:00
|
|
|
break;
|
|
|
|
/* Do not bother with large packets MPW cannot handle. */
|
2017-09-14 10:50:38 +00:00
|
|
|
if (segs_n > MLX5_MPW_DSEG_MAX) {
|
|
|
|
txq->stats.oerrors++;
|
2016-06-24 13:17:59 +00:00
|
|
|
break;
|
2017-09-14 10:50:38 +00:00
|
|
|
}
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts -= segs_n;
|
2016-06-24 13:17:59 +00:00
|
|
|
--pkts_n;
|
2018-04-08 12:41:20 +00:00
|
|
|
cs_flags = txq_ol_cksum_to_cs(buf);
|
2016-06-24 13:18:00 +00:00
|
|
|
/* Retrieve packet information. */
|
|
|
|
length = PKT_LEN(buf);
|
|
|
|
assert(length);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Start new session if packet differs. */
|
|
|
|
if ((mpw.state == MLX5_MPW_STATE_OPENED) &&
|
|
|
|
((mpw.len != length) ||
|
2016-06-24 13:18:00 +00:00
|
|
|
(segs_n != 1) ||
|
2016-09-20 08:53:46 +00:00
|
|
|
(mpw.wqe->eseg.cs_flags != cs_flags)))
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_close(txq, &mpw);
|
|
|
|
if (mpw.state == MLX5_MPW_STATE_CLOSED) {
|
2017-02-02 10:34:12 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_new(txq, &mpw, length);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw.wqe->eseg.cs_flags = cs_flags;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
2016-06-24 13:18:00 +00:00
|
|
|
/* 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);
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[elts_head++ & elts_m] = buf;
|
2016-06-24 13:18:00 +00:00
|
|
|
dseg = mpw.data.dseg[mpw.pkts_n];
|
|
|
|
addr = rte_pktmbuf_mtod(buf, uintptr_t);
|
|
|
|
*dseg = (struct mlx5_wqe_data_seg){
|
2017-09-17 10:42:02 +00:00
|
|
|
.byte_count = rte_cpu_to_be_32(DATA_LEN(buf)),
|
2017-07-06 18:41:10 +00:00
|
|
|
.lkey = mlx5_tx_mb2mr(txq, buf),
|
2017-09-17 10:42:02 +00:00
|
|
|
.addr = rte_cpu_to_be_64(addr),
|
2016-06-24 13:18:00 +00:00
|
|
|
};
|
|
|
|
#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);
|
2016-06-24 13:17:57 +00:00
|
|
|
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
|
2016-06-24 13:17:59 +00:00
|
|
|
++i;
|
|
|
|
} while (pkts_n);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Take a shortcut if nothing must be sent. */
|
|
|
|
if (unlikely(i == 0))
|
|
|
|
return 0;
|
|
|
|
/* Check whether completion threshold has been reached. */
|
2016-06-24 13:18:00 +00:00
|
|
|
/* "j" includes both packets and segments. */
|
|
|
|
comp = txq->elts_comp + j;
|
2016-06-24 13:17:57 +00:00
|
|
|
if (comp >= MLX5_TX_COMP_THRESH) {
|
2016-09-20 08:53:46 +00:00
|
|
|
volatile struct mlx5_wqe *wqe = mpw.wqe;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
|
|
|
/* Request completion on last WQE. */
|
2017-09-17 10:42:02 +00:00
|
|
|
wqe->ctrl[2] = rte_cpu_to_be_32(8);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Save elts_head in unused "immediate" field of WQE. */
|
2016-09-20 08:53:46 +00:00
|
|
|
wqe->ctrl[3] = elts_head;
|
2016-06-24 13:17:57 +00:00
|
|
|
txq->elts_comp = 0;
|
2017-12-27 03:55:45 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
++txq->cq_pi;
|
|
|
|
#endif
|
2016-06-24 13:17:57 +00:00
|
|
|
} 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);
|
2016-12-09 13:27:58 +00:00
|
|
|
mlx5_tx_dbrec(txq, mpw.wqe);
|
2016-06-24 13:17:57 +00:00
|
|
|
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
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_mpw_inline_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw,
|
|
|
|
uint32_t length)
|
2016-06-24 13:17:57 +00:00
|
|
|
{
|
2016-09-20 08:53:50 +00:00
|
|
|
uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1);
|
2016-09-20 08:53:46 +00:00
|
|
|
struct mlx5_wqe_inl_small *inl;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
|
|
|
mpw->state = MLX5_MPW_INL_STATE_OPENED;
|
|
|
|
mpw->pkts_n = 0;
|
|
|
|
mpw->len = length;
|
|
|
|
mpw->total_len = 0;
|
2016-11-24 16:03:31 +00:00
|
|
|
mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx);
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) |
|
|
|
|
(txq->wqe_ci << 8) |
|
|
|
|
MLX5_OPCODE_TSO);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw->wqe->ctrl[2] = 0;
|
|
|
|
mpw->wqe->ctrl[3] = 0;
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->eseg.mss = rte_cpu_to_be_16(length);
|
2016-09-20 08:53:46 +00:00
|
|
|
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.rsvd2 = 0;
|
|
|
|
inl = (struct mlx5_wqe_inl_small *)
|
|
|
|
(((uintptr_t)mpw->wqe) + 2 * MLX5_WQE_DWORD_SIZE);
|
|
|
|
mpw->data.raw = (uint8_t *)&inl->raw;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close a MPW inline session.
|
|
|
|
*
|
|
|
|
* @param txq
|
|
|
|
* Pointer to TX queue structure.
|
|
|
|
* @param mpw
|
|
|
|
* Pointer to MPW session structure.
|
|
|
|
*/
|
|
|
|
static inline void
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_mpw_inline_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
|
2016-06-24 13:17:57 +00:00
|
|
|
{
|
|
|
|
unsigned int size;
|
2016-09-20 08:53:46 +00:00
|
|
|
struct mlx5_wqe_inl_small *inl = (struct mlx5_wqe_inl_small *)
|
|
|
|
(((uintptr_t)mpw->wqe) + (2 * MLX5_WQE_DWORD_SIZE));
|
2016-06-24 13:17:57 +00:00
|
|
|
|
2016-09-20 08:53:46 +00:00
|
|
|
size = MLX5_WQE_SIZE - MLX5_MWQE64_INL_DATA + mpw->total_len;
|
2016-06-24 13:17:57 +00:00
|
|
|
/*
|
|
|
|
* Store size in multiple of 16 bytes. Control and Ethernet segments
|
|
|
|
* count as 2.
|
|
|
|
*/
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s |
|
|
|
|
MLX5_WQE_DS(size));
|
2016-06-24 13:17:57 +00:00
|
|
|
mpw->state = MLX5_MPW_STATE_CLOSED;
|
2017-09-17 10:42:02 +00:00
|
|
|
inl->byte_cnt = rte_cpu_to_be_32(mpw->total_len | MLX5_INLINE_SEG);
|
2016-09-20 08:53:46 +00:00
|
|
|
txq->wqe_ci += (size + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2017-10-09 14:44:40 +00:00
|
|
|
struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
|
2016-06-24 13:17:57 +00:00
|
|
|
uint16_t elts_head = txq->elts_head;
|
2017-07-06 18:41:06 +00:00
|
|
|
const uint16_t elts_n = 1 << txq->elts_n;
|
|
|
|
const uint16_t elts_m = elts_n - 1;
|
2016-06-24 13:17:59 +00:00
|
|
|
unsigned int i = 0;
|
2016-06-24 13:18:00 +00:00
|
|
|
unsigned int j = 0;
|
2017-07-06 18:41:06 +00:00
|
|
|
uint16_t max_elts;
|
2017-02-02 10:34:12 +00:00
|
|
|
uint16_t max_wqe;
|
2016-06-24 13:17:57 +00:00
|
|
|
unsigned int comp;
|
2016-09-14 11:53:55 +00:00
|
|
|
unsigned int inline_room = txq->max_inline * RTE_CACHE_LINE_SIZE;
|
2016-06-24 13:17:57 +00:00
|
|
|
struct mlx5_mpw mpw = {
|
|
|
|
.state = MLX5_MPW_STATE_CLOSED,
|
|
|
|
};
|
2017-02-02 10:34:12 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
2016-06-24 13:17:59 +00:00
|
|
|
if (unlikely(!pkts_n))
|
|
|
|
return 0;
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Prefetch first packet cacheline. */
|
2016-11-24 16:03:31 +00:00
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci));
|
|
|
|
rte_prefetch0(tx_mlx5_wqe(txq, txq->wqe_ci + 1));
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Start processing. */
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_complete(txq);
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts = (elts_n - (elts_head - txq->elts_tail));
|
2017-12-27 03:55:45 +00:00
|
|
|
/* A CQE slot must always be available. */
|
|
|
|
assert((1u << txq->cqe_n) - (txq->cq_pi - txq->cq_ci));
|
2016-06-24 13:17:59 +00:00
|
|
|
do {
|
2016-06-24 13:18:00 +00:00
|
|
|
struct rte_mbuf *buf = *(pkts++);
|
2016-06-24 13:17:57 +00:00
|
|
|
uintptr_t addr;
|
|
|
|
uint32_t length;
|
2016-06-24 13:18:00 +00:00
|
|
|
unsigned int segs_n = buf->nb_segs;
|
2017-11-20 15:35:47 +00:00
|
|
|
uint8_t cs_flags;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
2016-06-24 13:17:59 +00:00
|
|
|
/*
|
|
|
|
* Make sure there is enough room to store this packet and
|
|
|
|
* that one ring entry remains unused.
|
|
|
|
*/
|
2016-06-24 13:18:00 +00:00
|
|
|
assert(segs_n);
|
2017-07-06 18:41:06 +00:00
|
|
|
if (max_elts < segs_n)
|
2016-06-24 13:17:59 +00:00
|
|
|
break;
|
2016-06-24 13:18:00 +00:00
|
|
|
/* Do not bother with large packets MPW cannot handle. */
|
2017-09-14 10:50:38 +00:00
|
|
|
if (segs_n > MLX5_MPW_DSEG_MAX) {
|
|
|
|
txq->stats.oerrors++;
|
2016-06-24 13:18:00 +00:00
|
|
|
break;
|
2017-09-14 10:50:38 +00:00
|
|
|
}
|
2017-07-06 18:41:06 +00:00
|
|
|
max_elts -= segs_n;
|
2016-06-24 13:17:59 +00:00
|
|
|
--pkts_n;
|
2017-02-02 10:34:12 +00:00
|
|
|
/*
|
|
|
|
* Compute max_wqe in case less WQE were consumed in previous
|
|
|
|
* iteration.
|
|
|
|
*/
|
|
|
|
max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi);
|
2018-04-08 12:41:20 +00:00
|
|
|
cs_flags = txq_ol_cksum_to_cs(buf);
|
2016-06-24 13:18:00 +00:00
|
|
|
/* Retrieve packet information. */
|
|
|
|
length = PKT_LEN(buf);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Start new session if packet differs. */
|
|
|
|
if (mpw.state == MLX5_MPW_STATE_OPENED) {
|
|
|
|
if ((mpw.len != length) ||
|
2016-06-24 13:18:00 +00:00
|
|
|
(segs_n != 1) ||
|
2016-09-20 08:53:46 +00:00
|
|
|
(mpw.wqe->eseg.cs_flags != cs_flags))
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_close(txq, &mpw);
|
|
|
|
} else if (mpw.state == MLX5_MPW_INL_STATE_OPENED) {
|
|
|
|
if ((mpw.len != length) ||
|
2016-06-24 13:18:00 +00:00
|
|
|
(segs_n != 1) ||
|
2016-06-24 13:17:57 +00:00
|
|
|
(length > inline_room) ||
|
2016-09-20 08:53:46 +00:00
|
|
|
(mpw.wqe->eseg.cs_flags != cs_flags)) {
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_inline_close(txq, &mpw);
|
2016-09-14 11:53:55 +00:00
|
|
|
inline_room =
|
|
|
|
txq->max_inline * RTE_CACHE_LINE_SIZE;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mpw.state == MLX5_MPW_STATE_CLOSED) {
|
2016-06-24 13:18:00 +00:00
|
|
|
if ((segs_n != 1) ||
|
|
|
|
(length > inline_room)) {
|
2017-02-02 10:34:12 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_new(txq, &mpw, length);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw.wqe->eseg.cs_flags = cs_flags;
|
2016-06-24 13:17:57 +00:00
|
|
|
} else {
|
2017-02-02 10:34:12 +00:00
|
|
|
if (unlikely(max_wqe < wqe_inl_n))
|
|
|
|
break;
|
|
|
|
max_wqe -= wqe_inl_n;
|
2016-06-24 13:17:57 +00:00
|
|
|
mlx5_mpw_inline_new(txq, &mpw, length);
|
2016-09-20 08:53:46 +00:00
|
|
|
mpw.wqe->eseg.cs_flags = cs_flags;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-24 13:18:00 +00:00
|
|
|
/* Multi-segment packets must be alone in their MPW. */
|
|
|
|
assert((segs_n == 1) || (mpw.pkts_n == 0));
|
2016-06-24 13:17:57 +00:00
|
|
|
if (mpw.state == MLX5_MPW_STATE_OPENED) {
|
2016-09-14 11:53:55 +00:00
|
|
|
assert(inline_room ==
|
|
|
|
txq->max_inline * RTE_CACHE_LINE_SIZE);
|
2016-06-24 13:18:00 +00:00
|
|
|
#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
|
|
|
|
length = 0;
|
|
|
|
#endif
|
|
|
|
do {
|
|
|
|
volatile struct mlx5_wqe_data_seg *dseg;
|
|
|
|
|
|
|
|
assert(buf);
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[elts_head++ & elts_m] = buf;
|
2016-06-24 13:18:00 +00:00
|
|
|
dseg = mpw.data.dseg[mpw.pkts_n];
|
|
|
|
addr = rte_pktmbuf_mtod(buf, uintptr_t);
|
|
|
|
*dseg = (struct mlx5_wqe_data_seg){
|
2017-09-17 10:42:02 +00:00
|
|
|
.byte_count =
|
|
|
|
rte_cpu_to_be_32(DATA_LEN(buf)),
|
2017-07-06 18:41:10 +00:00
|
|
|
.lkey = mlx5_tx_mb2mr(txq, buf),
|
2017-09-17 10:42:02 +00:00
|
|
|
.addr = rte_cpu_to_be_64(addr),
|
2016-06-24 13:18:00 +00:00
|
|
|
};
|
|
|
|
#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);
|
2016-06-24 13:17:57 +00:00
|
|
|
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);
|
2016-06-24 13:18:00 +00:00
|
|
|
assert(length == DATA_LEN(buf));
|
|
|
|
addr = rte_pktmbuf_mtod(buf, uintptr_t);
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[elts_head++ & elts_m] = buf;
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Maximum number of bytes before wrapping. */
|
2016-11-24 16:03:31 +00:00
|
|
|
max = ((((uintptr_t)(txq->wqes)) +
|
|
|
|
(1 << txq->wqe_n) *
|
|
|
|
MLX5_WQE_SIZE) -
|
2016-06-24 13:17:57 +00:00
|
|
|
(uintptr_t)mpw.data.raw);
|
|
|
|
if (length > max) {
|
|
|
|
rte_memcpy((void *)(uintptr_t)mpw.data.raw,
|
|
|
|
(void *)addr,
|
|
|
|
max);
|
2016-11-24 16:03:31 +00:00
|
|
|
mpw.data.raw = (volatile void *)txq->wqes;
|
2016-06-24 13:17:57 +00:00
|
|
|
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);
|
2017-01-18 00:51:55 +00:00
|
|
|
|
|
|
|
if (length == max)
|
|
|
|
mpw.data.raw =
|
|
|
|
(volatile void *)txq->wqes;
|
|
|
|
else
|
|
|
|
mpw.data.raw += length;
|
2016-06-24 13:17:57 +00:00
|
|
|
}
|
|
|
|
++mpw.pkts_n;
|
2017-02-02 18:43:32 +00:00
|
|
|
mpw.total_len += length;
|
2016-06-24 13:18:00 +00:00
|
|
|
++j;
|
2016-06-24 13:17:57 +00:00
|
|
|
if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) {
|
|
|
|
mlx5_mpw_inline_close(txq, &mpw);
|
2016-09-14 11:53:55 +00:00
|
|
|
inline_room =
|
|
|
|
txq->max_inline * RTE_CACHE_LINE_SIZE;
|
2016-06-24 13:17:57 +00:00
|
|
|
} else {
|
|
|
|
inline_room -= length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment sent bytes counter. */
|
|
|
|
txq->stats.obytes += length;
|
|
|
|
#endif
|
2016-06-24 13:17:59 +00:00
|
|
|
++i;
|
|
|
|
} while (pkts_n);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Take a shortcut if nothing must be sent. */
|
|
|
|
if (unlikely(i == 0))
|
|
|
|
return 0;
|
|
|
|
/* Check whether completion threshold has been reached. */
|
2016-06-24 13:18:00 +00:00
|
|
|
/* "j" includes both packets and segments. */
|
|
|
|
comp = txq->elts_comp + j;
|
2016-06-24 13:17:57 +00:00
|
|
|
if (comp >= MLX5_TX_COMP_THRESH) {
|
2016-09-20 08:53:46 +00:00
|
|
|
volatile struct mlx5_wqe *wqe = mpw.wqe;
|
2016-06-24 13:17:57 +00:00
|
|
|
|
|
|
|
/* Request completion on last WQE. */
|
2017-09-17 10:42:02 +00:00
|
|
|
wqe->ctrl[2] = rte_cpu_to_be_32(8);
|
2016-06-24 13:17:57 +00:00
|
|
|
/* Save elts_head in unused "immediate" field of WQE. */
|
2016-09-20 08:53:46 +00:00
|
|
|
wqe->ctrl[3] = elts_head;
|
2016-06-24 13:17:57 +00:00
|
|
|
txq->elts_comp = 0;
|
2017-12-27 03:55:45 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
++txq->cq_pi;
|
|
|
|
#endif
|
2016-06-24 13:17:57 +00:00
|
|
|
} 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);
|
2016-12-09 13:27:58 +00:00
|
|
|
mlx5_tx_dbrec(txq, mpw.wqe);
|
2016-06-24 13:17:57 +00:00
|
|
|
txq->elts_head = elts_head;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2017-03-15 23:55:44 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_empw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, int padding)
|
2017-03-15 23:55:44 +00:00
|
|
|
{
|
|
|
|
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);
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[0] =
|
|
|
|
rte_cpu_to_be_32((MLX5_OPC_MOD_ENHANCED_MPSW << 24) |
|
|
|
|
(txq->wqe_ci << 8) |
|
|
|
|
MLX5_OPCODE_ENHANCED_MPSW);
|
2017-03-15 23:55:44 +00:00
|
|
|
mpw->wqe->ctrl[2] = 0;
|
|
|
|
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. */
|
2017-09-17 10:42:02 +00:00
|
|
|
*(volatile uint32_t *)addr = rte_cpu_to_be_32(MLX5_INLINE_SEG);
|
2017-03-15 23:55:44 +00:00
|
|
|
*(volatile uint32_t *)(addr + MLX5_WQE_DWORD_SIZE) =
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32(MLX5_INLINE_SEG);
|
2017-03-15 23:55:44 +00:00
|
|
|
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
|
2017-10-09 14:44:40 +00:00
|
|
|
mlx5_empw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
|
2017-03-15 23:55:44 +00:00
|
|
|
{
|
|
|
|
uint16_t ret;
|
|
|
|
|
|
|
|
/* Store size in multiple of 16 bytes. Control and Ethernet segments
|
|
|
|
* count as 2.
|
|
|
|
*/
|
2017-09-17 10:42:02 +00:00
|
|
|
mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s |
|
|
|
|
MLX5_WQE_DS(mpw->total_len));
|
2017-03-15 23:55:44 +00:00
|
|
|
mpw->state = MLX5_MPW_STATE_CLOSED;
|
|
|
|
ret = (mpw->total_len + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE;
|
|
|
|
txq->wqe_ci += ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-12-27 03:55:46 +00:00
|
|
|
* TX with Enhanced MPW support.
|
2017-03-15 23:55:44 +00:00
|
|
|
*
|
2017-12-27 03:55:46 +00:00
|
|
|
* @param txq
|
|
|
|
* Pointer to TX queue structure.
|
2017-03-15 23:55:44 +00:00
|
|
|
* @param[in] pkts
|
|
|
|
* Packets to transmit.
|
|
|
|
* @param pkts_n
|
|
|
|
* Number of packets in array.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Number of packets successfully transmitted (<= pkts_n).
|
|
|
|
*/
|
2017-12-27 03:55:46 +00:00
|
|
|
static inline uint16_t
|
|
|
|
txq_burst_empw(struct mlx5_txq_data *txq, struct rte_mbuf **pkts,
|
|
|
|
uint16_t pkts_n)
|
2017-03-15 23:55:44 +00:00
|
|
|
{
|
|
|
|
uint16_t elts_head = txq->elts_head;
|
2017-07-06 18:41:06 +00:00
|
|
|
const uint16_t elts_n = 1 << txq->elts_n;
|
|
|
|
const uint16_t elts_m = elts_n - 1;
|
2017-03-15 23:55:44 +00:00
|
|
|
unsigned int i = 0;
|
|
|
|
unsigned int j = 0;
|
2017-07-06 18:41:06 +00:00
|
|
|
uint16_t max_elts;
|
2017-03-15 23:55:44 +00:00
|
|
|
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;
|
|
|
|
struct mlx5_mpw mpw = {
|
|
|
|
.state = MLX5_MPW_STATE_CLOSED,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (unlikely(!pkts_n))
|
|
|
|
return 0;
|
|
|
|
/* Start processing. */
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_complete(txq);
|
2017-03-15 23:55:44 +00:00
|
|
|
max_elts = (elts_n - (elts_head - txq->elts_tail));
|
|
|
|
/* A CQE slot must always be available. */
|
|
|
|
assert((1u << txq->cqe_n) - (txq->cq_pi - txq->cq_ci));
|
|
|
|
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;
|
2017-11-20 15:35:47 +00:00
|
|
|
uint8_t cs_flags;
|
2017-03-15 23:55:44 +00:00
|
|
|
|
2017-12-27 03:55:47 +00:00
|
|
|
/* 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)
|
2017-03-15 23:55:44 +00:00
|
|
|
break;
|
2018-04-08 12:41:20 +00:00
|
|
|
cs_flags = txq_ol_cksum_to_cs(buf);
|
2017-03-15 23:55:44 +00:00
|
|
|
/* 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) {
|
2017-12-27 03:55:47 +00:00
|
|
|
if ((inl_pad + sizeof(struct mlx5_wqe_data_seg) >
|
|
|
|
mpw_room) ||
|
2017-03-15 23:55:44 +00:00
|
|
|
(length <= txq->inline_max_packet_sz &&
|
|
|
|
inl_pad + sizeof(inl_hdr) + length >
|
2017-12-27 03:55:47 +00:00
|
|
|
mpw_room) ||
|
2017-03-15 23:55:44 +00:00
|
|
|
(mpw.wqe->eseg.cs_flags != cs_flags))
|
|
|
|
max_wqe -= mlx5_empw_close(txq, &mpw);
|
|
|
|
}
|
|
|
|
if (unlikely(mpw.state == MLX5_MPW_STATE_CLOSED)) {
|
2017-12-27 03:55:47 +00:00
|
|
|
/* 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;
|
2017-03-15 23:55:44 +00:00
|
|
|
mpw.wqe->eseg.cs_flags = cs_flags;
|
|
|
|
} 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);
|
|
|
|
}
|
2018-02-26 17:50:57 +00:00
|
|
|
if (max_inline && do_inline) {
|
2017-03-15 23:55:44 +00:00
|
|
|
/* Inline packet into WQE. */
|
|
|
|
unsigned int max;
|
|
|
|
|
|
|
|
assert(mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED);
|
|
|
|
assert(length == DATA_LEN(buf));
|
2017-09-17 10:42:02 +00:00
|
|
|
inl_hdr = rte_cpu_to_be_32(length | MLX5_INLINE_SEG);
|
2017-03-15 23:55:44 +00:00
|
|
|
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);
|
2017-07-06 18:41:06 +00:00
|
|
|
(*txq->elts)[elts_head++ & elts_m] = buf;
|
2018-03-12 17:05:45 +00:00
|
|
|
addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf,
|
|
|
|
uintptr_t));
|
2017-03-15 23:55:44 +00:00
|
|
|
*dseg = (rte_v128u32_t) {
|
2017-09-17 10:42:02 +00:00
|
|
|
rte_cpu_to_be_32(length),
|
2017-07-06 18:41:10 +00:00
|
|
|
mlx5_tx_mb2mr(txq, buf),
|
2017-11-23 09:22:34 +00:00
|
|
|
addr,
|
|
|
|
addr >> 32,
|
2017-03-15 23:55:44 +00:00
|
|
|
};
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Request completion on last WQE. */
|
2017-09-17 10:42:02 +00:00
|
|
|
wqe->ctrl[2] = rte_cpu_to_be_32(8);
|
2017-03-15 23:55:44 +00:00
|
|
|
/* Save elts_head in unused "immediate" field of WQE. */
|
|
|
|
wqe->ctrl[3] = elts_head;
|
|
|
|
txq->elts_comp = 0;
|
|
|
|
txq->mpw_comp = txq->wqe_ci;
|
2017-12-27 03:55:45 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
++txq->cq_pi;
|
|
|
|
#endif
|
2017-03-15 23:55:44 +00:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2017-12-27 03:55:46 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:52:41 +00:00
|
|
|
/**
|
|
|
|
* Translate RX completion flags to packet type.
|
|
|
|
*
|
2018-04-23 12:33:03 +00:00
|
|
|
* @param[in] rxq
|
|
|
|
* Pointer to RX queue structure.
|
2016-06-24 13:17:52 +00:00
|
|
|
* @param[in] cqe
|
|
|
|
* Pointer to CQE.
|
2015-10-30 18:52:41 +00:00
|
|
|
*
|
2016-03-14 20:50:50 +00:00
|
|
|
* @note: fix mlx5_dev_supported_ptypes_get() if any change here.
|
|
|
|
*
|
2015-10-30 18:52:41 +00:00
|
|
|
* @return
|
|
|
|
* Packet type for struct rte_mbuf.
|
|
|
|
*/
|
|
|
|
static inline uint32_t
|
2018-04-23 12:33:03 +00:00
|
|
|
rxq_cq_to_pkt_type(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe)
|
2015-10-30 18:52:41 +00:00
|
|
|
{
|
2017-07-26 19:29:33 +00:00
|
|
|
uint8_t idx;
|
|
|
|
uint8_t pinfo = cqe->pkt_info;
|
|
|
|
uint16_t ptype = cqe->hdr_type_etc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The index to the array should have:
|
|
|
|
* bit[1:0] = l3_hdr_type
|
|
|
|
* bit[4:2] = l4_hdr_type
|
|
|
|
* bit[5] = ip_frag
|
|
|
|
* bit[6] = tunneled
|
|
|
|
* bit[7] = outer_l3_type
|
|
|
|
*/
|
|
|
|
idx = ((pinfo & 0x3) << 6) | ((ptype & 0xfc00) >> 10);
|
2018-04-23 12:33:03 +00:00
|
|
|
return mlx5_ptype_table[idx] | rxq->tunnel * !!(idx & (1 << 6));
|
2015-10-30 18:52:41 +00:00
|
|
|
}
|
|
|
|
|
2016-06-24 13:17:54 +00:00
|
|
|
/**
|
|
|
|
* Get size of the next packet for a given CQE. For compressed CQEs, the
|
|
|
|
* consumer index is updated only once all packets of the current one have
|
|
|
|
* been processed.
|
|
|
|
*
|
|
|
|
* @param rxq
|
|
|
|
* Pointer to RX queue.
|
|
|
|
* @param cqe
|
|
|
|
* CQE to process.
|
2016-09-28 12:11:18 +00:00
|
|
|
* @param[out] rss_hash
|
|
|
|
* Packet RSS Hash result.
|
2016-06-24 13:17:54 +00:00
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Packet size in bytes (0 if there is none), -1 in case of completion
|
|
|
|
* with error.
|
|
|
|
*/
|
|
|
|
static inline int
|
2017-10-09 14:44:39 +00:00
|
|
|
mlx5_rx_poll_len(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe,
|
2016-09-28 12:11:18 +00:00
|
|
|
uint16_t cqe_cnt, uint32_t *rss_hash)
|
2016-06-24 13:17:54 +00:00
|
|
|
{
|
|
|
|
struct rxq_zip *zip = &rxq->zip;
|
|
|
|
uint16_t cqe_n = cqe_cnt + 1;
|
|
|
|
int len = 0;
|
2017-01-17 02:09:40 +00:00
|
|
|
uint16_t idx, end;
|
2016-06-24 13:17:54 +00:00
|
|
|
|
|
|
|
/* Process compressed data in the CQE and mini arrays. */
|
|
|
|
if (zip->ai) {
|
|
|
|
volatile struct mlx5_mini_cqe8 (*mc)[8] =
|
|
|
|
(volatile struct mlx5_mini_cqe8 (*)[8])
|
2017-07-12 22:09:54 +00:00
|
|
|
(uintptr_t)(&(*rxq->cqes)[zip->ca & cqe_cnt].pkt_info);
|
2016-06-24 13:17:54 +00:00
|
|
|
|
2017-09-17 10:42:02 +00:00
|
|
|
len = rte_be_to_cpu_32((*mc)[zip->ai & 7].byte_cnt);
|
|
|
|
*rss_hash = rte_be_to_cpu_32((*mc)[zip->ai & 7].rx_hash_result);
|
2016-06-24 13:17:54 +00:00
|
|
|
if ((++zip->ai & 7) == 0) {
|
2017-01-17 02:09:40 +00:00
|
|
|
/* Invalidate consumed CQEs */
|
|
|
|
idx = zip->ca;
|
|
|
|
end = zip->na;
|
|
|
|
while (idx != end) {
|
|
|
|
(*rxq->cqes)[idx & cqe_cnt].op_own =
|
|
|
|
MLX5_CQE_INVALIDATE;
|
|
|
|
++idx;
|
|
|
|
}
|
2016-06-24 13:17:54 +00:00
|
|
|
/*
|
|
|
|
* Increment consumer index to skip the number of
|
|
|
|
* CQEs consumed. Hardware leaves holes in the CQ
|
|
|
|
* ring for software use.
|
|
|
|
*/
|
|
|
|
zip->ca = zip->na;
|
|
|
|
zip->na += 8;
|
|
|
|
}
|
|
|
|
if (unlikely(rxq->zip.ai == rxq->zip.cqe_cnt)) {
|
2017-01-17 02:09:40 +00:00
|
|
|
/* Invalidate the rest */
|
|
|
|
idx = zip->ca;
|
|
|
|
end = zip->cq_ci;
|
2016-06-24 13:17:54 +00:00
|
|
|
|
|
|
|
while (idx != end) {
|
2016-11-02 10:39:38 +00:00
|
|
|
(*rxq->cqes)[idx & cqe_cnt].op_own =
|
2016-06-24 13:17:54 +00:00
|
|
|
MLX5_CQE_INVALIDATE;
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
rxq->cq_ci = zip->cq_ci;
|
|
|
|
zip->ai = 0;
|
|
|
|
}
|
|
|
|
/* No compressed data, get next CQE and verify if it is compressed. */
|
|
|
|
} else {
|
|
|
|
int ret;
|
|
|
|
int8_t op_own;
|
|
|
|
|
2016-11-02 10:39:38 +00:00
|
|
|
ret = check_cqe(cqe, cqe_n, rxq->cq_ci);
|
2016-06-24 13:17:54 +00:00
|
|
|
if (unlikely(ret == 1))
|
|
|
|
return 0;
|
|
|
|
++rxq->cq_ci;
|
|
|
|
op_own = cqe->op_own;
|
2018-01-25 21:02:50 +00:00
|
|
|
rte_cio_rmb();
|
2016-06-24 13:17:54 +00:00
|
|
|
if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED) {
|
|
|
|
volatile struct mlx5_mini_cqe8 (*mc)[8] =
|
|
|
|
(volatile struct mlx5_mini_cqe8 (*)[8])
|
|
|
|
(uintptr_t)(&(*rxq->cqes)[rxq->cq_ci &
|
2017-07-12 22:09:54 +00:00
|
|
|
cqe_cnt].pkt_info);
|
2016-06-24 13:17:54 +00:00
|
|
|
|
|
|
|
/* Fix endianness. */
|
2017-09-17 10:42:02 +00:00
|
|
|
zip->cqe_cnt = rte_be_to_cpu_32(cqe->byte_cnt);
|
2016-06-24 13:17:54 +00:00
|
|
|
/*
|
|
|
|
* Current mini array position is the one returned by
|
|
|
|
* check_cqe64().
|
|
|
|
*
|
|
|
|
* If completion comprises several mini arrays, as a
|
|
|
|
* special case the second one is located 7 CQEs after
|
|
|
|
* the initial CQE instead of 8 for subsequent ones.
|
|
|
|
*/
|
2017-01-17 02:09:40 +00:00
|
|
|
zip->ca = rxq->cq_ci;
|
2016-06-24 13:17:54 +00:00
|
|
|
zip->na = zip->ca + 7;
|
|
|
|
/* Compute the next non compressed CQE. */
|
|
|
|
--rxq->cq_ci;
|
|
|
|
zip->cq_ci = rxq->cq_ci + zip->cqe_cnt;
|
|
|
|
/* Get packet size to return. */
|
2017-09-17 10:42:02 +00:00
|
|
|
len = rte_be_to_cpu_32((*mc)[0].byte_cnt);
|
|
|
|
*rss_hash = rte_be_to_cpu_32((*mc)[0].rx_hash_result);
|
2016-06-24 13:17:54 +00:00
|
|
|
zip->ai = 1;
|
2017-01-17 02:09:40 +00:00
|
|
|
/* Prefetch all the entries to be invalidated */
|
|
|
|
idx = zip->ca;
|
|
|
|
end = zip->cq_ci;
|
|
|
|
while (idx != end) {
|
|
|
|
rte_prefetch0(&(*rxq->cqes)[(idx) & cqe_cnt]);
|
|
|
|
++idx;
|
|
|
|
}
|
2016-06-24 13:17:54 +00:00
|
|
|
} else {
|
2017-09-17 10:42:02 +00:00
|
|
|
len = rte_be_to_cpu_32(cqe->byte_cnt);
|
|
|
|
*rss_hash = rte_be_to_cpu_32(cqe->rx_hash_res);
|
2016-06-24 13:17:54 +00:00
|
|
|
}
|
|
|
|
/* Error while receiving packet. */
|
|
|
|
if (unlikely(MLX5_CQE_OPCODE(op_own) == MLX5_CQE_RESP_ERR))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:52:41 +00:00
|
|
|
/**
|
|
|
|
* Translate RX completion flags to offload flags.
|
|
|
|
*
|
2016-06-24 13:17:52 +00:00
|
|
|
* @param[in] cqe
|
|
|
|
* Pointer to CQE.
|
2015-10-30 18:52:41 +00:00
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Offload flags (ol_flags) for struct rte_mbuf.
|
|
|
|
*/
|
|
|
|
static inline uint32_t
|
2018-04-23 12:33:04 +00:00
|
|
|
rxq_cq_to_ol_flags(volatile struct mlx5_cqe *cqe)
|
2015-10-30 18:52:41 +00:00
|
|
|
{
|
|
|
|
uint32_t ol_flags = 0;
|
2017-09-17 10:42:02 +00:00
|
|
|
uint16_t flags = rte_be_to_cpu_16(cqe->hdr_type_etc);
|
2017-01-20 15:27:29 +00:00
|
|
|
|
|
|
|
ol_flags =
|
|
|
|
TRANSPOSE(flags,
|
|
|
|
MLX5_CQE_RX_L3_HDR_VALID,
|
|
|
|
PKT_RX_IP_CKSUM_GOOD) |
|
|
|
|
TRANSPOSE(flags,
|
|
|
|
MLX5_CQE_RX_L4_HDR_VALID,
|
|
|
|
PKT_RX_L4_CKSUM_GOOD);
|
2015-10-30 18:52:41 +00:00
|
|
|
return ol_flags;
|
|
|
|
}
|
|
|
|
|
2018-05-09 11:13:48 +00:00
|
|
|
/**
|
|
|
|
* Fill in mbuf fields from RX completion flags.
|
|
|
|
* Note that pkt->ol_flags should be initialized outside of this function.
|
|
|
|
*
|
|
|
|
* @param rxq
|
|
|
|
* Pointer to RX queue.
|
|
|
|
* @param pkt
|
|
|
|
* mbuf to fill.
|
|
|
|
* @param cqe
|
|
|
|
* CQE to process.
|
|
|
|
* @param rss_hash_res
|
|
|
|
* Packet RSS Hash result.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
rxq_cq_to_mbuf(struct mlx5_rxq_data *rxq, struct rte_mbuf *pkt,
|
|
|
|
volatile struct mlx5_cqe *cqe, uint32_t rss_hash_res)
|
|
|
|
{
|
|
|
|
/* Update packet information. */
|
|
|
|
pkt->packet_type = rxq_cq_to_pkt_type(rxq, cqe);
|
|
|
|
if (rss_hash_res && rxq->rss_hash) {
|
|
|
|
pkt->hash.rss = rss_hash_res;
|
|
|
|
pkt->ol_flags |= PKT_RX_RSS_HASH;
|
|
|
|
}
|
|
|
|
if (rxq->mark && MLX5_FLOW_MARK_IS_VALID(cqe->sop_drop_qpn)) {
|
|
|
|
pkt->ol_flags |= PKT_RX_FDIR;
|
|
|
|
if (cqe->sop_drop_qpn !=
|
|
|
|
rte_cpu_to_be_32(MLX5_FLOW_MARK_DEFAULT)) {
|
|
|
|
uint32_t mark = cqe->sop_drop_qpn;
|
|
|
|
|
|
|
|
pkt->ol_flags |= PKT_RX_FDIR_ID;
|
|
|
|
pkt->hash.fdir.hi = mlx5_flow_mark_get(mark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rxq->csum)
|
|
|
|
pkt->ol_flags |= rxq_cq_to_ol_flags(cqe);
|
|
|
|
if (rxq->vlan_strip &&
|
|
|
|
(cqe->hdr_type_etc & rte_cpu_to_be_16(MLX5_CQE_VLAN_STRIPPED))) {
|
|
|
|
pkt->ol_flags |= PKT_RX_VLAN | PKT_RX_VLAN_STRIPPED;
|
|
|
|
pkt->vlan_tci = rte_be_to_cpu_16(cqe->vlan_info);
|
|
|
|
}
|
|
|
|
if (rxq->hw_timestamp) {
|
|
|
|
pkt->timestamp = rte_be_to_cpu_64(cqe->timestamp);
|
|
|
|
pkt->ol_flags |= PKT_RX_TIMESTAMP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:52:31 +00:00
|
|
|
/**
|
|
|
|
* DPDK callback for RX.
|
|
|
|
*
|
|
|
|
* @param dpdk_rxq
|
|
|
|
* Generic pointer to RX queue structure.
|
|
|
|
* @param[out] pkts
|
|
|
|
* Array to store received packets.
|
|
|
|
* @param pkts_n
|
|
|
|
* Maximum number of packets in array.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Number of packets successfully received (<= pkts_n).
|
|
|
|
*/
|
|
|
|
uint16_t
|
|
|
|
mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
|
|
|
|
{
|
2017-10-09 14:44:39 +00:00
|
|
|
struct mlx5_rxq_data *rxq = dpdk_rxq;
|
2016-09-20 08:53:47 +00:00
|
|
|
const unsigned int wqe_cnt = (1 << rxq->elts_n) - 1;
|
2016-09-20 08:53:48 +00:00
|
|
|
const unsigned int cqe_cnt = (1 << rxq->cqe_n) - 1;
|
2016-06-24 13:18:04 +00:00
|
|
|
const unsigned int sges_n = rxq->sges_n;
|
|
|
|
struct rte_mbuf *pkt = NULL;
|
|
|
|
struct rte_mbuf *seg = NULL;
|
2016-11-02 10:39:38 +00:00
|
|
|
volatile struct mlx5_cqe *cqe =
|
|
|
|
&(*rxq->cqes)[rxq->cq_ci & cqe_cnt];
|
2016-06-24 13:18:04 +00:00
|
|
|
unsigned int i = 0;
|
|
|
|
unsigned int rq_ci = rxq->rq_ci << sges_n;
|
2017-04-06 09:16:32 +00:00
|
|
|
int len = 0; /* keep its value across iterations. */
|
2015-10-30 18:52:31 +00:00
|
|
|
|
2016-06-24 13:18:04 +00:00
|
|
|
while (pkts_n) {
|
2016-06-24 13:17:52 +00:00
|
|
|
unsigned int idx = rq_ci & wqe_cnt;
|
2018-05-09 11:13:50 +00:00
|
|
|
volatile struct mlx5_wqe_data_seg *wqe =
|
|
|
|
&((volatile struct mlx5_wqe_data_seg *)rxq->wqes)[idx];
|
2016-06-24 13:18:04 +00:00
|
|
|
struct rte_mbuf *rep = (*rxq->elts)[idx];
|
2016-09-28 12:11:18 +00:00
|
|
|
uint32_t rss_hash_res = 0;
|
2015-10-30 18:52:31 +00:00
|
|
|
|
2016-06-24 13:18:04 +00:00
|
|
|
if (pkt)
|
|
|
|
NEXT(seg) = rep;
|
|
|
|
seg = rep;
|
|
|
|
rte_prefetch0(seg);
|
2016-06-24 13:17:52 +00:00
|
|
|
rte_prefetch0(cqe);
|
2016-06-24 13:18:04 +00:00
|
|
|
rte_prefetch0(wqe);
|
2016-05-11 14:43:46 +00:00
|
|
|
rep = rte_mbuf_raw_alloc(rxq->mp);
|
2015-10-30 18:52:31 +00:00
|
|
|
if (unlikely(rep == NULL)) {
|
2016-08-02 14:41:21 +00:00
|
|
|
++rxq->stats.rx_nombuf;
|
|
|
|
if (!pkt) {
|
|
|
|
/*
|
|
|
|
* no buffers before we even started,
|
|
|
|
* bail out silently.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
2016-07-08 12:43:26 +00:00
|
|
|
while (pkt != seg) {
|
|
|
|
assert(pkt != (*rxq->elts)[idx]);
|
2016-11-17 09:49:54 +00:00
|
|
|
rep = NEXT(pkt);
|
2017-04-04 16:28:02 +00:00
|
|
|
NEXT(pkt) = NULL;
|
|
|
|
NB_SEGS(pkt) = 1;
|
2017-04-04 16:28:01 +00:00
|
|
|
rte_mbuf_raw_free(pkt);
|
2016-11-17 09:49:54 +00:00
|
|
|
pkt = rep;
|
2016-06-24 13:18:04 +00:00
|
|
|
}
|
2016-06-24 13:17:52 +00:00
|
|
|
break;
|
2015-10-30 18:52:31 +00:00
|
|
|
}
|
2016-06-24 13:18:04 +00:00
|
|
|
if (!pkt) {
|
2016-11-02 10:39:38 +00:00
|
|
|
cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_cnt];
|
2016-09-28 12:11:18 +00:00
|
|
|
len = mlx5_rx_poll_len(rxq, cqe, cqe_cnt,
|
|
|
|
&rss_hash_res);
|
|
|
|
if (!len) {
|
2017-04-04 16:28:01 +00:00
|
|
|
rte_mbuf_raw_free(rep);
|
2016-06-24 13:18:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unlikely(len == -1)) {
|
|
|
|
/* RX error, packet is likely too large. */
|
2017-04-04 16:28:01 +00:00
|
|
|
rte_mbuf_raw_free(rep);
|
2016-06-24 13:18:04 +00:00
|
|
|
++rxq->stats.idropped;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
pkt = seg;
|
|
|
|
assert(len >= (rxq->crc_present << 2));
|
2016-07-07 16:34:45 +00:00
|
|
|
pkt->ol_flags = 0;
|
2018-05-09 11:13:48 +00:00
|
|
|
rxq_cq_to_mbuf(rxq, pkt, cqe, rss_hash_res);
|
2017-04-11 12:46:17 +00:00
|
|
|
if (rxq->crc_present)
|
|
|
|
len -= ETHER_CRC_LEN;
|
2016-06-24 13:18:04 +00:00
|
|
|
PKT_LEN(pkt) = len;
|
2016-06-24 13:17:54 +00:00
|
|
|
}
|
2016-06-24 13:18:04 +00:00
|
|
|
DATA_LEN(rep) = DATA_LEN(seg);
|
|
|
|
PKT_LEN(rep) = PKT_LEN(seg);
|
|
|
|
SET_DATA_OFF(rep, DATA_OFF(seg));
|
|
|
|
PORT(rep) = PORT(seg);
|
|
|
|
(*rxq->elts)[idx] = rep;
|
2016-06-24 13:17:52 +00:00
|
|
|
/*
|
|
|
|
* Fill NIC descriptor with the new buffer. The lkey and size
|
|
|
|
* of the buffers are already known, only the buffer address
|
|
|
|
* changes.
|
|
|
|
*/
|
2017-09-17 10:42:02 +00:00
|
|
|
wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
|
net/mlx5: add new memory region support
This is the new design of Memory Region (MR) for mlx PMD, in order to:
- Accommodate the new memory hotplug model.
- Support non-contiguous Mempool.
There are multiple layers for MR search.
L0 is to look up the last-hit entry which is pointed by mr_ctrl->mru (Most
Recently Used). If L0 misses, L1 is to look up the address in a fixed-sized
array by linear search. L0/L1 is in an inline function -
mlx5_mr_lookup_cache().
If L1 misses, the bottom-half function is called to look up the address
from the bigger local cache of the queue. This is L2 - mlx5_mr_addr2mr_bh()
and it is not an inline function. Data structure for L2 is the Binary Tree.
If L2 misses, the search falls into the slowest path which takes locks in
order to access global device cache (priv->mr.cache) which is also a B-tree
and caches the original MR list (priv->mr.mr_list) of the device. Unless
the global cache is overflowed, it is all-inclusive of the MR list. This is
L3 - mlx5_mr_lookup_dev(). The size of the L3 cache table is limited and
can't be expanded on the fly due to deadlock. Refer to the comments in the
code for the details - mr_lookup_dev(). If L3 is overflowed, the list will
have to be searched directly bypassing the cache although it is slower.
If L3 misses, a new MR for the address should be created -
mlx5_mr_create(). When it creates a new MR, it tries to register adjacent
memsegs as much as possible which are virtually contiguous around the
address. This must take two locks - memory_hotplug_lock and
priv->mr.rwlock. Due to memory_hotplug_lock, there can't be any
allocation/free of memory inside.
In the free callback of the memory hotplug event, freed space is searched
from the MR list and corresponding bits are cleared from the bitmap of MRs.
This can fragment a MR and the MR will have multiple search entries in the
caches. Once there's a change by the event, the global cache must be
rebuilt and all the per-queue caches will be flushed as well. If memory is
frequently freed in run-time, that may cause jitter on dataplane processing
in the worst case by incurring MR cache flush and rebuild. But, it would be
the least probable scenario.
To guarantee the most optimal performance, it is highly recommended to use
an EAL option - '--socket-mem'. Then, the reserved memory will be pinned
and won't be freed dynamically. And it is also recommended to configure
per-lcore cache of Mempool. Even though there're many MRs for a device or
MRs are highly fragmented, the cache of Mempool will be much helpful to
reduce misses on per-queue caches anyway.
'--legacy-mem' is also supported.
Signed-off-by: Yongseok Koh <yskoh@mellanox.com>
2018-05-09 11:09:04 +00:00
|
|
|
/* If there's only one MR, no need to replace LKey in WQE. */
|
|
|
|
if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
|
|
|
|
wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
|
2016-06-24 13:18:04 +00:00
|
|
|
if (len > DATA_LEN(seg)) {
|
|
|
|
len -= DATA_LEN(seg);
|
|
|
|
++NB_SEGS(pkt);
|
|
|
|
++rq_ci;
|
|
|
|
continue;
|
2016-03-03 14:27:14 +00:00
|
|
|
}
|
2016-06-24 13:18:04 +00:00
|
|
|
DATA_LEN(seg) = len;
|
2015-10-30 18:52:36 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment bytes counter. */
|
2016-06-24 13:18:04 +00:00
|
|
|
rxq->stats.ibytes += PKT_LEN(pkt);
|
2015-10-30 18:52:36 +00:00
|
|
|
#endif
|
2016-06-24 13:17:52 +00:00
|
|
|
/* Return packet. */
|
|
|
|
*(pkts++) = pkt;
|
2016-06-24 13:18:04 +00:00
|
|
|
pkt = NULL;
|
|
|
|
--pkts_n;
|
|
|
|
++i;
|
2016-06-24 13:17:54 +00:00
|
|
|
skip:
|
2016-06-24 13:18:04 +00:00
|
|
|
/* Align consumer index to the next stride. */
|
|
|
|
rq_ci >>= sges_n;
|
2016-06-24 13:17:52 +00:00
|
|
|
++rq_ci;
|
2016-06-24 13:18:04 +00:00
|
|
|
rq_ci <<= sges_n;
|
2015-10-30 18:52:31 +00:00
|
|
|
}
|
2016-06-24 13:18:04 +00:00
|
|
|
if (unlikely((i == 0) && ((rq_ci >> sges_n) == rxq->rq_ci)))
|
2015-10-30 18:52:31 +00:00
|
|
|
return 0;
|
2016-06-24 13:17:52 +00:00
|
|
|
/* Update the consumer index. */
|
2016-06-24 13:18:04 +00:00
|
|
|
rxq->rq_ci = rq_ci >> sges_n;
|
2018-01-25 21:02:49 +00:00
|
|
|
rte_cio_wmb();
|
2017-09-17 10:42:02 +00:00
|
|
|
*rxq->cq_db = rte_cpu_to_be_32(rxq->cq_ci);
|
2018-01-25 21:02:49 +00:00
|
|
|
rte_cio_wmb();
|
2017-09-17 10:42:02 +00:00
|
|
|
*rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
|
2015-10-30 18:52:36 +00:00
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment packets counter. */
|
2016-06-24 13:18:04 +00:00
|
|
|
rxq->stats.ipackets += i;
|
2015-10-30 18:52:36 +00:00
|
|
|
#endif
|
2016-06-24 13:18:04 +00:00
|
|
|
return i;
|
2015-10-30 18:52:31 +00:00
|
|
|
}
|
|
|
|
|
2018-05-09 11:13:50 +00:00
|
|
|
void
|
|
|
|
mlx5_mprq_buf_free_cb(void *addr __rte_unused, void *opaque)
|
|
|
|
{
|
|
|
|
struct mlx5_mprq_buf *buf = opaque;
|
|
|
|
|
|
|
|
if (rte_atomic16_read(&buf->refcnt) == 1) {
|
|
|
|
rte_mempool_put(buf->mp, buf);
|
|
|
|
} else if (rte_atomic16_add_return(&buf->refcnt, -1) == 0) {
|
|
|
|
rte_atomic16_set(&buf->refcnt, 1);
|
|
|
|
rte_mempool_put(buf->mp, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mlx5_mprq_buf_free(struct mlx5_mprq_buf *buf)
|
|
|
|
{
|
|
|
|
mlx5_mprq_buf_free_cb(NULL, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
mprq_buf_replace(struct mlx5_rxq_data *rxq, uint16_t rq_idx)
|
|
|
|
{
|
|
|
|
struct mlx5_mprq_buf *rep = rxq->mprq_repl;
|
|
|
|
volatile struct mlx5_wqe_data_seg *wqe =
|
|
|
|
&((volatile struct mlx5_wqe_mprq *)rxq->wqes)[rq_idx].dseg;
|
|
|
|
void *addr;
|
|
|
|
|
|
|
|
assert(rep != NULL);
|
|
|
|
/* Replace MPRQ buf. */
|
|
|
|
(*rxq->mprq_bufs)[rq_idx] = rep;
|
|
|
|
/* Replace WQE. */
|
|
|
|
addr = mlx5_mprq_buf_addr(rep);
|
|
|
|
wqe->addr = rte_cpu_to_be_64((uintptr_t)addr);
|
|
|
|
/* If there's only one MR, no need to replace LKey in WQE. */
|
|
|
|
if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
|
|
|
|
wqe->lkey = mlx5_rx_addr2mr(rxq, (uintptr_t)addr);
|
|
|
|
/* Stash a mbuf for next replacement. */
|
|
|
|
if (likely(!rte_mempool_get(rxq->mprq_mp, (void **)&rep)))
|
|
|
|
rxq->mprq_repl = rep;
|
|
|
|
else
|
|
|
|
rxq->mprq_repl = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DPDK callback for RX with Multi-Packet RQ support.
|
|
|
|
*
|
|
|
|
* @param dpdk_rxq
|
|
|
|
* Generic pointer to RX queue structure.
|
|
|
|
* @param[out] pkts
|
|
|
|
* Array to store received packets.
|
|
|
|
* @param pkts_n
|
|
|
|
* Maximum number of packets in array.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Number of packets successfully received (<= pkts_n).
|
|
|
|
*/
|
|
|
|
uint16_t
|
|
|
|
mlx5_rx_burst_mprq(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
|
|
|
|
{
|
|
|
|
struct mlx5_rxq_data *rxq = dpdk_rxq;
|
|
|
|
const unsigned int strd_n = 1 << rxq->strd_num_n;
|
|
|
|
const unsigned int strd_sz = 1 << rxq->strd_sz_n;
|
|
|
|
const unsigned int strd_shift =
|
|
|
|
MLX5_MPRQ_STRIDE_SHIFT_BYTE * rxq->strd_shift_en;
|
|
|
|
const unsigned int cq_mask = (1 << rxq->cqe_n) - 1;
|
|
|
|
const unsigned int wq_mask = (1 << rxq->elts_n) - 1;
|
|
|
|
volatile struct mlx5_cqe *cqe = &(*rxq->cqes)[rxq->cq_ci & cq_mask];
|
|
|
|
unsigned int i = 0;
|
|
|
|
uint16_t rq_ci = rxq->rq_ci;
|
|
|
|
uint16_t strd_idx = rxq->strd_ci;
|
|
|
|
struct mlx5_mprq_buf *buf = (*rxq->mprq_bufs)[rq_ci & wq_mask];
|
|
|
|
|
|
|
|
while (i < pkts_n) {
|
|
|
|
struct rte_mbuf *pkt;
|
|
|
|
void *addr;
|
|
|
|
int ret;
|
|
|
|
unsigned int len;
|
|
|
|
uint16_t consumed_strd;
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t byte_cnt;
|
|
|
|
uint32_t rss_hash_res = 0;
|
|
|
|
|
|
|
|
if (strd_idx == strd_n) {
|
|
|
|
/* Replace WQE only if the buffer is still in use. */
|
|
|
|
if (rte_atomic16_read(&buf->refcnt) > 1) {
|
|
|
|
mprq_buf_replace(rxq, rq_ci & wq_mask);
|
|
|
|
/* Release the old buffer. */
|
|
|
|
mlx5_mprq_buf_free(buf);
|
|
|
|
} else if (unlikely(rxq->mprq_repl == NULL)) {
|
|
|
|
struct mlx5_mprq_buf *rep;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Currently, the MPRQ mempool is out of buffer
|
|
|
|
* and doing memcpy regardless of the size of Rx
|
|
|
|
* packet. Retry allocation to get back to
|
|
|
|
* normal.
|
|
|
|
*/
|
|
|
|
if (!rte_mempool_get(rxq->mprq_mp,
|
|
|
|
(void **)&rep))
|
|
|
|
rxq->mprq_repl = rep;
|
|
|
|
}
|
|
|
|
/* Advance to the next WQE. */
|
|
|
|
strd_idx = 0;
|
|
|
|
++rq_ci;
|
|
|
|
buf = (*rxq->mprq_bufs)[rq_ci & wq_mask];
|
|
|
|
}
|
|
|
|
cqe = &(*rxq->cqes)[rxq->cq_ci & cq_mask];
|
|
|
|
ret = mlx5_rx_poll_len(rxq, cqe, cq_mask, &rss_hash_res);
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
if (unlikely(ret == -1)) {
|
|
|
|
/* RX error, packet is likely too large. */
|
|
|
|
++rxq->stats.idropped;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
byte_cnt = ret;
|
|
|
|
consumed_strd = (byte_cnt & MLX5_MPRQ_STRIDE_NUM_MASK) >>
|
|
|
|
MLX5_MPRQ_STRIDE_NUM_SHIFT;
|
|
|
|
assert(consumed_strd);
|
|
|
|
/* Calculate offset before adding up stride index. */
|
|
|
|
offset = strd_idx * strd_sz + strd_shift;
|
|
|
|
strd_idx += consumed_strd;
|
|
|
|
if (byte_cnt & MLX5_MPRQ_FILLER_MASK)
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* Currently configured to receive a packet per a stride. But if
|
|
|
|
* MTU is adjusted through kernel interface, device could
|
|
|
|
* consume multiple strides without raising an error. In this
|
|
|
|
* case, the packet should be dropped because it is bigger than
|
|
|
|
* the max_rx_pkt_len.
|
|
|
|
*/
|
|
|
|
if (unlikely(consumed_strd > 1)) {
|
|
|
|
++rxq->stats.idropped;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pkt = rte_pktmbuf_alloc(rxq->mp);
|
|
|
|
if (unlikely(pkt == NULL)) {
|
|
|
|
++rxq->stats.rx_nombuf;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
len = (byte_cnt & MLX5_MPRQ_LEN_MASK) >> MLX5_MPRQ_LEN_SHIFT;
|
|
|
|
assert((int)len >= (rxq->crc_present << 2));
|
|
|
|
if (rxq->crc_present)
|
|
|
|
len -= ETHER_CRC_LEN;
|
|
|
|
addr = RTE_PTR_ADD(mlx5_mprq_buf_addr(buf), offset);
|
|
|
|
/* Initialize the offload flag. */
|
|
|
|
pkt->ol_flags = 0;
|
|
|
|
/*
|
|
|
|
* Memcpy packets to the target mbuf if:
|
|
|
|
* - The size of packet is smaller than mprq_max_memcpy_len.
|
|
|
|
* - Out of buffer in the Mempool for Multi-Packet RQ.
|
|
|
|
*/
|
|
|
|
if (len <= rxq->mprq_max_memcpy_len || rxq->mprq_repl == NULL) {
|
|
|
|
/*
|
|
|
|
* When memcpy'ing packet due to out-of-buffer, the
|
|
|
|
* packet must be smaller than the target mbuf.
|
|
|
|
*/
|
|
|
|
if (unlikely(rte_pktmbuf_tailroom(pkt) < len)) {
|
|
|
|
rte_pktmbuf_free_seg(pkt);
|
|
|
|
++rxq->stats.idropped;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rte_memcpy(rte_pktmbuf_mtod(pkt, void *), addr, len);
|
|
|
|
} else {
|
|
|
|
rte_iova_t buf_iova;
|
|
|
|
struct rte_mbuf_ext_shared_info *shinfo;
|
|
|
|
uint16_t buf_len = consumed_strd * strd_sz;
|
|
|
|
|
|
|
|
/* Increment the refcnt of the whole chunk. */
|
|
|
|
rte_atomic16_add_return(&buf->refcnt, 1);
|
|
|
|
assert((uint16_t)rte_atomic16_read(&buf->refcnt) <=
|
|
|
|
strd_n + 1);
|
|
|
|
addr = RTE_PTR_SUB(addr, RTE_PKTMBUF_HEADROOM);
|
|
|
|
/*
|
|
|
|
* MLX5 device doesn't use iova but it is necessary in a
|
|
|
|
* case where the Rx packet is transmitted via a
|
|
|
|
* different PMD.
|
|
|
|
*/
|
|
|
|
buf_iova = rte_mempool_virt2iova(buf) +
|
|
|
|
RTE_PTR_DIFF(addr, buf);
|
|
|
|
shinfo = rte_pktmbuf_ext_shinfo_init_helper(addr,
|
|
|
|
&buf_len, mlx5_mprq_buf_free_cb, buf);
|
|
|
|
/*
|
|
|
|
* EXT_ATTACHED_MBUF will be set to pkt->ol_flags when
|
|
|
|
* attaching the stride to mbuf and more offload flags
|
|
|
|
* will be added below by calling rxq_cq_to_mbuf().
|
|
|
|
* Other fields will be overwritten.
|
|
|
|
*/
|
|
|
|
rte_pktmbuf_attach_extbuf(pkt, addr, buf_iova, buf_len,
|
|
|
|
shinfo);
|
|
|
|
rte_pktmbuf_reset_headroom(pkt);
|
|
|
|
assert(pkt->ol_flags == EXT_ATTACHED_MBUF);
|
|
|
|
/*
|
|
|
|
* Prevent potential overflow due to MTU change through
|
|
|
|
* kernel interface.
|
|
|
|
*/
|
|
|
|
if (unlikely(rte_pktmbuf_tailroom(pkt) < len)) {
|
|
|
|
rte_pktmbuf_free_seg(pkt);
|
|
|
|
++rxq->stats.idropped;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rxq_cq_to_mbuf(rxq, pkt, cqe, rss_hash_res);
|
|
|
|
PKT_LEN(pkt) = len;
|
|
|
|
DATA_LEN(pkt) = len;
|
|
|
|
PORT(pkt) = rxq->port_id;
|
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment bytes counter. */
|
|
|
|
rxq->stats.ibytes += PKT_LEN(pkt);
|
|
|
|
#endif
|
|
|
|
/* Return packet. */
|
|
|
|
*(pkts++) = pkt;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
/* Update the consumer indexes. */
|
|
|
|
rxq->strd_ci = strd_idx;
|
2018-05-12 01:35:44 +00:00
|
|
|
rte_cio_wmb();
|
2018-05-09 11:13:50 +00:00
|
|
|
*rxq->cq_db = rte_cpu_to_be_32(rxq->cq_ci);
|
|
|
|
if (rq_ci != rxq->rq_ci) {
|
|
|
|
rxq->rq_ci = rq_ci;
|
2018-05-12 01:35:44 +00:00
|
|
|
rte_cio_wmb();
|
2018-05-09 11:13:50 +00:00
|
|
|
*rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
|
|
|
|
}
|
|
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
|
|
/* Increment packets counter. */
|
|
|
|
rxq->stats.ipackets += i;
|
|
|
|
#endif
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:52:31 +00:00
|
|
|
/**
|
|
|
|
* Dummy DPDK callback for TX.
|
|
|
|
*
|
|
|
|
* This function is used to temporarily replace the real callback during
|
|
|
|
* unsafe control operations on the queue, or in case of error.
|
|
|
|
*
|
|
|
|
* @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
|
2018-03-05 12:20:59 +00:00
|
|
|
removed_tx_burst(void *dpdk_txq __rte_unused,
|
|
|
|
struct rte_mbuf **pkts __rte_unused,
|
|
|
|
uint16_t pkts_n __rte_unused)
|
2015-10-30 18:52:31 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dummy DPDK callback for RX.
|
|
|
|
*
|
|
|
|
* This function is used to temporarily replace the real callback during
|
|
|
|
* unsafe control operations on the queue, or in case of error.
|
|
|
|
*
|
|
|
|
* @param dpdk_rxq
|
|
|
|
* Generic pointer to RX queue structure.
|
|
|
|
* @param[out] pkts
|
|
|
|
* Array to store received packets.
|
|
|
|
* @param pkts_n
|
|
|
|
* Maximum number of packets in array.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Number of packets successfully received (<= pkts_n).
|
|
|
|
*/
|
|
|
|
uint16_t
|
2018-03-05 12:20:59 +00:00
|
|
|
removed_rx_burst(void *dpdk_txq __rte_unused,
|
|
|
|
struct rte_mbuf **pkts __rte_unused,
|
|
|
|
uint16_t pkts_n __rte_unused)
|
2015-10-30 18:52:31 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2017-07-06 18:41:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Vectorized Rx/Tx routines are not compiled in when required vector
|
|
|
|
* instructions are not supported on a target architecture. The following null
|
|
|
|
* stubs are needed for linkage when those are not included outside of this file
|
|
|
|
* (e.g. mlx5_rxtx_vec_sse.c for x86).
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint16_t __attribute__((weak))
|
2018-03-05 12:20:59 +00:00
|
|
|
mlx5_tx_burst_raw_vec(void *dpdk_txq __rte_unused,
|
|
|
|
struct rte_mbuf **pkts __rte_unused,
|
|
|
|
uint16_t pkts_n __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t __attribute__((weak))
|
2018-03-05 12:20:59 +00:00
|
|
|
mlx5_tx_burst_vec(void *dpdk_txq __rte_unused,
|
|
|
|
struct rte_mbuf **pkts __rte_unused,
|
|
|
|
uint16_t pkts_n __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t __attribute__((weak))
|
2018-03-05 12:20:59 +00:00
|
|
|
mlx5_rx_burst_vec(void *dpdk_txq __rte_unused,
|
|
|
|
struct rte_mbuf **pkts __rte_unused,
|
|
|
|
uint16_t pkts_n __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __attribute__((weak))
|
2018-03-05 12:21:04 +00:00
|
|
|
mlx5_check_raw_vec_tx_support(struct rte_eth_dev *dev __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __attribute__((weak))
|
2018-03-05 12:21:04 +00:00
|
|
|
mlx5_check_vec_tx_support(struct rte_eth_dev *dev __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __attribute__((weak))
|
2018-03-05 12:21:04 +00:00
|
|
|
mlx5_rxq_check_vec_support(struct mlx5_rxq_data *rxq __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __attribute__((weak))
|
2018-03-05 12:21:04 +00:00
|
|
|
mlx5_check_vec_rx_support(struct rte_eth_dev *dev __rte_unused)
|
2017-07-06 18:41:10 +00:00
|
|
|
{
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|