xref: /freebsd/lib/libnvmf/nvmf_controller.c (revision 9f0f30bc1f5f08d25243952bad3fdc6e13a75c2a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <sys/utsname.h>
9 #include <assert.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include "libnvmf.h"
16 #include "internal.h"
17 #include "nvmft_subr.h"
18 
19 bool
nvmf_nqn_valid_strict(const char * nqn)20 nvmf_nqn_valid_strict(const char *nqn)
21 {
22 	size_t len;
23 
24 	if (!nvmf_nqn_valid(nqn))
25 		return (false);
26 
27 	/*
28 	 * Stricter checks from the spec.  Linux does not seem to
29 	 * require these.
30 	 */
31 	len = strlen(nqn);
32 
33 	/*
34 	 * NVMF_NQN_MIN_LEN does not include '.' and require at least
35 	 * one character of a domain name.
36 	 */
37 	if (len < NVMF_NQN_MIN_LEN + 2)
38 		return (false);
39 	if (memcmp("nqn.", nqn, strlen("nqn.")) != 0)
40 		return (false);
41 	nqn += strlen("nqn.");
42 
43 	/* Next 4 digits must be a year. */
44 	for (u_int i = 0; i < 4; i++) {
45 		if (!isdigit(nqn[i]))
46 			return (false);
47 	}
48 	nqn += 4;
49 
50 	/* '-' between year and month. */
51 	if (nqn[0] != '-')
52 		return (false);
53 	nqn++;
54 
55 	/* 2 digit month. */
56 	for (u_int i = 0; i < 2; i++) {
57 		if (!isdigit(nqn[i]))
58 			return (false);
59 	}
60 	nqn += 2;
61 
62 	/* '.' between month and reverse domain name. */
63 	if (nqn[0] != '.')
64 		return (false);
65 	return (true);
66 }
67 
68 void
nvmf_init_cqe(void * cqe,const struct nvmf_capsule * nc,uint16_t status)69 nvmf_init_cqe(void *cqe, const struct nvmf_capsule *nc, uint16_t status)
70 {
71 	struct nvme_completion *cpl = cqe;
72 	const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
73 
74 	memset(cpl, 0, sizeof(*cpl));
75 	cpl->cid = cmd->cid;
76 	cpl->status = htole16(status);
77 }
78 
79 static struct nvmf_capsule *
nvmf_simple_response(const struct nvmf_capsule * nc,uint8_t sc_type,uint8_t sc_status)80 nvmf_simple_response(const struct nvmf_capsule *nc, uint8_t sc_type,
81     uint8_t sc_status)
82 {
83 	struct nvme_completion cpl;
84 	uint16_t status;
85 
86 	status = NVMEF(NVME_STATUS_SCT, sc_type) |
87 	    NVMEF(NVME_STATUS_SC, sc_status);
88 	nvmf_init_cqe(&cpl, nc, status);
89 	return (nvmf_allocate_response(nc->nc_qpair, &cpl));
90 }
91 
92 int
nvmf_controller_receive_capsule(struct nvmf_qpair * qp,struct nvmf_capsule ** ncp)93 nvmf_controller_receive_capsule(struct nvmf_qpair *qp,
94     struct nvmf_capsule **ncp)
95 {
96 	struct nvmf_capsule *nc;
97 	int error;
98 	uint8_t sc_status;
99 
100 	*ncp = NULL;
101 	error = nvmf_receive_capsule(qp, &nc);
102 	if (error != 0)
103 		return (error);
104 
105 	sc_status = nvmf_validate_command_capsule(nc);
106 	if (sc_status != NVME_SC_SUCCESS) {
107 		nvmf_send_generic_error(nc, sc_status);
108 		nvmf_free_capsule(nc);
109 		return (EPROTO);
110 	}
111 
112 	*ncp = nc;
113 	return (0);
114 }
115 
116 int
nvmf_controller_transmit_response(struct nvmf_capsule * nc)117 nvmf_controller_transmit_response(struct nvmf_capsule *nc)
118 {
119 	struct nvmf_qpair *qp = nc->nc_qpair;
120 
121 	/* Set SQHD. */
122 	if (qp->nq_flow_control) {
123 		qp->nq_sqhd = (qp->nq_sqhd + 1) % qp->nq_qsize;
124 		nc->nc_cqe.sqhd = htole16(qp->nq_sqhd);
125 	} else
126 		nc->nc_cqe.sqhd = 0;
127 
128 	return (nvmf_transmit_capsule(nc));
129 }
130 
131 int
nvmf_send_response(const struct nvmf_capsule * cc,const void * cqe)132 nvmf_send_response(const struct nvmf_capsule *cc, const void *cqe)
133 {
134 	struct nvmf_capsule *rc;
135 	int error;
136 
137 	rc = nvmf_allocate_response(cc->nc_qpair, cqe);
138 	if (rc == NULL)
139 		return (ENOMEM);
140 	error = nvmf_controller_transmit_response(rc);
141 	nvmf_free_capsule(rc);
142 	return (error);
143 }
144 
145 int
nvmf_send_error(const struct nvmf_capsule * cc,uint8_t sc_type,uint8_t sc_status)146 nvmf_send_error(const struct nvmf_capsule *cc, uint8_t sc_type,
147     uint8_t sc_status)
148 {
149 	struct nvmf_capsule *rc;
150 	int error;
151 
152 	rc = nvmf_simple_response(cc, sc_type, sc_status);
153 	error = nvmf_controller_transmit_response(rc);
154 	nvmf_free_capsule(rc);
155 	return (error);
156 }
157 
158 int
nvmf_send_generic_error(const struct nvmf_capsule * nc,uint8_t sc_status)159 nvmf_send_generic_error(const struct nvmf_capsule *nc, uint8_t sc_status)
160 {
161 	return (nvmf_send_error(nc, NVME_SCT_GENERIC, sc_status));
162 }
163 
164 int
nvmf_send_success(const struct nvmf_capsule * nc)165 nvmf_send_success(const struct nvmf_capsule *nc)
166 {
167 	return (nvmf_send_generic_error(nc, NVME_SC_SUCCESS));
168 }
169 
170 void
nvmf_connect_invalid_parameters(const struct nvmf_capsule * cc,bool data,uint16_t offset)171 nvmf_connect_invalid_parameters(const struct nvmf_capsule *cc, bool data,
172     uint16_t offset)
173 {
174 	struct nvmf_fabric_connect_rsp rsp;
175 	struct nvmf_capsule *rc;
176 
177 	nvmf_init_cqe(&rsp, cc,
178 	    NVMEF(NVME_STATUS_SCT, NVME_SCT_COMMAND_SPECIFIC) |
179 	    NVMEF(NVME_STATUS_SC, NVMF_FABRIC_SC_INVALID_PARAM));
180 	rsp.status_code_specific.invalid.ipo = htole16(offset);
181 	rsp.status_code_specific.invalid.iattr = data ? 1 : 0;
182 	rc = nvmf_allocate_response(cc->nc_qpair, &rsp);
183 	nvmf_transmit_capsule(rc);
184 	nvmf_free_capsule(rc);
185 }
186 
187 struct nvmf_qpair *
nvmf_accept(struct nvmf_association * na,const struct nvmf_qpair_params * params,struct nvmf_capsule ** ccp,struct nvmf_fabric_connect_data * data)188 nvmf_accept(struct nvmf_association *na, const struct nvmf_qpair_params *params,
189     struct nvmf_capsule **ccp, struct nvmf_fabric_connect_data *data)
190 {
191 	static const char hostid_zero[sizeof(data->hostid)];
192 	const struct nvmf_fabric_connect_cmd *cmd;
193 	struct nvmf_qpair *qp;
194 	struct nvmf_capsule *cc, *rc;
195 	u_int qsize;
196 	int error;
197 	uint16_t cntlid;
198 	uint8_t sc_status;
199 
200 	qp = NULL;
201 	cc = NULL;
202 	rc = NULL;
203 	*ccp = NULL;
204 	na_clear_error(na);
205 	if (!na->na_controller) {
206 		na_error(na, "Cannot accept on a host");
207 		goto error;
208 	}
209 
210 	qp = nvmf_allocate_qpair(na, params);
211 	if (qp == NULL)
212 		goto error;
213 
214 	/* Read the CONNECT capsule. */
215 	error = nvmf_receive_capsule(qp, &cc);
216 	if (error != 0) {
217 		na_error(na, "Failed to receive CONNECT: %s", strerror(error));
218 		goto error;
219 	}
220 
221 	sc_status = nvmf_validate_command_capsule(cc);
222 	if (sc_status != 0) {
223 		na_error(na, "CONNECT command failed to validate: %u",
224 		    sc_status);
225 		rc = nvmf_simple_response(cc, NVME_SCT_GENERIC, sc_status);
226 		goto error;
227 	}
228 
229 	cmd = nvmf_capsule_sqe(cc);
230 	if (cmd->opcode != NVME_OPC_FABRICS_COMMANDS ||
231 	    cmd->fctype != NVMF_FABRIC_COMMAND_CONNECT) {
232 		na_error(na, "Invalid opcode in CONNECT (%u,%u)", cmd->opcode,
233 		    cmd->fctype);
234 		rc = nvmf_simple_response(cc, NVME_SCT_GENERIC,
235 		    NVME_SC_INVALID_OPCODE);
236 		goto error;
237 	}
238 
239 	if (cmd->recfmt != htole16(0)) {
240 		na_error(na, "Unsupported CONNECT record format %u",
241 		    le16toh(cmd->recfmt));
242 		rc = nvmf_simple_response(cc, NVME_SCT_COMMAND_SPECIFIC,
243 		    NVMF_FABRIC_SC_INCOMPATIBLE_FORMAT);
244 		goto error;
245 	}
246 
247 	qsize = le16toh(cmd->sqsize) + 1;
248 	if (cmd->qid == 0) {
249 		/* Admin queue limits. */
250 		if (qsize < NVME_MIN_ADMIN_ENTRIES ||
251 		    qsize > NVME_MAX_ADMIN_ENTRIES ||
252 		    qsize > na->na_params.max_admin_qsize) {
253 			na_error(na, "Invalid queue size %u", qsize);
254 			nvmf_connect_invalid_parameters(cc, false,
255 			    offsetof(struct nvmf_fabric_connect_cmd, sqsize));
256 			goto error;
257 		}
258 		qp->nq_admin = true;
259 	} else {
260 		/* I/O queues not allowed for discovery. */
261 		if (na->na_params.max_io_qsize == 0) {
262 			na_error(na, "I/O queue on discovery controller");
263 			nvmf_connect_invalid_parameters(cc, false,
264 			    offsetof(struct nvmf_fabric_connect_cmd, qid));
265 			goto error;
266 		}
267 
268 		/* I/O queue limits. */
269 		if (qsize < NVME_MIN_IO_ENTRIES ||
270 		    qsize > NVME_MAX_IO_ENTRIES ||
271 		    qsize > na->na_params.max_io_qsize) {
272 			na_error(na, "Invalid queue size %u", qsize);
273 			nvmf_connect_invalid_parameters(cc, false,
274 			    offsetof(struct nvmf_fabric_connect_cmd, sqsize));
275 			goto error;
276 		}
277 
278 		/* KATO is reserved for I/O queues. */
279 		if (cmd->kato != 0) {
280 			na_error(na,
281 			    "KeepAlive timeout specified for I/O queue");
282 			nvmf_connect_invalid_parameters(cc, false,
283 			    offsetof(struct nvmf_fabric_connect_cmd, kato));
284 			goto error;
285 		}
286 		qp->nq_admin = false;
287 	}
288 	qp->nq_qsize = qsize;
289 
290 	/* Fetch CONNECT data. */
291 	if (nvmf_capsule_data_len(cc) != sizeof(*data)) {
292 		na_error(na, "Invalid data payload length for CONNECT: %zu",
293 		    nvmf_capsule_data_len(cc));
294 		nvmf_connect_invalid_parameters(cc, false,
295 		    offsetof(struct nvmf_fabric_connect_cmd, sgl1));
296 		goto error;
297 	}
298 
299 	error = nvmf_receive_controller_data(cc, 0, data, sizeof(*data));
300 	if (error != 0) {
301 		na_error(na, "Failed to read data for CONNECT: %s",
302 		    strerror(error));
303 		rc = nvmf_simple_response(cc, NVME_SCT_GENERIC,
304 		    NVME_SC_DATA_TRANSFER_ERROR);
305 		goto error;
306 	}
307 
308 	/* The hostid must be non-zero. */
309 	if (memcmp(data->hostid, hostid_zero, sizeof(hostid_zero)) == 0) {
310 		na_error(na, "HostID in CONNECT data is zero");
311 		nvmf_connect_invalid_parameters(cc, true,
312 		    offsetof(struct nvmf_fabric_connect_data, hostid));
313 		goto error;
314 	}
315 
316 	cntlid = le16toh(data->cntlid);
317 	if (cmd->qid == 0) {
318 		if (na->na_params.dynamic_controller_model) {
319 			if (cntlid != NVMF_CNTLID_DYNAMIC) {
320 				na_error(na, "Invalid controller ID %#x",
321 				    cntlid);
322 				nvmf_connect_invalid_parameters(cc, true,
323 				    offsetof(struct nvmf_fabric_connect_data,
324 					cntlid));
325 				goto error;
326 			}
327 		} else {
328 			if (cntlid > NVMF_CNTLID_STATIC_MAX &&
329 			    cntlid != NVMF_CNTLID_STATIC_ANY) {
330 				na_error(na, "Invalid controller ID %#x",
331 				    cntlid);
332 				nvmf_connect_invalid_parameters(cc, true,
333 				    offsetof(struct nvmf_fabric_connect_data,
334 					cntlid));
335 				goto error;
336 			}
337 		}
338 	} else {
339 		/* Wildcard Controller IDs are only valid on an Admin queue. */
340 		if (cntlid > NVMF_CNTLID_STATIC_MAX) {
341 			na_error(na, "Invalid controller ID %#x", cntlid);
342 			nvmf_connect_invalid_parameters(cc, true,
343 			    offsetof(struct nvmf_fabric_connect_data, cntlid));
344 			goto error;
345 		}
346 	}
347 
348 	/* Simple validation of each NQN. */
349 	if (!nvmf_nqn_valid(data->subnqn)) {
350 		na_error(na, "Invalid SubNQN %.*s", (int)sizeof(data->subnqn),
351 		    data->subnqn);
352 		nvmf_connect_invalid_parameters(cc, true,
353 		    offsetof(struct nvmf_fabric_connect_data, subnqn));
354 		goto error;
355 	}
356 	if (!nvmf_nqn_valid(data->hostnqn)) {
357 		na_error(na, "Invalid HostNQN %.*s", (int)sizeof(data->hostnqn),
358 		    data->hostnqn);
359 		nvmf_connect_invalid_parameters(cc, true,
360 		    offsetof(struct nvmf_fabric_connect_data, hostnqn));
361 		goto error;
362 	}
363 
364 	if (na->na_params.sq_flow_control ||
365 	    (cmd->cattr & NVMF_CONNECT_ATTR_DISABLE_SQ_FC) == 0)
366 		qp->nq_flow_control = true;
367 	else
368 		qp->nq_flow_control = false;
369 	qp->nq_sqhd = 0;
370 	qp->nq_kato = le32toh(cmd->kato);
371 	*ccp = cc;
372 	return (qp);
373 error:
374 	if (rc != NULL) {
375 		nvmf_transmit_capsule(rc);
376 		nvmf_free_capsule(rc);
377 	}
378 	if (cc != NULL)
379 		nvmf_free_capsule(cc);
380 	if (qp != NULL)
381 		nvmf_free_qpair(qp);
382 	return (NULL);
383 }
384 
385 int
nvmf_finish_accept(const struct nvmf_capsule * cc,uint16_t cntlid)386 nvmf_finish_accept(const struct nvmf_capsule *cc, uint16_t cntlid)
387 {
388 	struct nvmf_fabric_connect_rsp rsp;
389 	struct nvmf_qpair *qp = cc->nc_qpair;
390 	struct nvmf_capsule *rc;
391 	int error;
392 
393 	nvmf_init_cqe(&rsp, cc, 0);
394 	if (qp->nq_flow_control)
395 		rsp.sqhd = htole16(qp->nq_sqhd);
396 	else
397 		rsp.sqhd = htole16(0xffff);
398 	rsp.status_code_specific.success.cntlid = htole16(cntlid);
399 	rc = nvmf_allocate_response(qp, &rsp);
400 	if (rc == NULL)
401 		return (ENOMEM);
402 	error = nvmf_transmit_capsule(rc);
403 	nvmf_free_capsule(rc);
404 	if (error == 0)
405 		qp->nq_cntlid = cntlid;
406 	return (error);
407 }
408 
409 uint64_t
nvmf_controller_cap(struct nvmf_qpair * qp)410 nvmf_controller_cap(struct nvmf_qpair *qp)
411 {
412 	const struct nvmf_association *na = qp->nq_association;
413 
414 	return (_nvmf_controller_cap(na->na_params.max_io_qsize,
415 	    NVMF_CC_EN_TIMEOUT));
416 }
417 
418 bool
nvmf_validate_cc(struct nvmf_qpair * qp,uint64_t cap,uint32_t old_cc,uint32_t new_cc)419 nvmf_validate_cc(struct nvmf_qpair *qp, uint64_t cap, uint32_t old_cc,
420     uint32_t new_cc)
421 {
422 	const struct nvmf_association *na = qp->nq_association;
423 
424 	return (_nvmf_validate_cc(na->na_params.max_io_qsize, cap, old_cc,
425 	    new_cc));
426 }
427 
428 void
nvmf_init_discovery_controller_data(struct nvmf_qpair * qp,struct nvme_controller_data * cdata)429 nvmf_init_discovery_controller_data(struct nvmf_qpair *qp,
430     struct nvme_controller_data *cdata)
431 {
432 	const struct nvmf_association *na = qp->nq_association;
433 	struct utsname utsname;
434 	char *cp;
435 
436 	memset(cdata, 0, sizeof(*cdata));
437 
438 	/*
439 	 * 5.2 Figure 37 states model name and serial are reserved,
440 	 * but Linux includes them.  Don't bother with serial, but
441 	 * do set model name.
442 	 */
443 	uname(&utsname);
444 	nvmf_strpad(cdata->mn, utsname.sysname, sizeof(cdata->mn));
445 	nvmf_strpad(cdata->fr, utsname.release, sizeof(cdata->fr));
446 	cp = memchr(cdata->fr, '-', sizeof(cdata->fr));
447 	if (cp != NULL)
448 		memset(cp, ' ', sizeof(cdata->fr) - (cp - (char *)cdata->fr));
449 
450 	cdata->ctrlr_id = htole16(qp->nq_cntlid);
451 	cdata->ver = htole32(NVME_REV(1, 4));
452 	cdata->cntrltype = 2;
453 
454 	cdata->lpa = NVMEF(NVME_CTRLR_DATA_LPA_EXT_DATA, 1);
455 	cdata->elpe = 0;
456 
457 	cdata->maxcmd = htole16(na->na_params.max_admin_qsize);
458 
459 	/* Transport-specific? */
460 	cdata->sgls = htole32(
461 	    NVMEF(NVME_CTRLR_DATA_SGLS_TRANSPORT_DATA_BLOCK, 1) |
462 	    NVMEF(NVME_CTRLR_DATA_SGLS_ADDRESS_AS_OFFSET, 1) |
463 	    NVMEF(NVME_CTRLR_DATA_SGLS_NVM_COMMAND_SET, 1));
464 
465 	strlcpy(cdata->subnqn, NVMF_DISCOVERY_NQN, sizeof(cdata->subnqn));
466 }
467 
468 void
nvmf_init_io_controller_data(struct nvmf_qpair * qp,const char * serial,const char * subnqn,int nn,uint32_t ioccsz,struct nvme_controller_data * cdata)469 nvmf_init_io_controller_data(struct nvmf_qpair *qp, const char *serial,
470     const char *subnqn, int nn, uint32_t ioccsz,
471     struct nvme_controller_data *cdata)
472 {
473 	const struct nvmf_association *na = qp->nq_association;
474 	struct utsname utsname;
475 
476 	uname(&utsname);
477 
478 	memset(cdata, 0, sizeof(*cdata));
479 	_nvmf_init_io_controller_data(qp->nq_cntlid, na->na_params.max_io_qsize,
480 	    serial, utsname.sysname, utsname.release, subnqn, nn, ioccsz,
481 	    sizeof(struct nvme_completion), cdata);
482 }
483 
484 uint8_t
nvmf_get_log_page_id(const struct nvme_command * cmd)485 nvmf_get_log_page_id(const struct nvme_command *cmd)
486 {
487 	assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
488 	return (le32toh(cmd->cdw10) & 0xff);
489 }
490 
491 uint64_t
nvmf_get_log_page_length(const struct nvme_command * cmd)492 nvmf_get_log_page_length(const struct nvme_command *cmd)
493 {
494 	uint32_t numd;
495 
496 	assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
497 	numd = le32toh(cmd->cdw10) >> 16 | (le32toh(cmd->cdw11) & 0xffff) << 16;
498 	return ((numd + 1) * 4);
499 }
500 
501 uint64_t
nvmf_get_log_page_offset(const struct nvme_command * cmd)502 nvmf_get_log_page_offset(const struct nvme_command *cmd)
503 {
504 	assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
505 	return (le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32);
506 }
507 
508 int
nvmf_handoff_controller_qpair(struct nvmf_qpair * qp,const struct nvmf_fabric_connect_cmd * cmd,const struct nvmf_fabric_connect_data * data,struct nvmf_ioc_nv * nv)509 nvmf_handoff_controller_qpair(struct nvmf_qpair *qp,
510     const struct nvmf_fabric_connect_cmd *cmd,
511     const struct nvmf_fabric_connect_data *data, struct nvmf_ioc_nv *nv)
512 {
513 	nvlist_t *nvl, *nvl_qp;
514 	int error;
515 
516 	error = nvmf_kernel_handoff_params(qp, &nvl_qp);
517 	if (error)
518 		return (error);
519 
520 	nvl = nvlist_create(0);
521 	nvlist_add_number(nvl, "trtype", qp->nq_association->na_trtype);
522 	nvlist_move_nvlist(nvl, "params", nvl_qp);
523 	nvlist_add_binary(nvl, "cmd", cmd, sizeof(*cmd));
524 	nvlist_add_binary(nvl, "data", data, sizeof(*data));
525 
526 	error = nvmf_pack_ioc_nvlist(nv, nvl);
527 	nvlist_destroy(nvl);
528 	return (error);
529 }
530