// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* * Copyright (c) 2022, Microsoft Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #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 (!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 = EINVAL; } 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 = EINVAL; } return NULL; }