Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-block.git] / drivers / infiniband / hw / usnic / usnic_ib_qp_grp.c
CommitLineData
e3cf00d0
UM
1/*
2 * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.
3 *
3805eade
JS
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * BSD license below:
9 *
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
12 * conditions are met:
13 *
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer.
17 *
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
e3cf00d0
UM
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
256d6a6a 33#include <linux/bug.h>
e3cf00d0 34#include <linux/errno.h>
e3cf00d0
UM
35#include <linux/spinlock.h>
36
37#include "usnic_log.h"
38#include "usnic_vnic.h"
39#include "usnic_fwd.h"
40#include "usnic_uiom.h"
9f637f79 41#include "usnic_debugfs.h"
e3cf00d0
UM
42#include "usnic_ib_qp_grp.h"
43#include "usnic_ib_sysfs.h"
44#include "usnic_transport.h"
45
256d6a6a
UM
46#define DFLT_RQ_IDX 0
47
e3cf00d0
UM
48const char *usnic_ib_qp_grp_state_to_string(enum ib_qp_state state)
49{
50 switch (state) {
51 case IB_QPS_RESET:
52 return "Rst";
53 case IB_QPS_INIT:
54 return "Init";
55 case IB_QPS_RTR:
56 return "RTR";
57 case IB_QPS_RTS:
58 return "RTS";
59 case IB_QPS_SQD:
60 return "SQD";
61 case IB_QPS_SQE:
62 return "SQE";
63 case IB_QPS_ERR:
64 return "ERR";
65 default:
fc4fa6e1 66 return "UNKNOWN STATE";
e3cf00d0
UM
67
68 }
69}
70
71int usnic_ib_qp_grp_dump_hdr(char *buf, int buf_sz)
72{
73 return scnprintf(buf, buf_sz, "|QPN\t|State\t|PID\t|VF Idx\t|Fil ID");
74}
75
76int usnic_ib_qp_grp_dump_rows(void *obj, char *buf, int buf_sz)
77{
78 struct usnic_ib_qp_grp *qp_grp = obj;
256d6a6a 79 struct usnic_ib_qp_grp_flow *default_flow;
e3cf00d0 80 if (obj) {
256d6a6a
UM
81 default_flow = list_first_entry(&qp_grp->flows_lst,
82 struct usnic_ib_qp_grp_flow, link);
e3cf00d0
UM
83 return scnprintf(buf, buf_sz, "|%d\t|%s\t|%d\t|%hu\t|%d",
84 qp_grp->ibqp.qp_num,
85 usnic_ib_qp_grp_state_to_string(
86 qp_grp->state),
87 qp_grp->owner_pid,
88 usnic_vnic_get_index(qp_grp->vf->vnic),
256d6a6a 89 default_flow->flow->flow_id);
e3cf00d0
UM
90 } else {
91 return scnprintf(buf, buf_sz, "|N/A\t|N/A\t|N/A\t|N/A\t|N/A");
92 }
93}
94
256d6a6a
UM
95static struct usnic_vnic_res_chunk *
96get_qp_res_chunk(struct usnic_ib_qp_grp *qp_grp)
e3cf00d0 97{
8192d4ac 98 lockdep_assert_held(&qp_grp->lock);
256d6a6a
UM
99 /*
100 * The QP res chunk, used to derive qp indices,
101 * are just indices of the RQs
102 */
103 return usnic_ib_qp_grp_get_chunk(qp_grp, USNIC_VNIC_RES_TYPE_RQ);
e3cf00d0
UM
104}
105
106static int enable_qp_grp(struct usnic_ib_qp_grp *qp_grp)
107{
108
109 int status;
110 int i, vnic_idx;
111 struct usnic_vnic_res_chunk *res_chunk;
112 struct usnic_vnic_res *res;
113
8192d4ac 114 lockdep_assert_held(&qp_grp->lock);
e3cf00d0
UM
115
116 vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);
117
256d6a6a 118 res_chunk = get_qp_res_chunk(qp_grp);
5f4c7e4e 119 if (IS_ERR(res_chunk)) {
256d6a6a
UM
120 usnic_err("Unable to get qp res with err %ld\n",
121 PTR_ERR(res_chunk));
5f4c7e4e 122 return PTR_ERR(res_chunk);
e3cf00d0
UM
123 }
124
125 for (i = 0; i < res_chunk->cnt; i++) {
126 res = res_chunk->res[i];
256d6a6a 127 status = usnic_fwd_enable_qp(qp_grp->ufdev, vnic_idx,
e3cf00d0
UM
128 res->vnic_idx);
129 if (status) {
256d6a6a
UM
130 usnic_err("Failed to enable qp %d of %s:%d\n with err %d\n",
131 res->vnic_idx, qp_grp->ufdev->name,
e3cf00d0
UM
132 vnic_idx, status);
133 goto out_err;
134 }
135 }
136
137 return 0;
138
139out_err:
140 for (i--; i >= 0; i--) {
141 res = res_chunk->res[i];
256d6a6a 142 usnic_fwd_disable_qp(qp_grp->ufdev, vnic_idx,
e3cf00d0
UM
143 res->vnic_idx);
144 }
145
146 return status;
147}
148
149static int disable_qp_grp(struct usnic_ib_qp_grp *qp_grp)
150{
151 int i, vnic_idx;
152 struct usnic_vnic_res_chunk *res_chunk;
153 struct usnic_vnic_res *res;
154 int status = 0;
155
8192d4ac 156 lockdep_assert_held(&qp_grp->lock);
e3cf00d0
UM
157 vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);
158
256d6a6a 159 res_chunk = get_qp_res_chunk(qp_grp);
5f4c7e4e 160 if (IS_ERR(res_chunk)) {
256d6a6a 161 usnic_err("Unable to get qp res with err %ld\n",
e3cf00d0 162 PTR_ERR(res_chunk));
5f4c7e4e 163 return PTR_ERR(res_chunk);
e3cf00d0
UM
164 }
165
166 for (i = 0; i < res_chunk->cnt; i++) {
167 res = res_chunk->res[i];
256d6a6a 168 status = usnic_fwd_disable_qp(qp_grp->ufdev, vnic_idx,
e3cf00d0
UM
169 res->vnic_idx);
170 if (status) {
171 usnic_err("Failed to disable rq %d of %s:%d\n with err %d\n",
172 res->vnic_idx,
256d6a6a 173 qp_grp->ufdev->name,
e3cf00d0
UM
174 vnic_idx, status);
175 }
176 }
177
178 return status;
179
180}
181
256d6a6a
UM
182static int init_filter_action(struct usnic_ib_qp_grp *qp_grp,
183 struct usnic_filter_action *uaction)
184{
185 struct usnic_vnic_res_chunk *res_chunk;
186
187 res_chunk = usnic_ib_qp_grp_get_chunk(qp_grp, USNIC_VNIC_RES_TYPE_RQ);
5f4c7e4e 188 if (IS_ERR(res_chunk)) {
256d6a6a
UM
189 usnic_err("Unable to get %s with err %ld\n",
190 usnic_vnic_res_type_to_str(USNIC_VNIC_RES_TYPE_RQ),
191 PTR_ERR(res_chunk));
5f4c7e4e 192 return PTR_ERR(res_chunk);
256d6a6a
UM
193 }
194
195 uaction->vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);
196 uaction->action.type = FILTER_ACTION_RQ_STEERING;
197 uaction->action.u.rq_idx = res_chunk->res[DFLT_RQ_IDX]->vnic_idx;
198
199 return 0;
200}
201
202static struct usnic_ib_qp_grp_flow*
203create_roce_custom_flow(struct usnic_ib_qp_grp *qp_grp,
204 struct usnic_transport_spec *trans_spec)
205{
206 uint16_t port_num;
207 int err;
208 struct filter filter;
209 struct usnic_filter_action uaction;
210 struct usnic_ib_qp_grp_flow *qp_flow;
211 struct usnic_fwd_flow *flow;
212 enum usnic_transport_type trans_type;
213
214 trans_type = trans_spec->trans_type;
215 port_num = trans_spec->usnic_roce.port_num;
216
217 /* Reserve Port */
218 port_num = usnic_transport_rsrv_port(trans_type, port_num);
219 if (port_num == 0)
220 return ERR_PTR(-EINVAL);
221
222 /* Create Flow */
223 usnic_fwd_init_usnic_filter(&filter, port_num);
224 err = init_filter_action(qp_grp, &uaction);
225 if (err)
226 goto out_unreserve_port;
227
228 flow = usnic_fwd_alloc_flow(qp_grp->ufdev, &filter, &uaction);
229 if (IS_ERR_OR_NULL(flow)) {
6a54d9f9 230 err = flow ? PTR_ERR(flow) : -EFAULT;
256d6a6a
UM
231 goto out_unreserve_port;
232 }
233
234 /* Create Flow Handle */
235 qp_flow = kzalloc(sizeof(*qp_flow), GFP_ATOMIC);
fe274c5a
IY
236 if (!qp_flow) {
237 err = -ENOMEM;
256d6a6a
UM
238 goto out_dealloc_flow;
239 }
240 qp_flow->flow = flow;
241 qp_flow->trans_type = trans_type;
242 qp_flow->usnic_roce.port_num = port_num;
243 qp_flow->qp_grp = qp_grp;
244 return qp_flow;
245
246out_dealloc_flow:
247 usnic_fwd_dealloc_flow(flow);
248out_unreserve_port:
249 usnic_transport_unrsrv_port(trans_type, port_num);
250 return ERR_PTR(err);
251}
252
253static void release_roce_custom_flow(struct usnic_ib_qp_grp_flow *qp_flow)
254{
255 usnic_fwd_dealloc_flow(qp_flow->flow);
256 usnic_transport_unrsrv_port(qp_flow->trans_type,
257 qp_flow->usnic_roce.port_num);
258 kfree(qp_flow);
259}
260
e45e614e
UM
261static struct usnic_ib_qp_grp_flow*
262create_udp_flow(struct usnic_ib_qp_grp *qp_grp,
263 struct usnic_transport_spec *trans_spec)
264{
265 struct socket *sock;
266 int sock_fd;
267 int err;
268 struct filter filter;
269 struct usnic_filter_action uaction;
270 struct usnic_ib_qp_grp_flow *qp_flow;
271 struct usnic_fwd_flow *flow;
272 enum usnic_transport_type trans_type;
273 uint32_t addr;
274 uint16_t port_num;
275 int proto;
276
277 trans_type = trans_spec->trans_type;
278 sock_fd = trans_spec->udp.sock_fd;
279
280 /* Get and check socket */
281 sock = usnic_transport_get_socket(sock_fd);
282 if (IS_ERR_OR_NULL(sock))
283 return ERR_CAST(sock);
284
285 err = usnic_transport_sock_get_addr(sock, &proto, &addr, &port_num);
286 if (err)
287 goto out_put_sock;
288
289 if (proto != IPPROTO_UDP) {
290 usnic_err("Protocol for fd %d is not UDP", sock_fd);
291 err = -EPERM;
292 goto out_put_sock;
293 }
294
295 /* Create flow */
296 usnic_fwd_init_udp_filter(&filter, addr, port_num);
297 err = init_filter_action(qp_grp, &uaction);
298 if (err)
299 goto out_put_sock;
300
301 flow = usnic_fwd_alloc_flow(qp_grp->ufdev, &filter, &uaction);
302 if (IS_ERR_OR_NULL(flow)) {
6a54d9f9 303 err = flow ? PTR_ERR(flow) : -EFAULT;
e45e614e
UM
304 goto out_put_sock;
305 }
306
307 /* Create qp_flow */
308 qp_flow = kzalloc(sizeof(*qp_flow), GFP_ATOMIC);
fe274c5a
IY
309 if (!qp_flow) {
310 err = -ENOMEM;
e45e614e
UM
311 goto out_dealloc_flow;
312 }
313 qp_flow->flow = flow;
314 qp_flow->trans_type = trans_type;
315 qp_flow->udp.sock = sock;
316 qp_flow->qp_grp = qp_grp;
317 return qp_flow;
318
319out_dealloc_flow:
320 usnic_fwd_dealloc_flow(flow);
321out_put_sock:
322 usnic_transport_put_socket(sock);
323 return ERR_PTR(err);
324}
325
326static void release_udp_flow(struct usnic_ib_qp_grp_flow *qp_flow)
327{
328 usnic_fwd_dealloc_flow(qp_flow->flow);
329 usnic_transport_put_socket(qp_flow->udp.sock);
330 kfree(qp_flow);
331}
332
256d6a6a
UM
333static struct usnic_ib_qp_grp_flow*
334create_and_add_flow(struct usnic_ib_qp_grp *qp_grp,
335 struct usnic_transport_spec *trans_spec)
336{
337 struct usnic_ib_qp_grp_flow *qp_flow;
338 enum usnic_transport_type trans_type;
339
340 trans_type = trans_spec->trans_type;
341 switch (trans_type) {
342 case USNIC_TRANSPORT_ROCE_CUSTOM:
343 qp_flow = create_roce_custom_flow(qp_grp, trans_spec);
344 break;
e45e614e
UM
345 case USNIC_TRANSPORT_IPV4_UDP:
346 qp_flow = create_udp_flow(qp_grp, trans_spec);
347 break;
256d6a6a
UM
348 default:
349 usnic_err("Unsupported transport %u\n",
350 trans_spec->trans_type);
351 return ERR_PTR(-EINVAL);
352 }
353
9f637f79 354 if (!IS_ERR_OR_NULL(qp_flow)) {
256d6a6a 355 list_add_tail(&qp_flow->link, &qp_grp->flows_lst);
9f637f79
UM
356 usnic_debugfs_flow_add(qp_flow);
357 }
256d6a6a
UM
358
359
360 return qp_flow;
361}
362
363static void release_and_remove_flow(struct usnic_ib_qp_grp_flow *qp_flow)
364{
9f637f79 365 usnic_debugfs_flow_remove(qp_flow);
256d6a6a
UM
366 list_del(&qp_flow->link);
367
368 switch (qp_flow->trans_type) {
369 case USNIC_TRANSPORT_ROCE_CUSTOM:
370 release_roce_custom_flow(qp_flow);
371 break;
e45e614e
UM
372 case USNIC_TRANSPORT_IPV4_UDP:
373 release_udp_flow(qp_flow);
374 break;
256d6a6a
UM
375 default:
376 WARN(1, "Unsupported transport %u\n",
377 qp_flow->trans_type);
378 break;
379 }
380}
381
382static void release_and_remove_all_flows(struct usnic_ib_qp_grp *qp_grp)
383{
384 struct usnic_ib_qp_grp_flow *qp_flow, *tmp;
385 list_for_each_entry_safe(qp_flow, tmp, &qp_grp->flows_lst, link)
386 release_and_remove_flow(qp_flow);
387}
388
e3cf00d0
UM
389int usnic_ib_qp_grp_modify(struct usnic_ib_qp_grp *qp_grp,
390 enum ib_qp_state new_state,
256d6a6a 391 void *data)
e3cf00d0
UM
392{
393 int status = 0;
e3cf00d0
UM
394 struct ib_event ib_event;
395 enum ib_qp_state old_state;
256d6a6a
UM
396 struct usnic_transport_spec *trans_spec;
397 struct usnic_ib_qp_grp_flow *qp_flow;
e3cf00d0
UM
398
399 old_state = qp_grp->state;
256d6a6a 400 trans_spec = (struct usnic_transport_spec *) data;
e3cf00d0
UM
401
402 spin_lock(&qp_grp->lock);
403 switch (new_state) {
404 case IB_QPS_RESET:
405 switch (old_state) {
406 case IB_QPS_RESET:
407 /* NO-OP */
408 break;
409 case IB_QPS_INIT:
256d6a6a
UM
410 release_and_remove_all_flows(qp_grp);
411 status = 0;
e3cf00d0
UM
412 break;
413 case IB_QPS_RTR:
414 case IB_QPS_RTS:
415 case IB_QPS_ERR:
416 status = disable_qp_grp(qp_grp);
256d6a6a 417 release_and_remove_all_flows(qp_grp);
e3cf00d0
UM
418 break;
419 default:
420 status = -EINVAL;
421 }
422 break;
423 case IB_QPS_INIT:
424 switch (old_state) {
425 case IB_QPS_RESET:
256d6a6a
UM
426 if (trans_spec) {
427 qp_flow = create_and_add_flow(qp_grp,
428 trans_spec);
429 if (IS_ERR_OR_NULL(qp_flow)) {
6a54d9f9 430 status = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;
256d6a6a
UM
431 break;
432 }
433 } else {
434 /*
435 * Optional to specify filters.
436 */
437 status = 0;
438 }
e3cf00d0
UM
439 break;
440 case IB_QPS_INIT:
256d6a6a
UM
441 if (trans_spec) {
442 qp_flow = create_and_add_flow(qp_grp,
443 trans_spec);
444 if (IS_ERR_OR_NULL(qp_flow)) {
6a54d9f9 445 status = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;
256d6a6a
UM
446 break;
447 }
448 } else {
449 /*
450 * Doesn't make sense to go into INIT state
451 * from INIT state w/o adding filters.
452 */
453 status = -EINVAL;
454 }
e3cf00d0
UM
455 break;
456 case IB_QPS_RTR:
457 status = disable_qp_grp(qp_grp);
458 break;
459 case IB_QPS_RTS:
460 status = disable_qp_grp(qp_grp);
461 break;
462 default:
463 status = -EINVAL;
464 }
465 break;
466 case IB_QPS_RTR:
467 switch (old_state) {
468 case IB_QPS_INIT:
469 status = enable_qp_grp(qp_grp);
470 break;
471 default:
472 status = -EINVAL;
473 }
474 break;
475 case IB_QPS_RTS:
476 switch (old_state) {
477 case IB_QPS_RTR:
478 /* NO-OP FOR NOW */
479 break;
480 default:
481 status = -EINVAL;
482 }
483 break;
484 case IB_QPS_ERR:
485 ib_event.device = &qp_grp->vf->pf->ib_dev;
486 ib_event.element.qp = &qp_grp->ibqp;
487 ib_event.event = IB_EVENT_QP_FATAL;
488
489 switch (old_state) {
490 case IB_QPS_RESET:
491 qp_grp->ibqp.event_handler(&ib_event,
492 qp_grp->ibqp.qp_context);
493 break;
494 case IB_QPS_INIT:
256d6a6a 495 release_and_remove_all_flows(qp_grp);
e3cf00d0
UM
496 qp_grp->ibqp.event_handler(&ib_event,
497 qp_grp->ibqp.qp_context);
498 break;
499 case IB_QPS_RTR:
500 case IB_QPS_RTS:
501 status = disable_qp_grp(qp_grp);
256d6a6a 502 release_and_remove_all_flows(qp_grp);
e3cf00d0
UM
503 qp_grp->ibqp.event_handler(&ib_event,
504 qp_grp->ibqp.qp_context);
505 break;
506 default:
507 status = -EINVAL;
508 }
509 break;
510 default:
511 status = -EINVAL;
512 }
513 spin_unlock(&qp_grp->lock);
514
515 if (!status) {
516 qp_grp->state = new_state;
2547a366 517 usnic_info("Transitioned %u from %s to %s",
e3cf00d0
UM
518 qp_grp->grp_id,
519 usnic_ib_qp_grp_state_to_string(old_state),
520 usnic_ib_qp_grp_state_to_string(new_state));
521 } else {
9b13494c 522 usnic_err("Failed to transition %u from %s to %s",
e3cf00d0
UM
523 qp_grp->grp_id,
524 usnic_ib_qp_grp_state_to_string(old_state),
525 usnic_ib_qp_grp_state_to_string(new_state));
526 }
527
528 return status;
529}
530
531static struct usnic_vnic_res_chunk**
532alloc_res_chunk_list(struct usnic_vnic *vnic,
533 struct usnic_vnic_res_spec *res_spec, void *owner_obj)
534{
535 enum usnic_vnic_res_type res_type;
536 struct usnic_vnic_res_chunk **res_chunk_list;
537 int err, i, res_cnt, res_lst_sz;
538
539 for (res_lst_sz = 0;
540 res_spec->resources[res_lst_sz].type != USNIC_VNIC_RES_TYPE_EOL;
541 res_lst_sz++) {
542 /* Do Nothing */
543 }
544
6396bb22 545 res_chunk_list = kcalloc(res_lst_sz + 1, sizeof(*res_chunk_list),
e3cf00d0
UM
546 GFP_ATOMIC);
547 if (!res_chunk_list)
548 return ERR_PTR(-ENOMEM);
549
550 for (i = 0; res_spec->resources[i].type != USNIC_VNIC_RES_TYPE_EOL;
551 i++) {
552 res_type = res_spec->resources[i].type;
553 res_cnt = res_spec->resources[i].cnt;
554
555 res_chunk_list[i] = usnic_vnic_get_resources(vnic, res_type,
556 res_cnt, owner_obj);
557 if (IS_ERR_OR_NULL(res_chunk_list[i])) {
6a54d9f9
UM
558 err = res_chunk_list[i] ?
559 PTR_ERR(res_chunk_list[i]) : -ENOMEM;
e3cf00d0
UM
560 usnic_err("Failed to get %s from %s with err %d\n",
561 usnic_vnic_res_type_to_str(res_type),
562 usnic_vnic_pci_name(vnic),
563 err);
564 goto out_free_res;
565 }
566 }
567
568 return res_chunk_list;
569
570out_free_res:
dc92d146 571 for (i--; i >= 0; i--)
e3cf00d0
UM
572 usnic_vnic_put_resources(res_chunk_list[i]);
573 kfree(res_chunk_list);
574 return ERR_PTR(err);
575}
576
577static void free_qp_grp_res(struct usnic_vnic_res_chunk **res_chunk_list)
578{
579 int i;
580 for (i = 0; res_chunk_list[i]; i++)
581 usnic_vnic_put_resources(res_chunk_list[i]);
582 kfree(res_chunk_list);
583}
584
585static int qp_grp_and_vf_bind(struct usnic_ib_vf *vf,
586 struct usnic_ib_pd *pd,
587 struct usnic_ib_qp_grp *qp_grp)
588{
589 int err;
590 struct pci_dev *pdev;
591
8192d4ac 592 lockdep_assert_held(&vf->lock);
e3cf00d0
UM
593
594 pdev = usnic_vnic_get_pdev(vf->vnic);
595 if (vf->qp_grp_ref_cnt == 0) {
596 err = usnic_uiom_attach_dev_to_pd(pd->umem_pd, &pdev->dev);
597 if (err) {
598 usnic_err("Failed to attach %s to domain\n",
599 pci_name(pdev));
600 return err;
601 }
602 vf->pd = pd;
603 }
604 vf->qp_grp_ref_cnt++;
605
606 WARN_ON(vf->pd != pd);
607 qp_grp->vf = vf;
608
609 return 0;
610}
611
612static void qp_grp_and_vf_unbind(struct usnic_ib_qp_grp *qp_grp)
613{
614 struct pci_dev *pdev;
615 struct usnic_ib_pd *pd;
616
8192d4ac 617 lockdep_assert_held(&qp_grp->vf->lock);
e3cf00d0
UM
618
619 pd = qp_grp->vf->pd;
620 pdev = usnic_vnic_get_pdev(qp_grp->vf->vnic);
621 if (--qp_grp->vf->qp_grp_ref_cnt == 0) {
622 qp_grp->vf->pd = NULL;
623 usnic_uiom_detach_dev_from_pd(pd->umem_pd, &pdev->dev);
624 }
625 qp_grp->vf = NULL;
626}
627
628static void log_spec(struct usnic_vnic_res_spec *res_spec)
629{
630 char buf[512];
631 usnic_vnic_spec_dump(buf, sizeof(buf), res_spec);
632 usnic_dbg("%s\n", buf);
633}
634
256d6a6a
UM
635static int qp_grp_id_from_flow(struct usnic_ib_qp_grp_flow *qp_flow,
636 uint32_t *id)
637{
638 enum usnic_transport_type trans_type = qp_flow->trans_type;
e45e614e 639 int err;
f809309a 640 uint16_t port_num = 0;
256d6a6a
UM
641
642 switch (trans_type) {
643 case USNIC_TRANSPORT_ROCE_CUSTOM:
644 *id = qp_flow->usnic_roce.port_num;
645 break;
e45e614e
UM
646 case USNIC_TRANSPORT_IPV4_UDP:
647 err = usnic_transport_sock_get_addr(qp_flow->udp.sock,
648 NULL, NULL,
f809309a 649 &port_num);
e45e614e
UM
650 if (err)
651 return err;
f809309a
UM
652 /*
653 * Copy port_num to stack first and then to *id,
654 * so that the short to int cast works for little
655 * and big endian systems.
656 */
657 *id = port_num;
e45e614e 658 break;
256d6a6a
UM
659 default:
660 usnic_err("Unsupported transport %u\n", trans_type);
661 return -EINVAL;
662 }
663
664 return 0;
665}
666
514aee66
LR
667int usnic_ib_qp_grp_create(struct usnic_ib_qp_grp *qp_grp,
668 struct usnic_fwd_dev *ufdev, struct usnic_ib_vf *vf,
669 struct usnic_ib_pd *pd,
670 struct usnic_vnic_res_spec *res_spec,
671 struct usnic_transport_spec *transport_spec)
e3cf00d0 672{
e3cf00d0 673 int err;
256d6a6a
UM
674 enum usnic_transport_type transport = transport_spec->trans_type;
675 struct usnic_ib_qp_grp_flow *qp_flow;
e3cf00d0 676
8192d4ac 677 lockdep_assert_held(&vf->lock);
e3cf00d0
UM
678
679 err = usnic_vnic_res_spec_satisfied(&min_transport_spec[transport],
680 res_spec);
681 if (err) {
d12c416d 682 usnic_err("Spec does not meet minimum req for transport %d\n",
e3cf00d0
UM
683 transport);
684 log_spec(res_spec);
514aee66 685 return err;
e3cf00d0
UM
686 }
687
e3cf00d0
UM
688 qp_grp->res_chunk_list = alloc_res_chunk_list(vf->vnic, res_spec,
689 qp_grp);
514aee66
LR
690 if (IS_ERR_OR_NULL(qp_grp->res_chunk_list))
691 return qp_grp->res_chunk_list ?
692 PTR_ERR(qp_grp->res_chunk_list) :
693 -ENOMEM;
e3cf00d0 694
256d6a6a
UM
695 err = qp_grp_and_vf_bind(vf, pd, qp_grp);
696 if (err)
697 goto out_free_res;
698
699 INIT_LIST_HEAD(&qp_grp->flows_lst);
e3cf00d0
UM
700 spin_lock_init(&qp_grp->lock);
701 qp_grp->ufdev = ufdev;
e3cf00d0
UM
702 qp_grp->state = IB_QPS_RESET;
703 qp_grp->owner_pid = current->pid;
704
256d6a6a
UM
705 qp_flow = create_and_add_flow(qp_grp, transport_spec);
706 if (IS_ERR_OR_NULL(qp_flow)) {
707 usnic_err("Unable to create and add flow with err %ld\n",
708 PTR_ERR(qp_flow));
6a54d9f9 709 err = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;
256d6a6a
UM
710 goto out_qp_grp_vf_unbind;
711 }
e3cf00d0 712
256d6a6a 713 err = qp_grp_id_from_flow(qp_flow, &qp_grp->grp_id);
e3cf00d0 714 if (err)
256d6a6a
UM
715 goto out_release_flow;
716 qp_grp->ibqp.qp_num = qp_grp->grp_id;
e3cf00d0
UM
717
718 usnic_ib_sysfs_qpn_add(qp_grp);
719
514aee66 720 return 0;
e3cf00d0 721
256d6a6a
UM
722out_release_flow:
723 release_and_remove_flow(qp_flow);
724out_qp_grp_vf_unbind:
725 qp_grp_and_vf_unbind(qp_grp);
726out_free_res:
727 free_qp_grp_res(qp_grp->res_chunk_list);
514aee66 728 return err;
e3cf00d0
UM
729}
730
731void usnic_ib_qp_grp_destroy(struct usnic_ib_qp_grp *qp_grp)
732{
e3cf00d0
UM
733
734 WARN_ON(qp_grp->state != IB_QPS_RESET);
8192d4ac 735 lockdep_assert_held(&qp_grp->vf->lock);
e3cf00d0 736
9f637f79 737 release_and_remove_all_flows(qp_grp);
e3cf00d0
UM
738 usnic_ib_sysfs_qpn_remove(qp_grp);
739 qp_grp_and_vf_unbind(qp_grp);
740 free_qp_grp_res(qp_grp->res_chunk_list);
e3cf00d0
UM
741}
742
743struct usnic_vnic_res_chunk*
744usnic_ib_qp_grp_get_chunk(struct usnic_ib_qp_grp *qp_grp,
745 enum usnic_vnic_res_type res_type)
746{
747 int i;
748
749 for (i = 0; qp_grp->res_chunk_list[i]; i++) {
750 if (qp_grp->res_chunk_list[i]->type == res_type)
751 return qp_grp->res_chunk_list[i];
752 }
753
754 return ERR_PTR(-EINVAL);
755}