1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* |
3 | * Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. |
4 | */ |
5 | |
6 | #include <rdma/uverbs_std_types.h> |
7 | #include "rdma_core.h" |
8 | #include "uverbs.h" |
9 | #include "core_priv.h" |
10 | |
11 | static int uverbs_free_qp(struct ib_uobject *uobject, |
12 | enum rdma_remove_reason why, |
13 | struct uverbs_attr_bundle *attrs) |
14 | { |
15 | struct ib_qp *qp = uobject->object; |
16 | struct ib_uqp_object *uqp = |
17 | container_of(uobject, struct ib_uqp_object, uevent.uobject); |
18 | int ret; |
19 | |
20 | /* |
21 | * If this is a user triggered destroy then do not allow destruction |
22 | * until the user cleans up all the mcast bindings. Unlike in other |
23 | * places we forcibly clean up the mcast attachments for !DESTROY |
24 | * because the mcast attaches are not ubojects and will not be |
25 | * destroyed by anything else during cleanup processing. |
26 | */ |
27 | if (why == RDMA_REMOVE_DESTROY) { |
28 | if (!list_empty(head: &uqp->mcast_list)) |
29 | return -EBUSY; |
30 | } else if (qp == qp->real_qp) { |
31 | ib_uverbs_detach_umcast(qp, uobj: uqp); |
32 | } |
33 | |
34 | ret = ib_destroy_qp_user(qp, udata: &attrs->driver_udata); |
35 | if (ret) |
36 | return ret; |
37 | |
38 | if (uqp->uxrcd) |
39 | atomic_dec(v: &uqp->uxrcd->refcnt); |
40 | |
41 | ib_uverbs_release_uevent(uobj: &uqp->uevent); |
42 | return 0; |
43 | } |
44 | |
45 | static int check_creation_flags(enum ib_qp_type qp_type, |
46 | u32 create_flags) |
47 | { |
48 | create_flags &= ~IB_UVERBS_QP_CREATE_SQ_SIG_ALL; |
49 | |
50 | if (!create_flags || qp_type == IB_QPT_DRIVER) |
51 | return 0; |
52 | |
53 | if (qp_type != IB_QPT_RAW_PACKET && qp_type != IB_QPT_UD) |
54 | return -EINVAL; |
55 | |
56 | if ((create_flags & IB_UVERBS_QP_CREATE_SCATTER_FCS || |
57 | create_flags & IB_UVERBS_QP_CREATE_CVLAN_STRIPPING) && |
58 | qp_type != IB_QPT_RAW_PACKET) |
59 | return -EINVAL; |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | static void set_caps(struct ib_qp_init_attr *attr, |
65 | struct ib_uverbs_qp_cap *cap, bool req) |
66 | { |
67 | if (req) { |
68 | attr->cap.max_send_wr = cap->max_send_wr; |
69 | attr->cap.max_recv_wr = cap->max_recv_wr; |
70 | attr->cap.max_send_sge = cap->max_send_sge; |
71 | attr->cap.max_recv_sge = cap->max_recv_sge; |
72 | attr->cap.max_inline_data = cap->max_inline_data; |
73 | } else { |
74 | cap->max_send_wr = attr->cap.max_send_wr; |
75 | cap->max_recv_wr = attr->cap.max_recv_wr; |
76 | cap->max_send_sge = attr->cap.max_send_sge; |
77 | cap->max_recv_sge = attr->cap.max_recv_sge; |
78 | cap->max_inline_data = attr->cap.max_inline_data; |
79 | } |
80 | } |
81 | |
82 | static int UVERBS_HANDLER(UVERBS_METHOD_QP_CREATE)( |
83 | struct uverbs_attr_bundle *attrs) |
84 | { |
85 | struct ib_uqp_object *obj = container_of( |
86 | uverbs_attr_get_uobject(attrs, UVERBS_ATTR_CREATE_QP_HANDLE), |
87 | typeof(*obj), uevent.uobject); |
88 | struct ib_qp_init_attr attr = {}; |
89 | struct ib_uverbs_qp_cap cap = {}; |
90 | struct ib_rwq_ind_table *rwq_ind_tbl = NULL; |
91 | struct ib_qp *qp; |
92 | struct ib_pd *pd = NULL; |
93 | struct ib_srq *srq = NULL; |
94 | struct ib_cq *recv_cq = NULL; |
95 | struct ib_cq *send_cq = NULL; |
96 | struct ib_xrcd *xrcd = NULL; |
97 | struct ib_uobject *xrcd_uobj = NULL; |
98 | struct ib_device *device; |
99 | u64 user_handle; |
100 | int ret; |
101 | |
102 | ret = uverbs_copy_from_or_zero(&cap, attrs, |
103 | UVERBS_ATTR_CREATE_QP_CAP); |
104 | if (!ret) |
105 | ret = uverbs_copy_from(&user_handle, attrs, |
106 | UVERBS_ATTR_CREATE_QP_USER_HANDLE); |
107 | if (!ret) |
108 | ret = uverbs_get_const(&attr.qp_type, attrs, |
109 | UVERBS_ATTR_CREATE_QP_TYPE); |
110 | if (ret) |
111 | return ret; |
112 | |
113 | switch (attr.qp_type) { |
114 | case IB_QPT_XRC_TGT: |
115 | if (uverbs_attr_is_valid(attrs_bundle: attrs, |
116 | idx: UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE) || |
117 | uverbs_attr_is_valid(attrs_bundle: attrs, |
118 | idx: UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE) || |
119 | uverbs_attr_is_valid(attrs_bundle: attrs, |
120 | idx: UVERBS_ATTR_CREATE_QP_PD_HANDLE) || |
121 | uverbs_attr_is_valid(attrs_bundle: attrs, |
122 | idx: UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE)) |
123 | return -EINVAL; |
124 | |
125 | xrcd_uobj = uverbs_attr_get_uobject(attrs_bundle: attrs, |
126 | idx: UVERBS_ATTR_CREATE_QP_XRCD_HANDLE); |
127 | if (IS_ERR(ptr: xrcd_uobj)) |
128 | return PTR_ERR(ptr: xrcd_uobj); |
129 | |
130 | xrcd = (struct ib_xrcd *)xrcd_uobj->object; |
131 | if (!xrcd) |
132 | return -EINVAL; |
133 | device = xrcd->device; |
134 | break; |
135 | case IB_UVERBS_QPT_RAW_PACKET: |
136 | if (!capable(CAP_NET_RAW)) |
137 | return -EPERM; |
138 | fallthrough; |
139 | case IB_UVERBS_QPT_RC: |
140 | case IB_UVERBS_QPT_UC: |
141 | case IB_UVERBS_QPT_UD: |
142 | case IB_UVERBS_QPT_XRC_INI: |
143 | case IB_UVERBS_QPT_DRIVER: |
144 | if (uverbs_attr_is_valid(attrs_bundle: attrs, |
145 | idx: UVERBS_ATTR_CREATE_QP_XRCD_HANDLE) || |
146 | (uverbs_attr_is_valid(attrs_bundle: attrs, |
147 | idx: UVERBS_ATTR_CREATE_QP_SRQ_HANDLE) && |
148 | attr.qp_type == IB_QPT_XRC_INI)) |
149 | return -EINVAL; |
150 | |
151 | pd = uverbs_attr_get_obj(attrs_bundle: attrs, |
152 | idx: UVERBS_ATTR_CREATE_QP_PD_HANDLE); |
153 | if (IS_ERR(ptr: pd)) |
154 | return PTR_ERR(ptr: pd); |
155 | |
156 | rwq_ind_tbl = uverbs_attr_get_obj(attrs_bundle: attrs, |
157 | idx: UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE); |
158 | if (!IS_ERR(ptr: rwq_ind_tbl)) { |
159 | if (cap.max_recv_wr || cap.max_recv_sge || |
160 | uverbs_attr_is_valid(attrs_bundle: attrs, |
161 | idx: UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE) || |
162 | uverbs_attr_is_valid(attrs_bundle: attrs, |
163 | idx: UVERBS_ATTR_CREATE_QP_SRQ_HANDLE)) |
164 | return -EINVAL; |
165 | |
166 | /* send_cq is optional */ |
167 | if (cap.max_send_wr) { |
168 | send_cq = uverbs_attr_get_obj(attrs_bundle: attrs, |
169 | idx: UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE); |
170 | if (IS_ERR(ptr: send_cq)) |
171 | return PTR_ERR(ptr: send_cq); |
172 | } |
173 | attr.rwq_ind_tbl = rwq_ind_tbl; |
174 | } else { |
175 | send_cq = uverbs_attr_get_obj(attrs_bundle: attrs, |
176 | idx: UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE); |
177 | if (IS_ERR(ptr: send_cq)) |
178 | return PTR_ERR(ptr: send_cq); |
179 | |
180 | if (attr.qp_type != IB_QPT_XRC_INI) { |
181 | recv_cq = uverbs_attr_get_obj(attrs_bundle: attrs, |
182 | idx: UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE); |
183 | if (IS_ERR(ptr: recv_cq)) |
184 | return PTR_ERR(ptr: recv_cq); |
185 | } |
186 | } |
187 | |
188 | device = pd->device; |
189 | break; |
190 | default: |
191 | return -EINVAL; |
192 | } |
193 | |
194 | ret = uverbs_get_flags32(to: &attr.create_flags, attrs_bundle: attrs, |
195 | idx: UVERBS_ATTR_CREATE_QP_FLAGS, |
196 | allowed_bits: IB_UVERBS_QP_CREATE_BLOCK_MULTICAST_LOOPBACK | |
197 | IB_UVERBS_QP_CREATE_SCATTER_FCS | |
198 | IB_UVERBS_QP_CREATE_CVLAN_STRIPPING | |
199 | IB_UVERBS_QP_CREATE_PCI_WRITE_END_PADDING | |
200 | IB_UVERBS_QP_CREATE_SQ_SIG_ALL); |
201 | if (ret) |
202 | return ret; |
203 | |
204 | ret = check_creation_flags(qp_type: attr.qp_type, create_flags: attr.create_flags); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | if (uverbs_attr_is_valid(attrs_bundle: attrs, |
209 | idx: UVERBS_ATTR_CREATE_QP_SOURCE_QPN)) { |
210 | ret = uverbs_copy_from(&attr.source_qpn, attrs, |
211 | UVERBS_ATTR_CREATE_QP_SOURCE_QPN); |
212 | if (ret) |
213 | return ret; |
214 | attr.create_flags |= IB_QP_CREATE_SOURCE_QPN; |
215 | } |
216 | |
217 | srq = uverbs_attr_get_obj(attrs_bundle: attrs, |
218 | idx: UVERBS_ATTR_CREATE_QP_SRQ_HANDLE); |
219 | if (!IS_ERR(ptr: srq)) { |
220 | if ((srq->srq_type == IB_SRQT_XRC && |
221 | attr.qp_type != IB_QPT_XRC_TGT) || |
222 | (srq->srq_type != IB_SRQT_XRC && |
223 | attr.qp_type == IB_QPT_XRC_TGT)) |
224 | return -EINVAL; |
225 | attr.srq = srq; |
226 | } |
227 | |
228 | obj->uevent.event_file = ib_uverbs_get_async_event(attrs, |
229 | id: UVERBS_ATTR_CREATE_QP_EVENT_FD); |
230 | INIT_LIST_HEAD(list: &obj->uevent.event_list); |
231 | INIT_LIST_HEAD(list: &obj->mcast_list); |
232 | obj->uevent.uobject.user_handle = user_handle; |
233 | attr.event_handler = ib_uverbs_qp_event_handler; |
234 | attr.send_cq = send_cq; |
235 | attr.recv_cq = recv_cq; |
236 | attr.xrcd = xrcd; |
237 | if (attr.create_flags & IB_UVERBS_QP_CREATE_SQ_SIG_ALL) { |
238 | /* This creation bit is uverbs one, need to mask before |
239 | * calling drivers. It was added to prevent an extra user attr |
240 | * only for that when using ioctl. |
241 | */ |
242 | attr.create_flags &= ~IB_UVERBS_QP_CREATE_SQ_SIG_ALL; |
243 | attr.sq_sig_type = IB_SIGNAL_ALL_WR; |
244 | } else { |
245 | attr.sq_sig_type = IB_SIGNAL_REQ_WR; |
246 | } |
247 | |
248 | set_caps(attr: &attr, cap: &cap, req: true); |
249 | mutex_init(&obj->mcast_lock); |
250 | |
251 | qp = ib_create_qp_user(dev: device, pd, attr: &attr, udata: &attrs->driver_udata, uobj: obj, |
252 | KBUILD_MODNAME); |
253 | if (IS_ERR(ptr: qp)) { |
254 | ret = PTR_ERR(ptr: qp); |
255 | goto err_put; |
256 | } |
257 | ib_qp_usecnt_inc(qp); |
258 | |
259 | if (attr.qp_type == IB_QPT_XRC_TGT) { |
260 | obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, |
261 | uobject); |
262 | atomic_inc(v: &obj->uxrcd->refcnt); |
263 | } |
264 | |
265 | obj->uevent.uobject.object = qp; |
266 | uverbs_finalize_uobj_create(attrs_bundle: attrs, idx: UVERBS_ATTR_CREATE_QP_HANDLE); |
267 | |
268 | set_caps(attr: &attr, cap: &cap, req: false); |
269 | ret = uverbs_copy_to_struct_or_zero(bundle: attrs, |
270 | idx: UVERBS_ATTR_CREATE_QP_RESP_CAP, from: &cap, |
271 | size: sizeof(cap)); |
272 | if (ret) |
273 | return ret; |
274 | |
275 | ret = uverbs_copy_to(attrs_bundle: attrs, idx: UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, |
276 | from: &qp->qp_num, |
277 | size: sizeof(qp->qp_num)); |
278 | |
279 | return ret; |
280 | err_put: |
281 | if (obj->uevent.event_file) |
282 | uverbs_uobject_put(uobject: &obj->uevent.event_file->uobj); |
283 | return ret; |
284 | }; |
285 | |
286 | DECLARE_UVERBS_NAMED_METHOD( |
287 | UVERBS_METHOD_QP_CREATE, |
288 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_HANDLE, |
289 | UVERBS_OBJECT_QP, |
290 | UVERBS_ACCESS_NEW, |
291 | UA_MANDATORY), |
292 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_XRCD_HANDLE, |
293 | UVERBS_OBJECT_XRCD, |
294 | UVERBS_ACCESS_READ, |
295 | UA_OPTIONAL), |
296 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_PD_HANDLE, |
297 | UVERBS_OBJECT_PD, |
298 | UVERBS_ACCESS_READ, |
299 | UA_OPTIONAL), |
300 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_SRQ_HANDLE, |
301 | UVERBS_OBJECT_SRQ, |
302 | UVERBS_ACCESS_READ, |
303 | UA_OPTIONAL), |
304 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE, |
305 | UVERBS_OBJECT_CQ, |
306 | UVERBS_ACCESS_READ, |
307 | UA_OPTIONAL), |
308 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE, |
309 | UVERBS_OBJECT_CQ, |
310 | UVERBS_ACCESS_READ, |
311 | UA_OPTIONAL), |
312 | UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE, |
313 | UVERBS_OBJECT_RWQ_IND_TBL, |
314 | UVERBS_ACCESS_READ, |
315 | UA_OPTIONAL), |
316 | UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_USER_HANDLE, |
317 | UVERBS_ATTR_TYPE(u64), |
318 | UA_MANDATORY), |
319 | UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_CAP, |
320 | UVERBS_ATTR_STRUCT(struct ib_uverbs_qp_cap, |
321 | max_inline_data), |
322 | UA_MANDATORY), |
323 | UVERBS_ATTR_CONST_IN(UVERBS_ATTR_CREATE_QP_TYPE, |
324 | enum ib_uverbs_qp_type, |
325 | UA_MANDATORY), |
326 | UVERBS_ATTR_FLAGS_IN(UVERBS_ATTR_CREATE_QP_FLAGS, |
327 | enum ib_uverbs_qp_create_flags, |
328 | UA_OPTIONAL), |
329 | UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_QP_SOURCE_QPN, |
330 | UVERBS_ATTR_TYPE(u32), |
331 | UA_OPTIONAL), |
332 | UVERBS_ATTR_FD(UVERBS_ATTR_CREATE_QP_EVENT_FD, |
333 | UVERBS_OBJECT_ASYNC_EVENT, |
334 | UVERBS_ACCESS_READ, |
335 | UA_OPTIONAL), |
336 | UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_QP_RESP_CAP, |
337 | UVERBS_ATTR_STRUCT(struct ib_uverbs_qp_cap, |
338 | max_inline_data), |
339 | UA_MANDATORY), |
340 | UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, |
341 | UVERBS_ATTR_TYPE(u32), |
342 | UA_MANDATORY), |
343 | UVERBS_ATTR_UHW()); |
344 | |
345 | static int UVERBS_HANDLER(UVERBS_METHOD_QP_DESTROY)( |
346 | struct uverbs_attr_bundle *attrs) |
347 | { |
348 | struct ib_uobject *uobj = |
349 | uverbs_attr_get_uobject(attrs_bundle: attrs, idx: UVERBS_ATTR_DESTROY_QP_HANDLE); |
350 | struct ib_uqp_object *obj = |
351 | container_of(uobj, struct ib_uqp_object, uevent.uobject); |
352 | struct ib_uverbs_destroy_qp_resp resp = { |
353 | .events_reported = obj->uevent.events_reported |
354 | }; |
355 | |
356 | return uverbs_copy_to(attrs_bundle: attrs, idx: UVERBS_ATTR_DESTROY_QP_RESP, from: &resp, |
357 | size: sizeof(resp)); |
358 | } |
359 | |
360 | DECLARE_UVERBS_NAMED_METHOD( |
361 | UVERBS_METHOD_QP_DESTROY, |
362 | UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_QP_HANDLE, |
363 | UVERBS_OBJECT_QP, |
364 | UVERBS_ACCESS_DESTROY, |
365 | UA_MANDATORY), |
366 | UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_DESTROY_QP_RESP, |
367 | UVERBS_ATTR_TYPE(struct ib_uverbs_destroy_qp_resp), |
368 | UA_MANDATORY)); |
369 | |
370 | DECLARE_UVERBS_NAMED_OBJECT( |
371 | UVERBS_OBJECT_QP, |
372 | UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), uverbs_free_qp), |
373 | &UVERBS_METHOD(UVERBS_METHOD_QP_CREATE), |
374 | &UVERBS_METHOD(UVERBS_METHOD_QP_DESTROY)); |
375 | |
376 | const struct uapi_definition uverbs_def_obj_qp[] = { |
377 | UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_QP, |
378 | UAPI_DEF_OBJ_NEEDS_FN(destroy_qp)), |
379 | {} |
380 | }; |
381 | |