xref: /spdk/lib/rdma_provider/rdma_provider_verbs.c (revision c6c1234de9e0015e670dd0b51bf6ce39ee0e07bd)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2020 Intel Corporation. All rights reserved.
3  *   Copyright (c) Mellanox Technologies LTD. All rights reserved.
4  *   Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5  */
6 
7 #include <rdma/rdma_cma.h>
8 
9 #include "spdk/stdinc.h"
10 #include "spdk/string.h"
11 #include "spdk/likely.h"
12 
13 #include "spdk_internal/rdma_provider.h"
14 #include "spdk/log.h"
15 
16 struct spdk_rdma_provider_qp *
17 spdk_rdma_provider_qp_create(struct rdma_cm_id *cm_id,
18 			     struct spdk_rdma_provider_qp_init_attr *qp_attr)
19 {
20 	struct spdk_rdma_provider_qp *spdk_rdma_qp;
21 	int rc;
22 	struct ibv_qp_init_attr attr = {
23 		.qp_context = qp_attr->qp_context,
24 		.send_cq = qp_attr->send_cq,
25 		.recv_cq = qp_attr->recv_cq,
26 		.srq = qp_attr->srq,
27 		.cap = qp_attr->cap,
28 		.qp_type = IBV_QPT_RC
29 	};
30 
31 	spdk_rdma_qp = calloc(1, sizeof(*spdk_rdma_qp));
32 	if (!spdk_rdma_qp) {
33 		SPDK_ERRLOG("qp memory allocation failed\n");
34 		return NULL;
35 	}
36 
37 	if (qp_attr->stats) {
38 		spdk_rdma_qp->stats = qp_attr->stats;
39 		spdk_rdma_qp->shared_stats = true;
40 	} else {
41 		spdk_rdma_qp->stats = calloc(1, sizeof(*spdk_rdma_qp->stats));
42 		if (!spdk_rdma_qp->stats) {
43 			SPDK_ERRLOG("qp statistics memory allocation failed\n");
44 			free(spdk_rdma_qp);
45 			return NULL;
46 		}
47 	}
48 
49 	rc = rdma_create_qp(cm_id, qp_attr->pd, &attr);
50 	if (rc) {
51 		SPDK_ERRLOG("Failed to create qp, rc %d, errno %s (%d)\n", rc, spdk_strerror(errno), errno);
52 		free(spdk_rdma_qp);
53 		return NULL;
54 	}
55 
56 	qp_attr->cap = attr.cap;
57 	spdk_rdma_qp->qp = cm_id->qp;
58 	spdk_rdma_qp->cm_id = cm_id;
59 
60 	return spdk_rdma_qp;
61 }
62 
63 int
64 spdk_rdma_provider_qp_accept(struct spdk_rdma_provider_qp *spdk_rdma_qp,
65 			     struct rdma_conn_param *conn_param)
66 {
67 	assert(spdk_rdma_qp != NULL);
68 	assert(spdk_rdma_qp->cm_id != NULL);
69 
70 	return rdma_accept(spdk_rdma_qp->cm_id, conn_param);
71 }
72 
73 int
74 spdk_rdma_provider_qp_complete_connect(struct spdk_rdma_provider_qp *spdk_rdma_qp)
75 {
76 	/* Nothing to be done for Verbs */
77 	return 0;
78 }
79 
80 void
81 spdk_rdma_provider_qp_destroy(struct spdk_rdma_provider_qp *spdk_rdma_qp)
82 {
83 	assert(spdk_rdma_qp != NULL);
84 
85 	if (spdk_rdma_qp->send_wrs.first != NULL) {
86 		SPDK_WARNLOG("Destroying qpair with queued Work Requests\n");
87 	}
88 
89 	if (spdk_rdma_qp->qp) {
90 		rdma_destroy_qp(spdk_rdma_qp->cm_id);
91 	}
92 
93 	if (!spdk_rdma_qp->shared_stats) {
94 		free(spdk_rdma_qp->stats);
95 	}
96 
97 	free(spdk_rdma_qp);
98 }
99 
100 int
101 spdk_rdma_provider_qp_disconnect(struct spdk_rdma_provider_qp *spdk_rdma_qp)
102 {
103 	int rc = 0;
104 
105 	assert(spdk_rdma_qp != NULL);
106 
107 	if (spdk_rdma_qp->cm_id) {
108 		rc = rdma_disconnect(spdk_rdma_qp->cm_id);
109 		if (rc) {
110 			if (errno == EINVAL && spdk_rdma_qp->qp->context->device->transport_type == IBV_TRANSPORT_IWARP) {
111 				/* rdma_disconnect may return an error and set errno to EINVAL in case of iWARP.
112 				 * This behaviour is expected since iWARP handles disconnect event other than IB and
113 				 * qpair is already in error state when we call rdma_disconnect */
114 				return 0;
115 			}
116 			SPDK_ERRLOG("rdma_disconnect failed, errno %s (%d)\n", spdk_strerror(errno), errno);
117 		}
118 	}
119 
120 	return rc;
121 }
122 
123 bool
124 spdk_rdma_provider_qp_queue_send_wrs(struct spdk_rdma_provider_qp *spdk_rdma_qp,
125 				     struct ibv_send_wr *first)
126 {
127 	struct ibv_send_wr *last;
128 
129 	assert(spdk_rdma_qp);
130 	assert(first);
131 
132 	spdk_rdma_qp->stats->send.num_submitted_wrs++;
133 	last = first;
134 	while (last->next != NULL) {
135 		last = last->next;
136 		spdk_rdma_qp->stats->send.num_submitted_wrs++;
137 	}
138 
139 	if (spdk_rdma_qp->send_wrs.first == NULL) {
140 		spdk_rdma_qp->send_wrs.first = first;
141 		spdk_rdma_qp->send_wrs.last = last;
142 		return true;
143 	} else {
144 		spdk_rdma_qp->send_wrs.last->next = first;
145 		spdk_rdma_qp->send_wrs.last = last;
146 		return false;
147 	}
148 }
149 
150 int
151 spdk_rdma_provider_qp_flush_send_wrs(struct spdk_rdma_provider_qp *spdk_rdma_qp,
152 				     struct ibv_send_wr **bad_wr)
153 {
154 	int rc;
155 
156 	assert(spdk_rdma_qp);
157 	assert(bad_wr);
158 
159 	if (spdk_unlikely(!spdk_rdma_qp->send_wrs.first)) {
160 		return 0;
161 	}
162 
163 	rc = ibv_post_send(spdk_rdma_qp->qp, spdk_rdma_qp->send_wrs.first, bad_wr);
164 
165 	spdk_rdma_qp->send_wrs.first = NULL;
166 	spdk_rdma_qp->stats->send.doorbell_updates++;
167 
168 	return rc;
169 }
170