| /* |
| * Copyright (c) 2020 Mellanox Technologies, Ltd. All rights reserved. |
| * |
| * This software is available to you under a choice of one of two |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * 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. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <infiniband/cmd_write.h> |
| #include "ibverbs.h" |
| |
| enum { |
| CREATE_QP_EX_SUP_CREATE_FLAGS = IBV_QP_CREATE_BLOCK_SELF_MCAST_LB | |
| IBV_QP_CREATE_SCATTER_FCS | |
| IBV_QP_CREATE_CVLAN_STRIPPING | |
| IBV_QP_CREATE_SOURCE_QPN | |
| IBV_QP_CREATE_PCI_WRITE_END_PADDING |
| }; |
| |
| |
| static void set_qp(struct verbs_qp *vqp, |
| struct ibv_qp *qp_in, |
| struct ibv_qp_init_attr_ex *attr_ex, |
| struct verbs_xrcd *vxrcd) |
| { |
| struct ibv_qp *qp = vqp ? &vqp->qp : qp_in; |
| |
| qp->qp_context = attr_ex->qp_context; |
| qp->pd = attr_ex->pd; |
| qp->send_cq = attr_ex->send_cq; |
| qp->recv_cq = attr_ex->recv_cq; |
| qp->srq = attr_ex->srq; |
| qp->qp_type = attr_ex->qp_type; |
| qp->state = IBV_QPS_RESET; |
| qp->events_completed = 0; |
| pthread_mutex_init(&qp->mutex, NULL); |
| pthread_cond_init(&qp->cond, NULL); |
| |
| if (vqp) { |
| vqp->comp_mask = 0; |
| if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_XRCD) { |
| vqp->comp_mask |= VERBS_QP_XRCD; |
| vqp->xrcd = vxrcd; |
| } |
| } |
| } |
| |
| static int ibv_icmd_create_qp(struct ibv_context *context, |
| struct verbs_qp *vqp, |
| struct ibv_qp *qp_in, |
| struct ibv_qp_init_attr_ex *attr_ex, |
| struct ibv_command_buffer *link) |
| { |
| DECLARE_FBCMD_BUFFER(cmdb, UVERBS_OBJECT_QP, UVERBS_METHOD_QP_CREATE, 15, link); |
| struct verbs_ex_private *priv = get_priv(context); |
| struct ib_uverbs_attr *handle; |
| uint32_t qp_num; |
| uint32_t pd_handle; |
| uint32_t send_cq_handle = 0; |
| uint32_t recv_cq_handle = 0; |
| int ret; |
| struct ibv_qp *qp = vqp ? &vqp->qp : qp_in; |
| struct verbs_xrcd *vxrcd = NULL; |
| uint32_t create_flags = 0; |
| |
| qp->context = context; |
| |
| switch (attr_ex->qp_type) { |
| case IBV_QPT_XRC_RECV: |
| if (!(attr_ex->comp_mask & IBV_QP_INIT_ATTR_XRCD)) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| vxrcd = container_of(attr_ex->xrcd, struct verbs_xrcd, xrcd); |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_XRCD_HANDLE, vxrcd->handle); |
| pd_handle = vxrcd->handle; |
| break; |
| case IBV_QPT_RC: |
| case IBV_QPT_UD: |
| case IBV_QPT_UC: |
| case IBV_QPT_RAW_PACKET: |
| case IBV_QPT_XRC_SEND: |
| case IBV_QPT_DRIVER: |
| if (!(attr_ex->comp_mask & IBV_QP_INIT_ATTR_PD)) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_PD_HANDLE, attr_ex->pd->handle); |
| pd_handle = attr_ex->pd->handle; |
| |
| if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) { |
| if (attr_ex->cap.max_recv_wr || attr_ex->cap.max_recv_sge || |
| attr_ex->recv_cq || attr_ex->srq) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| fallback_require_ex(cmdb); |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE, |
| attr_ex->rwq_ind_tbl->ind_tbl_handle); |
| |
| /* send_cq is optional */ |
| if (attr_ex->cap.max_send_wr) { |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE, |
| attr_ex->send_cq->handle); |
| send_cq_handle = attr_ex->send_cq->handle; |
| } |
| } else { |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE, |
| attr_ex->send_cq->handle); |
| send_cq_handle = attr_ex->send_cq->handle; |
| |
| if (attr_ex->qp_type != IBV_QPT_XRC_SEND) { |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE, |
| attr_ex->recv_cq->handle); |
| recv_cq_handle = attr_ex->recv_cq->handle; |
| } |
| } |
| |
| /* compatible with kernel code from the 'write' mode */ |
| if (attr_ex->qp_type == IBV_QPT_XRC_SEND) { |
| attr_ex->cap.max_recv_wr = 0; |
| attr_ex->cap.max_recv_sge = 0; |
| } |
| |
| break; |
| default: |
| errno = EINVAL; |
| return errno; |
| } |
| |
| handle = fill_attr_out_obj(cmdb, UVERBS_ATTR_CREATE_QP_HANDLE); |
| fill_attr_const_in(cmdb, UVERBS_ATTR_CREATE_QP_TYPE, attr_ex->qp_type); |
| fill_attr_in_uint64(cmdb, UVERBS_ATTR_CREATE_QP_USER_HANDLE, (uintptr_t)qp); |
| |
| static_assert(offsetof(struct ibv_qp_cap, max_send_wr) == |
| offsetof(struct ib_uverbs_qp_cap, max_send_wr), "Bad layout"); |
| static_assert(offsetof(struct ibv_qp_cap, max_recv_wr) == |
| offsetof(struct ib_uverbs_qp_cap, max_recv_wr), "Bad layout"); |
| static_assert(offsetof(struct ibv_qp_cap, max_send_sge) == |
| offsetof(struct ib_uverbs_qp_cap, max_send_sge), "Bad layout"); |
| static_assert(offsetof(struct ibv_qp_cap, max_recv_sge) == |
| offsetof(struct ib_uverbs_qp_cap, max_recv_sge), "Bad layout"); |
| static_assert(offsetof(struct ibv_qp_cap, max_inline_data) == |
| offsetof(struct ib_uverbs_qp_cap, max_inline_data), "Bad layout"); |
| |
| fill_attr_in_ptr(cmdb, UVERBS_ATTR_CREATE_QP_CAP, &attr_ex->cap); |
| fill_attr_in_fd(cmdb, UVERBS_ATTR_CREATE_QP_EVENT_FD, context->async_fd); |
| |
| if (priv->imported) |
| fallback_require_ioctl(cmdb); |
| |
| if (attr_ex->sq_sig_all) |
| create_flags |= IB_UVERBS_QP_CREATE_SQ_SIG_ALL; |
| |
| if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_CREATE_FLAGS) { |
| if (attr_ex->create_flags & ~CREATE_QP_EX_SUP_CREATE_FLAGS) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| fallback_require_ex(cmdb); |
| create_flags |= attr_ex->create_flags; |
| |
| if (attr_ex->create_flags & IBV_QP_CREATE_SOURCE_QPN) { |
| fill_attr_in_uint32(cmdb, UVERBS_ATTR_CREATE_QP_SOURCE_QPN, |
| attr_ex->source_qpn); |
| /* source QPN is a self attribute once moving to ioctl, |
| * no extra bit is supported. |
| */ |
| create_flags &= ~IBV_QP_CREATE_SOURCE_QPN; |
| } |
| } |
| |
| if (create_flags) |
| fill_attr_in_uint32(cmdb, UVERBS_ATTR_CREATE_QP_FLAGS, |
| create_flags); |
| |
| if (attr_ex->srq) |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SRQ_HANDLE, attr_ex->srq->handle); |
| |
| fill_attr_out_ptr(cmdb, UVERBS_ATTR_CREATE_QP_RESP_CAP, &attr_ex->cap); |
| fill_attr_out_ptr(cmdb, UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, &qp_num); |
| |
| switch (execute_ioctl_fallback(context, create_qp, cmdb, &ret)) { |
| case TRY_WRITE: { |
| if (abi_ver > 4) { |
| DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP); |
| |
| *req = (struct ib_uverbs_create_qp){ |
| .pd_handle = pd_handle, |
| .user_handle = (uintptr_t)qp, |
| .max_send_wr = attr_ex->cap.max_send_wr, |
| .max_recv_wr = attr_ex->cap.max_recv_wr, |
| .max_send_sge = attr_ex->cap.max_send_sge, |
| .max_recv_sge = attr_ex->cap.max_recv_sge, |
| .max_inline_data = attr_ex->cap.max_inline_data, |
| .sq_sig_all = attr_ex->sq_sig_all, |
| .qp_type = attr_ex->qp_type, |
| .srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0, |
| .is_srq = !!attr_ex->srq, |
| .recv_cq_handle = recv_cq_handle, |
| .send_cq_handle = send_cq_handle, |
| }; |
| |
| ret = execute_write_bufs( |
| context, IB_USER_VERBS_CMD_CREATE_QP, req, resp); |
| if (ret) |
| return ret; |
| |
| qp->handle = resp->qp_handle; |
| qp->qp_num = resp->qpn; |
| |
| attr_ex->cap.max_recv_sge = resp->max_recv_sge; |
| attr_ex->cap.max_send_sge = resp->max_send_sge; |
| attr_ex->cap.max_recv_wr = resp->max_recv_wr; |
| attr_ex->cap.max_send_wr = resp->max_send_wr; |
| attr_ex->cap.max_inline_data = resp->max_inline_data; |
| |
| } else if (abi_ver == 4) { |
| DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP_V4); |
| |
| *req = (struct ib_uverbs_create_qp){ |
| .pd_handle = pd_handle, |
| .user_handle = (uintptr_t)qp, |
| .max_send_wr = attr_ex->cap.max_send_wr, |
| .max_recv_wr = attr_ex->cap.max_recv_wr, |
| .max_send_sge = attr_ex->cap.max_send_sge, |
| .max_recv_sge = attr_ex->cap.max_recv_sge, |
| .max_inline_data = attr_ex->cap.max_inline_data, |
| .sq_sig_all = attr_ex->sq_sig_all, |
| .qp_type = attr_ex->qp_type, |
| .srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0, |
| .is_srq = !!attr_ex->srq, |
| .recv_cq_handle = recv_cq_handle, |
| .send_cq_handle = send_cq_handle, |
| }; |
| |
| ret = execute_write_bufs( |
| context, IB_USER_VERBS_CMD_CREATE_QP_V4, req, resp); |
| if (ret) |
| return ret; |
| |
| qp->handle = resp->qp_handle; |
| qp->qp_num = resp->qpn; |
| |
| attr_ex->cap.max_recv_sge = resp->max_recv_sge; |
| attr_ex->cap.max_send_sge = resp->max_send_sge; |
| attr_ex->cap.max_recv_wr = resp->max_recv_wr; |
| attr_ex->cap.max_send_wr = resp->max_send_wr; |
| attr_ex->cap.max_inline_data = resp->max_inline_data; |
| } else { |
| DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP_V3); |
| |
| *req = (struct ib_uverbs_create_qp){ |
| .pd_handle = pd_handle, |
| .user_handle = (uintptr_t)qp, |
| .max_send_wr = attr_ex->cap.max_send_wr, |
| .max_recv_wr = attr_ex->cap.max_recv_wr, |
| .max_send_sge = attr_ex->cap.max_send_sge, |
| .max_recv_sge = attr_ex->cap.max_recv_sge, |
| .max_inline_data = attr_ex->cap.max_inline_data, |
| .sq_sig_all = attr_ex->sq_sig_all, |
| .qp_type = attr_ex->qp_type, |
| .srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0, |
| .is_srq = !!attr_ex->srq, |
| .recv_cq_handle = recv_cq_handle, |
| .send_cq_handle = send_cq_handle, |
| }; |
| |
| ret = execute_write_bufs( |
| context, IB_USER_VERBS_CMD_CREATE_QP_V3, req, resp); |
| if (ret) |
| return ret; |
| |
| qp->handle = resp->qp_handle; |
| qp->qp_num = resp->qpn; |
| } |
| |
| set_qp(vqp, qp, attr_ex, vxrcd); |
| return 0; |
| } |
| |
| case TRY_WRITE_EX: { |
| DECLARE_LEGACY_UHW_BUFS_EX(link, |
| IB_USER_VERBS_EX_CMD_CREATE_QP); |
| |
| *req = (struct ib_uverbs_ex_create_qp){ |
| .pd_handle = pd_handle, |
| .user_handle = (uintptr_t)qp, |
| .max_send_wr = attr_ex->cap.max_send_wr, |
| .max_recv_wr = attr_ex->cap.max_recv_wr, |
| .max_send_sge = attr_ex->cap.max_send_sge, |
| .max_recv_sge = attr_ex->cap.max_recv_sge, |
| .max_inline_data = attr_ex->cap.max_inline_data, |
| .sq_sig_all = attr_ex->sq_sig_all, |
| .qp_type = attr_ex->qp_type, |
| .srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0, |
| .is_srq = !!attr_ex->srq, |
| .recv_cq_handle = recv_cq_handle, |
| .send_cq_handle = send_cq_handle, |
| }; |
| |
| if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_CREATE_FLAGS) { |
| req->create_flags = attr_ex->create_flags; |
| |
| if (attr_ex->create_flags & IBV_QP_CREATE_SOURCE_QPN) |
| req->source_qpn = attr_ex->source_qpn; |
| } |
| |
| if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) { |
| req->rwq_ind_tbl_handle = attr_ex->rwq_ind_tbl->ind_tbl_handle; |
| req->comp_mask = IB_UVERBS_CREATE_QP_MASK_IND_TABLE; |
| } |
| |
| ret = execute_write_bufs_ex( |
| context, IB_USER_VERBS_EX_CMD_CREATE_QP, req, resp); |
| if (ret) |
| return ret; |
| |
| qp->handle = resp->base.qp_handle; |
| qp->qp_num = resp->base.qpn; |
| |
| attr_ex->cap.max_recv_sge = resp->base.max_recv_sge; |
| attr_ex->cap.max_send_sge = resp->base.max_send_sge; |
| attr_ex->cap.max_recv_wr = resp->base.max_recv_wr; |
| attr_ex->cap.max_send_wr = resp->base.max_send_wr; |
| attr_ex->cap.max_inline_data = resp->base.max_inline_data; |
| set_qp(vqp, qp, attr_ex, vxrcd); |
| return 0; |
| } |
| |
| case SUCCESS: |
| break; |
| |
| default: |
| return ret; |
| } |
| |
| qp->handle = read_attr_obj(UVERBS_ATTR_CREATE_QP_HANDLE, handle); |
| qp->qp_num = qp_num; |
| set_qp(vqp, qp, attr_ex, vxrcd); |
| |
| return 0; |
| } |
| |
| int ibv_cmd_create_qp(struct ibv_pd *pd, |
| struct ibv_qp *qp, struct ibv_qp_init_attr *attr, |
| struct ibv_create_qp *cmd, size_t cmd_size, |
| struct ib_uverbs_create_qp_resp *resp, size_t resp_size) |
| { |
| DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP, |
| UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp, |
| resp_size); |
| |
| struct ibv_qp_init_attr_ex attr_ex = {}; |
| int ret; |
| |
| attr_ex.qp_context = attr->qp_context; |
| attr_ex.send_cq = attr->send_cq; |
| attr_ex.recv_cq = attr->recv_cq; |
| attr_ex.srq = attr->srq; |
| attr_ex.cap = attr->cap; |
| attr_ex.qp_type = attr->qp_type; |
| attr_ex.sq_sig_all = attr->sq_sig_all; |
| attr_ex.comp_mask = IBV_QP_INIT_ATTR_PD; |
| attr_ex.pd = pd; |
| ret = ibv_icmd_create_qp(pd->context, NULL, qp, &attr_ex, cmdb); |
| if (!ret) |
| memcpy(&attr->cap, &attr_ex.cap, sizeof(attr_ex.cap)); |
| |
| return ret; |
| } |
| |
| int ibv_cmd_create_qp_ex(struct ibv_context *context, |
| struct verbs_qp *qp, |
| struct ibv_qp_init_attr_ex *attr_ex, |
| struct ibv_create_qp *cmd, size_t cmd_size, |
| struct ib_uverbs_create_qp_resp *resp, size_t resp_size) |
| { |
| DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP, |
| UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp, |
| resp_size); |
| |
| if (!check_comp_mask(attr_ex->comp_mask, |
| IBV_QP_INIT_ATTR_PD | |
| IBV_QP_INIT_ATTR_XRCD | |
| IBV_QP_INIT_ATTR_SEND_OPS_FLAGS)) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| return ibv_icmd_create_qp(context, qp, NULL, attr_ex, cmdb); |
| } |
| |
| int ibv_cmd_create_qp_ex2(struct ibv_context *context, |
| struct verbs_qp *qp, |
| struct ibv_qp_init_attr_ex *attr_ex, |
| struct ibv_create_qp_ex *cmd, |
| size_t cmd_size, |
| struct ib_uverbs_ex_create_qp_resp *resp, |
| size_t resp_size) |
| { |
| DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP, |
| UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp, |
| resp_size); |
| |
| if (!check_comp_mask(attr_ex->comp_mask, |
| IBV_QP_INIT_ATTR_PD | |
| IBV_QP_INIT_ATTR_XRCD | |
| IBV_QP_INIT_ATTR_CREATE_FLAGS | |
| IBV_QP_INIT_ATTR_MAX_TSO_HEADER | |
| IBV_QP_INIT_ATTR_IND_TABLE | |
| IBV_QP_INIT_ATTR_RX_HASH | |
| IBV_QP_INIT_ATTR_SEND_OPS_FLAGS)) { |
| errno = EINVAL; |
| return errno; |
| } |
| |
| return ibv_icmd_create_qp(context, qp, NULL, attr_ex, cmdb); |
| } |
| |
| int ibv_cmd_destroy_qp(struct ibv_qp *qp) |
| { |
| DECLARE_FBCMD_BUFFER(cmdb, UVERBS_OBJECT_QP, UVERBS_METHOD_QP_DESTROY, 2, |
| NULL); |
| struct ib_uverbs_destroy_qp_resp resp; |
| int ret; |
| |
| fill_attr_out_ptr(cmdb, UVERBS_ATTR_DESTROY_QP_RESP, &resp); |
| fill_attr_in_obj(cmdb, UVERBS_ATTR_DESTROY_QP_HANDLE, qp->handle); |
| |
| switch (execute_ioctl_fallback(qp->context, destroy_qp, cmdb, &ret)) { |
| case TRY_WRITE: { |
| struct ibv_destroy_qp req; |
| |
| req.core_payload = (struct ib_uverbs_destroy_qp){ |
| .qp_handle = qp->handle, |
| }; |
| |
| ret = execute_cmd_write(qp->context, |
| IB_USER_VERBS_CMD_DESTROY_QP, &req, |
| sizeof(req), &resp, sizeof(resp)); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| if (verbs_is_destroy_err(&ret)) |
| return ret; |
| |
| pthread_mutex_lock(&qp->mutex); |
| while (qp->events_completed != resp.events_reported) |
| pthread_cond_wait(&qp->cond, &qp->mutex); |
| pthread_mutex_unlock(&qp->mutex); |
| |
| return 0; |
| } |