blob: b575768659915c2b35a6ae3a0e6b6bf1b1c77ed0 [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_wq, IB_USER_VERBS_EX_CMD_CREATE_WQ,
mana_ib_create_wq, empty);
DECLARE_DRV_CMD(mana_create_rwq_ind_table,
IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL, empty, empty);
int mana_modify_wq(struct ibv_wq *ibwq, struct ibv_wq_attr *attr)
{
return EOPNOTSUPP;
}
struct ibv_wq *mana_create_wq(struct ibv_context *context,
struct ibv_wq_init_attr *attr)
{
int ret;
struct mana_context *ctx = to_mctx(context);
struct mana_wq *wq;
struct mana_create_wq wq_cmd = {};
struct mana_create_wq_resp wq_resp = {};
struct mana_ib_create_wq *wq_cmd_drv;
if (attr->max_wr > MAX_SEND_BUFFERS_PER_QUEUE) {
verbs_err(verbs_get_ctx(context),
"max_wr %d exceeds MAX_SEND_BUFFERS_PER_QUEUE\n",
attr->max_wr);
errno = EINVAL;
return NULL;
}
if (get_wqe_size(attr->max_sge) > MAX_RX_WQE_SIZE) {
verbs_err(verbs_get_ctx(context),
"max_sge %d exceeding WQE size limit\n",
attr->max_sge);
errno = EINVAL;
return NULL;
}
if (!ctx->extern_alloc.alloc || !ctx->extern_alloc.free) {
verbs_err(verbs_get_ctx(context),
"WQ buffer needs to be externally allocated\n");
errno = EINVAL;
return NULL;
}
wq = calloc(1, sizeof(*wq));
if (!wq)
return NULL;
wq->sge = attr->max_sge;
wq->buf_size = attr->max_wr * get_wqe_size(attr->max_sge);
wq->buf_size = align_hw_size(wq->buf_size);
wq->buf = ctx->extern_alloc.alloc(wq->buf_size, ctx->extern_alloc.data);
if (!wq->buf) {
errno = ENOMEM;
goto free_wq;
}
wq->wqe = attr->max_wr;
wq_cmd_drv = &wq_cmd.drv_payload;
wq_cmd_drv->wq_buf_addr = (uintptr_t)wq->buf;
wq_cmd_drv->wq_buf_size = wq->buf_size;
ret = ibv_cmd_create_wq(context, attr, &wq->ibwq, &wq_cmd.ibv_cmd,
sizeof(wq_cmd), &wq_resp.ibv_resp,
sizeof(wq_resp));
if (ret) {
verbs_err(verbs_get_ctx(context), "Failed to Create WQ\n");
ctx->extern_alloc.free(wq->buf, ctx->extern_alloc.data);
errno = ret;
goto free_wq;
}
return &wq->ibwq;
free_wq:
free(wq);
return NULL;
}
int mana_destroy_wq(struct ibv_wq *ibwq)
{
struct mana_wq *wq = container_of(ibwq, struct mana_wq, ibwq);
struct mana_context *ctx = to_mctx(ibwq->context);
int ret;
if (!ctx->extern_alloc.free) {
verbs_err(verbs_get_ctx(ibwq->context),
"WQ needs external alloc context\n");
return EINVAL;
}
ret = ibv_cmd_destroy_wq(ibwq);
if (ret) {
verbs_err(verbs_get_ctx(ibwq->context),
"Failed to destroy WQ\n");
return ret;
}
ctx->extern_alloc.free(wq->buf, ctx->extern_alloc.data);
free(wq);
return 0;
}
struct ibv_rwq_ind_table *
mana_create_rwq_ind_table(struct ibv_context *context,
struct ibv_rwq_ind_table_init_attr *init_attr)
{
int ret;
struct mana_rwq_ind_table *ind_table;
struct mana_create_rwq_ind_table_resp resp = {};
int i;
ind_table = calloc(1, sizeof(*ind_table));
if (!ind_table)
return NULL;
ret = ibv_cmd_create_rwq_ind_table(context, init_attr,
&ind_table->ib_ind_table,
&resp.ibv_resp, sizeof(resp));
if (ret) {
verbs_err(verbs_get_ctx(context),
"Failed to create RWQ IND table\n");
errno = ret;
goto free_ind_table;
}
ind_table->ind_tbl_size = 1 << init_attr->log_ind_tbl_size;
ind_table->ind_tbl =
calloc(ind_table->ind_tbl_size, sizeof(struct ibv_wq *));
if (!ind_table->ind_tbl) {
errno = ENOMEM;
goto free_ind_table;
}
for (i = 0; i < ind_table->ind_tbl_size; i++)
ind_table->ind_tbl[i] = init_attr->ind_tbl[i];
return &ind_table->ib_ind_table;
free_ind_table:
free(ind_table);
return NULL;
}
int mana_destroy_rwq_ind_table(struct ibv_rwq_ind_table *rwq_ind_table)
{
struct mana_rwq_ind_table *ind_table = container_of(
rwq_ind_table, struct mana_rwq_ind_table, ib_ind_table);
int ret;
ret = ibv_cmd_destroy_rwq_ind_table(&ind_table->ib_ind_table);
if (ret) {
verbs_err(verbs_get_ctx(rwq_ind_table->context),
"Failed to destroy RWQ IND table\n");
goto fail;
}
free(ind_table->ind_tbl);
free(ind_table);
fail:
return ret;
}