| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* |
| * Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. |
| */ |
| |
| #include <rdma/uverbs_std_types.h> |
| #include "rdma_core.h" |
| #include "uverbs.h" |
| #include "core_priv.h" |
| |
| static int uverbs_free_qp(struct ib_uobject *uobject, |
| enum rdma_remove_reason why, |
| struct uverbs_attr_bundle *attrs) |
| { |
| struct ib_qp *qp = uobject->object; |
| struct ib_uqp_object *uqp = |
| container_of(uobject, struct ib_uqp_object, uevent.uobject); |
| int ret; |
| |
| /* |
| * If this is a user triggered destroy then do not allow destruction |
| * until the user cleans up all the mcast bindings. Unlike in other |
| * places we forcibly clean up the mcast attachments for !DESTROY |
| * because the mcast attaches are not ubojects and will not be |
| * destroyed by anything else during cleanup processing. |
| */ |
| if (why == RDMA_REMOVE_DESTROY) { |
| if (!list_empty(&uqp->mcast_list)) |
| return -EBUSY; |
| } else if (qp == qp->real_qp) { |
| ib_uverbs_detach_umcast(qp, uqp); |
| } |
| |
| ret = ib_destroy_qp_user(qp, &attrs->driver_udata); |
| if (ret) |
| return ret; |
| |
| if (uqp->uxrcd) |
| atomic_dec(&uqp->uxrcd->refcnt); |
| |
| ib_uverbs_release_uevent(&uqp->uevent); |
| return 0; |
| } |
| |
| static int check_creation_flags(enum ib_qp_type qp_type, |
| u32 create_flags) |
| { |
| create_flags &= ~IB_UVERBS_QP_CREATE_SQ_SIG_ALL; |
| |
| if (!create_flags || qp_type == IB_QPT_DRIVER) |
| return 0; |
| |
| if (qp_type != IB_QPT_RAW_PACKET && qp_type != IB_QPT_UD) |
| return -EINVAL; |
| |
| if ((create_flags & IB_UVERBS_QP_CREATE_SCATTER_FCS || |
| create_flags & IB_UVERBS_QP_CREATE_CVLAN_STRIPPING) && |
| qp_type != IB_QPT_RAW_PACKET) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static void set_caps(struct ib_qp_init_attr *attr, |
| struct ib_uverbs_qp_cap *cap, bool req) |
| { |
| if (req) { |
| attr->cap.max_send_wr = cap->max_send_wr; |
| attr->cap.max_recv_wr = cap->max_recv_wr; |
| attr->cap.max_send_sge = cap->max_send_sge; |
| attr->cap.max_recv_sge = cap->max_recv_sge; |
| attr->cap.max_inline_data = cap->max_inline_data; |
| } else { |
| cap->max_send_wr = attr->cap.max_send_wr; |
| cap->max_recv_wr = attr->cap.max_recv_wr; |
| cap->max_send_sge = attr->cap.max_send_sge; |
| cap->max_recv_sge = attr->cap.max_recv_sge; |
| cap->max_inline_data = attr->cap.max_inline_data; |
| } |
| } |
| |
| static int UVERBS_HANDLER(UVERBS_METHOD_QP_CREATE)( |
| struct uverbs_attr_bundle *attrs) |
| { |
| struct ib_uqp_object *obj = container_of( |
| uverbs_attr_get_uobject(attrs, UVERBS_ATTR_CREATE_QP_HANDLE), |
| typeof(*obj), uevent.uobject); |
| struct ib_qp_init_attr attr = {}; |
| struct ib_uverbs_qp_cap cap = {}; |
| struct ib_rwq_ind_table *rwq_ind_tbl = NULL; |
| struct ib_qp *qp; |
| struct ib_pd *pd = NULL; |
| struct ib_srq *srq = NULL; |
| struct ib_cq *recv_cq = NULL; |
| struct ib_cq *send_cq = NULL; |
| struct ib_xrcd *xrcd = NULL; |
| struct ib_uobject *xrcd_uobj = NULL; |
| struct ib_device *device; |
| u64 user_handle; |
| int ret; |
| |
| ret = uverbs_copy_from_or_zero(&cap, attrs, |
| UVERBS_ATTR_CREATE_QP_CAP); |
| if (!ret) |
| ret = uverbs_copy_from(&user_handle, attrs, |
| UVERBS_ATTR_CREATE_QP_USER_HANDLE); |
| if (!ret) |
| ret = uverbs_get_const(&attr.qp_type, attrs, |
| UVERBS_ATTR_CREATE_QP_TYPE); |
| if (ret) |
| return ret; |
| |
| switch (attr.qp_type) { |
| case IB_QPT_XRC_TGT: |
| if (uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE) || |
| uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE) || |
| uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_PD_HANDLE) || |
| uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE)) |
| return -EINVAL; |
| |
| xrcd_uobj = uverbs_attr_get_uobject(attrs, |
| UVERBS_ATTR_CREATE_QP_XRCD_HANDLE); |
| if (IS_ERR(xrcd_uobj)) |
| return PTR_ERR(xrcd_uobj); |
| |
| xrcd = (struct ib_xrcd *)xrcd_uobj->object; |
| if (!xrcd) |
| return -EINVAL; |
| device = xrcd->device; |
| break; |
| case IB_UVERBS_QPT_RAW_PACKET: |
| if (!capable(CAP_NET_RAW)) |
| return -EPERM; |
| fallthrough; |
| case IB_UVERBS_QPT_RC: |
| case IB_UVERBS_QPT_UC: |
| case IB_UVERBS_QPT_UD: |
| case IB_UVERBS_QPT_XRC_INI: |
| case IB_UVERBS_QPT_DRIVER: |
| if (uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_XRCD_HANDLE) || |
| (uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_SRQ_HANDLE) && |
| attr.qp_type == IB_QPT_XRC_INI)) |
| return -EINVAL; |
| |
| pd = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_PD_HANDLE); |
| if (IS_ERR(pd)) |
| return PTR_ERR(pd); |
| |
| rwq_ind_tbl = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE); |
| if (!IS_ERR(rwq_ind_tbl)) { |
| if (cap.max_recv_wr || cap.max_recv_sge || |
| uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE) || |
| uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_SRQ_HANDLE)) |
| return -EINVAL; |
| |
| /* send_cq is optinal */ |
| if (cap.max_send_wr) { |
| send_cq = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE); |
| if (IS_ERR(send_cq)) |
| return PTR_ERR(send_cq); |
| } |
| attr.rwq_ind_tbl = rwq_ind_tbl; |
| } else { |
| send_cq = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE); |
| if (IS_ERR(send_cq)) |
| return PTR_ERR(send_cq); |
| |
| if (attr.qp_type != IB_QPT_XRC_INI) { |
| recv_cq = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE); |
| if (IS_ERR(recv_cq)) |
| return PTR_ERR(recv_cq); |
| } |
| } |
| |
| device = pd->device; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ret = uverbs_get_flags32(&attr.create_flags, attrs, |
| UVERBS_ATTR_CREATE_QP_FLAGS, |
| IB_UVERBS_QP_CREATE_BLOCK_MULTICAST_LOOPBACK | |
| IB_UVERBS_QP_CREATE_SCATTER_FCS | |
| IB_UVERBS_QP_CREATE_CVLAN_STRIPPING | |
| IB_UVERBS_QP_CREATE_PCI_WRITE_END_PADDING | |
| IB_UVERBS_QP_CREATE_SQ_SIG_ALL); |
| if (ret) |
| return ret; |
| |
| ret = check_creation_flags(attr.qp_type, attr.create_flags); |
| if (ret) |
| return ret; |
| |
| if (uverbs_attr_is_valid(attrs, |
| UVERBS_ATTR_CREATE_QP_SOURCE_QPN)) { |
| ret = uverbs_copy_from(&attr.source_qpn, attrs, |
| UVERBS_ATTR_CREATE_QP_SOURCE_QPN); |
| if (ret) |
| return ret; |
| attr.create_flags |= IB_QP_CREATE_SOURCE_QPN; |
| } |
| |
| srq = uverbs_attr_get_obj(attrs, |
| UVERBS_ATTR_CREATE_QP_SRQ_HANDLE); |
| if (!IS_ERR(srq)) { |
| if ((srq->srq_type == IB_SRQT_XRC && |
| attr.qp_type != IB_QPT_XRC_TGT) || |
| (srq->srq_type != IB_SRQT_XRC && |
| attr.qp_type == IB_QPT_XRC_TGT)) |
| return -EINVAL; |
| attr.srq = srq; |
| } |
| |
| obj->uevent.event_file = ib_uverbs_get_async_event(attrs, |
| UVERBS_ATTR_CREATE_QP_EVENT_FD); |
| INIT_LIST_HEAD(&obj->uevent.event_list); |
| INIT_LIST_HEAD(&obj->mcast_list); |
| obj->uevent.uobject.user_handle = user_handle; |
| attr.event_handler = ib_uverbs_qp_event_handler; |
| attr.send_cq = send_cq; |
| attr.recv_cq = recv_cq; |
| attr.xrcd = xrcd; |
| if (attr.create_flags & IB_UVERBS_QP_CREATE_SQ_SIG_ALL) { |
| /* This creation bit is uverbs one, need to mask before |
| * calling drivers. It was added to prevent an extra user attr |
| * only for that when using ioctl. |
| */ |
| attr.create_flags &= ~IB_UVERBS_QP_CREATE_SQ_SIG_ALL; |
| attr.sq_sig_type = IB_SIGNAL_ALL_WR; |
| } else { |
| attr.sq_sig_type = IB_SIGNAL_REQ_WR; |
| } |
| |
| set_caps(&attr, &cap, true); |
| mutex_init(&obj->mcast_lock); |
| |
| qp = ib_create_qp_user(device, pd, &attr, &attrs->driver_udata, obj, |
| KBUILD_MODNAME); |
| if (IS_ERR(qp)) { |
| ret = PTR_ERR(qp); |
| goto err_put; |
| } |
| ib_qp_usecnt_inc(qp); |
| |
| if (attr.qp_type == IB_QPT_XRC_TGT) { |
| obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, |
| uobject); |
| atomic_inc(&obj->uxrcd->refcnt); |
| } |
| |
| obj->uevent.uobject.object = qp; |
| uverbs_finalize_uobj_create(attrs, UVERBS_ATTR_CREATE_QP_HANDLE); |
| |
| set_caps(&attr, &cap, false); |
| ret = uverbs_copy_to_struct_or_zero(attrs, |
| UVERBS_ATTR_CREATE_QP_RESP_CAP, &cap, |
| sizeof(cap)); |
| if (ret) |
| return ret; |
| |
| ret = uverbs_copy_to(attrs, UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, |
| &qp->qp_num, |
| sizeof(qp->qp_num)); |
| |
| return ret; |
| err_put: |
| if (obj->uevent.event_file) |
| uverbs_uobject_put(&obj->uevent.event_file->uobj); |
| return ret; |
| }; |
| |
| DECLARE_UVERBS_NAMED_METHOD( |
| UVERBS_METHOD_QP_CREATE, |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_HANDLE, |
| UVERBS_OBJECT_QP, |
| UVERBS_ACCESS_NEW, |
| UA_MANDATORY), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_XRCD_HANDLE, |
| UVERBS_OBJECT_XRCD, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_PD_HANDLE, |
| UVERBS_OBJECT_PD, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_SRQ_HANDLE, |
| UVERBS_OBJECT_SRQ, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE, |
| UVERBS_OBJECT_CQ, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE, |
| UVERBS_OBJECT_CQ, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE, |
| UVERBS_OBJECT_RWQ_IND_TBL, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_USER_HANDLE, |
| UVERBS_ATTR_TYPE(u64), |
| UA_MANDATORY), |
| UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_CAP, |
| UVERBS_ATTR_STRUCT(struct ib_uverbs_qp_cap, |
| max_inline_data), |
| UA_MANDATORY), |
| UVERBS_ATTR_CONST_IN(UVERBS_ATTR_CREATE_QP_TYPE, |
| enum ib_uverbs_qp_type, |
| UA_MANDATORY), |
| UVERBS_ATTR_FLAGS_IN(UVERBS_ATTR_CREATE_QP_FLAGS, |
| enum ib_uverbs_qp_create_flags, |
| UA_OPTIONAL), |
| UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_SOURCE_QPN, |
| UVERBS_ATTR_TYPE(u32), |
| UA_OPTIONAL), |
| UVERBS_ATTR_FD(UVERBS_ATTR_CREATE_QP_EVENT_FD, |
| UVERBS_OBJECT_ASYNC_EVENT, |
| UVERBS_ACCESS_READ, |
| UA_OPTIONAL), |
| UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_QP_RESP_CAP, |
| UVERBS_ATTR_STRUCT(struct ib_uverbs_qp_cap, |
| max_inline_data), |
| UA_MANDATORY), |
| UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, |
| UVERBS_ATTR_TYPE(u32), |
| UA_MANDATORY), |
| UVERBS_ATTR_UHW()); |
| |
| static int UVERBS_HANDLER(UVERBS_METHOD_QP_DESTROY)( |
| struct uverbs_attr_bundle *attrs) |
| { |
| struct ib_uobject *uobj = |
| uverbs_attr_get_uobject(attrs, UVERBS_ATTR_DESTROY_QP_HANDLE); |
| struct ib_uqp_object *obj = |
| container_of(uobj, struct ib_uqp_object, uevent.uobject); |
| struct ib_uverbs_destroy_qp_resp resp = { |
| .events_reported = obj->uevent.events_reported |
| }; |
| |
| return uverbs_copy_to(attrs, UVERBS_ATTR_DESTROY_QP_RESP, &resp, |
| sizeof(resp)); |
| } |
| |
| DECLARE_UVERBS_NAMED_METHOD( |
| UVERBS_METHOD_QP_DESTROY, |
| UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_QP_HANDLE, |
| UVERBS_OBJECT_QP, |
| UVERBS_ACCESS_DESTROY, |
| UA_MANDATORY), |
| UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_DESTROY_QP_RESP, |
| UVERBS_ATTR_TYPE(struct ib_uverbs_destroy_qp_resp), |
| UA_MANDATORY)); |
| |
| DECLARE_UVERBS_NAMED_OBJECT( |
| UVERBS_OBJECT_QP, |
| UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), uverbs_free_qp), |
| &UVERBS_METHOD(UVERBS_METHOD_QP_CREATE), |
| &UVERBS_METHOD(UVERBS_METHOD_QP_DESTROY)); |
| |
| const struct uapi_definition uverbs_def_obj_qp[] = { |
| UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_QP, |
| UAPI_DEF_OBJ_NEEDS_FN(destroy_qp)), |
| {} |
| }; |