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