2017-09-01 08:07:00 +00:00
|
|
|
/*-
|
|
|
|
* BSD LICENSE
|
|
|
|
*
|
|
|
|
* Copyright 2017 6WIND S.A.
|
|
|
|
* Copyright 2017 Mellanox
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of 6WIND S.A. nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* Rx queues configuration for mlx4 driver.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/* Verbs headers do not support -pedantic. */
|
|
|
|
#ifdef PEDANTIC
|
|
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
|
|
#endif
|
|
|
|
#include <infiniband/verbs.h>
|
|
|
|
#ifdef PEDANTIC
|
|
|
|
#pragma GCC diagnostic error "-Wpedantic"
|
|
|
|
#endif
|
|
|
|
|
2017-10-12 12:29:57 +00:00
|
|
|
#include <rte_byteorder.h>
|
2017-09-01 08:07:00 +00:00
|
|
|
#include <rte_common.h>
|
|
|
|
#include <rte_errno.h>
|
|
|
|
#include <rte_ethdev.h>
|
2017-10-12 12:19:29 +00:00
|
|
|
#include <rte_flow.h>
|
2017-09-01 08:07:00 +00:00
|
|
|
#include <rte_malloc.h>
|
|
|
|
#include <rte_mbuf.h>
|
|
|
|
#include <rte_mempool.h>
|
|
|
|
|
|
|
|
#include "mlx4.h"
|
2017-10-12 12:19:27 +00:00
|
|
|
#include "mlx4_flow.h"
|
2017-09-01 08:07:00 +00:00
|
|
|
#include "mlx4_rxtx.h"
|
|
|
|
#include "mlx4_utils.h"
|
|
|
|
|
net/mlx4: add RSS flow rule action support
This patch dissociates single-queue indirection tables and hash QP objects
from Rx queue structures to relinquish their control to users through the
RSS flow rule action, while simultaneously allowing multiple queues to be
associated with RSS contexts.
Flow rules share identical RSS contexts (hashed fields, hash key, target
queues) to save on memory and other resources. The trade-off is some added
complexity due to reference counters management on RSS contexts.
The QUEUE action is re-implemented on top of an automatically-generated
single-queue RSS context.
The following hardware limitations apply to RSS contexts:
- The number of queues in a group must be a power of two.
- Queue indices must be consecutive, for instance the [0 1 2 3] set is
allowed, however [3 2 1 0], [0 2 1 3] and [0 0 1 1 2 3 3 3] are not.
- The first queue of a group must be aligned to a multiple of the context
size, e.g. if queues [0 1 2 3 4] are defined globally, allowed group
combinations are [0 1] and [2 3]; groups [1 2] and [3 4] are not
supported.
- RSS hash key, while configurable per context, must be exactly 40 bytes
long.
- The only supported hash algorithm is Toeplitz.
Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
2017-10-12 12:19:41 +00:00
|
|
|
/**
|
|
|
|
* Historical RSS hash key.
|
|
|
|
*
|
|
|
|
* This used to be the default for mlx4 in Linux before v3.19 switched to
|
|
|
|
* generating random hash keys through netdev_rss_key_fill().
|
|
|
|
*
|
|
|
|
* It is used in this PMD for consistency with past DPDK releases but can
|
|
|
|
* now be overridden through user configuration.
|
|
|
|
*
|
|
|
|
* Note: this is not const to work around API quirks.
|
|
|
|
*/
|
|
|
|
uint8_t
|
|
|
|
mlx4_rss_hash_key_default[MLX4_RSS_HASH_KEY_SIZE] = {
|
|
|
|
0x2c, 0xc6, 0x81, 0xd1,
|
|
|
|
0x5b, 0xdb, 0xf4, 0xf7,
|
|
|
|
0xfc, 0xa2, 0x83, 0x19,
|
|
|
|
0xdb, 0x1a, 0x3e, 0x94,
|
|
|
|
0x6b, 0x9e, 0x38, 0xd9,
|
|
|
|
0x2c, 0x9c, 0x03, 0xd1,
|
|
|
|
0xad, 0x99, 0x44, 0xa7,
|
|
|
|
0xd9, 0x56, 0x3d, 0x59,
|
|
|
|
0x06, 0x3c, 0x25, 0xf3,
|
|
|
|
0xfc, 0x1f, 0xdc, 0x2a,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtain a RSS context with specified properties.
|
|
|
|
*
|
|
|
|
* Used when creating a flow rule targeting one or several Rx queues.
|
|
|
|
*
|
|
|
|
* If a matching RSS context already exists, it is returned with its
|
|
|
|
* reference count incremented.
|
|
|
|
*
|
|
|
|
* @param priv
|
|
|
|
* Pointer to private structure.
|
|
|
|
* @param fields
|
|
|
|
* Fields for RSS processing (Verbs format).
|
|
|
|
* @param[in] key
|
|
|
|
* Hash key to use (whose size is exactly MLX4_RSS_HASH_KEY_SIZE).
|
|
|
|
* @param queues
|
|
|
|
* Number of target queues.
|
|
|
|
* @param[in] queue_id
|
|
|
|
* Target queues.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Pointer to RSS context on success, NULL otherwise and rte_errno is set.
|
|
|
|
*/
|
|
|
|
struct mlx4_rss *
|
|
|
|
mlx4_rss_get(struct priv *priv, uint64_t fields,
|
|
|
|
uint8_t key[MLX4_RSS_HASH_KEY_SIZE],
|
|
|
|
uint16_t queues, const uint16_t queue_id[])
|
|
|
|
{
|
|
|
|
struct mlx4_rss *rss;
|
|
|
|
size_t queue_id_size = sizeof(queue_id[0]) * queues;
|
|
|
|
|
|
|
|
LIST_FOREACH(rss, &priv->rss, next)
|
|
|
|
if (fields == rss->fields &&
|
|
|
|
queues == rss->queues &&
|
|
|
|
!memcmp(key, rss->key, MLX4_RSS_HASH_KEY_SIZE) &&
|
|
|
|
!memcmp(queue_id, rss->queue_id, queue_id_size)) {
|
|
|
|
++rss->refcnt;
|
|
|
|
return rss;
|
|
|
|
}
|
|
|
|
rss = rte_malloc(__func__, offsetof(struct mlx4_rss, queue_id) +
|
|
|
|
queue_id_size, 0);
|
|
|
|
if (!rss)
|
|
|
|
goto error;
|
|
|
|
*rss = (struct mlx4_rss){
|
|
|
|
.priv = priv,
|
|
|
|
.refcnt = 1,
|
|
|
|
.usecnt = 0,
|
|
|
|
.qp = NULL,
|
|
|
|
.ind = NULL,
|
|
|
|
.fields = fields,
|
|
|
|
.queues = queues,
|
|
|
|
};
|
|
|
|
memcpy(rss->key, key, MLX4_RSS_HASH_KEY_SIZE);
|
|
|
|
memcpy(rss->queue_id, queue_id, queue_id_size);
|
|
|
|
LIST_INSERT_HEAD(&priv->rss, rss, next);
|
|
|
|
return rss;
|
|
|
|
error:
|
|
|
|
rte_errno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release a RSS context instance.
|
|
|
|
*
|
|
|
|
* Used when destroying a flow rule targeting one or several Rx queues.
|
|
|
|
*
|
|
|
|
* This function decrements the reference count of the context and destroys
|
|
|
|
* it after reaching 0. The context must have no users at this point; all
|
|
|
|
* prior calls to mlx4_rss_attach() must have been followed by matching
|
|
|
|
* calls to mlx4_rss_detach().
|
|
|
|
*
|
|
|
|
* @param rss
|
|
|
|
* RSS context to release.
|
|
|
|
*/
|
|
|
|
void mlx4_rss_put(struct mlx4_rss *rss)
|
|
|
|
{
|
|
|
|
assert(rss->refcnt);
|
|
|
|
if (--rss->refcnt)
|
|
|
|
return;
|
|
|
|
assert(!rss->usecnt);
|
|
|
|
assert(!rss->qp);
|
|
|
|
assert(!rss->ind);
|
|
|
|
LIST_REMOVE(rss, next);
|
|
|
|
rte_free(rss);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attach a user to a RSS context instance.
|
|
|
|
*
|
|
|
|
* Used when the RSS QP and indirection table objects must be instantiated,
|
|
|
|
* that is, when a flow rule must be enabled.
|
|
|
|
*
|
|
|
|
* This function increments the usage count of the context.
|
|
|
|
*
|
|
|
|
* @param rss
|
|
|
|
* RSS context to attach to.
|
|
|
|
*/
|
|
|
|
int mlx4_rss_attach(struct mlx4_rss *rss)
|
|
|
|
{
|
|
|
|
assert(rss->refcnt);
|
|
|
|
if (rss->usecnt++) {
|
|
|
|
assert(rss->qp);
|
|
|
|
assert(rss->ind);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ibv_wq *ind_tbl[rss->queues];
|
|
|
|
struct priv *priv = rss->priv;
|
|
|
|
const char *msg;
|
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!rte_is_power_of_2(RTE_DIM(ind_tbl))) {
|
|
|
|
msg = "number of RSS queues must be a power of two";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
for (i = 0; i != RTE_DIM(ind_tbl); ++i) {
|
|
|
|
uint16_t id = rss->queue_id[i];
|
|
|
|
struct rxq *rxq = NULL;
|
|
|
|
|
|
|
|
if (id < priv->dev->data->nb_rx_queues)
|
|
|
|
rxq = priv->dev->data->rx_queues[id];
|
|
|
|
if (!rxq) {
|
|
|
|
msg = "RSS target queue is not configured";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
ind_tbl[i] = rxq->wq;
|
|
|
|
}
|
|
|
|
rss->ind = ibv_create_rwq_ind_table
|
|
|
|
(priv->ctx,
|
|
|
|
&(struct ibv_rwq_ind_table_init_attr){
|
|
|
|
.log_ind_tbl_size = rte_log2_u32(RTE_DIM(ind_tbl)),
|
|
|
|
.ind_tbl = ind_tbl,
|
|
|
|
.comp_mask = 0,
|
|
|
|
});
|
|
|
|
if (!rss->ind) {
|
|
|
|
msg = "RSS indirection table creation failure";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
rss->qp = ibv_create_qp_ex
|
|
|
|
(priv->ctx,
|
|
|
|
&(struct ibv_qp_init_attr_ex){
|
|
|
|
.comp_mask = (IBV_QP_INIT_ATTR_PD |
|
|
|
|
IBV_QP_INIT_ATTR_RX_HASH |
|
|
|
|
IBV_QP_INIT_ATTR_IND_TABLE),
|
|
|
|
.qp_type = IBV_QPT_RAW_PACKET,
|
|
|
|
.pd = priv->pd,
|
|
|
|
.rwq_ind_tbl = rss->ind,
|
|
|
|
.rx_hash_conf = {
|
|
|
|
.rx_hash_function = IBV_RX_HASH_FUNC_TOEPLITZ,
|
|
|
|
.rx_hash_key_len = MLX4_RSS_HASH_KEY_SIZE,
|
|
|
|
.rx_hash_key = rss->key,
|
|
|
|
.rx_hash_fields_mask = rss->fields,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!rss->qp) {
|
|
|
|
msg = "RSS hash QP creation failure";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
ret = ibv_modify_qp
|
|
|
|
(rss->qp,
|
|
|
|
&(struct ibv_qp_attr){
|
|
|
|
.qp_state = IBV_QPS_INIT,
|
|
|
|
.port_num = priv->port,
|
|
|
|
},
|
|
|
|
IBV_QP_STATE | IBV_QP_PORT);
|
|
|
|
if (ret) {
|
|
|
|
msg = "failed to switch RSS hash QP to INIT state";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
ret = ibv_modify_qp
|
|
|
|
(rss->qp,
|
|
|
|
&(struct ibv_qp_attr){
|
|
|
|
.qp_state = IBV_QPS_RTR,
|
|
|
|
},
|
|
|
|
IBV_QP_STATE);
|
|
|
|
if (ret) {
|
|
|
|
msg = "failed to switch RSS hash QP to RTR state";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
ERROR("mlx4: %s", msg);
|
|
|
|
--rss->usecnt;
|
|
|
|
rte_errno = EINVAL;
|
|
|
|
return -rte_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detach a user from a RSS context instance.
|
|
|
|
*
|
|
|
|
* Used when disabling (not destroying) a flow rule.
|
|
|
|
*
|
|
|
|
* This function decrements the usage count of the context and destroys
|
|
|
|
* usage resources after reaching 0.
|
|
|
|
*
|
|
|
|
* @param rss
|
|
|
|
* RSS context to detach from.
|
|
|
|
*/
|
|
|
|
void mlx4_rss_detach(struct mlx4_rss *rss)
|
|
|
|
{
|
|
|
|
assert(rss->refcnt);
|
|
|
|
assert(rss->qp);
|
|
|
|
assert(rss->ind);
|
|
|
|
if (--rss->usecnt)
|
|
|
|
return;
|
|
|
|
claim_zero(ibv_destroy_qp(rss->qp));
|
|
|
|
rss->qp = NULL;
|
|
|
|
claim_zero(ibv_destroy_rwq_ind_table(rss->ind));
|
|
|
|
rss->ind = NULL;
|
|
|
|
}
|
|
|
|
|
2017-09-01 08:07:00 +00:00
|
|
|
/**
|
|
|
|
* Allocate Rx queue elements.
|
|
|
|
*
|
|
|
|
* @param rxq
|
|
|
|
* Pointer to Rx queue structure.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
|
|
*/
|
|
|
|
static int
|
2017-10-12 12:19:38 +00:00
|
|
|
mlx4_rxq_alloc_elts(struct rxq *rxq)
|
2017-09-01 08:07:00 +00:00
|
|
|
{
|
2017-10-12 12:29:57 +00:00
|
|
|
const uint32_t elts_n = 1 << rxq->elts_n;
|
|
|
|
const uint32_t sges_n = 1 << rxq->sges_n;
|
|
|
|
struct rte_mbuf *(*elts)[elts_n] = rxq->elts;
|
2017-09-01 08:07:00 +00:00
|
|
|
unsigned int i;
|
|
|
|
|
2017-10-12 12:29:57 +00:00
|
|
|
assert(rte_is_power_of_2(elts_n));
|
2017-10-12 12:19:38 +00:00
|
|
|
for (i = 0; i != RTE_DIM(*elts); ++i) {
|
2017-10-12 12:29:57 +00:00
|
|
|
volatile struct mlx4_wqe_data_seg *scat = &(*rxq->wqes)[i];
|
2017-09-01 08:07:00 +00:00
|
|
|
struct rte_mbuf *buf = rte_pktmbuf_alloc(rxq->mp);
|
|
|
|
|
|
|
|
if (buf == NULL) {
|
2017-10-12 12:19:38 +00:00
|
|
|
while (i--) {
|
2017-10-12 12:29:57 +00:00
|
|
|
rte_pktmbuf_free_seg((*elts)[i]);
|
|
|
|
(*elts)[i] = NULL;
|
2017-10-12 12:19:38 +00:00
|
|
|
}
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = ENOMEM;
|
2017-10-12 12:19:38 +00:00
|
|
|
return -rte_errno;
|
2017-09-01 08:07:00 +00:00
|
|
|
}
|
|
|
|
/* Headroom is reserved by rte_pktmbuf_alloc(). */
|
|
|
|
assert(buf->data_off == RTE_PKTMBUF_HEADROOM);
|
|
|
|
/* Buffer is supposed to be empty. */
|
|
|
|
assert(rte_pktmbuf_data_len(buf) == 0);
|
|
|
|
assert(rte_pktmbuf_pkt_len(buf) == 0);
|
2017-10-12 12:29:57 +00:00
|
|
|
/* Only the first segment keeps headroom. */
|
|
|
|
if (i % sges_n)
|
|
|
|
buf->data_off = 0;
|
|
|
|
buf->port = rxq->port_id;
|
|
|
|
buf->data_len = rte_pktmbuf_tailroom(buf);
|
|
|
|
buf->pkt_len = rte_pktmbuf_tailroom(buf);
|
|
|
|
buf->nb_segs = 1;
|
|
|
|
*scat = (struct mlx4_wqe_data_seg){
|
|
|
|
.addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf,
|
|
|
|
uintptr_t)),
|
|
|
|
.byte_count = rte_cpu_to_be_32(buf->data_len),
|
|
|
|
.lkey = rte_cpu_to_be_32(rxq->mr->lkey),
|
|
|
|
};
|
|
|
|
(*elts)[i] = buf;
|
2017-09-01 08:07:00 +00:00
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
DEBUG("%p: allocated and configured %u segments (max %u packets)",
|
|
|
|
(void *)rxq, elts_n, elts_n / sges_n);
|
2017-09-01 08:07:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free Rx queue elements.
|
|
|
|
*
|
|
|
|
* @param rxq
|
|
|
|
* Pointer to Rx queue structure.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
mlx4_rxq_free_elts(struct rxq *rxq)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
2017-10-12 12:29:57 +00:00
|
|
|
struct rte_mbuf *(*elts)[1 << rxq->elts_n] = rxq->elts;
|
2017-09-01 08:07:00 +00:00
|
|
|
|
2017-10-12 12:29:57 +00:00
|
|
|
DEBUG("%p: freeing Rx queue elements", (void *)rxq);
|
2017-10-12 12:19:38 +00:00
|
|
|
for (i = 0; (i != RTE_DIM(*elts)); ++i) {
|
2017-10-12 12:29:57 +00:00
|
|
|
if (!(*elts)[i])
|
2017-10-12 12:19:38 +00:00
|
|
|
continue;
|
2017-10-12 12:29:57 +00:00
|
|
|
rte_pktmbuf_free_seg((*elts)[i]);
|
|
|
|
(*elts)[i] = NULL;
|
2017-10-12 12:19:38 +00:00
|
|
|
}
|
2017-09-01 08:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-10-12 12:19:37 +00:00
|
|
|
* DPDK callback to configure a Rx queue.
|
2017-09-01 08:07:00 +00:00
|
|
|
*
|
|
|
|
* @param dev
|
|
|
|
* Pointer to Ethernet device structure.
|
2017-10-12 12:19:37 +00:00
|
|
|
* @param idx
|
|
|
|
* Rx queue index.
|
2017-09-01 08:07:00 +00:00
|
|
|
* @param desc
|
|
|
|
* Number of descriptors to configure in queue.
|
|
|
|
* @param socket
|
|
|
|
* NUMA socket on which memory must be allocated.
|
|
|
|
* @param[in] conf
|
|
|
|
* Thresholds parameters.
|
|
|
|
* @param mp
|
|
|
|
* Memory pool for buffer allocations.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
|
|
*/
|
2017-10-12 12:19:37 +00:00
|
|
|
int
|
|
|
|
mlx4_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
|
|
|
|
unsigned int socket, const struct rte_eth_rxconf *conf,
|
|
|
|
struct rte_mempool *mp)
|
2017-09-01 08:07:00 +00:00
|
|
|
{
|
|
|
|
struct priv *priv = dev->data->dev_private;
|
2017-10-12 12:29:57 +00:00
|
|
|
struct mlx4dv_obj mlxdv;
|
|
|
|
struct mlx4dv_rwq dv_rwq;
|
|
|
|
struct mlx4dv_cq dv_cq;
|
2017-10-12 12:19:37 +00:00
|
|
|
uint32_t mb_len = rte_pktmbuf_data_room_size(mp);
|
2017-10-12 12:29:57 +00:00
|
|
|
struct rte_mbuf *(*elts)[rte_align32pow2(desc)];
|
2017-10-12 12:19:37 +00:00
|
|
|
struct rxq *rxq;
|
2017-10-12 12:19:38 +00:00
|
|
|
struct mlx4_malloc_vec vec[] = {
|
|
|
|
{
|
|
|
|
.align = RTE_CACHE_LINE_SIZE,
|
|
|
|
.size = sizeof(*rxq),
|
|
|
|
.addr = (void **)&rxq,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.align = RTE_CACHE_LINE_SIZE,
|
|
|
|
.size = sizeof(*elts),
|
|
|
|
.addr = (void **)&elts,
|
|
|
|
},
|
|
|
|
};
|
2017-09-01 08:07:00 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
(void)conf; /* Thresholds configuration (ignored). */
|
2017-10-12 12:19:37 +00:00
|
|
|
DEBUG("%p: configuring queue %u for %u descriptors",
|
|
|
|
(void *)dev, idx, desc);
|
|
|
|
if (idx >= dev->data->nb_rx_queues) {
|
|
|
|
rte_errno = EOVERFLOW;
|
|
|
|
ERROR("%p: queue index out of range (%u >= %u)",
|
|
|
|
(void *)dev, idx, dev->data->nb_rx_queues);
|
|
|
|
return -rte_errno;
|
|
|
|
}
|
|
|
|
rxq = dev->data->rx_queues[idx];
|
|
|
|
if (rxq) {
|
|
|
|
rte_errno = EEXIST;
|
|
|
|
ERROR("%p: Rx queue %u already configured, release it first",
|
|
|
|
(void *)dev, idx);
|
|
|
|
return -rte_errno;
|
|
|
|
}
|
|
|
|
if (!desc) {
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = EINVAL;
|
|
|
|
ERROR("%p: invalid number of Rx descriptors", (void *)dev);
|
2017-10-12 12:19:37 +00:00
|
|
|
return -rte_errno;
|
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
if (desc != RTE_DIM(*elts)) {
|
|
|
|
desc = RTE_DIM(*elts);
|
|
|
|
WARN("%p: increased number of descriptors in Rx queue %u"
|
|
|
|
" to the next power of two (%u)",
|
|
|
|
(void *)dev, idx, desc);
|
|
|
|
}
|
2017-10-12 12:19:37 +00:00
|
|
|
/* Allocate and initialize Rx queue. */
|
2017-10-12 12:19:38 +00:00
|
|
|
mlx4_zmallocv_socket("RXQ", vec, RTE_DIM(vec), socket);
|
2017-10-12 12:19:37 +00:00
|
|
|
if (!rxq) {
|
|
|
|
ERROR("%p: unable to allocate queue index %u",
|
|
|
|
(void *)dev, idx);
|
|
|
|
return -rte_errno;
|
2017-09-01 08:07:00 +00:00
|
|
|
}
|
2017-10-12 12:19:37 +00:00
|
|
|
*rxq = (struct rxq){
|
|
|
|
.priv = priv,
|
|
|
|
.mp = mp,
|
|
|
|
.port_id = dev->data->port_id,
|
2017-10-12 12:29:57 +00:00
|
|
|
.sges_n = 0,
|
|
|
|
.elts_n = rte_log2_u32(desc),
|
2017-10-12 12:19:38 +00:00
|
|
|
.elts = elts,
|
2017-10-12 12:29:59 +00:00
|
|
|
/* Toggle Rx checksum offload if hardware supports it. */
|
|
|
|
.csum = (priv->hw_csum &&
|
|
|
|
dev->data->dev_conf.rxmode.hw_ip_checksum),
|
|
|
|
.csum_l2tun = (priv->hw_csum_l2tun &&
|
|
|
|
dev->data->dev_conf.rxmode.hw_ip_checksum),
|
2017-10-13 09:31:05 +00:00
|
|
|
.stats = {
|
|
|
|
.idx = idx,
|
|
|
|
},
|
2017-10-12 12:19:37 +00:00
|
|
|
.socket = socket,
|
|
|
|
};
|
2017-09-01 08:07:00 +00:00
|
|
|
/* Enable scattered packets support for this queue if necessary. */
|
|
|
|
assert(mb_len >= RTE_PKTMBUF_HEADROOM);
|
|
|
|
if (dev->data->dev_conf.rxmode.max_rx_pkt_len <=
|
|
|
|
(mb_len - RTE_PKTMBUF_HEADROOM)) {
|
|
|
|
;
|
|
|
|
} else if (dev->data->dev_conf.rxmode.enable_scatter) {
|
2017-10-12 12:29:57 +00:00
|
|
|
uint32_t size =
|
|
|
|
RTE_PKTMBUF_HEADROOM +
|
|
|
|
dev->data->dev_conf.rxmode.max_rx_pkt_len;
|
|
|
|
uint32_t sges_n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the number of SGEs needed for a full packet
|
|
|
|
* and round it to the next power of two.
|
|
|
|
*/
|
|
|
|
sges_n = rte_log2_u32((size / mb_len) + !!(size % mb_len));
|
|
|
|
rxq->sges_n = sges_n;
|
|
|
|
/* Make sure sges_n did not overflow. */
|
|
|
|
size = mb_len * (1 << rxq->sges_n);
|
|
|
|
size -= RTE_PKTMBUF_HEADROOM;
|
|
|
|
if (size < dev->data->dev_conf.rxmode.max_rx_pkt_len) {
|
|
|
|
rte_errno = EOVERFLOW;
|
|
|
|
ERROR("%p: too many SGEs (%u) needed to handle"
|
|
|
|
" requested maximum packet size %u",
|
|
|
|
(void *)dev,
|
|
|
|
1 << sges_n,
|
|
|
|
dev->data->dev_conf.rxmode.max_rx_pkt_len);
|
|
|
|
goto error;
|
|
|
|
}
|
2017-09-01 08:07:00 +00:00
|
|
|
} else {
|
|
|
|
WARN("%p: the requested maximum Rx packet size (%u) is"
|
|
|
|
" larger than a single mbuf (%u) and scattered"
|
|
|
|
" mode has not been requested",
|
|
|
|
(void *)dev,
|
|
|
|
dev->data->dev_conf.rxmode.max_rx_pkt_len,
|
|
|
|
mb_len - RTE_PKTMBUF_HEADROOM);
|
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
DEBUG("%p: maximum number of segments per packet: %u",
|
|
|
|
(void *)dev, 1 << rxq->sges_n);
|
|
|
|
if (desc % (1 << rxq->sges_n)) {
|
|
|
|
rte_errno = EINVAL;
|
|
|
|
ERROR("%p: number of Rx queue descriptors (%u) is not a"
|
|
|
|
" multiple of maximum segments per packet (%u)",
|
|
|
|
(void *)dev,
|
|
|
|
desc,
|
|
|
|
1 << rxq->sges_n);
|
|
|
|
goto error;
|
|
|
|
}
|
2017-09-01 08:07:00 +00:00
|
|
|
/* Use the entire Rx mempool as the memory region. */
|
2017-10-12 12:19:37 +00:00
|
|
|
rxq->mr = mlx4_mp2mr(priv->pd, mp);
|
|
|
|
if (!rxq->mr) {
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = EINVAL;
|
|
|
|
ERROR("%p: MR creation failure: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (dev->data->dev_conf.intr_conf.rxq) {
|
2017-10-12 12:19:37 +00:00
|
|
|
rxq->channel = ibv_create_comp_channel(priv->ctx);
|
|
|
|
if (rxq->channel == NULL) {
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = ENOMEM;
|
|
|
|
ERROR("%p: Rx interrupt completion channel creation"
|
|
|
|
" failure: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
2017-10-12 12:19:37 +00:00
|
|
|
if (mlx4_fd_set_non_blocking(rxq->channel->fd) < 0) {
|
2017-09-01 08:07:00 +00:00
|
|
|
ERROR("%p: unable to make Rx interrupt completion"
|
|
|
|
" channel non-blocking: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
rxq->cq = ibv_create_cq(priv->ctx, desc >> rxq->sges_n, NULL,
|
|
|
|
rxq->channel, 0);
|
2017-10-12 12:19:37 +00:00
|
|
|
if (!rxq->cq) {
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = ENOMEM;
|
|
|
|
ERROR("%p: CQ creation failure: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
2017-10-12 12:19:39 +00:00
|
|
|
rxq->wq = ibv_create_wq
|
|
|
|
(priv->ctx,
|
|
|
|
&(struct ibv_wq_init_attr){
|
|
|
|
.wq_type = IBV_WQT_RQ,
|
2017-10-12 12:29:57 +00:00
|
|
|
.max_wr = desc >> rxq->sges_n,
|
|
|
|
.max_sge = 1 << rxq->sges_n,
|
2017-10-12 12:19:39 +00:00
|
|
|
.pd = priv->pd,
|
|
|
|
.cq = rxq->cq,
|
|
|
|
});
|
|
|
|
if (!rxq->wq) {
|
|
|
|
rte_errno = errno ? errno : EINVAL;
|
|
|
|
ERROR("%p: WQ creation failure: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
ret = ibv_modify_wq
|
|
|
|
(rxq->wq,
|
|
|
|
&(struct ibv_wq_attr){
|
|
|
|
.attr_mask = IBV_WQ_ATTR_STATE,
|
|
|
|
.wq_state = IBV_WQS_RDY,
|
|
|
|
});
|
|
|
|
if (ret) {
|
|
|
|
rte_errno = ret;
|
|
|
|
ERROR("%p: WQ state to IBV_WPS_RDY failed: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
|
|
|
goto error;
|
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
/* Retrieve device queue information. */
|
|
|
|
mlxdv.cq.in = rxq->cq;
|
|
|
|
mlxdv.cq.out = &dv_cq;
|
|
|
|
mlxdv.rwq.in = rxq->wq;
|
|
|
|
mlxdv.rwq.out = &dv_rwq;
|
|
|
|
ret = mlx4dv_init_obj(&mlxdv, MLX4DV_OBJ_RWQ | MLX4DV_OBJ_CQ);
|
2017-09-01 08:07:00 +00:00
|
|
|
if (ret) {
|
2017-10-12 12:29:57 +00:00
|
|
|
rte_errno = EINVAL;
|
|
|
|
ERROR("%p: failed to obtain device information", (void *)dev);
|
2017-09-01 08:07:00 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2017-10-12 12:29:57 +00:00
|
|
|
rxq->wqes =
|
|
|
|
(volatile struct mlx4_wqe_data_seg (*)[])
|
|
|
|
((uintptr_t)dv_rwq.buf.buf + dv_rwq.rq.offset);
|
|
|
|
rxq->rq_db = dv_rwq.rdb;
|
|
|
|
rxq->rq_ci = 0;
|
|
|
|
rxq->mcq.buf = dv_cq.buf.buf;
|
|
|
|
rxq->mcq.cqe_cnt = dv_cq.cqe_cnt;
|
|
|
|
rxq->mcq.set_ci_db = dv_cq.set_ci_db;
|
|
|
|
rxq->mcq.cqe_64 = (dv_cq.cqe_size & 64) ? 1 : 0;
|
|
|
|
ret = mlx4_rxq_alloc_elts(rxq);
|
2017-09-01 08:07:00 +00:00
|
|
|
if (ret) {
|
2017-10-12 12:29:57 +00:00
|
|
|
ERROR("%p: RXQ allocation failed: %s",
|
|
|
|
(void *)dev, strerror(rte_errno));
|
2017-09-01 08:07:00 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2017-10-12 12:19:37 +00:00
|
|
|
DEBUG("%p: adding Rx queue %p to list", (void *)dev, (void *)rxq);
|
|
|
|
dev->data->rx_queues[idx] = rxq;
|
2017-10-19 16:11:05 +00:00
|
|
|
/* Update doorbell counter. */
|
|
|
|
rxq->rq_ci = desc >> rxq->sges_n;
|
|
|
|
rte_wmb();
|
|
|
|
*rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
|
|
|
|
return 0;
|
2017-09-01 08:07:00 +00:00
|
|
|
error:
|
2017-10-12 12:19:37 +00:00
|
|
|
dev->data->rx_queues[idx] = NULL;
|
2017-09-01 08:07:00 +00:00
|
|
|
ret = rte_errno;
|
2017-10-12 12:19:37 +00:00
|
|
|
mlx4_rx_queue_release(rxq);
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_errno = ret;
|
|
|
|
assert(rte_errno > 0);
|
|
|
|
return -rte_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DPDK callback to release a Rx queue.
|
|
|
|
*
|
|
|
|
* @param dpdk_rxq
|
|
|
|
* Generic Rx queue pointer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mlx4_rx_queue_release(void *dpdk_rxq)
|
|
|
|
{
|
|
|
|
struct rxq *rxq = (struct rxq *)dpdk_rxq;
|
|
|
|
struct priv *priv;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (rxq == NULL)
|
|
|
|
return;
|
|
|
|
priv = rxq->priv;
|
2017-09-01 08:07:06 +00:00
|
|
|
for (i = 0; i != priv->dev->data->nb_rx_queues; ++i)
|
|
|
|
if (priv->dev->data->rx_queues[i] == rxq) {
|
2017-09-01 08:07:00 +00:00
|
|
|
DEBUG("%p: removing Rx queue %p from list",
|
|
|
|
(void *)priv->dev, (void *)rxq);
|
2017-09-01 08:07:06 +00:00
|
|
|
priv->dev->data->rx_queues[i] = NULL;
|
2017-09-01 08:07:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-10-12 12:19:37 +00:00
|
|
|
mlx4_rxq_free_elts(rxq);
|
2017-10-12 12:19:39 +00:00
|
|
|
if (rxq->wq)
|
|
|
|
claim_zero(ibv_destroy_wq(rxq->wq));
|
2017-10-12 12:19:37 +00:00
|
|
|
if (rxq->cq)
|
|
|
|
claim_zero(ibv_destroy_cq(rxq->cq));
|
|
|
|
if (rxq->channel)
|
|
|
|
claim_zero(ibv_destroy_comp_channel(rxq->channel));
|
|
|
|
if (rxq->mr)
|
|
|
|
claim_zero(ibv_dereg_mr(rxq->mr));
|
2017-09-01 08:07:00 +00:00
|
|
|
rte_free(rxq);
|
|
|
|
}
|