xref: /spdk/lib/nvme/nvme_fabric.c (revision 1078198e78653b2f39414c1566740018d76ee73d)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2018 Intel Corporation. All rights reserved.
3  *   Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
4  *   Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5  */
6 
7 /*
8  * NVMe over Fabrics transport-independent functions
9  */
10 
11 #include "nvme_internal.h"
12 
13 #include "spdk/endian.h"
14 #include "spdk/string.h"
15 
16 struct nvme_fabric_prop_ctx {
17 	uint64_t		value;
18 	int			size;
19 	spdk_nvme_reg_cb	cb_fn;
20 	void			*cb_arg;
21 };
22 
23 static int
24 nvme_fabric_prop_set_cmd(struct spdk_nvme_ctrlr *ctrlr,
25 			 uint32_t offset, uint8_t size, uint64_t value,
26 			 spdk_nvme_cmd_cb cb_fn, void *cb_arg)
27 {
28 	struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
29 
30 	assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
31 
32 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
33 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET;
34 	cmd.ofst = offset;
35 	cmd.attrib.size = size;
36 	cmd.value.u64 = value;
37 
38 	return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
39 					     NULL, 0, cb_fn, cb_arg);
40 }
41 
42 static int
43 nvme_fabric_prop_set_cmd_sync(struct spdk_nvme_ctrlr *ctrlr,
44 			      uint32_t offset, uint8_t size, uint64_t value)
45 {
46 	struct nvme_completion_poll_status *status;
47 	int rc;
48 
49 	status = calloc(1, sizeof(*status));
50 	if (!status) {
51 		SPDK_ERRLOG("Failed to allocate status tracker\n");
52 		return -ENOMEM;
53 	}
54 
55 	rc = nvme_fabric_prop_set_cmd(ctrlr, offset, size, value,
56 				      nvme_completion_poll_cb, status);
57 	if (rc < 0) {
58 		free(status);
59 		return rc;
60 	}
61 
62 	if (nvme_wait_for_completion_robust_lock(ctrlr->adminq, status, &ctrlr->ctrlr_lock)) {
63 		if (!status->timed_out) {
64 			free(status);
65 		}
66 		SPDK_ERRLOG("Property Set failed\n");
67 		return -1;
68 	}
69 	free(status);
70 
71 	return 0;
72 }
73 
74 static void
75 nvme_fabric_prop_set_cmd_done(void *ctx, const struct spdk_nvme_cpl *cpl)
76 {
77 	struct nvme_fabric_prop_ctx *prop_ctx = ctx;
78 
79 	prop_ctx->cb_fn(prop_ctx->cb_arg, prop_ctx->value, cpl);
80 	free(prop_ctx);
81 }
82 
83 static int
84 nvme_fabric_prop_set_cmd_async(struct spdk_nvme_ctrlr *ctrlr,
85 			       uint32_t offset, uint8_t size, uint64_t value,
86 			       spdk_nvme_reg_cb cb_fn, void *cb_arg)
87 {
88 	struct nvme_fabric_prop_ctx *ctx;
89 	int rc;
90 
91 	ctx = calloc(1, sizeof(*ctx));
92 	if (ctx == NULL) {
93 		SPDK_ERRLOG("Failed to allocate fabrics property context\n");
94 		return -ENOMEM;
95 	}
96 
97 	ctx->value = value;
98 	ctx->cb_fn = cb_fn;
99 	ctx->cb_arg = cb_arg;
100 
101 	rc = nvme_fabric_prop_set_cmd(ctrlr, offset, size, value,
102 				      nvme_fabric_prop_set_cmd_done, ctx);
103 	if (rc != 0) {
104 		SPDK_ERRLOG("Failed to send Property Set fabrics command\n");
105 		free(ctx);
106 	}
107 
108 	return rc;
109 }
110 
111 static int
112 nvme_fabric_prop_get_cmd(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint8_t size,
113 			 spdk_nvme_cmd_cb cb_fn, void *cb_arg)
114 {
115 	struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
116 
117 	assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
118 
119 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
120 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET;
121 	cmd.ofst = offset;
122 	cmd.attrib.size = size;
123 
124 	return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
125 					     NULL, 0, cb_fn, cb_arg);
126 }
127 
128 static int
129 nvme_fabric_prop_get_cmd_sync(struct spdk_nvme_ctrlr *ctrlr,
130 			      uint32_t offset, uint8_t size, uint64_t *value)
131 {
132 	struct nvme_completion_poll_status *status;
133 	struct spdk_nvmf_fabric_prop_get_rsp *response;
134 	int rc;
135 
136 	status = calloc(1, sizeof(*status));
137 	if (!status) {
138 		SPDK_ERRLOG("Failed to allocate status tracker\n");
139 		return -ENOMEM;
140 	}
141 
142 	rc = nvme_fabric_prop_get_cmd(ctrlr, offset, size, nvme_completion_poll_cb, status);
143 	if (rc < 0) {
144 		free(status);
145 		return rc;
146 	}
147 
148 	if (nvme_wait_for_completion_robust_lock(ctrlr->adminq, status, &ctrlr->ctrlr_lock)) {
149 		if (!status->timed_out) {
150 			free(status);
151 		}
152 		SPDK_ERRLOG("Property Get failed\n");
153 		return -1;
154 	}
155 
156 	response = (struct spdk_nvmf_fabric_prop_get_rsp *)&status->cpl;
157 
158 	if (size == SPDK_NVMF_PROP_SIZE_4) {
159 		*value = response->value.u32.low;
160 	} else {
161 		*value = response->value.u64;
162 	}
163 
164 	free(status);
165 
166 	return 0;
167 }
168 
169 static void
170 nvme_fabric_prop_get_cmd_done(void *ctx, const struct spdk_nvme_cpl *cpl)
171 {
172 	struct nvme_fabric_prop_ctx *prop_ctx = ctx;
173 	struct spdk_nvmf_fabric_prop_get_rsp *response;
174 	uint64_t value = 0;
175 
176 	if (spdk_nvme_cpl_is_success(cpl)) {
177 		response = (struct spdk_nvmf_fabric_prop_get_rsp *)cpl;
178 
179 		switch (prop_ctx->size) {
180 		case SPDK_NVMF_PROP_SIZE_4:
181 			value = response->value.u32.low;
182 			break;
183 		case SPDK_NVMF_PROP_SIZE_8:
184 			value = response->value.u64;
185 			break;
186 		default:
187 			assert(0 && "Should never happen");
188 		}
189 	}
190 
191 	prop_ctx->cb_fn(prop_ctx->cb_arg, value, cpl);
192 	free(prop_ctx);
193 }
194 
195 static int
196 nvme_fabric_prop_get_cmd_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint8_t size,
197 			       spdk_nvme_reg_cb cb_fn, void *cb_arg)
198 {
199 	struct nvme_fabric_prop_ctx *ctx;
200 	int rc;
201 
202 	ctx = calloc(1, sizeof(*ctx));
203 	if (ctx == NULL) {
204 		SPDK_ERRLOG("Failed to allocate fabrics property context\n");
205 		return -ENOMEM;
206 	}
207 
208 	ctx->size = size;
209 	ctx->cb_fn = cb_fn;
210 	ctx->cb_arg = cb_arg;
211 
212 	rc = nvme_fabric_prop_get_cmd(ctrlr, offset, size, nvme_fabric_prop_get_cmd_done, ctx);
213 	if (rc != 0) {
214 		SPDK_ERRLOG("Failed to send Property Get fabrics command\n");
215 		free(ctx);
216 	}
217 
218 	return rc;
219 }
220 
221 int
222 nvme_fabric_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
223 {
224 	return nvme_fabric_prop_set_cmd_sync(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value);
225 }
226 
227 int
228 nvme_fabric_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
229 {
230 	return nvme_fabric_prop_set_cmd_sync(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
231 }
232 
233 int
234 nvme_fabric_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
235 {
236 	uint64_t tmp_value;
237 	int rc;
238 	rc = nvme_fabric_prop_get_cmd_sync(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, &tmp_value);
239 
240 	if (!rc) {
241 		*value = (uint32_t)tmp_value;
242 	}
243 	return rc;
244 }
245 
246 int
247 nvme_fabric_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
248 {
249 	return nvme_fabric_prop_get_cmd_sync(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
250 }
251 
252 int
253 nvme_fabric_ctrlr_set_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
254 				  uint32_t value, spdk_nvme_reg_cb cb_fn, void *cb_arg)
255 {
256 	return nvme_fabric_prop_set_cmd_async(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value,
257 					      cb_fn, cb_arg);
258 }
259 
260 int
261 nvme_fabric_ctrlr_set_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
262 				  uint64_t value, spdk_nvme_reg_cb cb_fn, void *cb_arg)
263 {
264 	return nvme_fabric_prop_set_cmd_async(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value,
265 					      cb_fn, cb_arg);
266 }
267 
268 int
269 nvme_fabric_ctrlr_get_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
270 				  spdk_nvme_reg_cb cb_fn, void *cb_arg)
271 {
272 	return nvme_fabric_prop_get_cmd_async(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, cb_fn, cb_arg);
273 }
274 
275 int
276 nvme_fabric_ctrlr_get_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
277 				  spdk_nvme_reg_cb cb_fn, void *cb_arg)
278 {
279 	return nvme_fabric_prop_get_cmd_async(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, cb_fn, cb_arg);
280 }
281 
282 static void
283 nvme_fabric_discover_probe(struct spdk_nvmf_discovery_log_page_entry *entry,
284 			   struct spdk_nvme_probe_ctx *probe_ctx,
285 			   int discover_priority)
286 {
287 	struct spdk_nvme_transport_id trid;
288 	uint8_t *end;
289 	size_t len;
290 
291 	memset(&trid, 0, sizeof(trid));
292 
293 	if (entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY_CURRENT ||
294 	    entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
295 		SPDK_WARNLOG("Skipping unsupported current discovery service or"
296 			     " discovery service referral\n");
297 		return;
298 	} else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) {
299 		SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype);
300 		return;
301 	}
302 
303 	trid.trtype = entry->trtype;
304 	spdk_nvme_transport_id_populate_trstring(&trid, spdk_nvme_transport_id_trtype_str(entry->trtype));
305 	if (!spdk_nvme_transport_available_by_name(trid.trstring)) {
306 		SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n",
307 			     trid.trtype);
308 		return;
309 	}
310 
311 	trid.adrfam = entry->adrfam;
312 
313 	/* Ensure that subnqn is null terminated. */
314 	end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1);
315 	if (!end) {
316 		SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n");
317 		return;
318 	}
319 	len = end - entry->subnqn;
320 	memcpy(trid.subnqn, entry->subnqn, len);
321 	trid.subnqn[len] = '\0';
322 
323 	/* Convert traddr to a null terminated string. */
324 	len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' ');
325 	memcpy(trid.traddr, entry->traddr, len);
326 	if (spdk_str_chomp(trid.traddr) != 0) {
327 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRADDR\n");
328 	}
329 
330 	/* Convert trsvcid to a null terminated string. */
331 	len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' ');
332 	memcpy(trid.trsvcid, entry->trsvcid, len);
333 	if (spdk_str_chomp(trid.trsvcid) != 0) {
334 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRSVCID\n");
335 	}
336 
337 	SPDK_DEBUGLOG(nvme, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n",
338 		      trid.subnqn, trid.trtype,
339 		      trid.traddr, trid.trsvcid);
340 
341 	/* Copy the priority from the discovery ctrlr */
342 	trid.priority = discover_priority;
343 
344 	nvme_ctrlr_probe(&trid, probe_ctx, NULL);
345 }
346 
347 static int
348 nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
349 				   void *log_page, uint32_t size, uint64_t offset)
350 {
351 	struct nvme_completion_poll_status *status;
352 	int rc;
353 
354 	status = calloc(1, sizeof(*status));
355 	if (!status) {
356 		SPDK_ERRLOG("Failed to allocate status tracker\n");
357 		return -ENOMEM;
358 	}
359 
360 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset,
361 					      nvme_completion_poll_cb, status);
362 	if (rc < 0) {
363 		free(status);
364 		return -1;
365 	}
366 
367 	if (nvme_wait_for_completion(ctrlr->adminq, status)) {
368 		if (!status->timed_out) {
369 			free(status);
370 		}
371 		return -1;
372 	}
373 	free(status);
374 
375 	return 0;
376 }
377 
378 int
379 nvme_fabric_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
380 		       bool direct_connect)
381 {
382 	struct spdk_nvme_ctrlr_opts discovery_opts;
383 	struct spdk_nvme_ctrlr *discovery_ctrlr;
384 	int rc;
385 	struct nvme_completion_poll_status *status;
386 
387 	if (strcmp(probe_ctx->trid.subnqn, SPDK_NVMF_DISCOVERY_NQN) != 0) {
388 		/* It is not a discovery_ctrlr info and try to directly connect it */
389 		rc = nvme_ctrlr_probe(&probe_ctx->trid, probe_ctx, NULL);
390 		return rc;
391 	}
392 
393 	spdk_nvme_ctrlr_get_default_ctrlr_opts(&discovery_opts, sizeof(discovery_opts));
394 	if (direct_connect && probe_ctx->probe_cb) {
395 		probe_ctx->probe_cb(probe_ctx->cb_ctx, &probe_ctx->trid, &discovery_opts);
396 	}
397 
398 	discovery_ctrlr = nvme_transport_ctrlr_construct(&probe_ctx->trid, &discovery_opts, NULL);
399 	if (discovery_ctrlr == NULL) {
400 		return -1;
401 	}
402 
403 	while (discovery_ctrlr->state != NVME_CTRLR_STATE_READY) {
404 		if (nvme_ctrlr_process_init(discovery_ctrlr) != 0) {
405 			nvme_ctrlr_destruct(discovery_ctrlr);
406 			return -1;
407 		}
408 	}
409 
410 	status = calloc(1, sizeof(*status));
411 	if (!status) {
412 		SPDK_ERRLOG("Failed to allocate status tracker\n");
413 		nvme_ctrlr_destruct(discovery_ctrlr);
414 		return -ENOMEM;
415 	}
416 
417 	/* get the cdata info */
418 	rc = nvme_ctrlr_cmd_identify(discovery_ctrlr, SPDK_NVME_IDENTIFY_CTRLR, 0, 0, 0,
419 				     &discovery_ctrlr->cdata, sizeof(discovery_ctrlr->cdata),
420 				     nvme_completion_poll_cb, status);
421 	if (rc != 0) {
422 		SPDK_ERRLOG("Failed to identify cdata\n");
423 		nvme_ctrlr_destruct(discovery_ctrlr);
424 		free(status);
425 		return rc;
426 	}
427 
428 	if (nvme_wait_for_completion(discovery_ctrlr->adminq, status)) {
429 		SPDK_ERRLOG("nvme_identify_controller failed!\n");
430 		nvme_ctrlr_destruct(discovery_ctrlr);
431 		if (!status->timed_out) {
432 			free(status);
433 		}
434 		return -ENXIO;
435 	}
436 
437 	free(status);
438 
439 	/* Direct attach through spdk_nvme_connect() API */
440 	if (direct_connect == true) {
441 		/* Set the ready state to skip the normal init process */
442 		discovery_ctrlr->state = NVME_CTRLR_STATE_READY;
443 		nvme_ctrlr_connected(probe_ctx, discovery_ctrlr);
444 		nvme_ctrlr_add_process(discovery_ctrlr, 0);
445 		return 0;
446 	}
447 
448 	rc = nvme_fabric_ctrlr_discover(discovery_ctrlr, probe_ctx);
449 	nvme_ctrlr_destruct(discovery_ctrlr);
450 	return rc;
451 }
452 
453 int
454 nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr,
455 			   struct spdk_nvme_probe_ctx *probe_ctx)
456 {
457 	struct spdk_nvmf_discovery_log_page *log_page;
458 	struct spdk_nvmf_discovery_log_page_entry *log_page_entry;
459 	char buffer[4096];
460 	int rc;
461 	uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0;
462 	uint64_t remaining_num_rec = 0;
463 	uint16_t recfmt;
464 
465 	memset(buffer, 0x0, 4096);
466 	buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page,
467 				    entries[0])) /
468 				   sizeof(struct spdk_nvmf_discovery_log_page_entry);
469 	buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry);
470 	do {
471 		rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset);
472 		if (rc < 0) {
473 			SPDK_DEBUGLOG(nvme, "Get Log Page - Discovery error\n");
474 			return rc;
475 		}
476 
477 		if (!remaining_num_rec) {
478 			log_page = (struct spdk_nvmf_discovery_log_page *)buffer;
479 			recfmt = from_le16(&log_page->recfmt);
480 			if (recfmt != 0) {
481 				SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
482 				return -EPROTO;
483 			}
484 			remaining_num_rec = log_page->numrec;
485 			log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]);
486 			log_page_entry = &log_page->entries[0];
487 			numrec = spdk_min(remaining_num_rec, buffer_max_entries_first);
488 		} else {
489 			numrec = spdk_min(remaining_num_rec, buffer_max_entries);
490 			log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer;
491 		}
492 
493 		for (i = 0; i < numrec; i++) {
494 			nvme_fabric_discover_probe(log_page_entry++, probe_ctx, ctrlr->trid.priority);
495 		}
496 		remaining_num_rec -= numrec;
497 		log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
498 	} while (remaining_num_rec != 0);
499 
500 	return 0;
501 }
502 
503 int
504 nvme_fabric_qpair_connect_async(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
505 {
506 	struct nvme_completion_poll_status *status;
507 	struct spdk_nvmf_fabric_connect_cmd cmd;
508 	struct spdk_nvmf_fabric_connect_data *nvmf_data;
509 	struct spdk_nvme_ctrlr *ctrlr;
510 	struct nvme_request *req;
511 	int rc;
512 
513 	if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) {
514 		return -EINVAL;
515 	}
516 
517 	ctrlr = qpair->ctrlr;
518 	if (!ctrlr) {
519 		return -EINVAL;
520 	}
521 
522 	nvmf_data = spdk_zmalloc(sizeof(*nvmf_data), 0, NULL,
523 				 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
524 	if (!nvmf_data) {
525 		SPDK_ERRLOG("nvmf_data allocation error\n");
526 		return -ENOMEM;
527 	}
528 
529 	status = calloc(1, sizeof(*status));
530 	if (!status) {
531 		SPDK_ERRLOG("Failed to allocate status tracker\n");
532 		spdk_free(nvmf_data);
533 		return -ENOMEM;
534 	}
535 
536 	status->dma_data = nvmf_data;
537 
538 	memset(&cmd, 0, sizeof(cmd));
539 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
540 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT;
541 	cmd.qid = qpair->id;
542 	cmd.sqsize = num_entries - 1;
543 	cmd.kato = ctrlr->opts.keep_alive_timeout_ms;
544 
545 	assert(qpair->reserved_req != NULL);
546 	req = qpair->reserved_req;
547 	memcpy(&req->cmd, &cmd, sizeof(cmd));
548 
549 	if (nvme_qpair_is_admin_queue(qpair)) {
550 		nvmf_data->cntlid = 0xFFFF;
551 	} else {
552 		nvmf_data->cntlid = ctrlr->cntlid;
553 	}
554 
555 	SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id),
556 			   "host ID size mismatch");
557 	memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid));
558 	snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn);
559 	snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn);
560 
561 	NVME_INIT_REQUEST(req, nvme_completion_poll_cb, status, NVME_PAYLOAD_CONTIG(nvmf_data, NULL),
562 			  sizeof(*nvmf_data), 0);
563 
564 	rc = nvme_qpair_submit_request(qpair, req);
565 	if (rc < 0) {
566 		SPDK_ERRLOG("Failed to allocate/submit FABRIC_CONNECT command, rc %d\n", rc);
567 		spdk_free(status->dma_data);
568 		free(status);
569 		return rc;
570 	}
571 
572 	/* If we time out, the qpair will abort the request upon destruction. */
573 	if (ctrlr->opts.fabrics_connect_timeout_us > 0) {
574 		status->timeout_tsc = spdk_get_ticks() + ctrlr->opts.fabrics_connect_timeout_us *
575 				      spdk_get_ticks_hz() / SPDK_SEC_TO_USEC;
576 	}
577 
578 	qpair->poll_status = status;
579 	return 0;
580 }
581 
582 int
583 nvme_fabric_qpair_connect_poll(struct spdk_nvme_qpair *qpair)
584 {
585 	struct nvme_completion_poll_status *status;
586 	struct spdk_nvmf_fabric_connect_rsp *rsp;
587 	struct spdk_nvme_ctrlr *ctrlr;
588 	int rc = 0;
589 
590 	ctrlr = qpair->ctrlr;
591 	status = qpair->poll_status;
592 
593 	if (nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL) == -EAGAIN) {
594 		return -EAGAIN;
595 	}
596 
597 	if (status->timed_out || spdk_nvme_cpl_is_error(&status->cpl)) {
598 		SPDK_ERRLOG("Connect command failed, rc %d, trtype:%s adrfam:%s "
599 			    "traddr:%s trsvcid:%s subnqn:%s\n",
600 			    status->timed_out ? -ECANCELED : -EIO,
601 			    spdk_nvme_transport_id_trtype_str(ctrlr->trid.trtype),
602 			    spdk_nvme_transport_id_adrfam_str(ctrlr->trid.adrfam),
603 			    ctrlr->trid.traddr,
604 			    ctrlr->trid.trsvcid,
605 			    ctrlr->trid.subnqn);
606 		if (status->timed_out) {
607 			rc = -ECANCELED;
608 		} else {
609 			SPDK_ERRLOG("Connect command completed with error: sct %d, sc %d\n",
610 				    status->cpl.status.sct, status->cpl.status.sc);
611 			rc = -EIO;
612 		}
613 
614 		goto finish;
615 	}
616 
617 	if (nvme_qpair_is_admin_queue(qpair)) {
618 		rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status->cpl;
619 		ctrlr->cntlid = rsp->status_code_specific.success.cntlid;
620 		SPDK_DEBUGLOG(nvme, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid);
621 	}
622 finish:
623 	qpair->poll_status = NULL;
624 	if (!status->timed_out) {
625 		spdk_free(status->dma_data);
626 		free(status);
627 	}
628 
629 	return rc;
630 }
631 
632 int
633 nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
634 {
635 	int rc;
636 
637 	rc = nvme_fabric_qpair_connect_async(qpair, num_entries);
638 	if (rc) {
639 		return rc;
640 	}
641 
642 	do {
643 		/* Wait until the command completes or times out */
644 		rc = nvme_fabric_qpair_connect_poll(qpair);
645 	} while (rc == -EAGAIN);
646 
647 	return rc;
648 }
649