xref: /spdk/lib/nvme/nvme_fabric.c (revision 806744b7c81d91effc0d64616d26527fe34f35b7)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) 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) {
294 		SPDK_WARNLOG("Skipping unsupported discovery service referral\n");
295 		return;
296 	} else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) {
297 		SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype);
298 		return;
299 	}
300 
301 	trid.trtype = entry->trtype;
302 	spdk_nvme_transport_id_populate_trstring(&trid, spdk_nvme_transport_id_trtype_str(entry->trtype));
303 	if (!spdk_nvme_transport_available_by_name(trid.trstring)) {
304 		SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n",
305 			     trid.trtype);
306 		return;
307 	}
308 
309 	trid.adrfam = entry->adrfam;
310 
311 	/* Ensure that subnqn is null terminated. */
312 	end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1);
313 	if (!end) {
314 		SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n");
315 		return;
316 	}
317 	len = end - entry->subnqn;
318 	memcpy(trid.subnqn, entry->subnqn, len);
319 	trid.subnqn[len] = '\0';
320 
321 	/* Convert traddr to a null terminated string. */
322 	len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' ');
323 	memcpy(trid.traddr, entry->traddr, len);
324 	if (spdk_str_chomp(trid.traddr) != 0) {
325 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRADDR\n");
326 	}
327 
328 	/* Convert trsvcid to a null terminated string. */
329 	len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' ');
330 	memcpy(trid.trsvcid, entry->trsvcid, len);
331 	if (spdk_str_chomp(trid.trsvcid) != 0) {
332 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRSVCID\n");
333 	}
334 
335 	SPDK_DEBUGLOG(nvme, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n",
336 		      trid.subnqn, trid.trtype,
337 		      trid.traddr, trid.trsvcid);
338 
339 	/* Copy the priority from the discovery ctrlr */
340 	trid.priority = discover_priority;
341 
342 	nvme_ctrlr_probe(&trid, probe_ctx, NULL);
343 }
344 
345 static int
346 nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
347 				   void *log_page, uint32_t size, uint64_t offset)
348 {
349 	struct nvme_completion_poll_status *status;
350 	int rc;
351 
352 	status = calloc(1, sizeof(*status));
353 	if (!status) {
354 		SPDK_ERRLOG("Failed to allocate status tracker\n");
355 		return -ENOMEM;
356 	}
357 
358 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset,
359 					      nvme_completion_poll_cb, status);
360 	if (rc < 0) {
361 		free(status);
362 		return -1;
363 	}
364 
365 	if (nvme_wait_for_completion(ctrlr->adminq, status)) {
366 		if (!status->timed_out) {
367 			free(status);
368 		}
369 		return -1;
370 	}
371 	free(status);
372 
373 	return 0;
374 }
375 
376 int
377 nvme_fabric_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
378 		       bool direct_connect)
379 {
380 	struct spdk_nvme_ctrlr_opts discovery_opts;
381 	struct spdk_nvme_ctrlr *discovery_ctrlr;
382 	int rc;
383 	struct nvme_completion_poll_status *status;
384 
385 	if (strcmp(probe_ctx->trid.subnqn, SPDK_NVMF_DISCOVERY_NQN) != 0) {
386 		/* It is not a discovery_ctrlr info and try to directly connect it */
387 		rc = nvme_ctrlr_probe(&probe_ctx->trid, probe_ctx, NULL);
388 		return rc;
389 	}
390 
391 	spdk_nvme_ctrlr_get_default_ctrlr_opts(&discovery_opts, sizeof(discovery_opts));
392 	if (direct_connect && probe_ctx->probe_cb) {
393 		probe_ctx->probe_cb(probe_ctx->cb_ctx, &probe_ctx->trid, &discovery_opts);
394 	}
395 
396 	discovery_ctrlr = nvme_transport_ctrlr_construct(&probe_ctx->trid, &discovery_opts, NULL);
397 	if (discovery_ctrlr == NULL) {
398 		return -1;
399 	}
400 
401 	while (discovery_ctrlr->state != NVME_CTRLR_STATE_READY) {
402 		if (nvme_ctrlr_process_init(discovery_ctrlr) != 0) {
403 			nvme_ctrlr_destruct(discovery_ctrlr);
404 			return -1;
405 		}
406 	}
407 
408 	status = calloc(1, sizeof(*status));
409 	if (!status) {
410 		SPDK_ERRLOG("Failed to allocate status tracker\n");
411 		nvme_ctrlr_destruct(discovery_ctrlr);
412 		return -ENOMEM;
413 	}
414 
415 	/* get the cdata info */
416 	rc = nvme_ctrlr_cmd_identify(discovery_ctrlr, SPDK_NVME_IDENTIFY_CTRLR, 0, 0, 0,
417 				     &discovery_ctrlr->cdata, sizeof(discovery_ctrlr->cdata),
418 				     nvme_completion_poll_cb, status);
419 	if (rc != 0) {
420 		SPDK_ERRLOG("Failed to identify cdata\n");
421 		nvme_ctrlr_destruct(discovery_ctrlr);
422 		free(status);
423 		return rc;
424 	}
425 
426 	if (nvme_wait_for_completion(discovery_ctrlr->adminq, status)) {
427 		SPDK_ERRLOG("nvme_identify_controller failed!\n");
428 		nvme_ctrlr_destruct(discovery_ctrlr);
429 		if (!status->timed_out) {
430 			free(status);
431 		}
432 		return -ENXIO;
433 	}
434 
435 	free(status);
436 
437 	/* Direct attach through spdk_nvme_connect() API */
438 	if (direct_connect == true) {
439 		/* Set the ready state to skip the normal init process */
440 		discovery_ctrlr->state = NVME_CTRLR_STATE_READY;
441 		nvme_ctrlr_connected(probe_ctx, discovery_ctrlr);
442 		nvme_ctrlr_add_process(discovery_ctrlr, 0);
443 		return 0;
444 	}
445 
446 	rc = nvme_fabric_ctrlr_discover(discovery_ctrlr, probe_ctx);
447 	nvme_ctrlr_destruct(discovery_ctrlr);
448 	return rc;
449 }
450 
451 int
452 nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr,
453 			   struct spdk_nvme_probe_ctx *probe_ctx)
454 {
455 	struct spdk_nvmf_discovery_log_page *log_page;
456 	struct spdk_nvmf_discovery_log_page_entry *log_page_entry;
457 	char buffer[4096];
458 	int rc;
459 	uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0;
460 	uint64_t remaining_num_rec = 0;
461 	uint16_t recfmt;
462 
463 	memset(buffer, 0x0, 4096);
464 	buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page,
465 				    entries[0])) /
466 				   sizeof(struct spdk_nvmf_discovery_log_page_entry);
467 	buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry);
468 	do {
469 		rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset);
470 		if (rc < 0) {
471 			SPDK_DEBUGLOG(nvme, "Get Log Page - Discovery error\n");
472 			return rc;
473 		}
474 
475 		if (!remaining_num_rec) {
476 			log_page = (struct spdk_nvmf_discovery_log_page *)buffer;
477 			recfmt = from_le16(&log_page->recfmt);
478 			if (recfmt != 0) {
479 				SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
480 				return -EPROTO;
481 			}
482 			remaining_num_rec = log_page->numrec;
483 			log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]);
484 			log_page_entry = &log_page->entries[0];
485 			numrec = spdk_min(remaining_num_rec, buffer_max_entries_first);
486 		} else {
487 			numrec = spdk_min(remaining_num_rec, buffer_max_entries);
488 			log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer;
489 		}
490 
491 		for (i = 0; i < numrec; i++) {
492 			nvme_fabric_discover_probe(log_page_entry++, probe_ctx, ctrlr->trid.priority);
493 		}
494 		remaining_num_rec -= numrec;
495 		log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
496 	} while (remaining_num_rec != 0);
497 
498 	return 0;
499 }
500 
501 int
502 nvme_fabric_qpair_connect_async(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
503 {
504 	struct nvme_completion_poll_status *status;
505 	struct spdk_nvmf_fabric_connect_cmd cmd;
506 	struct spdk_nvmf_fabric_connect_data *nvmf_data;
507 	struct spdk_nvme_ctrlr *ctrlr;
508 	struct nvme_request *req;
509 	int rc;
510 
511 	if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) {
512 		return -EINVAL;
513 	}
514 
515 	ctrlr = qpair->ctrlr;
516 	if (!ctrlr) {
517 		return -EINVAL;
518 	}
519 
520 	nvmf_data = spdk_zmalloc(sizeof(*nvmf_data), 0, NULL,
521 				 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
522 	if (!nvmf_data) {
523 		SPDK_ERRLOG("nvmf_data allocation error\n");
524 		return -ENOMEM;
525 	}
526 
527 	status = calloc(1, sizeof(*status));
528 	if (!status) {
529 		SPDK_ERRLOG("Failed to allocate status tracker\n");
530 		spdk_free(nvmf_data);
531 		return -ENOMEM;
532 	}
533 
534 	status->dma_data = nvmf_data;
535 
536 	memset(&cmd, 0, sizeof(cmd));
537 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
538 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT;
539 	cmd.qid = qpair->id;
540 	cmd.sqsize = num_entries - 1;
541 	cmd.kato = ctrlr->opts.keep_alive_timeout_ms;
542 
543 	assert(qpair->reserved_req != NULL);
544 	req = qpair->reserved_req;
545 	memcpy(&req->cmd, &cmd, sizeof(cmd));
546 
547 	if (nvme_qpair_is_admin_queue(qpair)) {
548 		nvmf_data->cntlid = 0xFFFF;
549 	} else {
550 		nvmf_data->cntlid = ctrlr->cntlid;
551 	}
552 
553 	SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id),
554 			   "host ID size mismatch");
555 	memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid));
556 	snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn);
557 	snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn);
558 
559 	NVME_INIT_REQUEST(req, nvme_completion_poll_cb, status, NVME_PAYLOAD_CONTIG(nvmf_data, NULL),
560 			  sizeof(*nvmf_data), 0);
561 
562 	rc = nvme_qpair_submit_request(qpair, req);
563 	if (rc < 0) {
564 		SPDK_ERRLOG("Failed to allocate/submit FABRIC_CONNECT command, rc %d\n", rc);
565 		spdk_free(status->dma_data);
566 		free(status);
567 		return rc;
568 	}
569 
570 	/* If we time out, the qpair will abort the request upon destruction. */
571 	if (ctrlr->opts.fabrics_connect_timeout_us > 0) {
572 		status->timeout_tsc = spdk_get_ticks() + ctrlr->opts.fabrics_connect_timeout_us *
573 				      spdk_get_ticks_hz() / SPDK_SEC_TO_USEC;
574 	}
575 
576 	qpair->poll_status = status;
577 	return 0;
578 }
579 
580 int
581 nvme_fabric_qpair_connect_poll(struct spdk_nvme_qpair *qpair)
582 {
583 	struct nvme_completion_poll_status *status;
584 	struct spdk_nvmf_fabric_connect_rsp *rsp;
585 	struct spdk_nvme_ctrlr *ctrlr;
586 	int rc = 0;
587 
588 	ctrlr = qpair->ctrlr;
589 	status = qpair->poll_status;
590 
591 	if (nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL) == -EAGAIN) {
592 		return -EAGAIN;
593 	}
594 
595 	if (status->timed_out || spdk_nvme_cpl_is_error(&status->cpl)) {
596 		SPDK_ERRLOG("Connect command failed, rc %d, trtype:%s adrfam:%s "
597 			    "traddr:%s trsvcid:%s subnqn:%s\n",
598 			    status->timed_out ? -ECANCELED : -EIO,
599 			    spdk_nvme_transport_id_trtype_str(ctrlr->trid.trtype),
600 			    spdk_nvme_transport_id_adrfam_str(ctrlr->trid.adrfam),
601 			    ctrlr->trid.traddr,
602 			    ctrlr->trid.trsvcid,
603 			    ctrlr->trid.subnqn);
604 		if (status->timed_out) {
605 			rc = -ECANCELED;
606 		} else {
607 			SPDK_ERRLOG("Connect command completed with error: sct %d, sc %d\n",
608 				    status->cpl.status.sct, status->cpl.status.sc);
609 			rc = -EIO;
610 		}
611 
612 		goto finish;
613 	}
614 
615 	if (nvme_qpair_is_admin_queue(qpair)) {
616 		rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status->cpl;
617 		ctrlr->cntlid = rsp->status_code_specific.success.cntlid;
618 		SPDK_DEBUGLOG(nvme, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid);
619 	}
620 finish:
621 	qpair->poll_status = NULL;
622 	if (!status->timed_out) {
623 		spdk_free(status->dma_data);
624 		free(status);
625 	}
626 
627 	return rc;
628 }
629 
630 int
631 nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
632 {
633 	int rc;
634 
635 	rc = nvme_fabric_qpair_connect_async(qpair, num_entries);
636 	if (rc) {
637 		return rc;
638 	}
639 
640 	do {
641 		/* Wait until the command completes or times out */
642 		rc = nvme_fabric_qpair_connect_poll(qpair);
643 	} while (rc == -EAGAIN);
644 
645 	return rc;
646 }
647