xref: /spdk/lib/nvme/nvme_fabric.c (revision b30d57cdad6d2bc75cc1e4e2ebbcebcb0d98dcfa)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation. All rights reserved.
5  *   Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * NVMe over Fabrics transport-independent functions
36  */
37 
38 #include "nvme_internal.h"
39 
40 #include "spdk/endian.h"
41 #include "spdk/string.h"
42 
43 static int
44 nvme_fabric_prop_set_cmd(struct spdk_nvme_ctrlr *ctrlr,
45 			 uint32_t offset, uint8_t size, uint64_t value)
46 {
47 	struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
48 	struct nvme_completion_poll_status *status;
49 	int rc;
50 
51 	assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
52 
53 	status = calloc(1, sizeof(*status));
54 	if (!status) {
55 		SPDK_ERRLOG("Failed to allocate status tracker\n");
56 		return -ENOMEM;
57 	}
58 
59 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
60 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET;
61 	cmd.ofst = offset;
62 	cmd.attrib.size = size;
63 	cmd.value.u64 = value;
64 
65 	rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
66 					   NULL, 0,
67 					   nvme_completion_poll_cb, status);
68 	if (rc < 0) {
69 		free(status);
70 		return rc;
71 	}
72 
73 	if (nvme_wait_for_completion(ctrlr->adminq, status)) {
74 		if (!status->timed_out) {
75 			free(status);
76 		}
77 		SPDK_ERRLOG("Property Set failed\n");
78 		return -1;
79 	}
80 	free(status);
81 
82 	return 0;
83 }
84 
85 static int
86 nvme_fabric_prop_get_cmd(struct spdk_nvme_ctrlr *ctrlr,
87 			 uint32_t offset, uint8_t size, uint64_t *value)
88 {
89 	struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
90 	struct nvme_completion_poll_status *status;
91 	struct spdk_nvmf_fabric_prop_get_rsp *response;
92 	int rc;
93 
94 	assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
95 
96 	status = calloc(1, sizeof(*status));
97 	if (!status) {
98 		SPDK_ERRLOG("Failed to allocate status tracker\n");
99 		return -ENOMEM;
100 	}
101 
102 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
103 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET;
104 	cmd.ofst = offset;
105 	cmd.attrib.size = size;
106 
107 	rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
108 					   NULL, 0, nvme_completion_poll_cb,
109 					   status);
110 	if (rc < 0) {
111 		free(status);
112 		return rc;
113 	}
114 
115 	if (nvme_wait_for_completion(ctrlr->adminq, status)) {
116 		if (!status->timed_out) {
117 			free(status);
118 		}
119 		SPDK_ERRLOG("Property Get failed\n");
120 		return -1;
121 	}
122 
123 	response = (struct spdk_nvmf_fabric_prop_get_rsp *)&status->cpl;
124 
125 	if (size == SPDK_NVMF_PROP_SIZE_4) {
126 		*value = response->value.u32.low;
127 	} else {
128 		*value = response->value.u64;
129 	}
130 
131 	free(status);
132 
133 	return 0;
134 }
135 
136 int
137 nvme_fabric_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
138 {
139 	return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value);
140 }
141 
142 int
143 nvme_fabric_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
144 {
145 	return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
146 }
147 
148 int
149 nvme_fabric_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
150 {
151 	uint64_t tmp_value;
152 	int rc;
153 	rc = nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, &tmp_value);
154 
155 	if (!rc) {
156 		*value = (uint32_t)tmp_value;
157 	}
158 	return rc;
159 }
160 
161 int
162 nvme_fabric_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
163 {
164 	return nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
165 }
166 
167 static void
168 nvme_fabric_discover_probe(struct spdk_nvmf_discovery_log_page_entry *entry,
169 			   struct spdk_nvme_probe_ctx *probe_ctx,
170 			   int discover_priority)
171 {
172 	struct spdk_nvme_transport_id trid;
173 	uint8_t *end;
174 	size_t len;
175 
176 	memset(&trid, 0, sizeof(trid));
177 
178 	if (entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
179 		SPDK_WARNLOG("Skipping unsupported discovery service referral\n");
180 		return;
181 	} else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) {
182 		SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype);
183 		return;
184 	}
185 
186 	trid.trtype = entry->trtype;
187 	spdk_nvme_transport_id_populate_trstring(&trid, spdk_nvme_transport_id_trtype_str(entry->trtype));
188 	if (!spdk_nvme_transport_available_by_name(trid.trstring)) {
189 		SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n",
190 			     trid.trtype);
191 		return;
192 	}
193 
194 	snprintf(trid.trstring, sizeof(trid.trstring), "%s", probe_ctx->trid.trstring);
195 	trid.adrfam = entry->adrfam;
196 
197 	/* Ensure that subnqn is null terminated. */
198 	end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1);
199 	if (!end) {
200 		SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n");
201 		return;
202 	}
203 	len = end - entry->subnqn;
204 	memcpy(trid.subnqn, entry->subnqn, len);
205 	trid.subnqn[len] = '\0';
206 
207 	/* Convert traddr to a null terminated string. */
208 	len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' ');
209 	memcpy(trid.traddr, entry->traddr, len);
210 	if (spdk_str_chomp(trid.traddr) != 0) {
211 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRADDR\n");
212 	}
213 
214 	/* Convert trsvcid to a null terminated string. */
215 	len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' ');
216 	memcpy(trid.trsvcid, entry->trsvcid, len);
217 	if (spdk_str_chomp(trid.trsvcid) != 0) {
218 		SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRSVCID\n");
219 	}
220 
221 	SPDK_DEBUGLOG(nvme, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n",
222 		      trid.subnqn, trid.trtype,
223 		      trid.traddr, trid.trsvcid);
224 
225 	/* Copy the priority from the discovery ctrlr */
226 	trid.priority = discover_priority;
227 
228 	nvme_ctrlr_probe(&trid, probe_ctx, NULL);
229 }
230 
231 static int
232 nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
233 				   void *log_page, uint32_t size, uint64_t offset)
234 {
235 	struct nvme_completion_poll_status *status;
236 	int rc;
237 
238 	status = calloc(1, sizeof(*status));
239 	if (!status) {
240 		SPDK_ERRLOG("Failed to allocate status tracker\n");
241 		return -ENOMEM;
242 	}
243 
244 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset,
245 					      nvme_completion_poll_cb, status);
246 	if (rc < 0) {
247 		free(status);
248 		return -1;
249 	}
250 
251 	if (nvme_wait_for_completion(ctrlr->adminq, status)) {
252 		if (!status->timed_out) {
253 			free(status);
254 		}
255 		return -1;
256 	}
257 	free(status);
258 
259 	return 0;
260 }
261 
262 int
263 nvme_fabric_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
264 		       bool direct_connect)
265 {
266 	struct spdk_nvme_ctrlr_opts discovery_opts;
267 	struct spdk_nvme_ctrlr *discovery_ctrlr;
268 	union spdk_nvme_cc_register cc;
269 	int rc;
270 	struct nvme_completion_poll_status *status;
271 
272 	if (strcmp(probe_ctx->trid.subnqn, SPDK_NVMF_DISCOVERY_NQN) != 0) {
273 		/* It is not a discovery_ctrlr info and try to directly connect it */
274 		rc = nvme_ctrlr_probe(&probe_ctx->trid, probe_ctx, NULL);
275 		return rc;
276 	}
277 
278 	spdk_nvme_ctrlr_get_default_ctrlr_opts(&discovery_opts, sizeof(discovery_opts));
279 	/* For discovery_ctrlr set the timeout to 0 */
280 	discovery_opts.keep_alive_timeout_ms = 0;
281 
282 	discovery_ctrlr = nvme_transport_ctrlr_construct(&probe_ctx->trid, &discovery_opts, NULL);
283 	if (discovery_ctrlr == NULL) {
284 		return -1;
285 	}
286 	nvme_qpair_set_state(discovery_ctrlr->adminq, NVME_QPAIR_ENABLED);
287 
288 	/* TODO: this should be using the normal NVMe controller initialization process +1 */
289 	cc.raw = 0;
290 	cc.bits.en = 1;
291 	cc.bits.iosqes = 6; /* SQ entry size == 64 == 2^6 */
292 	cc.bits.iocqes = 4; /* CQ entry size == 16 == 2^4 */
293 	rc = nvme_transport_ctrlr_set_reg_4(discovery_ctrlr, offsetof(struct spdk_nvme_registers, cc.raw),
294 					    cc.raw);
295 	if (rc < 0) {
296 		SPDK_ERRLOG("Failed to set cc\n");
297 		nvme_ctrlr_destruct(discovery_ctrlr);
298 		return -1;
299 	}
300 
301 	status = calloc(1, sizeof(*status));
302 	if (!status) {
303 		SPDK_ERRLOG("Failed to allocate status tracker\n");
304 		nvme_ctrlr_destruct(discovery_ctrlr);
305 		return -ENOMEM;
306 	}
307 
308 	/* get the cdata info */
309 	rc = nvme_ctrlr_cmd_identify(discovery_ctrlr, SPDK_NVME_IDENTIFY_CTRLR, 0, 0, 0,
310 				     &discovery_ctrlr->cdata, sizeof(discovery_ctrlr->cdata),
311 				     nvme_completion_poll_cb, status);
312 	if (rc != 0) {
313 		SPDK_ERRLOG("Failed to identify cdata\n");
314 		nvme_ctrlr_destruct(discovery_ctrlr);
315 		free(status);
316 		return rc;
317 	}
318 
319 	if (nvme_wait_for_completion(discovery_ctrlr->adminq, status)) {
320 		SPDK_ERRLOG("nvme_identify_controller failed!\n");
321 		nvme_ctrlr_destruct(discovery_ctrlr);
322 		if (!status->timed_out) {
323 			free(status);
324 		}
325 		return -ENXIO;
326 	}
327 
328 	free(status);
329 
330 	/* Direct attach through spdk_nvme_connect() API */
331 	if (direct_connect == true) {
332 		/* Set the ready state to skip the normal init process */
333 		discovery_ctrlr->state = NVME_CTRLR_STATE_READY;
334 		nvme_ctrlr_connected(probe_ctx, discovery_ctrlr);
335 		nvme_ctrlr_add_process(discovery_ctrlr, 0);
336 		return 0;
337 	}
338 
339 	rc = nvme_fabric_ctrlr_discover(discovery_ctrlr, probe_ctx);
340 	nvme_ctrlr_destruct(discovery_ctrlr);
341 	return rc;
342 }
343 
344 int
345 nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr,
346 			   struct spdk_nvme_probe_ctx *probe_ctx)
347 {
348 	struct spdk_nvmf_discovery_log_page *log_page;
349 	struct spdk_nvmf_discovery_log_page_entry *log_page_entry;
350 	char buffer[4096];
351 	int rc;
352 	uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0;
353 	uint64_t remaining_num_rec = 0;
354 	uint16_t recfmt;
355 
356 	memset(buffer, 0x0, 4096);
357 	buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page,
358 				    entries[0])) /
359 				   sizeof(struct spdk_nvmf_discovery_log_page_entry);
360 	buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry);
361 	do {
362 		rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset);
363 		if (rc < 0) {
364 			SPDK_DEBUGLOG(nvme, "Get Log Page - Discovery error\n");
365 			return rc;
366 		}
367 
368 		if (!remaining_num_rec) {
369 			log_page = (struct spdk_nvmf_discovery_log_page *)buffer;
370 			recfmt = from_le16(&log_page->recfmt);
371 			if (recfmt != 0) {
372 				SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
373 				return -EPROTO;
374 			}
375 			remaining_num_rec = log_page->numrec;
376 			log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]);
377 			log_page_entry = &log_page->entries[0];
378 			numrec = spdk_min(remaining_num_rec, buffer_max_entries_first);
379 		} else {
380 			numrec = spdk_min(remaining_num_rec, buffer_max_entries);
381 			log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer;
382 		}
383 
384 		for (i = 0; i < numrec; i++) {
385 			nvme_fabric_discover_probe(log_page_entry++, probe_ctx, ctrlr->trid.priority);
386 		}
387 		remaining_num_rec -= numrec;
388 		log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
389 	} while (remaining_num_rec != 0);
390 
391 	return 0;
392 }
393 
394 int
395 nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
396 {
397 	struct nvme_completion_poll_status *status;
398 	struct spdk_nvmf_fabric_connect_rsp *rsp;
399 	struct spdk_nvmf_fabric_connect_cmd cmd;
400 	struct spdk_nvmf_fabric_connect_data *nvmf_data;
401 	struct spdk_nvme_ctrlr *ctrlr;
402 	int rc;
403 
404 	if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) {
405 		return -EINVAL;
406 	}
407 
408 	ctrlr = qpair->ctrlr;
409 	if (!ctrlr) {
410 		return -EINVAL;
411 	}
412 
413 	nvmf_data = spdk_zmalloc(sizeof(*nvmf_data), 0, NULL,
414 				 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
415 	if (!nvmf_data) {
416 		SPDK_ERRLOG("nvmf_data allocation error\n");
417 		return -ENOMEM;
418 	}
419 
420 	status = calloc(1, sizeof(*status));
421 	if (!status) {
422 		SPDK_ERRLOG("Failed to allocate status tracker\n");
423 		spdk_free(nvmf_data);
424 		return -ENOMEM;
425 	}
426 
427 	memset(&cmd, 0, sizeof(cmd));
428 	cmd.opcode = SPDK_NVME_OPC_FABRIC;
429 	cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT;
430 	cmd.qid = qpair->id;
431 	cmd.sqsize = num_entries - 1;
432 	cmd.kato = ctrlr->opts.keep_alive_timeout_ms;
433 
434 	if (nvme_qpair_is_admin_queue(qpair)) {
435 		nvmf_data->cntlid = 0xFFFF;
436 	} else {
437 		nvmf_data->cntlid = ctrlr->cntlid;
438 	}
439 
440 	SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id),
441 			   "host ID size mismatch");
442 	memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid));
443 	snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn);
444 	snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn);
445 
446 	rc = spdk_nvme_ctrlr_cmd_io_raw(ctrlr, qpair,
447 					(struct spdk_nvme_cmd *)&cmd,
448 					nvmf_data, sizeof(*nvmf_data),
449 					nvme_completion_poll_cb, status);
450 	if (rc < 0) {
451 		SPDK_ERRLOG("Connect command failed\n");
452 		spdk_free(nvmf_data);
453 		free(status);
454 		return rc;
455 	}
456 
457 	/* If we time out, the qpair will abort the request upon destruction. */
458 	if (nvme_wait_for_completion_timeout(qpair, status, ctrlr->opts.fabrics_connect_timeout_us)) {
459 		SPDK_ERRLOG("Connect command failed\n");
460 		spdk_free(nvmf_data);
461 		if (!status->timed_out) {
462 			free(status);
463 		}
464 		return -EIO;
465 	}
466 
467 	if (nvme_qpair_is_admin_queue(qpair)) {
468 		rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status->cpl;
469 		ctrlr->cntlid = rsp->status_code_specific.success.cntlid;
470 		SPDK_DEBUGLOG(nvme, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid);
471 	}
472 
473 	spdk_free(nvmf_data);
474 	free(status);
475 	return 0;
476 }
477