blob: 22e5ff953277e527776890a2726b0ccae7190ae4 [file] [log] [blame]
// 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_alloc_ucontext, IB_USER_VERBS_CMD_GET_CONTEXT, empty,
empty);
DECLARE_DRV_CMD(mana_alloc_pd, IB_USER_VERBS_CMD_ALLOC_PD, empty, empty);
static const struct verbs_match_ent hca_table[] = {
VERBS_DRIVER_ID(RDMA_DRIVER_MANA),
{},
};
struct mana_context *to_mctx(struct ibv_context *ibctx)
{
return container_of(ibctx, struct mana_context, ibv_ctx.context);
}
void *mana_alloc_mem(uint32_t size)
{
void *buf;
buf = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED)
return NULL;
return buf;
}
int mana_query_device_ex(struct ibv_context *context,
const struct ibv_query_device_ex_input *input,
struct ibv_device_attr_ex *attr, size_t attr_size)
{
struct ib_uverbs_ex_query_device_resp resp;
size_t resp_size = sizeof(resp);
int ret;
ret = ibv_cmd_query_device_any(context, input, attr, attr_size, &resp,
&resp_size);
verbs_debug(verbs_get_ctx(context),
"device attr max_qp %d max_qp_wr %d max_cqe %d\n",
attr->orig_attr.max_qp, attr->orig_attr.max_qp_wr,
attr->orig_attr.max_cqe);
return ret;
}
int mana_query_port(struct ibv_context *context, uint8_t port,
struct ibv_port_attr *attr)
{
struct ibv_query_port cmd;
return ibv_cmd_query_port(context, port, attr, &cmd, sizeof(cmd));
}
struct ibv_pd *mana_alloc_pd(struct ibv_context *context)
{
struct ibv_alloc_pd cmd;
struct mana_alloc_pd_resp resp;
struct mana_pd *pd;
int ret;
pd = calloc(1, sizeof(*pd));
if (!pd)
return NULL;
ret = ibv_cmd_alloc_pd(context, &pd->ibv_pd, &cmd, sizeof(cmd),
&resp.ibv_resp, sizeof(resp));
if (ret) {
verbs_err(verbs_get_ctx(context), "Failed to allocate PD\n");
errno = ret;
free(pd);
return NULL;
}
return &pd->ibv_pd;
}
struct ibv_pd *
mana_alloc_parent_domain(struct ibv_context *context,
struct ibv_parent_domain_init_attr *attr)
{
struct mana_parent_domain *mparent_domain;
if (ibv_check_alloc_parent_domain(attr)) {
errno = EINVAL;
return NULL;
}
if (!check_comp_mask(attr->comp_mask,
IBV_PARENT_DOMAIN_INIT_ATTR_PD_CONTEXT)) {
verbs_err(
verbs_get_ctx(context),
"This driver supports IBV_PARENT_DOMAIN_INIT_ATTR_PD_CONTEXT only\n");
errno = EOPNOTSUPP;
return NULL;
}
mparent_domain = calloc(1, sizeof(*mparent_domain));
if (!mparent_domain) {
errno = ENOMEM;
return NULL;
}
mparent_domain->mpd.mprotection_domain =
container_of(attr->pd, struct mana_pd, ibv_pd);
ibv_initialize_parent_domain(&mparent_domain->mpd.ibv_pd, attr->pd);
if (attr->comp_mask & IBV_PARENT_DOMAIN_INIT_ATTR_PD_CONTEXT)
mparent_domain->pd_context = attr->pd_context;
return &mparent_domain->mpd.ibv_pd;
}
int mana_dealloc_pd(struct ibv_pd *ibpd)
{
int ret;
struct mana_pd *pd = container_of(ibpd, struct mana_pd, ibv_pd);
if (pd->mprotection_domain) {
struct mana_parent_domain *parent_domain =
container_of(pd, struct mana_parent_domain, mpd);
free(parent_domain);
return 0;
}
ret = ibv_cmd_dealloc_pd(ibpd);
if (ret) {
verbs_err(verbs_get_ctx(ibpd->context),
"Failed to deallocate PD\n");
return ret;
}
free(pd);
return 0;
}
struct ibv_mr *mana_reg_mr(struct ibv_pd *pd, void *addr, size_t length,
uint64_t hca_va, int access)
{
struct verbs_mr *vmr;
struct ibv_reg_mr cmd;
struct ib_uverbs_reg_mr_resp resp;
int ret;
vmr = malloc(sizeof(*vmr));
if (!vmr)
return NULL;
ret = ibv_cmd_reg_mr(pd, addr, length, hca_va, access, vmr, &cmd,
sizeof(cmd), &resp, sizeof(resp));
if (ret) {
verbs_err(verbs_get_ctx(pd->context),
"Failed to register MR\n");
errno = ret;
free(vmr);
return NULL;
}
return &vmr->ibv_mr;
}
int mana_dereg_mr(struct verbs_mr *vmr)
{
int ret;
ret = ibv_cmd_dereg_mr(vmr);
if (ret) {
verbs_err(verbs_get_ctx(vmr->ibv_mr.context),
"Failed to deregister MR\n");
return ret;
}
free(vmr);
return 0;
}
static void mana_free_context(struct ibv_context *ibctx)
{
struct mana_context *context = to_mctx(ibctx);
int i;
for (i = 0; i < MANA_QP_TABLE_SIZE; ++i) {
if (context->qp_table[i].refcnt)
free(context->qp_table[i].table);
}
pthread_mutex_destroy(&context->qp_table_mutex);
munmap(context->db_page, DOORBELL_PAGE_SIZE);
verbs_uninit_context(&context->ibv_ctx);
free(context);
}
static const struct verbs_context_ops mana_ctx_ops = {
.alloc_pd = mana_alloc_pd,
.alloc_parent_domain = mana_alloc_parent_domain,
.create_cq = mana_create_cq,
.create_qp = mana_create_qp,
.create_qp_ex = mana_create_qp_ex,
.create_rwq_ind_table = mana_create_rwq_ind_table,
.create_wq = mana_create_wq,
.dealloc_pd = mana_dealloc_pd,
.dereg_mr = mana_dereg_mr,
.destroy_cq = mana_destroy_cq,
.destroy_qp = mana_destroy_qp,
.destroy_rwq_ind_table = mana_destroy_rwq_ind_table,
.destroy_wq = mana_destroy_wq,
.free_context = mana_free_context,
.modify_wq = mana_modify_wq,
.modify_qp = mana_modify_qp,
.poll_cq = mana_poll_cq,
.post_recv = mana_post_recv,
.post_send = mana_post_send,
.query_device_ex = mana_query_device_ex,
.query_port = mana_query_port,
.reg_mr = mana_reg_mr,
.req_notify_cq = mana_arm_cq,
};
static struct verbs_device *mana_device_alloc(struct verbs_sysfs_dev *sysfs_dev)
{
struct mana_device *dev;
dev = calloc(1, sizeof(*dev));
if (!dev)
return NULL;
return &dev->verbs_dev;
}
static void mana_uninit_device(struct verbs_device *verbs_device)
{
struct mana_device *dev =
container_of(verbs_device, struct mana_device, verbs_dev);
free(dev);
}
static struct verbs_context *mana_alloc_context(struct ibv_device *ibdev,
int cmd_fd, void *private_data)
{
int ret, i;
struct mana_context *context;
struct mana_alloc_ucontext_resp resp;
struct ibv_get_context cmd;
context = verbs_init_and_alloc_context(ibdev, cmd_fd, context, ibv_ctx,
RDMA_DRIVER_MANA);
if (!context)
return NULL;
ret = ibv_cmd_get_context(&context->ibv_ctx, &cmd, sizeof(cmd),
&resp.ibv_resp, sizeof(resp));
if (ret) {
verbs_err(&context->ibv_ctx, "Failed to get ucontext\n");
errno = ret;
goto free_ctx;
}
verbs_set_ops(&context->ibv_ctx, &mana_ctx_ops);
pthread_mutex_init(&context->qp_table_mutex, NULL);
for (i = 0; i < MANA_QP_TABLE_SIZE; ++i)
context->qp_table[i].refcnt = 0;
context->db_page = mmap(NULL, DOORBELL_PAGE_SIZE, PROT_WRITE,
MAP_SHARED, context->ibv_ctx.context.cmd_fd, 0);
if (context->db_page == MAP_FAILED) {
verbs_err(&context->ibv_ctx, "Failed to map doorbell page\n");
errno = ENOENT;
goto free_ctx;
}
verbs_debug(&context->ibv_ctx, "Mapped db_page=%p\n", context->db_page);
return &context->ibv_ctx;
free_ctx:
verbs_uninit_context(&context->ibv_ctx);
free(context);
return NULL;
}
static const struct verbs_device_ops mana_dev_ops = {
.name = "mana",
.match_min_abi_version = MANA_IB_UVERBS_ABI_VERSION,
.match_max_abi_version = MANA_IB_UVERBS_ABI_VERSION,
.match_table = hca_table,
.alloc_device = mana_device_alloc,
.uninit_device = mana_uninit_device,
.alloc_context = mana_alloc_context,
};
PROVIDER_DRIVER(mana, mana_dev_ops);