blob: d86fa4767d874e1c76ca9b0e1ee2d3e23fc8f430 [file] [log] [blame] [edit]
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <util/compiler.h>
#include <util/util.h>
#include <sys/mman.h>
#include <infiniband/driver.h>
#include <infiniband/kern-abi.h>
#include <rdma/mana-abi.h>
#include <kernel-abi/mana-abi.h>
#include "mana.h"
DECLARE_DRV_CMD(mana_create_qp, IB_USER_VERBS_CMD_CREATE_QP, mana_ib_create_qp,
mana_ib_create_qp_resp);
DECLARE_DRV_CMD(mana_create_qp_ex, IB_USER_VERBS_EX_CMD_CREATE_QP,
mana_ib_create_qp_rss, mana_ib_create_qp_rss_resp);
static struct ibv_qp *mana_create_qp_raw(struct ibv_pd *ibpd,
struct ibv_qp_init_attr *attr)
{
int ret;
struct mana_cq *cq;
struct mana_qp *qp;
struct mana_pd *pd = container_of(ibpd, struct mana_pd, ibv_pd);
struct mana_parent_domain *mpd;
uint32_t port;
struct mana_create_qp qp_cmd = {};
struct mana_create_qp_resp qp_resp = {};
struct mana_ib_create_qp *qp_cmd_drv;
struct mana_ib_create_qp_resp *qp_resp_drv;
struct mana_context *ctx = to_mctx(ibpd->context);
/* This is a RAW QP, pd is a parent domain with port number */
if (!pd->mprotection_domain) {
verbs_err(verbs_get_ctx(ibpd->context),
"Create RAW QP should use parent domain\n");
errno = EINVAL;
return NULL;
}
mpd = container_of(pd, struct mana_parent_domain, mpd);
port = (uint32_t)(uintptr_t)mpd->pd_context;
cq = container_of(attr->send_cq, struct mana_cq, ibcq);
if (attr->cap.max_send_wr > MAX_SEND_BUFFERS_PER_QUEUE) {
verbs_err(verbs_get_ctx(ibpd->context),
"max_send_wr %d exceeds MAX_SEND_BUFFERS_PER_QUEUE\n",
attr->cap.max_send_wr);
errno = EINVAL;
return NULL;
}
if (get_wqe_size(attr->cap.max_send_sge) > MAX_TX_WQE_SIZE) {
verbs_err(verbs_get_ctx(ibpd->context),
"max_send_sge %d exceeding queue size limits\n",
attr->cap.max_send_sge);
errno = EINVAL;
return NULL;
}
if (!ctx->extern_alloc.alloc || !ctx->extern_alloc.free) {
verbs_err(verbs_get_ctx(ibpd->context),
"RAW QP requires extern alloc for buffers\n");
errno = EINVAL;
return NULL;
}
qp = calloc(1, sizeof(*qp));
if (!qp)
return NULL;
qp->send_buf_size =
attr->cap.max_send_wr * get_wqe_size(attr->cap.max_send_sge);
qp->send_buf_size = align_hw_size(qp->send_buf_size);
qp->send_buf = ctx->extern_alloc.alloc(qp->send_buf_size,
ctx->extern_alloc.data);
if (!qp->send_buf) {
errno = ENOMEM;
goto free_qp;
}
qp_cmd_drv = &qp_cmd.drv_payload;
qp_resp_drv = &qp_resp.drv_payload;
qp_cmd_drv->sq_buf_addr = (uintptr_t)qp->send_buf;
qp_cmd_drv->sq_buf_size = qp->send_buf_size;
qp_cmd_drv->port = port;
ret = ibv_cmd_create_qp(ibpd, &qp->ibqp.qp, attr, &qp_cmd.ibv_cmd,
sizeof(qp_cmd), &qp_resp.ibv_resp,
sizeof(qp_resp));
if (ret) {
verbs_err(verbs_get_ctx(ibpd->context), "Create QP failed\n");
ctx->extern_alloc.free(qp->send_buf, ctx->extern_alloc.data);
errno = ret;
goto free_qp;
}
qp->sqid = qp_resp_drv->sqid;
qp->tx_vp_offset = qp_resp_drv->tx_vp_offset;
qp->send_wqe_count = attr->cap.max_send_wr;
cq->cqid = qp_resp_drv->cqid;
return &qp->ibqp.qp;
free_qp:
free(qp);
return NULL;
}
struct ibv_qp *mana_create_qp(struct ibv_pd *ibpd,
struct ibv_qp_init_attr *attr)
{
switch (attr->qp_type) {
case IBV_QPT_RAW_PACKET:
return mana_create_qp_raw(ibpd, attr);
default:
verbs_err(verbs_get_ctx(ibpd->context),
"QP type %u is not supported\n", attr->qp_type);
errno = EOPNOTSUPP;
}
return NULL;
}
int mana_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask)
{
return EOPNOTSUPP;
}
int mana_destroy_qp(struct ibv_qp *ibqp)
{
int ret;
struct mana_qp *qp = container_of(ibqp, struct mana_qp, ibqp.qp);
struct mana_context *ctx = to_mctx(ibqp->context);
if (!ctx->extern_alloc.free) {
/*
* This version of driver doesn't support allocating buffers
* in rdma-core.
*/
verbs_err(verbs_get_ctx(ibqp->context),
"Invalid context in Destroy QP\n");
return -EINVAL;
}
ret = ibv_cmd_destroy_qp(ibqp);
if (ret) {
verbs_err(verbs_get_ctx(ibqp->context), "Destroy QP failed\n");
return ret;
}
ctx->extern_alloc.free(qp->send_buf, ctx->extern_alloc.data);
free(qp);
return 0;
}
static struct ibv_qp *mana_create_qp_ex_raw(struct ibv_context *context,
struct ibv_qp_init_attr_ex *attr)
{
struct mana_create_qp_ex cmd = {};
struct mana_ib_create_qp_rss *cmd_drv;
struct mana_create_qp_ex_resp resp = {};
struct mana_ib_create_qp_rss_resp *cmd_resp;
struct mana_qp *qp;
struct mana_pd *pd = container_of(attr->pd, struct mana_pd, ibv_pd);
struct mana_parent_domain *mpd;
uint32_t port;
int ret;
cmd_drv = &cmd.drv_payload;
cmd_resp = &resp.drv_payload;
/* For a RAW QP, pd is a parent domain with port number */
if (!pd->mprotection_domain) {
verbs_err(verbs_get_ctx(context),
"RAW QP needs to be on a parent domain\n");
errno = EINVAL;
return NULL;
}
if (attr->rx_hash_conf.rx_hash_key_len !=
MANA_IB_TOEPLITZ_HASH_KEY_SIZE_IN_BYTES) {
verbs_err(verbs_get_ctx(context),
"Invalid RX hash key length\n");
errno = EINVAL;
return NULL;
}
mpd = container_of(pd, struct mana_parent_domain, mpd);
port = (uint32_t)(uintptr_t)mpd->pd_context;
qp = calloc(1, sizeof(*qp));
if (!qp)
return NULL;
cmd_drv->rx_hash_fields_mask = attr->rx_hash_conf.rx_hash_fields_mask;
cmd_drv->rx_hash_function = attr->rx_hash_conf.rx_hash_function;
cmd_drv->rx_hash_key_len = attr->rx_hash_conf.rx_hash_key_len;
if (cmd_drv->rx_hash_key_len)
memcpy(cmd_drv->rx_hash_key, attr->rx_hash_conf.rx_hash_key,
cmd_drv->rx_hash_key_len);
cmd_drv->port = port;
ret = ibv_cmd_create_qp_ex2(context, &qp->ibqp, attr, &cmd.ibv_cmd,
sizeof(cmd), &resp.ibv_resp, sizeof(resp));
if (ret) {
verbs_err(verbs_get_ctx(context), "Create QP EX failed\n");
free(qp);
errno = ret;
return NULL;
}
if (attr->rwq_ind_tbl) {
struct mana_rwq_ind_table *ind_table =
container_of(attr->rwq_ind_tbl,
struct mana_rwq_ind_table, ib_ind_table);
for (int i = 0; i < ind_table->ind_tbl_size; i++) {
struct mana_wq *wq = container_of(ind_table->ind_tbl[i],
struct mana_wq, ibwq);
struct mana_cq *cq =
container_of(wq->ibwq.cq, struct mana_cq, ibcq);
wq->wqid = cmd_resp->entries[i].wqid;
cq->cqid = cmd_resp->entries[i].cqid;
}
}
return &qp->ibqp.qp;
}
struct ibv_qp *mana_create_qp_ex(struct ibv_context *context,
struct ibv_qp_init_attr_ex *attr)
{
switch (attr->qp_type) {
case IBV_QPT_RAW_PACKET:
return mana_create_qp_ex_raw(context, attr);
default:
verbs_err(verbs_get_ctx(context),
"QP type %u is not supported\n", attr->qp_type);
errno = EOPNOTSUPP;
}
return NULL;
}