xref: /spdk/lib/nvme/nvme_transport.c (revision d4d9a181db314d25e048ca14d48f0a7019ae5fa3)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2016 Intel Corporation.
3  *   All rights reserved.
4  *   Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved.
5  *   Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
6  */
7 
8 /*
9  * NVMe transport abstraction
10  */
11 
12 #include "nvme_internal.h"
13 #include "spdk/queue.h"
14 
15 #define SPDK_MAX_NUM_OF_TRANSPORTS 16
16 
17 struct spdk_nvme_transport {
18 	struct spdk_nvme_transport_ops	ops;
19 	TAILQ_ENTRY(spdk_nvme_transport)	link;
20 };
21 
22 TAILQ_HEAD(nvme_transport_list, spdk_nvme_transport) g_spdk_nvme_transports =
23 	TAILQ_HEAD_INITIALIZER(g_spdk_nvme_transports);
24 
25 struct spdk_nvme_transport g_spdk_transports[SPDK_MAX_NUM_OF_TRANSPORTS] = {};
26 int g_current_transport_index = 0;
27 
28 struct spdk_nvme_transport_opts g_spdk_nvme_transport_opts = {
29 	.rdma_srq_size = 0,
30 	.rdma_max_cq_size = 0,
31 };
32 
33 const struct spdk_nvme_transport *
34 nvme_get_first_transport(void)
35 {
36 	return TAILQ_FIRST(&g_spdk_nvme_transports);
37 }
38 
39 const struct spdk_nvme_transport *
40 nvme_get_next_transport(const struct spdk_nvme_transport *transport)
41 {
42 	return TAILQ_NEXT(transport, link);
43 }
44 
45 /*
46  * Unfortunately, due to NVMe PCIe multiprocess support, we cannot store the
47  * transport object in either the controller struct or the admin qpair. This means
48  * that a lot of admin related transport calls will have to call nvme_get_transport
49  * in order to know which functions to call.
50  * In the I/O path, we have the ability to store the transport struct in the I/O
51  * qpairs to avoid taking a performance hit.
52  */
53 const struct spdk_nvme_transport *
54 nvme_get_transport(const char *transport_name)
55 {
56 	struct spdk_nvme_transport *registered_transport;
57 
58 	TAILQ_FOREACH(registered_transport, &g_spdk_nvme_transports, link) {
59 		if (strcasecmp(transport_name, registered_transport->ops.name) == 0) {
60 			return registered_transport;
61 		}
62 	}
63 
64 	return NULL;
65 }
66 
67 bool
68 spdk_nvme_transport_available(enum spdk_nvme_transport_type trtype)
69 {
70 	return nvme_get_transport(spdk_nvme_transport_id_trtype_str(trtype)) == NULL ? false : true;
71 }
72 
73 bool
74 spdk_nvme_transport_available_by_name(const char *transport_name)
75 {
76 	return nvme_get_transport(transport_name) == NULL ? false : true;
77 }
78 
79 void
80 spdk_nvme_transport_register(const struct spdk_nvme_transport_ops *ops)
81 {
82 	struct spdk_nvme_transport *new_transport;
83 
84 	if (nvme_get_transport(ops->name)) {
85 		SPDK_ERRLOG("Double registering NVMe transport %s is prohibited.\n", ops->name);
86 		assert(false);
87 	}
88 
89 	if (g_current_transport_index == SPDK_MAX_NUM_OF_TRANSPORTS) {
90 		SPDK_ERRLOG("Unable to register new NVMe transport.\n");
91 		assert(false);
92 		return;
93 	}
94 	new_transport = &g_spdk_transports[g_current_transport_index++];
95 
96 	new_transport->ops = *ops;
97 	TAILQ_INSERT_TAIL(&g_spdk_nvme_transports, new_transport, link);
98 }
99 
100 struct spdk_nvme_ctrlr *nvme_transport_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
101 		const struct spdk_nvme_ctrlr_opts *opts,
102 		void *devhandle)
103 {
104 	const struct spdk_nvme_transport *transport = nvme_get_transport(trid->trstring);
105 	struct spdk_nvme_ctrlr *ctrlr;
106 
107 	if (transport == NULL) {
108 		SPDK_ERRLOG("Transport %s doesn't exist.", trid->trstring);
109 		return NULL;
110 	}
111 
112 	ctrlr = transport->ops.ctrlr_construct(trid, opts, devhandle);
113 
114 	return ctrlr;
115 }
116 
117 int
118 nvme_transport_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
119 			  bool direct_connect)
120 {
121 	const struct spdk_nvme_transport *transport = nvme_get_transport(probe_ctx->trid.trstring);
122 
123 	if (transport == NULL) {
124 		SPDK_ERRLOG("Transport %s doesn't exist.", probe_ctx->trid.trstring);
125 		return -ENOENT;
126 	}
127 
128 	return transport->ops.ctrlr_scan(probe_ctx, direct_connect);
129 }
130 
131 int
132 nvme_transport_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
133 {
134 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
135 
136 	assert(transport != NULL);
137 	return transport->ops.ctrlr_destruct(ctrlr);
138 }
139 
140 int
141 nvme_transport_ctrlr_enable(struct spdk_nvme_ctrlr *ctrlr)
142 {
143 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
144 
145 	assert(transport != NULL);
146 	return transport->ops.ctrlr_enable(ctrlr);
147 }
148 
149 int
150 nvme_transport_ctrlr_ready(struct spdk_nvme_ctrlr *ctrlr)
151 {
152 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
153 
154 	assert(transport != NULL);
155 	if (transport->ops.ctrlr_ready) {
156 		return transport->ops.ctrlr_ready(ctrlr);
157 	}
158 
159 	return 0;
160 }
161 
162 int
163 nvme_transport_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
164 {
165 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
166 
167 	assert(transport != NULL);
168 	return transport->ops.ctrlr_set_reg_4(ctrlr, offset, value);
169 }
170 
171 int
172 nvme_transport_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
173 {
174 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
175 
176 	assert(transport != NULL);
177 	return transport->ops.ctrlr_set_reg_8(ctrlr, offset, value);
178 }
179 
180 int
181 nvme_transport_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
182 {
183 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
184 
185 	assert(transport != NULL);
186 	return transport->ops.ctrlr_get_reg_4(ctrlr, offset, value);
187 }
188 
189 int
190 nvme_transport_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
191 {
192 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
193 
194 	assert(transport != NULL);
195 	return transport->ops.ctrlr_get_reg_8(ctrlr, offset, value);
196 }
197 
198 static int
199 nvme_queue_register_operation_completion(struct spdk_nvme_ctrlr *ctrlr, uint64_t value,
200 		spdk_nvme_reg_cb cb_fn, void *cb_ctx)
201 {
202 	struct nvme_register_completion *ctx;
203 
204 	ctx = spdk_zmalloc(sizeof(*ctx), 0, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
205 	if (ctx == NULL) {
206 		return -ENOMEM;
207 	}
208 
209 	ctx->cpl.status.sct = SPDK_NVME_SCT_GENERIC;
210 	ctx->cpl.status.sc = SPDK_NVME_SC_SUCCESS;
211 	ctx->cb_fn = cb_fn;
212 	ctx->cb_ctx = cb_ctx;
213 	ctx->value = value;
214 	ctx->pid = getpid();
215 
216 	nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
217 	STAILQ_INSERT_TAIL(&ctrlr->register_operations, ctx, stailq);
218 	nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
219 
220 	return 0;
221 }
222 
223 int
224 nvme_transport_ctrlr_set_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value,
225 				     spdk_nvme_reg_cb cb_fn, void *cb_arg)
226 {
227 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
228 	int rc;
229 
230 	assert(transport != NULL);
231 	if (transport->ops.ctrlr_set_reg_4_async == NULL) {
232 		rc = transport->ops.ctrlr_set_reg_4(ctrlr, offset, value);
233 		if (rc != 0) {
234 			return rc;
235 		}
236 
237 		return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
238 	}
239 
240 	return transport->ops.ctrlr_set_reg_4_async(ctrlr, offset, value, cb_fn, cb_arg);
241 }
242 
243 int
244 nvme_transport_ctrlr_set_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value,
245 				     spdk_nvme_reg_cb cb_fn, void *cb_arg)
246 
247 {
248 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
249 	int rc;
250 
251 	assert(transport != NULL);
252 	if (transport->ops.ctrlr_set_reg_8_async == NULL) {
253 		rc = transport->ops.ctrlr_set_reg_8(ctrlr, offset, value);
254 		if (rc != 0) {
255 			return rc;
256 		}
257 
258 		return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
259 	}
260 
261 	return transport->ops.ctrlr_set_reg_8_async(ctrlr, offset, value, cb_fn, cb_arg);
262 }
263 
264 int
265 nvme_transport_ctrlr_get_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
266 				     spdk_nvme_reg_cb cb_fn, void *cb_arg)
267 {
268 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
269 	uint32_t value;
270 	int rc;
271 
272 	assert(transport != NULL);
273 	if (transport->ops.ctrlr_get_reg_4_async == NULL) {
274 		rc = transport->ops.ctrlr_get_reg_4(ctrlr, offset, &value);
275 		if (rc != 0) {
276 			return rc;
277 		}
278 
279 		return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
280 	}
281 
282 	return transport->ops.ctrlr_get_reg_4_async(ctrlr, offset, cb_fn, cb_arg);
283 }
284 
285 int
286 nvme_transport_ctrlr_get_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
287 				     spdk_nvme_reg_cb cb_fn, void *cb_arg)
288 {
289 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
290 	uint64_t value;
291 	int rc;
292 
293 	assert(transport != NULL);
294 	if (transport->ops.ctrlr_get_reg_8_async == NULL) {
295 		rc = transport->ops.ctrlr_get_reg_8(ctrlr, offset, &value);
296 		if (rc != 0) {
297 			return rc;
298 		}
299 
300 		return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
301 	}
302 
303 	return transport->ops.ctrlr_get_reg_8_async(ctrlr, offset, cb_fn, cb_arg);
304 }
305 
306 uint32_t
307 nvme_transport_ctrlr_get_max_xfer_size(struct spdk_nvme_ctrlr *ctrlr)
308 {
309 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
310 
311 	assert(transport != NULL);
312 	return transport->ops.ctrlr_get_max_xfer_size(ctrlr);
313 }
314 
315 uint16_t
316 nvme_transport_ctrlr_get_max_sges(struct spdk_nvme_ctrlr *ctrlr)
317 {
318 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
319 
320 	assert(transport != NULL);
321 	return transport->ops.ctrlr_get_max_sges(ctrlr);
322 }
323 
324 int
325 nvme_transport_ctrlr_reserve_cmb(struct spdk_nvme_ctrlr *ctrlr)
326 {
327 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
328 
329 	assert(transport != NULL);
330 	if (transport->ops.ctrlr_reserve_cmb != NULL) {
331 		return transport->ops.ctrlr_reserve_cmb(ctrlr);
332 	}
333 
334 	return -ENOTSUP;
335 }
336 
337 void *
338 nvme_transport_ctrlr_map_cmb(struct spdk_nvme_ctrlr *ctrlr, size_t *size)
339 {
340 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
341 
342 	assert(transport != NULL);
343 	if (transport->ops.ctrlr_map_cmb != NULL) {
344 		return transport->ops.ctrlr_map_cmb(ctrlr, size);
345 	}
346 
347 	return NULL;
348 }
349 
350 int
351 nvme_transport_ctrlr_unmap_cmb(struct spdk_nvme_ctrlr *ctrlr)
352 {
353 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
354 
355 	assert(transport != NULL);
356 	if (transport->ops.ctrlr_unmap_cmb != NULL) {
357 		return transport->ops.ctrlr_unmap_cmb(ctrlr);
358 	}
359 
360 	return 0;
361 }
362 
363 int
364 nvme_transport_ctrlr_enable_pmr(struct spdk_nvme_ctrlr *ctrlr)
365 {
366 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
367 
368 	assert(transport != NULL);
369 	if (transport->ops.ctrlr_enable_pmr != NULL) {
370 		return transport->ops.ctrlr_enable_pmr(ctrlr);
371 	}
372 
373 	return -ENOSYS;
374 }
375 
376 int
377 nvme_transport_ctrlr_disable_pmr(struct spdk_nvme_ctrlr *ctrlr)
378 {
379 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
380 
381 	assert(transport != NULL);
382 	if (transport->ops.ctrlr_disable_pmr != NULL) {
383 		return transport->ops.ctrlr_disable_pmr(ctrlr);
384 	}
385 
386 	return -ENOSYS;
387 }
388 
389 void *
390 nvme_transport_ctrlr_map_pmr(struct spdk_nvme_ctrlr *ctrlr, size_t *size)
391 {
392 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
393 
394 	assert(transport != NULL);
395 	if (transport->ops.ctrlr_map_pmr != NULL) {
396 		return transport->ops.ctrlr_map_pmr(ctrlr, size);
397 	}
398 
399 	return NULL;
400 }
401 
402 int
403 nvme_transport_ctrlr_unmap_pmr(struct spdk_nvme_ctrlr *ctrlr)
404 {
405 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
406 
407 	assert(transport != NULL);
408 	if (transport->ops.ctrlr_unmap_pmr != NULL) {
409 		return transport->ops.ctrlr_unmap_pmr(ctrlr);
410 	}
411 
412 	return -ENOSYS;
413 }
414 
415 struct spdk_nvme_qpair *
416 nvme_transport_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
417 				     const struct spdk_nvme_io_qpair_opts *opts)
418 {
419 	struct spdk_nvme_qpair *qpair;
420 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
421 
422 	assert(transport != NULL);
423 	qpair = transport->ops.ctrlr_create_io_qpair(ctrlr, qid, opts);
424 	if (qpair != NULL && !nvme_qpair_is_admin_queue(qpair)) {
425 		qpair->transport = transport;
426 	}
427 
428 	return qpair;
429 }
430 
431 void
432 nvme_transport_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
433 {
434 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
435 	int rc;
436 
437 	assert(transport != NULL);
438 
439 	/* Do not rely on qpair->transport.  For multi-process cases, a foreign process may delete
440 	 * the IO qpair, in which case the transport object would be invalid (each process has their
441 	 * own unique transport objects since they contain function pointers).  So we look up the
442 	 * transport object in the delete_io_qpair case.
443 	 */
444 	rc = transport->ops.ctrlr_delete_io_qpair(ctrlr, qpair);
445 	if (rc != 0) {
446 		SPDK_ERRLOG("transport %s returned non-zero for ctrlr_delete_io_qpair op\n",
447 			    transport->ops.name);
448 		assert(false);
449 	}
450 }
451 
452 static void
453 nvme_transport_connect_qpair_fail(struct spdk_nvme_qpair *qpair, void *unused)
454 {
455 	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
456 
457 	/* If the qpair was unable to reconnect, restore the original failure reason */
458 	qpair->transport_failure_reason = qpair->last_transport_failure_reason;
459 	nvme_transport_ctrlr_disconnect_qpair(ctrlr, qpair);
460 }
461 
462 int
463 nvme_transport_ctrlr_connect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
464 {
465 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
466 	int rc;
467 
468 	assert(transport != NULL);
469 	if (!nvme_qpair_is_admin_queue(qpair)) {
470 		qpair->transport = transport;
471 	}
472 
473 	qpair->last_transport_failure_reason = qpair->transport_failure_reason;
474 	qpair->transport_failure_reason = SPDK_NVME_QPAIR_FAILURE_NONE;
475 
476 	nvme_qpair_set_state(qpair, NVME_QPAIR_CONNECTING);
477 	rc = transport->ops.ctrlr_connect_qpair(ctrlr, qpair);
478 	if (rc != 0) {
479 		goto err;
480 	}
481 
482 	if (qpair->poll_group) {
483 		rc = nvme_poll_group_connect_qpair(qpair);
484 		if (rc) {
485 			goto err;
486 		}
487 	}
488 
489 	if (!qpair->async) {
490 		/* Busy wait until the qpair exits the connecting state */
491 		while (nvme_qpair_get_state(qpair) == NVME_QPAIR_CONNECTING) {
492 			if (qpair->poll_group && spdk_nvme_ctrlr_is_fabrics(ctrlr)) {
493 				rc = spdk_nvme_poll_group_process_completions(
494 					     qpair->poll_group->group, 0,
495 					     nvme_transport_connect_qpair_fail);
496 			} else {
497 				rc = spdk_nvme_qpair_process_completions(qpair, 0);
498 			}
499 
500 			if (rc < 0) {
501 				goto err;
502 			}
503 		}
504 	}
505 
506 	return 0;
507 err:
508 	nvme_transport_connect_qpair_fail(qpair, NULL);
509 	if (nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTING) {
510 		assert(qpair->async == true);
511 		/* Let the caller to poll the qpair until it is actually disconnected. */
512 		return 0;
513 	}
514 
515 	return rc;
516 }
517 
518 void
519 nvme_transport_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
520 {
521 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
522 
523 	if (nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTING ||
524 	    nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTED) {
525 		return;
526 	}
527 
528 	nvme_qpair_set_state(qpair, NVME_QPAIR_DISCONNECTING);
529 	assert(transport != NULL);
530 
531 	if (qpair->poll_group && (qpair->active_proc == nvme_ctrlr_get_current_process(ctrlr))) {
532 		nvme_poll_group_disconnect_qpair(qpair);
533 	}
534 
535 	transport->ops.ctrlr_disconnect_qpair(ctrlr, qpair);
536 }
537 
538 void
539 nvme_transport_ctrlr_disconnect_qpair_done(struct spdk_nvme_qpair *qpair)
540 {
541 	if (qpair->active_proc == nvme_ctrlr_get_current_process(qpair->ctrlr) ||
542 	    nvme_qpair_is_admin_queue(qpair)) {
543 		nvme_qpair_abort_all_queued_reqs(qpair);
544 	}
545 	nvme_qpair_set_state(qpair, NVME_QPAIR_DISCONNECTED);
546 }
547 
548 int
549 nvme_transport_ctrlr_get_memory_domains(const struct spdk_nvme_ctrlr *ctrlr,
550 					struct spdk_memory_domain **domains, int array_size)
551 {
552 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
553 
554 	assert(transport != NULL);
555 	if (transport->ops.ctrlr_get_memory_domains) {
556 		return transport->ops.ctrlr_get_memory_domains(ctrlr, domains, array_size);
557 	}
558 
559 	return 0;
560 }
561 
562 void
563 nvme_transport_qpair_abort_reqs(struct spdk_nvme_qpair *qpair)
564 {
565 	const struct spdk_nvme_transport *transport;
566 
567 	if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
568 		qpair->transport->ops.qpair_abort_reqs(qpair, qpair->abort_dnr);
569 	} else {
570 		transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
571 		assert(transport != NULL);
572 		transport->ops.qpair_abort_reqs(qpair, qpair->abort_dnr);
573 	}
574 }
575 
576 int
577 nvme_transport_qpair_reset(struct spdk_nvme_qpair *qpair)
578 {
579 	const struct spdk_nvme_transport *transport;
580 
581 	if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
582 		return qpair->transport->ops.qpair_reset(qpair);
583 	}
584 
585 	transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
586 	assert(transport != NULL);
587 	return transport->ops.qpair_reset(qpair);
588 }
589 
590 int
591 nvme_transport_qpair_submit_request(struct spdk_nvme_qpair *qpair, struct nvme_request *req)
592 {
593 	const struct spdk_nvme_transport *transport;
594 
595 	if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
596 		return qpair->transport->ops.qpair_submit_request(qpair, req);
597 	}
598 
599 	transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
600 	assert(transport != NULL);
601 	return transport->ops.qpair_submit_request(qpair, req);
602 }
603 
604 int32_t
605 nvme_transport_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
606 {
607 	const struct spdk_nvme_transport *transport;
608 
609 	if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
610 		return qpair->transport->ops.qpair_process_completions(qpair, max_completions);
611 	}
612 
613 	transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
614 	assert(transport != NULL);
615 	return transport->ops.qpair_process_completions(qpair, max_completions);
616 }
617 
618 int
619 nvme_transport_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
620 				      int (*iter_fn)(struct nvme_request *req, void *arg),
621 				      void *arg)
622 {
623 	const struct spdk_nvme_transport *transport;
624 
625 	if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
626 		return qpair->transport->ops.qpair_iterate_requests(qpair, iter_fn, arg);
627 	}
628 
629 	transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
630 	assert(transport != NULL);
631 	return transport->ops.qpair_iterate_requests(qpair, iter_fn, arg);
632 }
633 
634 void
635 nvme_transport_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
636 {
637 	const struct spdk_nvme_transport *transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
638 
639 	assert(transport != NULL);
640 	transport->ops.admin_qpair_abort_aers(qpair);
641 }
642 
643 struct spdk_nvme_transport_poll_group *
644 nvme_transport_poll_group_create(const struct spdk_nvme_transport *transport)
645 {
646 	struct spdk_nvme_transport_poll_group *group = NULL;
647 
648 	group = transport->ops.poll_group_create();
649 	if (group) {
650 		group->transport = transport;
651 		STAILQ_INIT(&group->connected_qpairs);
652 		STAILQ_INIT(&group->disconnected_qpairs);
653 		group->num_connected_qpairs = 0;
654 	}
655 
656 	return group;
657 }
658 
659 struct spdk_nvme_transport_poll_group *
660 nvme_transport_qpair_get_optimal_poll_group(const struct spdk_nvme_transport *transport,
661 		struct spdk_nvme_qpair *qpair)
662 {
663 	if (transport->ops.qpair_get_optimal_poll_group) {
664 		return transport->ops.qpair_get_optimal_poll_group(qpair);
665 	} else {
666 		return NULL;
667 	}
668 }
669 
670 int
671 nvme_transport_poll_group_add(struct spdk_nvme_transport_poll_group *tgroup,
672 			      struct spdk_nvme_qpair *qpair)
673 {
674 	int rc;
675 
676 	rc = tgroup->transport->ops.poll_group_add(tgroup, qpair);
677 	if (rc == 0) {
678 		qpair->poll_group = tgroup;
679 		assert(nvme_qpair_get_state(qpair) < NVME_QPAIR_CONNECTED);
680 		qpair->poll_group_tailq_head = &tgroup->disconnected_qpairs;
681 		STAILQ_INSERT_TAIL(&tgroup->disconnected_qpairs, qpair, poll_group_stailq);
682 	}
683 
684 	return rc;
685 }
686 
687 int
688 nvme_transport_poll_group_remove(struct spdk_nvme_transport_poll_group *tgroup,
689 				 struct spdk_nvme_qpair *qpair)
690 {
691 	int rc __attribute__((unused));
692 
693 	if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
694 		return -EINVAL;
695 	} else if (qpair->poll_group_tailq_head != &tgroup->disconnected_qpairs) {
696 		return -ENOENT;
697 	}
698 
699 	rc = tgroup->transport->ops.poll_group_remove(tgroup, qpair);
700 	assert(rc == 0);
701 
702 	STAILQ_REMOVE(&tgroup->disconnected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
703 
704 	qpair->poll_group = NULL;
705 	qpair->poll_group_tailq_head = NULL;
706 
707 	return 0;
708 }
709 
710 int64_t
711 nvme_transport_poll_group_process_completions(struct spdk_nvme_transport_poll_group *tgroup,
712 		uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
713 {
714 	return tgroup->transport->ops.poll_group_process_completions(tgroup, completions_per_qpair,
715 			disconnected_qpair_cb);
716 }
717 
718 int
719 nvme_transport_poll_group_destroy(struct spdk_nvme_transport_poll_group *tgroup)
720 {
721 	return tgroup->transport->ops.poll_group_destroy(tgroup);
722 }
723 
724 int
725 nvme_transport_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
726 {
727 	struct spdk_nvme_transport_poll_group *tgroup;
728 	int rc __attribute__((unused));
729 
730 	tgroup = qpair->poll_group;
731 
732 	if (qpair->poll_group_tailq_head == &tgroup->disconnected_qpairs) {
733 		return 0;
734 	}
735 
736 	if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
737 		rc = tgroup->transport->ops.poll_group_disconnect_qpair(qpair);
738 		assert(rc == 0);
739 
740 		qpair->poll_group_tailq_head = &tgroup->disconnected_qpairs;
741 		STAILQ_REMOVE(&tgroup->connected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
742 		assert(tgroup->num_connected_qpairs > 0);
743 		tgroup->num_connected_qpairs--;
744 		STAILQ_INSERT_TAIL(&tgroup->disconnected_qpairs, qpair, poll_group_stailq);
745 
746 		return 0;
747 	}
748 
749 	return -EINVAL;
750 }
751 
752 int
753 nvme_transport_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
754 {
755 	struct spdk_nvme_transport_poll_group *tgroup;
756 	int rc;
757 
758 	tgroup = qpair->poll_group;
759 
760 	if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
761 		return 0;
762 	}
763 
764 	if (qpair->poll_group_tailq_head == &tgroup->disconnected_qpairs) {
765 		rc = tgroup->transport->ops.poll_group_connect_qpair(qpair);
766 		if (rc == 0) {
767 			qpair->poll_group_tailq_head = &tgroup->connected_qpairs;
768 			STAILQ_REMOVE(&tgroup->disconnected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
769 			STAILQ_INSERT_TAIL(&tgroup->connected_qpairs, qpair, poll_group_stailq);
770 			tgroup->num_connected_qpairs++;
771 		}
772 
773 		return rc == -EINPROGRESS ? 0 : rc;
774 	}
775 
776 
777 	return -EINVAL;
778 }
779 
780 int
781 nvme_transport_poll_group_get_stats(struct spdk_nvme_transport_poll_group *tgroup,
782 				    struct spdk_nvme_transport_poll_group_stat **stats)
783 {
784 	if (tgroup->transport->ops.poll_group_get_stats) {
785 		return tgroup->transport->ops.poll_group_get_stats(tgroup, stats);
786 	}
787 	return -ENOTSUP;
788 }
789 
790 void
791 nvme_transport_poll_group_free_stats(struct spdk_nvme_transport_poll_group *tgroup,
792 				     struct spdk_nvme_transport_poll_group_stat *stats)
793 {
794 	if (tgroup->transport->ops.poll_group_free_stats) {
795 		tgroup->transport->ops.poll_group_free_stats(tgroup, stats);
796 	}
797 }
798 
799 spdk_nvme_transport_type_t
800 nvme_transport_get_trtype(const struct spdk_nvme_transport *transport)
801 {
802 	return transport->ops.type;
803 }
804 
805 void
806 spdk_nvme_transport_get_opts(struct spdk_nvme_transport_opts *opts, size_t opts_size)
807 {
808 	if (opts == NULL) {
809 		SPDK_ERRLOG("opts should not be NULL.\n");
810 		return;
811 	}
812 
813 	if (opts_size == 0) {
814 		SPDK_ERRLOG("opts_size should not be zero.\n");
815 		return;
816 	}
817 
818 	opts->opts_size = opts_size;
819 
820 #define SET_FIELD(field) \
821 	if (offsetof(struct spdk_nvme_transport_opts, field) + sizeof(opts->field) <= opts_size) { \
822 		opts->field = g_spdk_nvme_transport_opts.field; \
823 	} \
824 
825 	SET_FIELD(rdma_srq_size);
826 	SET_FIELD(rdma_max_cq_size);
827 
828 	/* Do not remove this statement, you should always update this statement when you adding a new field,
829 	 * and do not forget to add the SET_FIELD statement for your added field. */
830 	SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_transport_opts) == 24, "Incorrect size");
831 
832 #undef SET_FIELD
833 }
834 
835 int
836 spdk_nvme_transport_set_opts(const struct spdk_nvme_transport_opts *opts, size_t opts_size)
837 {
838 	if (opts == NULL) {
839 		SPDK_ERRLOG("opts should not be NULL.\n");
840 		return -EINVAL;
841 	}
842 
843 	if (opts_size == 0) {
844 		SPDK_ERRLOG("opts_size should not be zero.\n");
845 		return -EINVAL;
846 	}
847 
848 #define SET_FIELD(field) \
849 	if (offsetof(struct spdk_nvme_transport_opts, field) + sizeof(opts->field) <= opts->opts_size) { \
850 		g_spdk_nvme_transport_opts.field = opts->field; \
851 	} \
852 
853 	SET_FIELD(rdma_srq_size);
854 	SET_FIELD(rdma_max_cq_size);
855 
856 	g_spdk_nvme_transport_opts.opts_size = opts->opts_size;
857 
858 #undef SET_FIELD
859 
860 	return 0;
861 }
862 
863 volatile struct spdk_nvme_registers *
864 spdk_nvme_ctrlr_get_registers(struct spdk_nvme_ctrlr *ctrlr)
865 {
866 	const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
867 
868 	if (transport == NULL) {
869 		/* Transport does not exist. */
870 		return NULL;
871 	}
872 
873 	if (transport->ops.ctrlr_get_registers) {
874 		return transport->ops.ctrlr_get_registers(ctrlr);
875 	}
876 
877 	return NULL;
878 }
879