xref: /src/sys/dev/nvmf/controller/nvmft_controller.c (revision 8ced50767933f3e2949456367d4d9a64797daec3)
1a15f7c96SJohn Baldwin /*-
2a15f7c96SJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
3a15f7c96SJohn Baldwin  *
4a15f7c96SJohn Baldwin  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5a15f7c96SJohn Baldwin  * Written by: John Baldwin <jhb@FreeBSD.org>
6a15f7c96SJohn Baldwin  */
7a15f7c96SJohn Baldwin 
8a15f7c96SJohn Baldwin #include <sys/param.h>
9a15f7c96SJohn Baldwin #include <sys/callout.h>
10a15f7c96SJohn Baldwin #include <sys/kernel.h>
11a15f7c96SJohn Baldwin #include <sys/lock.h>
12a15f7c96SJohn Baldwin #include <sys/malloc.h>
13a15f7c96SJohn Baldwin #include <sys/mbuf.h>
14a15f7c96SJohn Baldwin #include <sys/memdesc.h>
15a15f7c96SJohn Baldwin #include <sys/mutex.h>
16a15f7c96SJohn Baldwin #include <sys/sbuf.h>
17a15f7c96SJohn Baldwin #include <sys/taskqueue.h>
18a15f7c96SJohn Baldwin 
19a15f7c96SJohn Baldwin #include <dev/nvmf/nvmf_transport.h>
20a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_subr.h>
21a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_var.h>
22a15f7c96SJohn Baldwin 
23a15f7c96SJohn Baldwin static void	nvmft_controller_shutdown(void *arg, int pending);
24a15f7c96SJohn Baldwin static void	nvmft_controller_terminate(void *arg, int pending);
25a15f7c96SJohn Baldwin 
26a15f7c96SJohn Baldwin int
nvmft_printf(struct nvmft_controller * ctrlr,const char * fmt,...)27a15f7c96SJohn Baldwin nvmft_printf(struct nvmft_controller *ctrlr, const char *fmt, ...)
28a15f7c96SJohn Baldwin {
29a15f7c96SJohn Baldwin 	char buf[128];
30a15f7c96SJohn Baldwin 	struct sbuf sb;
31a15f7c96SJohn Baldwin 	va_list ap;
32a15f7c96SJohn Baldwin 	size_t retval;
33a15f7c96SJohn Baldwin 
3499651c30SDavid E. O'Brien 	sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN | SBUF_INCLUDENUL);
35a15f7c96SJohn Baldwin 	sbuf_set_drain(&sb, sbuf_printf_drain, &retval);
36a15f7c96SJohn Baldwin 
37a15f7c96SJohn Baldwin 	sbuf_printf(&sb, "nvmft%u: ", ctrlr->cntlid);
38a15f7c96SJohn Baldwin 
39a15f7c96SJohn Baldwin 	va_start(ap, fmt);
40a15f7c96SJohn Baldwin 	sbuf_vprintf(&sb, fmt, ap);
41a15f7c96SJohn Baldwin 	va_end(ap);
42a15f7c96SJohn Baldwin 
43a15f7c96SJohn Baldwin 	sbuf_finish(&sb);
44a15f7c96SJohn Baldwin 	sbuf_delete(&sb);
45a15f7c96SJohn Baldwin 
46a15f7c96SJohn Baldwin 	return (retval);
47a15f7c96SJohn Baldwin }
48a15f7c96SJohn Baldwin 
49a15f7c96SJohn Baldwin static struct nvmft_controller *
nvmft_controller_alloc(struct nvmft_port * np,uint16_t cntlid,const struct nvmf_fabric_connect_data * data)50a15f7c96SJohn Baldwin nvmft_controller_alloc(struct nvmft_port *np, uint16_t cntlid,
51a15f7c96SJohn Baldwin     const struct nvmf_fabric_connect_data *data)
52a15f7c96SJohn Baldwin {
53a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
54a15f7c96SJohn Baldwin 
55a15f7c96SJohn Baldwin 	ctrlr = malloc(sizeof(*ctrlr), M_NVMFT, M_WAITOK | M_ZERO);
56a15f7c96SJohn Baldwin 	ctrlr->cntlid = cntlid;
57a15f7c96SJohn Baldwin 	ctrlr->np = np;
58a15f7c96SJohn Baldwin 	mtx_init(&ctrlr->lock, "nvmft controller", NULL, MTX_DEF);
59a15f7c96SJohn Baldwin 	callout_init(&ctrlr->ka_timer, 1);
60a15f7c96SJohn Baldwin 	TASK_INIT(&ctrlr->shutdown_task, 0, nvmft_controller_shutdown, ctrlr);
61a15f7c96SJohn Baldwin 	TIMEOUT_TASK_INIT(taskqueue_thread, &ctrlr->terminate_task, 0,
62a15f7c96SJohn Baldwin 	    nvmft_controller_terminate, ctrlr);
63a15f7c96SJohn Baldwin 
64a15f7c96SJohn Baldwin 	ctrlr->cdata = np->cdata;
65a15f7c96SJohn Baldwin 	ctrlr->cdata.ctrlr_id = htole16(cntlid);
66a15f7c96SJohn Baldwin 	memcpy(ctrlr->hostid, data->hostid, sizeof(ctrlr->hostid));
67a15f7c96SJohn Baldwin 	memcpy(ctrlr->hostnqn, data->hostnqn, sizeof(ctrlr->hostnqn));
68a15f7c96SJohn Baldwin 	ctrlr->hip.power_cycles[0] = 1;
69a15f7c96SJohn Baldwin 	ctrlr->create_time = sbinuptime();
70a15f7c96SJohn Baldwin 
71a15f7c96SJohn Baldwin 	ctrlr->changed_ns = malloc(sizeof(*ctrlr->changed_ns), M_NVMFT,
72a15f7c96SJohn Baldwin 	    M_WAITOK | M_ZERO);
73a15f7c96SJohn Baldwin 
74a15f7c96SJohn Baldwin 	return (ctrlr);
75a15f7c96SJohn Baldwin }
76a15f7c96SJohn Baldwin 
77a15f7c96SJohn Baldwin static void
nvmft_controller_free(struct nvmft_controller * ctrlr)78a15f7c96SJohn Baldwin nvmft_controller_free(struct nvmft_controller *ctrlr)
79a15f7c96SJohn Baldwin {
80a15f7c96SJohn Baldwin 	mtx_destroy(&ctrlr->lock);
81a15f7c96SJohn Baldwin 	MPASS(ctrlr->io_qpairs == NULL);
82a15f7c96SJohn Baldwin 	free(ctrlr->changed_ns, M_NVMFT);
83a15f7c96SJohn Baldwin 	free(ctrlr, M_NVMFT);
84a15f7c96SJohn Baldwin }
85a15f7c96SJohn Baldwin 
86a15f7c96SJohn Baldwin static void
nvmft_keep_alive_timer(void * arg)87a15f7c96SJohn Baldwin nvmft_keep_alive_timer(void *arg)
88a15f7c96SJohn Baldwin {
89a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = arg;
90a15f7c96SJohn Baldwin 	int traffic;
91a15f7c96SJohn Baldwin 
92a15f7c96SJohn Baldwin 	if (ctrlr->shutdown)
93a15f7c96SJohn Baldwin 		return;
94a15f7c96SJohn Baldwin 
95a15f7c96SJohn Baldwin 	traffic = atomic_readandclear_int(&ctrlr->ka_active_traffic);
96a15f7c96SJohn Baldwin 	if (traffic == 0) {
97a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
98a15f7c96SJohn Baldwin 		    "disconnecting due to KeepAlive timeout\n");
99a15f7c96SJohn Baldwin 		nvmft_controller_error(ctrlr, NULL, ETIMEDOUT);
100a15f7c96SJohn Baldwin 		return;
101a15f7c96SJohn Baldwin 	}
102a15f7c96SJohn Baldwin 
103a15f7c96SJohn Baldwin 	callout_schedule_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0, C_HARDCLOCK);
104a15f7c96SJohn Baldwin }
105a15f7c96SJohn Baldwin 
1068ced5076SJohn Baldwin static void
nvmft_update_cdata(struct nvmft_controller * ctrlr)1078ced5076SJohn Baldwin nvmft_update_cdata(struct nvmft_controller *ctrlr)
1088ced5076SJohn Baldwin {
1098ced5076SJohn Baldwin 	uint32_t ioccsz, val;
1108ced5076SJohn Baldwin 
1118ced5076SJohn Baldwin 	val = nvmft_max_ioccsz(ctrlr->admin);
1128ced5076SJohn Baldwin 	if (val != 0) {
1138ced5076SJohn Baldwin 		ioccsz = le32toh(ctrlr->cdata.ioccsz) * 16;
1148ced5076SJohn Baldwin 		if (val < ioccsz)
1158ced5076SJohn Baldwin 			ctrlr->cdata.ioccsz = htole32(val / 16);
1168ced5076SJohn Baldwin 	}
1178ced5076SJohn Baldwin }
1188ced5076SJohn Baldwin 
119a15f7c96SJohn Baldwin int
nvmft_handoff_admin_queue(struct nvmft_port * np,enum nvmf_trtype trtype,const nvlist_t * params,const struct nvmf_fabric_connect_cmd * cmd,const struct nvmf_fabric_connect_data * data)120365b89e8SJohn Baldwin nvmft_handoff_admin_queue(struct nvmft_port *np, enum nvmf_trtype trtype,
121365b89e8SJohn Baldwin     const nvlist_t *params, const struct nvmf_fabric_connect_cmd *cmd,
122a15f7c96SJohn Baldwin     const struct nvmf_fabric_connect_data *data)
123a15f7c96SJohn Baldwin {
124a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
125a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
126a15f7c96SJohn Baldwin 	uint32_t kato;
127a15f7c96SJohn Baldwin 	int cntlid;
128a15f7c96SJohn Baldwin 
129a15f7c96SJohn Baldwin 	if (cmd->qid != htole16(0))
130a15f7c96SJohn Baldwin 		return (EINVAL);
131a15f7c96SJohn Baldwin 
132365b89e8SJohn Baldwin 	qp = nvmft_qpair_init(trtype, params, 0, "admin queue");
133dcfa6669SJohn Baldwin 	if (qp == NULL) {
134dcfa6669SJohn Baldwin 		printf("NVMFT: Failed to setup admin queue from %.*s\n",
135dcfa6669SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
136dcfa6669SJohn Baldwin 		return (ENXIO);
137dcfa6669SJohn Baldwin 	}
138a15f7c96SJohn Baldwin 
13997ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
140a15f7c96SJohn Baldwin 	cntlid = alloc_unr(np->ids);
141a15f7c96SJohn Baldwin 	if (cntlid == -1) {
14297ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
143a15f7c96SJohn Baldwin 		printf("NVMFT: Unable to allocate controller for %.*s\n",
144a15f7c96SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
145a15f7c96SJohn Baldwin 		nvmft_connect_error(qp, cmd, NVME_SCT_COMMAND_SPECIFIC,
146a15f7c96SJohn Baldwin 		    NVMF_FABRIC_SC_INVALID_HOST);
147a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
148a15f7c96SJohn Baldwin 		return (ENOMEM);
149a15f7c96SJohn Baldwin 	}
150a15f7c96SJohn Baldwin 
151a15f7c96SJohn Baldwin #ifdef INVARIANTS
152a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
153a15f7c96SJohn Baldwin 		KASSERT(ctrlr->cntlid != cntlid,
154a15f7c96SJohn Baldwin 		    ("%s: duplicate controllers with id %d", __func__, cntlid));
155a15f7c96SJohn Baldwin 	}
156a15f7c96SJohn Baldwin #endif
15797ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
158a15f7c96SJohn Baldwin 
159a15f7c96SJohn Baldwin 	ctrlr = nvmft_controller_alloc(np, cntlid, data);
16097ca2adaSJohn Baldwin 
16197ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
16297ca2adaSJohn Baldwin 	if (!np->online) {
16397ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
16497ca2adaSJohn Baldwin 		nvmft_controller_free(ctrlr);
16597ca2adaSJohn Baldwin 		free_unr(np->ids, cntlid);
16697ca2adaSJohn Baldwin 		nvmft_qpair_destroy(qp);
16797ca2adaSJohn Baldwin 		return (ENXIO);
16897ca2adaSJohn Baldwin 	}
16997ca2adaSJohn Baldwin 	nvmft_port_ref(np);
17097ca2adaSJohn Baldwin 	TAILQ_INSERT_TAIL(&np->controllers, ctrlr, link);
17197ca2adaSJohn Baldwin 
172a15f7c96SJohn Baldwin 	nvmft_printf(ctrlr, "associated with %.*s\n",
173a15f7c96SJohn Baldwin 	    (int)sizeof(data->hostnqn), data->hostnqn);
174a15f7c96SJohn Baldwin 	ctrlr->admin = qp;
175365b89e8SJohn Baldwin 	ctrlr->trtype = trtype;
1768ced5076SJohn Baldwin 	nvmft_update_cdata(ctrlr);
177a15f7c96SJohn Baldwin 
178a15f7c96SJohn Baldwin 	/*
179a15f7c96SJohn Baldwin 	 * The spec requires a non-zero KeepAlive timer, but allow a
180a15f7c96SJohn Baldwin 	 * zero KATO value to match Linux.
181a15f7c96SJohn Baldwin 	 */
182a15f7c96SJohn Baldwin 	kato = le32toh(cmd->kato);
183a15f7c96SJohn Baldwin 	if (kato != 0) {
184a15f7c96SJohn Baldwin 		/*
185a15f7c96SJohn Baldwin 		 * Round up to 1 second matching granularity
186a15f7c96SJohn Baldwin 		 * advertised in cdata.
187a15f7c96SJohn Baldwin 		 */
188a15f7c96SJohn Baldwin 		ctrlr->ka_sbt = mstosbt(roundup(kato, 1000));
189a15f7c96SJohn Baldwin 		callout_reset_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0,
190a15f7c96SJohn Baldwin 		    nvmft_keep_alive_timer, ctrlr, C_HARDCLOCK);
191a15f7c96SJohn Baldwin 	}
19297ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
193a15f7c96SJohn Baldwin 
194a15f7c96SJohn Baldwin 	nvmft_finish_accept(qp, cmd, ctrlr);
195a15f7c96SJohn Baldwin 
196a15f7c96SJohn Baldwin 	return (0);
197a15f7c96SJohn Baldwin }
198a15f7c96SJohn Baldwin 
199a15f7c96SJohn Baldwin int
nvmft_handoff_io_queue(struct nvmft_port * np,enum nvmf_trtype trtype,const nvlist_t * params,const struct nvmf_fabric_connect_cmd * cmd,const struct nvmf_fabric_connect_data * data)200365b89e8SJohn Baldwin nvmft_handoff_io_queue(struct nvmft_port *np, enum nvmf_trtype trtype,
201365b89e8SJohn Baldwin     const nvlist_t *params, const struct nvmf_fabric_connect_cmd *cmd,
202a15f7c96SJohn Baldwin     const struct nvmf_fabric_connect_data *data)
203a15f7c96SJohn Baldwin {
204a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
205a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
206a15f7c96SJohn Baldwin 	char name[16];
207a15f7c96SJohn Baldwin 	uint16_t cntlid, qid;
208a15f7c96SJohn Baldwin 
209a15f7c96SJohn Baldwin 	qid = le16toh(cmd->qid);
210a15f7c96SJohn Baldwin 	if (qid == 0)
211a15f7c96SJohn Baldwin 		return (EINVAL);
212a15f7c96SJohn Baldwin 	cntlid = le16toh(data->cntlid);
213a15f7c96SJohn Baldwin 
214a15f7c96SJohn Baldwin 	snprintf(name, sizeof(name), "I/O queue %u", qid);
215365b89e8SJohn Baldwin 	qp = nvmft_qpair_init(trtype, params, qid, name);
216dcfa6669SJohn Baldwin 	if (qp == NULL) {
217dcfa6669SJohn Baldwin 		printf("NVMFT: Failed to setup I/O queue %u from %.*s\n", qid,
218dcfa6669SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
219dcfa6669SJohn Baldwin 		return (ENXIO);
220dcfa6669SJohn Baldwin 	}
221a15f7c96SJohn Baldwin 
22297ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
223a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
224a15f7c96SJohn Baldwin 		if (ctrlr->cntlid == cntlid)
225a15f7c96SJohn Baldwin 			break;
226a15f7c96SJohn Baldwin 	}
227a15f7c96SJohn Baldwin 	if (ctrlr == NULL) {
22897ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
229a15f7c96SJohn Baldwin 		printf("NVMFT: Nonexistent controller %u for I/O queue %u from %.*s\n",
230a15f7c96SJohn Baldwin 		    ctrlr->cntlid, qid, (int)sizeof(data->hostnqn),
231a15f7c96SJohn Baldwin 		    data->hostnqn);
232a15f7c96SJohn Baldwin 		nvmft_connect_invalid_parameters(qp, cmd, true,
233a15f7c96SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_data, cntlid));
234a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
235a15f7c96SJohn Baldwin 		return (ENOENT);
236a15f7c96SJohn Baldwin 	}
237a15f7c96SJohn Baldwin 
238a15f7c96SJohn Baldwin 	if (memcmp(ctrlr->hostid, data->hostid, sizeof(ctrlr->hostid)) != 0) {
23997ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
240a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
241a15f7c96SJohn Baldwin 		    "hostid mismatch for I/O queue %u from %.*s\n", qid,
242a15f7c96SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
243a15f7c96SJohn Baldwin 		nvmft_connect_invalid_parameters(qp, cmd, true,
244a15f7c96SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_data, hostid));
245a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
246a15f7c96SJohn Baldwin 		return (EINVAL);
247a15f7c96SJohn Baldwin 	}
248a15f7c96SJohn Baldwin 	if (memcmp(ctrlr->hostnqn, data->hostnqn, sizeof(ctrlr->hostnqn)) != 0) {
24997ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
250a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
251a15f7c96SJohn Baldwin 		    "hostnqn mismatch for I/O queue %u from %.*s\n", qid,
252a15f7c96SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
253a15f7c96SJohn Baldwin 		nvmft_connect_invalid_parameters(qp, cmd, true,
254a15f7c96SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_data, hostnqn));
255a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
256a15f7c96SJohn Baldwin 		return (EINVAL);
257a15f7c96SJohn Baldwin 	}
258a15f7c96SJohn Baldwin 
259365b89e8SJohn Baldwin 	/* XXX: Require trtype == ctrlr->trtype? */
260a15f7c96SJohn Baldwin 
261a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
262a15f7c96SJohn Baldwin 	if (ctrlr->shutdown) {
263a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
26497ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
265a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
266a15f7c96SJohn Baldwin 		    "attempt to create I/O queue %u on disabled controller from %.*s\n",
267a15f7c96SJohn Baldwin 		    qid, (int)sizeof(data->hostnqn), data->hostnqn);
268a15f7c96SJohn Baldwin 		nvmft_connect_invalid_parameters(qp, cmd, true,
269a15f7c96SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_data, cntlid));
270a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
271a15f7c96SJohn Baldwin 		return (EINVAL);
272a15f7c96SJohn Baldwin 	}
273a15f7c96SJohn Baldwin 	if (ctrlr->num_io_queues == 0) {
274a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
27597ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
276a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
277a15f7c96SJohn Baldwin 		    "attempt to create I/O queue %u without enabled queues from %.*s\n",
278a15f7c96SJohn Baldwin 		    qid, (int)sizeof(data->hostnqn), data->hostnqn);
279a15f7c96SJohn Baldwin 		nvmft_connect_error(qp, cmd, NVME_SCT_GENERIC,
280a15f7c96SJohn Baldwin 		    NVME_SC_COMMAND_SEQUENCE_ERROR);
281a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
282a15f7c96SJohn Baldwin 		return (EINVAL);
283a15f7c96SJohn Baldwin 	}
284a15f7c96SJohn Baldwin 	if (cmd->qid > ctrlr->num_io_queues) {
285a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
28697ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
287a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
288a15f7c96SJohn Baldwin 		    "attempt to create invalid I/O queue %u from %.*s\n", qid,
289a15f7c96SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
290a15f7c96SJohn Baldwin 		nvmft_connect_invalid_parameters(qp, cmd, false,
291a15f7c96SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_cmd, qid));
292a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
293a15f7c96SJohn Baldwin 		return (EINVAL);
294a15f7c96SJohn Baldwin 	}
295a15f7c96SJohn Baldwin 	if (ctrlr->io_qpairs[qid - 1].qp != NULL) {
296a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
29797ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
298a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
299a15f7c96SJohn Baldwin 		    "attempt to re-create I/O queue %u from %.*s\n", qid,
300a15f7c96SJohn Baldwin 		    (int)sizeof(data->hostnqn), data->hostnqn);
301a15f7c96SJohn Baldwin 		nvmft_connect_error(qp, cmd, NVME_SCT_GENERIC,
302a15f7c96SJohn Baldwin 		    NVME_SC_COMMAND_SEQUENCE_ERROR);
303a15f7c96SJohn Baldwin 		nvmft_qpair_destroy(qp);
304a15f7c96SJohn Baldwin 		return (EINVAL);
305a15f7c96SJohn Baldwin 	}
306a15f7c96SJohn Baldwin 
307a15f7c96SJohn Baldwin 	ctrlr->io_qpairs[qid - 1].qp = qp;
308a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
30997ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
310a15f7c96SJohn Baldwin 	nvmft_finish_accept(qp, cmd, ctrlr);
311a15f7c96SJohn Baldwin 
312a15f7c96SJohn Baldwin 	return (0);
313a15f7c96SJohn Baldwin }
314a15f7c96SJohn Baldwin 
315a15f7c96SJohn Baldwin static void
nvmft_controller_shutdown(void * arg,int pending)316a15f7c96SJohn Baldwin nvmft_controller_shutdown(void *arg, int pending)
317a15f7c96SJohn Baldwin {
318a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = arg;
319a15f7c96SJohn Baldwin 
320a15f7c96SJohn Baldwin 	MPASS(pending == 1);
321a15f7c96SJohn Baldwin 
322a15f7c96SJohn Baldwin 	/*
323a15f7c96SJohn Baldwin 	 * Shutdown all I/O queues to terminate pending datamoves and
324a15f7c96SJohn Baldwin 	 * stop receiving new commands.
325a15f7c96SJohn Baldwin 	 */
326a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
327a15f7c96SJohn Baldwin 	for (u_int i = 0; i < ctrlr->num_io_queues; i++) {
328a15f7c96SJohn Baldwin 		if (ctrlr->io_qpairs[i].qp != NULL) {
329a15f7c96SJohn Baldwin 			ctrlr->io_qpairs[i].shutdown = true;
330a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
331a15f7c96SJohn Baldwin 			nvmft_qpair_shutdown(ctrlr->io_qpairs[i].qp);
332a15f7c96SJohn Baldwin 			mtx_lock(&ctrlr->lock);
333a15f7c96SJohn Baldwin 		}
334a15f7c96SJohn Baldwin 	}
335a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
336a15f7c96SJohn Baldwin 
337a15f7c96SJohn Baldwin 	/* Terminate active CTL commands. */
338a15f7c96SJohn Baldwin 	nvmft_terminate_commands(ctrlr);
339a15f7c96SJohn Baldwin 
340a15f7c96SJohn Baldwin 	/* Wait for all pending CTL commands to complete. */
341a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
342a15f7c96SJohn Baldwin 	while (ctrlr->pending_commands != 0)
343a15f7c96SJohn Baldwin 		mtx_sleep(&ctrlr->pending_commands, &ctrlr->lock, 0, "nvmftsh",
344a15f7c96SJohn Baldwin 		    hz / 100);
345a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
346a15f7c96SJohn Baldwin 
347a15f7c96SJohn Baldwin 	/* Delete all of the I/O queues. */
348a15f7c96SJohn Baldwin 	for (u_int i = 0; i < ctrlr->num_io_queues; i++) {
349a15f7c96SJohn Baldwin 		if (ctrlr->io_qpairs[i].qp != NULL)
350a15f7c96SJohn Baldwin 			nvmft_qpair_destroy(ctrlr->io_qpairs[i].qp);
351a15f7c96SJohn Baldwin 	}
352a15f7c96SJohn Baldwin 	free(ctrlr->io_qpairs, M_NVMFT);
353a15f7c96SJohn Baldwin 	ctrlr->io_qpairs = NULL;
354a15f7c96SJohn Baldwin 
355a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
356a15f7c96SJohn Baldwin 	ctrlr->num_io_queues = 0;
357a15f7c96SJohn Baldwin 
358a15f7c96SJohn Baldwin 	/* Mark shutdown complete. */
359a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CSTS_REG_SHST, ctrlr->csts) == NVME_SHST_OCCURRING) {
360a15f7c96SJohn Baldwin 		ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_SHST);
361a15f7c96SJohn Baldwin 		ctrlr->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE);
362a15f7c96SJohn Baldwin 	}
363a15f7c96SJohn Baldwin 
364a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CSTS_REG_CFS, ctrlr->csts) == 0) {
365a15f7c96SJohn Baldwin 		ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_RDY);
366a15f7c96SJohn Baldwin 		ctrlr->shutdown = false;
367a15f7c96SJohn Baldwin 	}
368a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
369a15f7c96SJohn Baldwin 
370a15f7c96SJohn Baldwin 	/*
371a15f7c96SJohn Baldwin 	 * If the admin queue was closed while shutting down or a
372a15f7c96SJohn Baldwin 	 * fatal controller error has occurred, terminate the
373a15f7c96SJohn Baldwin 	 * association immediately, otherwise wait up to 2 minutes
374a15f7c96SJohn Baldwin 	 * (NVMe-over-Fabrics 1.1 4.6).
375a15f7c96SJohn Baldwin 	 */
376a15f7c96SJohn Baldwin 	if (ctrlr->admin_closed || NVMEV(NVME_CSTS_REG_CFS, ctrlr->csts) != 0)
377a15f7c96SJohn Baldwin 		nvmft_controller_terminate(ctrlr, 0);
378a15f7c96SJohn Baldwin 	else
379a15f7c96SJohn Baldwin 		taskqueue_enqueue_timeout(taskqueue_thread,
380a15f7c96SJohn Baldwin 		    &ctrlr->terminate_task, hz * 60 * 2);
381a15f7c96SJohn Baldwin }
382a15f7c96SJohn Baldwin 
383a15f7c96SJohn Baldwin static void
nvmft_controller_terminate(void * arg,int pending)384a15f7c96SJohn Baldwin nvmft_controller_terminate(void *arg, int pending)
385a15f7c96SJohn Baldwin {
386a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = arg;
387a15f7c96SJohn Baldwin 	struct nvmft_port *np;
388a15f7c96SJohn Baldwin 	bool wakeup_np;
389a15f7c96SJohn Baldwin 
390a15f7c96SJohn Baldwin 	/* If the controller has been re-enabled, nothing to do. */
391a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
392a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) != 0) {
393a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
394a15f7c96SJohn Baldwin 
395a15f7c96SJohn Baldwin 		if (ctrlr->ka_sbt != 0)
396a15f7c96SJohn Baldwin 			callout_schedule_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0,
397a15f7c96SJohn Baldwin 			    C_HARDCLOCK);
398a15f7c96SJohn Baldwin 		return;
399a15f7c96SJohn Baldwin 	}
400a15f7c96SJohn Baldwin 
401a15f7c96SJohn Baldwin 	/* Disable updates to CC while destroying admin qpair. */
402a15f7c96SJohn Baldwin 	ctrlr->shutdown = true;
403a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
404a15f7c96SJohn Baldwin 
405a15f7c96SJohn Baldwin 	nvmft_qpair_destroy(ctrlr->admin);
406a15f7c96SJohn Baldwin 
407a15f7c96SJohn Baldwin 	/* Remove association (CNTLID). */
408a15f7c96SJohn Baldwin 	np = ctrlr->np;
40997ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
410a15f7c96SJohn Baldwin 	TAILQ_REMOVE(&np->controllers, ctrlr, link);
411a15f7c96SJohn Baldwin 	wakeup_np = (!np->online && TAILQ_EMPTY(&np->controllers));
41297ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
41397ca2adaSJohn Baldwin 	free_unr(np->ids, ctrlr->cntlid);
414a15f7c96SJohn Baldwin 	if (wakeup_np)
415a15f7c96SJohn Baldwin 		wakeup(np);
416a15f7c96SJohn Baldwin 
417a15f7c96SJohn Baldwin 	callout_drain(&ctrlr->ka_timer);
418a15f7c96SJohn Baldwin 
419a15f7c96SJohn Baldwin 	nvmft_printf(ctrlr, "association terminated\n");
420a15f7c96SJohn Baldwin 	nvmft_controller_free(ctrlr);
421a15f7c96SJohn Baldwin 	nvmft_port_rele(np);
422a15f7c96SJohn Baldwin }
423a15f7c96SJohn Baldwin 
424a15f7c96SJohn Baldwin void
nvmft_controller_error(struct nvmft_controller * ctrlr,struct nvmft_qpair * qp,int error)425a15f7c96SJohn Baldwin nvmft_controller_error(struct nvmft_controller *ctrlr, struct nvmft_qpair *qp,
426a15f7c96SJohn Baldwin     int error)
427a15f7c96SJohn Baldwin {
428a15f7c96SJohn Baldwin 	/*
429a15f7c96SJohn Baldwin 	 * If a queue pair is closed, that isn't an error per se.
430a15f7c96SJohn Baldwin 	 * That just means additional commands cannot be received on
431a15f7c96SJohn Baldwin 	 * that queue pair.
432a15f7c96SJohn Baldwin 	 *
433a15f7c96SJohn Baldwin 	 * If the admin queue pair is closed while idle or while
434a15f7c96SJohn Baldwin 	 * shutting down, terminate the association immediately.
435a15f7c96SJohn Baldwin 	 *
436a15f7c96SJohn Baldwin 	 * If an I/O queue pair is closed, just ignore it.
437a15f7c96SJohn Baldwin 	 */
438a15f7c96SJohn Baldwin 	if (error == 0) {
439a15f7c96SJohn Baldwin 		if (qp != ctrlr->admin)
440a15f7c96SJohn Baldwin 			return;
441a15f7c96SJohn Baldwin 
442a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
443a15f7c96SJohn Baldwin 		if (ctrlr->shutdown) {
444a15f7c96SJohn Baldwin 			ctrlr->admin_closed = true;
445a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
446a15f7c96SJohn Baldwin 			return;
447a15f7c96SJohn Baldwin 		}
448a15f7c96SJohn Baldwin 
449a15f7c96SJohn Baldwin 		if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) == 0) {
450a15f7c96SJohn Baldwin 			MPASS(ctrlr->num_io_queues == 0);
451a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
452a15f7c96SJohn Baldwin 
453a15f7c96SJohn Baldwin 			/*
454a15f7c96SJohn Baldwin 			 * Ok to drop lock here since ctrlr->cc can't
455a15f7c96SJohn Baldwin 			 * change if the admin queue pair has closed.
456a15f7c96SJohn Baldwin 			 * This also means no new queues can be handed
457a15f7c96SJohn Baldwin 			 * off, etc.  Note that since there are no I/O
458a15f7c96SJohn Baldwin 			 * queues, only the admin queue needs to be
459a15f7c96SJohn Baldwin 			 * destroyed, so it is safe to skip
460a15f7c96SJohn Baldwin 			 * nvmft_controller_shutdown and just schedule
461a15f7c96SJohn Baldwin 			 * nvmft_controller_terminate.  Note that we
462a15f7c96SJohn Baldwin 			 * cannot call nvmft_controller_terminate from
463a15f7c96SJohn Baldwin 			 * here directly as this is called from the
464a15f7c96SJohn Baldwin 			 * transport layer and freeing the admin qpair
465a15f7c96SJohn Baldwin 			 * might deadlock waiting for the current
466a15f7c96SJohn Baldwin 			 * thread to exit.
467a15f7c96SJohn Baldwin 			 */
468a15f7c96SJohn Baldwin 			if (taskqueue_cancel_timeout(taskqueue_thread,
469a15f7c96SJohn Baldwin 			    &ctrlr->terminate_task, NULL) == 0)
470a15f7c96SJohn Baldwin 				taskqueue_enqueue_timeout(taskqueue_thread,
471a15f7c96SJohn Baldwin 				    &ctrlr->terminate_task, 0);
472a15f7c96SJohn Baldwin 			return;
473a15f7c96SJohn Baldwin 		}
474a15f7c96SJohn Baldwin 
475a15f7c96SJohn Baldwin 		/*
476a15f7c96SJohn Baldwin 		 * Treat closing of the admin queue pair while enabled
477a15f7c96SJohn Baldwin 		 * as a transport error.  Note that the admin queue
478a15f7c96SJohn Baldwin 		 * pair has been closed.
479a15f7c96SJohn Baldwin 		 */
480a15f7c96SJohn Baldwin 		ctrlr->admin_closed = true;
481a15f7c96SJohn Baldwin 	} else
482a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
483a15f7c96SJohn Baldwin 
484a15f7c96SJohn Baldwin 	/* Ignore transport errors if we are already shutting down. */
485a15f7c96SJohn Baldwin 	if (ctrlr->shutdown) {
486a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
487a15f7c96SJohn Baldwin 		return;
488a15f7c96SJohn Baldwin 	}
489a15f7c96SJohn Baldwin 
490a15f7c96SJohn Baldwin 	ctrlr->csts |= NVMEF(NVME_CSTS_REG_CFS, 1);
491a15f7c96SJohn Baldwin 	ctrlr->cc &= ~NVMEM(NVME_CC_REG_EN);
492a15f7c96SJohn Baldwin 	ctrlr->shutdown = true;
493a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
494a15f7c96SJohn Baldwin 
495a15f7c96SJohn Baldwin 	callout_stop(&ctrlr->ka_timer);
496a15f7c96SJohn Baldwin 	taskqueue_enqueue(taskqueue_thread, &ctrlr->shutdown_task);
497a15f7c96SJohn Baldwin }
498a15f7c96SJohn Baldwin 
499a15f7c96SJohn Baldwin /* Wrapper around m_getm2 that also sets m_len in the mbufs in the chain. */
500a15f7c96SJohn Baldwin static struct mbuf *
m_getml(size_t len,int how)501a15f7c96SJohn Baldwin m_getml(size_t len, int how)
502a15f7c96SJohn Baldwin {
503a15f7c96SJohn Baldwin 	struct mbuf *m, *n;
504a15f7c96SJohn Baldwin 
505a15f7c96SJohn Baldwin 	m = m_getm2(NULL, len, how, MT_DATA, 0);
506a15f7c96SJohn Baldwin 	if (m == NULL)
507a15f7c96SJohn Baldwin 		return (NULL);
508a15f7c96SJohn Baldwin 	for (n = m; len > 0; n = n->m_next) {
509a15f7c96SJohn Baldwin 		n->m_len = M_SIZE(n);
510a15f7c96SJohn Baldwin 		if (n->m_len >= len) {
511a15f7c96SJohn Baldwin 			n->m_len = len;
512a15f7c96SJohn Baldwin 			MPASS(n->m_next == NULL);
513a15f7c96SJohn Baldwin 		}
514a15f7c96SJohn Baldwin 		len -= n->m_len;
515a15f7c96SJohn Baldwin 	}
516a15f7c96SJohn Baldwin 	return (m);
517a15f7c96SJohn Baldwin }
518a15f7c96SJohn Baldwin 
519a15f7c96SJohn Baldwin static void
m_zero(struct mbuf * m,u_int offset,u_int len)520a15f7c96SJohn Baldwin m_zero(struct mbuf *m, u_int offset, u_int len)
521a15f7c96SJohn Baldwin {
522a15f7c96SJohn Baldwin 	u_int todo;
523a15f7c96SJohn Baldwin 
524a15f7c96SJohn Baldwin 	if (len == 0)
525a15f7c96SJohn Baldwin 		return;
526a15f7c96SJohn Baldwin 
527a15f7c96SJohn Baldwin 	while (m->m_len <= offset) {
528a15f7c96SJohn Baldwin 		offset -= m->m_len;
529a15f7c96SJohn Baldwin 		m = m->m_next;
530a15f7c96SJohn Baldwin 	}
531a15f7c96SJohn Baldwin 
532a15f7c96SJohn Baldwin 	todo = m->m_len - offset;
533a15f7c96SJohn Baldwin 	if (todo > len)
534a15f7c96SJohn Baldwin 		todo = len;
535a15f7c96SJohn Baldwin 	memset(mtodo(m, offset), 0, todo);
536a15f7c96SJohn Baldwin 	m = m->m_next;
537a15f7c96SJohn Baldwin 	len -= todo;
538a15f7c96SJohn Baldwin 
539a15f7c96SJohn Baldwin 	while (len > 0) {
540a15f7c96SJohn Baldwin 		todo = m->m_len;
541a15f7c96SJohn Baldwin 		if (todo > len)
542a15f7c96SJohn Baldwin 			todo = len;
543a15f7c96SJohn Baldwin 		memset(mtod(m, void *), 0, todo);
544a15f7c96SJohn Baldwin 		m = m->m_next;
545a15f7c96SJohn Baldwin 		len -= todo;
546a15f7c96SJohn Baldwin 	}
547a15f7c96SJohn Baldwin }
548a15f7c96SJohn Baldwin 
549a15f7c96SJohn Baldwin static void
handle_get_log_page(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvme_command * cmd)550a15f7c96SJohn Baldwin handle_get_log_page(struct nvmft_controller *ctrlr,
551a15f7c96SJohn Baldwin     struct nvmf_capsule *nc, const struct nvme_command *cmd)
552a15f7c96SJohn Baldwin {
553a15f7c96SJohn Baldwin 	struct mbuf *m;
554a15f7c96SJohn Baldwin 	uint64_t offset;
555a15f7c96SJohn Baldwin 	uint32_t numd;
556a15f7c96SJohn Baldwin 	size_t len, todo;
557a15f7c96SJohn Baldwin 	u_int status;
558a15f7c96SJohn Baldwin 	uint8_t lid;
559a15f7c96SJohn Baldwin 	bool rae;
560a15f7c96SJohn Baldwin 
561a15f7c96SJohn Baldwin 	lid = le32toh(cmd->cdw10) & 0xff;
562a15f7c96SJohn Baldwin 	rae = (le32toh(cmd->cdw10) & (1U << 15)) != 0;
563a15f7c96SJohn Baldwin 	numd = le32toh(cmd->cdw10) >> 16 | le32toh(cmd->cdw11) << 16;
564a15f7c96SJohn Baldwin 	offset = le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32;
565a15f7c96SJohn Baldwin 
566a15f7c96SJohn Baldwin 	if (offset % 3 != 0) {
567a15f7c96SJohn Baldwin 		status = NVME_SC_INVALID_FIELD;
568a15f7c96SJohn Baldwin 		goto done;
569a15f7c96SJohn Baldwin 	}
570a15f7c96SJohn Baldwin 
571a15f7c96SJohn Baldwin 	len = (numd + 1) * 4;
572a15f7c96SJohn Baldwin 
573a15f7c96SJohn Baldwin 	switch (lid) {
574a15f7c96SJohn Baldwin 	case NVME_LOG_ERROR:
575a15f7c96SJohn Baldwin 		todo = 0;
576a15f7c96SJohn Baldwin 
577a15f7c96SJohn Baldwin 		m = m_getml(len, M_WAITOK);
578a15f7c96SJohn Baldwin 		if (todo != len)
579a15f7c96SJohn Baldwin 			m_zero(m, todo, len - todo);
580a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m, len);
581a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
582a15f7c96SJohn Baldwin 		break;
583a15f7c96SJohn Baldwin 	case NVME_LOG_HEALTH_INFORMATION:
584a15f7c96SJohn Baldwin 	{
585a15f7c96SJohn Baldwin 		struct nvme_health_information_page hip;
586a15f7c96SJohn Baldwin 
587a15f7c96SJohn Baldwin 		if (offset >= sizeof(hip)) {
588a15f7c96SJohn Baldwin 			status = NVME_SC_INVALID_FIELD;
589a15f7c96SJohn Baldwin 			goto done;
590a15f7c96SJohn Baldwin 		}
591a15f7c96SJohn Baldwin 		todo = sizeof(hip) - offset;
592a15f7c96SJohn Baldwin 		if (todo > len)
593a15f7c96SJohn Baldwin 			todo = len;
594a15f7c96SJohn Baldwin 
595a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
596a15f7c96SJohn Baldwin 		hip = ctrlr->hip;
597a15f7c96SJohn Baldwin 		hip.controller_busy_time[0] =
598a15f7c96SJohn Baldwin 		    sbintime_getsec(ctrlr->busy_total) / 60;
599a15f7c96SJohn Baldwin 		hip.power_on_hours[0] =
600a15f7c96SJohn Baldwin 		    sbintime_getsec(sbinuptime() - ctrlr->create_time) / 3600;
601a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
602a15f7c96SJohn Baldwin 
603a15f7c96SJohn Baldwin 		m = m_getml(len, M_WAITOK);
604a15f7c96SJohn Baldwin 		m_copyback(m, 0, todo, (char *)&hip + offset);
605a15f7c96SJohn Baldwin 		if (todo != len)
606a15f7c96SJohn Baldwin 			m_zero(m, todo, len - todo);
607a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m, len);
608a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
609a15f7c96SJohn Baldwin 		break;
610a15f7c96SJohn Baldwin 	}
611a15f7c96SJohn Baldwin 	case NVME_LOG_FIRMWARE_SLOT:
612a15f7c96SJohn Baldwin 		if (offset >= sizeof(ctrlr->np->fp)) {
613a15f7c96SJohn Baldwin 			status = NVME_SC_INVALID_FIELD;
614a15f7c96SJohn Baldwin 			goto done;
615a15f7c96SJohn Baldwin 		}
616a15f7c96SJohn Baldwin 		todo = sizeof(ctrlr->np->fp) - offset;
617a15f7c96SJohn Baldwin 		if (todo > len)
618a15f7c96SJohn Baldwin 			todo = len;
619a15f7c96SJohn Baldwin 
620a15f7c96SJohn Baldwin 		m = m_getml(len, M_WAITOK);
621a15f7c96SJohn Baldwin 		m_copyback(m, 0, todo, (char *)&ctrlr->np->fp + offset);
622a15f7c96SJohn Baldwin 		if (todo != len)
623a15f7c96SJohn Baldwin 			m_zero(m, todo, len - todo);
624a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m, len);
625a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
626a15f7c96SJohn Baldwin 		break;
627a15f7c96SJohn Baldwin 	case NVME_LOG_CHANGED_NAMESPACE:
628a15f7c96SJohn Baldwin 		if (offset >= sizeof(*ctrlr->changed_ns)) {
629a15f7c96SJohn Baldwin 			status = NVME_SC_INVALID_FIELD;
630a15f7c96SJohn Baldwin 			goto done;
631a15f7c96SJohn Baldwin 		}
632a15f7c96SJohn Baldwin 		todo = sizeof(*ctrlr->changed_ns) - offset;
633a15f7c96SJohn Baldwin 		if (todo > len)
634a15f7c96SJohn Baldwin 			todo = len;
635a15f7c96SJohn Baldwin 
636a15f7c96SJohn Baldwin 		m = m_getml(len, M_WAITOK);
637a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
638a15f7c96SJohn Baldwin 		m_copyback(m, 0, todo, (char *)ctrlr->changed_ns + offset);
639a15f7c96SJohn Baldwin 		if (offset == 0 && len == sizeof(*ctrlr->changed_ns))
640a15f7c96SJohn Baldwin 			memset(ctrlr->changed_ns, 0,
641a15f7c96SJohn Baldwin 			    sizeof(*ctrlr->changed_ns));
642a15f7c96SJohn Baldwin 		if (!rae)
643a15f7c96SJohn Baldwin 			ctrlr->changed_ns_reported = false;
644a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
645a15f7c96SJohn Baldwin 		if (todo != len)
646a15f7c96SJohn Baldwin 			m_zero(m, todo, len - todo);
647a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m, len);
648a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
649a15f7c96SJohn Baldwin 		break;
650a15f7c96SJohn Baldwin 	default:
651a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "Unsupported page %#x for GET_LOG_PAGE\n",
652a15f7c96SJohn Baldwin 		    lid);
653a15f7c96SJohn Baldwin 		status = NVME_SC_INVALID_FIELD;
654a15f7c96SJohn Baldwin 		break;
655a15f7c96SJohn Baldwin 	}
656a15f7c96SJohn Baldwin 
657a15f7c96SJohn Baldwin done:
658a15f7c96SJohn Baldwin 	if (status == NVMF_SUCCESS_SENT)
659a15f7c96SJohn Baldwin 		nvmft_command_completed(ctrlr->admin, nc);
660a15f7c96SJohn Baldwin 	else
661a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc, status);
662a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
663a15f7c96SJohn Baldwin }
664a15f7c96SJohn Baldwin 
665a15f7c96SJohn Baldwin static void
m_free_nslist(struct mbuf * m)666a15f7c96SJohn Baldwin m_free_nslist(struct mbuf *m)
667a15f7c96SJohn Baldwin {
668a15f7c96SJohn Baldwin 	free(m->m_ext.ext_arg1, M_NVMFT);
669a15f7c96SJohn Baldwin }
670a15f7c96SJohn Baldwin 
671a15f7c96SJohn Baldwin static void
handle_identify_command(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvme_command * cmd)672a15f7c96SJohn Baldwin handle_identify_command(struct nvmft_controller *ctrlr,
673a15f7c96SJohn Baldwin     struct nvmf_capsule *nc, const struct nvme_command *cmd)
674a15f7c96SJohn Baldwin {
675a15f7c96SJohn Baldwin 	struct mbuf *m;
676a15f7c96SJohn Baldwin 	size_t data_len;
677a15f7c96SJohn Baldwin 	u_int status;
678a15f7c96SJohn Baldwin 	uint8_t cns;
679a15f7c96SJohn Baldwin 
680a15f7c96SJohn Baldwin 	cns = le32toh(cmd->cdw10) & 0xFF;
681a15f7c96SJohn Baldwin 	data_len = nvmf_capsule_data_len(nc);
682a15f7c96SJohn Baldwin 	if (data_len != sizeof(ctrlr->cdata)) {
683a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
684a15f7c96SJohn Baldwin 		    "Invalid length %zu for IDENTIFY with CNS %#x\n", data_len,
685a15f7c96SJohn Baldwin 		    cns);
686a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc,
687a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_OPCODE);
688a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
689a15f7c96SJohn Baldwin 		return;
690a15f7c96SJohn Baldwin 	}
691a15f7c96SJohn Baldwin 
692a15f7c96SJohn Baldwin 	switch (cns) {
693a15f7c96SJohn Baldwin 	case 0:	/* Namespace data. */
694a15f7c96SJohn Baldwin 	case 3:	/* Namespace Identification Descriptor list. */
695a15f7c96SJohn Baldwin 		nvmft_dispatch_command(ctrlr->admin, nc, true);
696a15f7c96SJohn Baldwin 		return;
697a15f7c96SJohn Baldwin 	case 1:
698a15f7c96SJohn Baldwin 		/* Controller data. */
699a15f7c96SJohn Baldwin 		m = m_getml(sizeof(ctrlr->cdata), M_WAITOK);
700a15f7c96SJohn Baldwin 		m_copyback(m, 0, sizeof(ctrlr->cdata), (void *)&ctrlr->cdata);
701a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m,
702a15f7c96SJohn Baldwin 		    sizeof(ctrlr->cdata));
703a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
704a15f7c96SJohn Baldwin 		break;
705a15f7c96SJohn Baldwin 	case 2:
706a15f7c96SJohn Baldwin 	{
707a15f7c96SJohn Baldwin 		/* Active namespace list. */
708a15f7c96SJohn Baldwin 		struct nvme_ns_list *nslist;
709a15f7c96SJohn Baldwin 		uint32_t nsid;
710a15f7c96SJohn Baldwin 
711a15f7c96SJohn Baldwin 		nsid = le32toh(cmd->nsid);
712a15f7c96SJohn Baldwin 		if (nsid >= 0xfffffffe) {
713a15f7c96SJohn Baldwin 			status = NVME_SC_INVALID_FIELD;
714a15f7c96SJohn Baldwin 			break;
715a15f7c96SJohn Baldwin 		}
716a15f7c96SJohn Baldwin 
717a15f7c96SJohn Baldwin 		nslist = malloc(sizeof(*nslist), M_NVMFT, M_WAITOK | M_ZERO);
718a15f7c96SJohn Baldwin 		nvmft_populate_active_nslist(ctrlr->np, nsid, nslist);
719a15f7c96SJohn Baldwin 		m = m_get(M_WAITOK, MT_DATA);
720a15f7c96SJohn Baldwin 		m_extadd(m, (void *)nslist, sizeof(*nslist), m_free_nslist,
721a15f7c96SJohn Baldwin 		    nslist, NULL, 0, EXT_CTL);
722a15f7c96SJohn Baldwin 		m->m_len = sizeof(*nslist);
723a15f7c96SJohn Baldwin 		status = nvmf_send_controller_data(nc, 0, m, m->m_len);
724a15f7c96SJohn Baldwin 		MPASS(status != NVMF_MORE);
725a15f7c96SJohn Baldwin 		break;
726a15f7c96SJohn Baldwin 	}
727a15f7c96SJohn Baldwin 	default:
728a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "Unsupported CNS %#x for IDENTIFY\n", cns);
729a15f7c96SJohn Baldwin 		status = NVME_SC_INVALID_FIELD;
730a15f7c96SJohn Baldwin 		break;
731a15f7c96SJohn Baldwin 	}
732a15f7c96SJohn Baldwin 
733a15f7c96SJohn Baldwin 	if (status == NVMF_SUCCESS_SENT)
734a15f7c96SJohn Baldwin 		nvmft_command_completed(ctrlr->admin, nc);
735a15f7c96SJohn Baldwin 	else
736a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc, status);
737a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
738a15f7c96SJohn Baldwin }
739a15f7c96SJohn Baldwin 
740a15f7c96SJohn Baldwin static void
handle_set_features(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvme_command * cmd)741a15f7c96SJohn Baldwin handle_set_features(struct nvmft_controller *ctrlr,
742a15f7c96SJohn Baldwin     struct nvmf_capsule *nc, const struct nvme_command *cmd)
743a15f7c96SJohn Baldwin {
744a15f7c96SJohn Baldwin 	struct nvme_completion cqe;
745a15f7c96SJohn Baldwin 	uint8_t fid;
746a15f7c96SJohn Baldwin 
747a15f7c96SJohn Baldwin 	fid = NVMEV(NVME_FEAT_SET_FID, le32toh(cmd->cdw10));
748a15f7c96SJohn Baldwin 	switch (fid) {
749a15f7c96SJohn Baldwin 	case NVME_FEAT_NUMBER_OF_QUEUES:
750a15f7c96SJohn Baldwin 	{
751a15f7c96SJohn Baldwin 		uint32_t num_queues;
752a15f7c96SJohn Baldwin 		struct nvmft_io_qpair *io_qpairs;
753a15f7c96SJohn Baldwin 
754a15f7c96SJohn Baldwin 		num_queues = le32toh(cmd->cdw11) & 0xffff;
755a15f7c96SJohn Baldwin 
756a15f7c96SJohn Baldwin 		/* 5.12.1.7: 65535 is invalid. */
757a15f7c96SJohn Baldwin 		if (num_queues == 65535)
758a15f7c96SJohn Baldwin 			goto error;
759a15f7c96SJohn Baldwin 
760a15f7c96SJohn Baldwin 		/* Fabrics requires the same number of SQs and CQs. */
761a15f7c96SJohn Baldwin 		if (le32toh(cmd->cdw11) >> 16 != num_queues)
762a15f7c96SJohn Baldwin 			goto error;
763a15f7c96SJohn Baldwin 
764a15f7c96SJohn Baldwin 		/* Convert to 1's based */
765a15f7c96SJohn Baldwin 		num_queues++;
766a15f7c96SJohn Baldwin 
767a15f7c96SJohn Baldwin 		io_qpairs = mallocarray(num_queues, sizeof(*io_qpairs),
768a15f7c96SJohn Baldwin 		    M_NVMFT, M_WAITOK | M_ZERO);
769a15f7c96SJohn Baldwin 
770a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
771a15f7c96SJohn Baldwin 		if (ctrlr->num_io_queues != 0) {
772a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
773a15f7c96SJohn Baldwin 			free(io_qpairs, M_NVMFT);
774a15f7c96SJohn Baldwin 			nvmft_send_generic_error(ctrlr->admin, nc,
775a15f7c96SJohn Baldwin 			    NVME_SC_COMMAND_SEQUENCE_ERROR);
776a15f7c96SJohn Baldwin 			nvmf_free_capsule(nc);
777a15f7c96SJohn Baldwin 			return;
778a15f7c96SJohn Baldwin 		}
779a15f7c96SJohn Baldwin 
780a15f7c96SJohn Baldwin 		ctrlr->num_io_queues = num_queues;
781a15f7c96SJohn Baldwin 		ctrlr->io_qpairs = io_qpairs;
782a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
783a15f7c96SJohn Baldwin 
784a15f7c96SJohn Baldwin 		nvmft_init_cqe(&cqe, nc, 0);
785a15f7c96SJohn Baldwin 		cqe.cdw0 = cmd->cdw11;
786a15f7c96SJohn Baldwin 		nvmft_send_response(ctrlr->admin, &cqe);
787a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
788a15f7c96SJohn Baldwin 		return;
789a15f7c96SJohn Baldwin 	}
790a15f7c96SJohn Baldwin 	case NVME_FEAT_ASYNC_EVENT_CONFIGURATION:
791a15f7c96SJohn Baldwin 	{
792a15f7c96SJohn Baldwin 		uint32_t aer_mask;
793a15f7c96SJohn Baldwin 
794a15f7c96SJohn Baldwin 		aer_mask = le32toh(cmd->cdw11);
795a15f7c96SJohn Baldwin 
796a15f7c96SJohn Baldwin 		/* Check for any reserved or unimplemented feature bits. */
797a15f7c96SJohn Baldwin 		if ((aer_mask & 0xffffc000) != 0)
798a15f7c96SJohn Baldwin 			goto error;
799a15f7c96SJohn Baldwin 
800a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
801a15f7c96SJohn Baldwin 		ctrlr->aer_mask = aer_mask;
802a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
803a15f7c96SJohn Baldwin 		nvmft_send_success(ctrlr->admin, nc);
8040d48d1ffSJohn Baldwin 		nvmf_free_capsule(nc);
805a15f7c96SJohn Baldwin 		return;
806a15f7c96SJohn Baldwin 	}
807a15f7c96SJohn Baldwin 	default:
808a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
809a15f7c96SJohn Baldwin 		    "Unsupported feature ID %u for SET_FEATURES\n", fid);
810a15f7c96SJohn Baldwin 		goto error;
811a15f7c96SJohn Baldwin 	}
812a15f7c96SJohn Baldwin 
813a15f7c96SJohn Baldwin error:
814a15f7c96SJohn Baldwin 	nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD);
815a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
816a15f7c96SJohn Baldwin }
817a15f7c96SJohn Baldwin 
818a15f7c96SJohn Baldwin static bool
update_cc(struct nvmft_controller * ctrlr,uint32_t new_cc,bool * need_shutdown)819a15f7c96SJohn Baldwin update_cc(struct nvmft_controller *ctrlr, uint32_t new_cc, bool *need_shutdown)
820a15f7c96SJohn Baldwin {
821a15f7c96SJohn Baldwin 	struct nvmft_port *np = ctrlr->np;
822a15f7c96SJohn Baldwin 	uint32_t changes;
823a15f7c96SJohn Baldwin 
824a15f7c96SJohn Baldwin 	*need_shutdown = false;
825a15f7c96SJohn Baldwin 
826a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
827a15f7c96SJohn Baldwin 
828a15f7c96SJohn Baldwin 	/* Don't allow any changes while shutting down. */
829a15f7c96SJohn Baldwin 	if (ctrlr->shutdown) {
830a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
831a15f7c96SJohn Baldwin 		return (false);
832a15f7c96SJohn Baldwin 	}
833a15f7c96SJohn Baldwin 
834a15f7c96SJohn Baldwin 	if (!_nvmf_validate_cc(np->max_io_qsize, np->cap, ctrlr->cc, new_cc)) {
835a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
836a15f7c96SJohn Baldwin 		return (false);
837a15f7c96SJohn Baldwin 	}
838a15f7c96SJohn Baldwin 
839a15f7c96SJohn Baldwin 	changes = ctrlr->cc ^ new_cc;
840a15f7c96SJohn Baldwin 	ctrlr->cc = new_cc;
841a15f7c96SJohn Baldwin 
842a15f7c96SJohn Baldwin 	/* Handle shutdown requests. */
843a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CC_REG_SHN, changes) != 0 &&
844a15f7c96SJohn Baldwin 	    NVMEV(NVME_CC_REG_SHN, new_cc) != 0) {
845a15f7c96SJohn Baldwin 		ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_SHST);
846a15f7c96SJohn Baldwin 		ctrlr->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_OCCURRING);
847a15f7c96SJohn Baldwin 		ctrlr->cc &= ~NVMEM(NVME_CC_REG_EN);
848a15f7c96SJohn Baldwin 		ctrlr->shutdown = true;
849a15f7c96SJohn Baldwin 		*need_shutdown = true;
850a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "shutdown requested\n");
851a15f7c96SJohn Baldwin 	}
852a15f7c96SJohn Baldwin 
853a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CC_REG_EN, changes) != 0) {
854a15f7c96SJohn Baldwin 		if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) {
855a15f7c96SJohn Baldwin 			/* Controller reset. */
856a15f7c96SJohn Baldwin 			nvmft_printf(ctrlr, "reset requested\n");
857a15f7c96SJohn Baldwin 			ctrlr->shutdown = true;
858a15f7c96SJohn Baldwin 			*need_shutdown = true;
859a15f7c96SJohn Baldwin 		} else
860a15f7c96SJohn Baldwin 			ctrlr->csts |= NVMEF(NVME_CSTS_REG_RDY, 1);
861a15f7c96SJohn Baldwin 	}
862a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
863a15f7c96SJohn Baldwin 
864a15f7c96SJohn Baldwin 	return (true);
865a15f7c96SJohn Baldwin }
866a15f7c96SJohn Baldwin 
867a15f7c96SJohn Baldwin static void
handle_property_get(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvmf_fabric_prop_get_cmd * pget)868a15f7c96SJohn Baldwin handle_property_get(struct nvmft_controller *ctrlr, struct nvmf_capsule *nc,
869a15f7c96SJohn Baldwin     const struct nvmf_fabric_prop_get_cmd *pget)
870a15f7c96SJohn Baldwin {
871a15f7c96SJohn Baldwin 	struct nvmf_fabric_prop_get_rsp rsp;
872a15f7c96SJohn Baldwin 
873a15f7c96SJohn Baldwin 	nvmft_init_cqe(&rsp, nc, 0);
874a15f7c96SJohn Baldwin 
875a15f7c96SJohn Baldwin 	switch (le32toh(pget->ofst)) {
876a15f7c96SJohn Baldwin 	case NVMF_PROP_CAP:
877a15f7c96SJohn Baldwin 		if (pget->attrib.size != NVMF_PROP_SIZE_8)
878a15f7c96SJohn Baldwin 			goto error;
879a15f7c96SJohn Baldwin 		rsp.value.u64 = htole64(ctrlr->np->cap);
880a15f7c96SJohn Baldwin 		break;
881a15f7c96SJohn Baldwin 	case NVMF_PROP_VS:
882a15f7c96SJohn Baldwin 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
883a15f7c96SJohn Baldwin 			goto error;
884a15f7c96SJohn Baldwin 		rsp.value.u32.low = ctrlr->cdata.ver;
885a15f7c96SJohn Baldwin 		break;
886a15f7c96SJohn Baldwin 	case NVMF_PROP_CC:
887a15f7c96SJohn Baldwin 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
888a15f7c96SJohn Baldwin 			goto error;
889a15f7c96SJohn Baldwin 		rsp.value.u32.low = htole32(ctrlr->cc);
890a15f7c96SJohn Baldwin 		break;
891a15f7c96SJohn Baldwin 	case NVMF_PROP_CSTS:
892a15f7c96SJohn Baldwin 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
893a15f7c96SJohn Baldwin 			goto error;
894a15f7c96SJohn Baldwin 		rsp.value.u32.low = htole32(ctrlr->csts);
895a15f7c96SJohn Baldwin 		break;
896a15f7c96SJohn Baldwin 	default:
897a15f7c96SJohn Baldwin 		goto error;
898a15f7c96SJohn Baldwin 	}
899a15f7c96SJohn Baldwin 
900a15f7c96SJohn Baldwin 	nvmft_send_response(ctrlr->admin, &rsp);
901a15f7c96SJohn Baldwin 	return;
902a15f7c96SJohn Baldwin error:
903a15f7c96SJohn Baldwin 	nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD);
904a15f7c96SJohn Baldwin }
905a15f7c96SJohn Baldwin 
906a15f7c96SJohn Baldwin static void
handle_property_set(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvmf_fabric_prop_set_cmd * pset)907a15f7c96SJohn Baldwin handle_property_set(struct nvmft_controller *ctrlr, struct nvmf_capsule *nc,
908a15f7c96SJohn Baldwin     const struct nvmf_fabric_prop_set_cmd *pset)
909a15f7c96SJohn Baldwin {
910a15f7c96SJohn Baldwin 	bool need_shutdown;
911a15f7c96SJohn Baldwin 
912a15f7c96SJohn Baldwin 	need_shutdown = false;
913a15f7c96SJohn Baldwin 	switch (le32toh(pset->ofst)) {
914a15f7c96SJohn Baldwin 	case NVMF_PROP_CC:
915a15f7c96SJohn Baldwin 		if (pset->attrib.size != NVMF_PROP_SIZE_4)
916a15f7c96SJohn Baldwin 			goto error;
917a15f7c96SJohn Baldwin 		if (!update_cc(ctrlr, le32toh(pset->value.u32.low),
918a15f7c96SJohn Baldwin 		    &need_shutdown))
919a15f7c96SJohn Baldwin 			goto error;
920a15f7c96SJohn Baldwin 		break;
921a15f7c96SJohn Baldwin 	default:
922a15f7c96SJohn Baldwin 		goto error;
923a15f7c96SJohn Baldwin 	}
924a15f7c96SJohn Baldwin 
925a15f7c96SJohn Baldwin 	nvmft_send_success(ctrlr->admin, nc);
926a15f7c96SJohn Baldwin 	if (need_shutdown) {
927a15f7c96SJohn Baldwin 		callout_stop(&ctrlr->ka_timer);
928a15f7c96SJohn Baldwin 		taskqueue_enqueue(taskqueue_thread, &ctrlr->shutdown_task);
929a15f7c96SJohn Baldwin 	}
930a15f7c96SJohn Baldwin 	return;
931a15f7c96SJohn Baldwin error:
932a15f7c96SJohn Baldwin 	nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD);
933a15f7c96SJohn Baldwin }
934a15f7c96SJohn Baldwin 
935a15f7c96SJohn Baldwin static void
handle_admin_fabrics_command(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc,const struct nvmf_fabric_cmd * fc)936a15f7c96SJohn Baldwin handle_admin_fabrics_command(struct nvmft_controller *ctrlr,
937a15f7c96SJohn Baldwin     struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc)
938a15f7c96SJohn Baldwin {
939a15f7c96SJohn Baldwin 	switch (fc->fctype) {
940a15f7c96SJohn Baldwin 	case NVMF_FABRIC_COMMAND_PROPERTY_GET:
941a15f7c96SJohn Baldwin 		handle_property_get(ctrlr, nc,
942a15f7c96SJohn Baldwin 		    (const struct nvmf_fabric_prop_get_cmd *)fc);
943a15f7c96SJohn Baldwin 		break;
944a15f7c96SJohn Baldwin 	case NVMF_FABRIC_COMMAND_PROPERTY_SET:
945a15f7c96SJohn Baldwin 		handle_property_set(ctrlr, nc,
946a15f7c96SJohn Baldwin 		    (const struct nvmf_fabric_prop_set_cmd *)fc);
947a15f7c96SJohn Baldwin 		break;
948a15f7c96SJohn Baldwin 	case NVMF_FABRIC_COMMAND_CONNECT:
949a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
950a15f7c96SJohn Baldwin 		    "CONNECT command on connected admin queue\n");
951a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc,
952a15f7c96SJohn Baldwin 		    NVME_SC_COMMAND_SEQUENCE_ERROR);
953a15f7c96SJohn Baldwin 		break;
954a15f7c96SJohn Baldwin 	case NVMF_FABRIC_COMMAND_DISCONNECT:
955a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "DISCONNECT command on admin queue\n");
956a15f7c96SJohn Baldwin 		nvmft_send_error(ctrlr->admin, nc, NVME_SCT_COMMAND_SPECIFIC,
957a15f7c96SJohn Baldwin 		    NVMF_FABRIC_SC_INVALID_QUEUE_TYPE);
958a15f7c96SJohn Baldwin 		break;
959a15f7c96SJohn Baldwin 	default:
960a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "Unsupported fabrics command %#x\n",
961a15f7c96SJohn Baldwin 		    fc->fctype);
962a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc,
963a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_OPCODE);
964a15f7c96SJohn Baldwin 		break;
965a15f7c96SJohn Baldwin 	}
966a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
967a15f7c96SJohn Baldwin }
968a15f7c96SJohn Baldwin 
969a15f7c96SJohn Baldwin void
nvmft_handle_admin_command(struct nvmft_controller * ctrlr,struct nvmf_capsule * nc)970a15f7c96SJohn Baldwin nvmft_handle_admin_command(struct nvmft_controller *ctrlr,
971a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
972a15f7c96SJohn Baldwin {
973a15f7c96SJohn Baldwin 	const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
974a15f7c96SJohn Baldwin 
975a15f7c96SJohn Baldwin 	/* Only permit Fabrics commands while a controller is disabled. */
976a15f7c96SJohn Baldwin 	if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) == 0 &&
977a15f7c96SJohn Baldwin 	    cmd->opc != NVME_OPC_FABRICS_COMMANDS) {
978a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
979f5541f9fSJohn Baldwin 		    "Unsupported admin opcode %#x while disabled\n", cmd->opc);
980a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc,
981a15f7c96SJohn Baldwin 		    NVME_SC_COMMAND_SEQUENCE_ERROR);
982a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
983a15f7c96SJohn Baldwin 		return;
984a15f7c96SJohn Baldwin 	}
985a15f7c96SJohn Baldwin 
986a15f7c96SJohn Baldwin 	atomic_store_int(&ctrlr->ka_active_traffic, 1);
987a15f7c96SJohn Baldwin 
988a15f7c96SJohn Baldwin 	switch (cmd->opc) {
989a15f7c96SJohn Baldwin 	case NVME_OPC_GET_LOG_PAGE:
990a15f7c96SJohn Baldwin 		handle_get_log_page(ctrlr, nc, cmd);
991a15f7c96SJohn Baldwin 		break;
992a15f7c96SJohn Baldwin 	case NVME_OPC_IDENTIFY:
993a15f7c96SJohn Baldwin 		handle_identify_command(ctrlr, nc, cmd);
994a15f7c96SJohn Baldwin 		break;
995a15f7c96SJohn Baldwin 	case NVME_OPC_SET_FEATURES:
996a15f7c96SJohn Baldwin 		handle_set_features(ctrlr, nc, cmd);
997a15f7c96SJohn Baldwin 		break;
998a15f7c96SJohn Baldwin 	case NVME_OPC_ASYNC_EVENT_REQUEST:
999a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
1000a15f7c96SJohn Baldwin 		if (ctrlr->aer_pending == NVMFT_NUM_AER) {
1001a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
1002a15f7c96SJohn Baldwin 			nvmft_send_error(ctrlr->admin, nc,
1003a15f7c96SJohn Baldwin 			    NVME_SCT_COMMAND_SPECIFIC,
1004a15f7c96SJohn Baldwin 			    NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED);
1005a15f7c96SJohn Baldwin 		} else {
1006a15f7c96SJohn Baldwin 			/* NB: Store the CID without byte-swapping. */
1007a15f7c96SJohn Baldwin 			ctrlr->aer_cids[ctrlr->aer_pidx] = cmd->cid;
1008a15f7c96SJohn Baldwin 			ctrlr->aer_pending++;
1009a15f7c96SJohn Baldwin 			ctrlr->aer_pidx = (ctrlr->aer_pidx + 1) % NVMFT_NUM_AER;
1010a15f7c96SJohn Baldwin 			mtx_unlock(&ctrlr->lock);
1011a15f7c96SJohn Baldwin 		}
1012a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
1013a15f7c96SJohn Baldwin 		break;
1014a15f7c96SJohn Baldwin 	case NVME_OPC_KEEP_ALIVE:
1015a15f7c96SJohn Baldwin 		nvmft_send_success(ctrlr->admin, nc);
1016a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
1017a15f7c96SJohn Baldwin 		break;
1018a15f7c96SJohn Baldwin 	case NVME_OPC_FABRICS_COMMANDS:
1019a15f7c96SJohn Baldwin 		handle_admin_fabrics_command(ctrlr, nc,
1020a15f7c96SJohn Baldwin 		    (const struct nvmf_fabric_cmd *)cmd);
1021a15f7c96SJohn Baldwin 		break;
1022a15f7c96SJohn Baldwin 	default:
1023a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "Unsupported admin opcode %#x\n", cmd->opc);
1024a15f7c96SJohn Baldwin 		nvmft_send_generic_error(ctrlr->admin, nc,
1025a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_OPCODE);
1026a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
1027a15f7c96SJohn Baldwin 		break;
1028a15f7c96SJohn Baldwin 	}
1029a15f7c96SJohn Baldwin }
1030a15f7c96SJohn Baldwin 
1031a15f7c96SJohn Baldwin void
nvmft_handle_io_command(struct nvmft_qpair * qp,uint16_t qid,struct nvmf_capsule * nc)1032a15f7c96SJohn Baldwin nvmft_handle_io_command(struct nvmft_qpair *qp, uint16_t qid,
1033a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
1034a15f7c96SJohn Baldwin {
1035a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = nvmft_qpair_ctrlr(qp);
1036a15f7c96SJohn Baldwin 	const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
1037a15f7c96SJohn Baldwin 
1038a15f7c96SJohn Baldwin 	atomic_store_int(&ctrlr->ka_active_traffic, 1);
1039a15f7c96SJohn Baldwin 
1040a15f7c96SJohn Baldwin 	switch (cmd->opc) {
1041a15f7c96SJohn Baldwin 	case NVME_OPC_FLUSH:
1042a15f7c96SJohn Baldwin 		if (cmd->nsid == htole32(0xffffffff)) {
1043a15f7c96SJohn Baldwin 			nvmft_send_generic_error(qp, nc,
1044a15f7c96SJohn Baldwin 			    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
1045a15f7c96SJohn Baldwin 			nvmf_free_capsule(nc);
1046a15f7c96SJohn Baldwin 			break;
1047a15f7c96SJohn Baldwin 		}
1048a15f7c96SJohn Baldwin 		/* FALLTHROUGH */
1049a15f7c96SJohn Baldwin 	case NVME_OPC_WRITE:
1050a15f7c96SJohn Baldwin 	case NVME_OPC_READ:
1051a15f7c96SJohn Baldwin 	case NVME_OPC_WRITE_UNCORRECTABLE:
1052a15f7c96SJohn Baldwin 	case NVME_OPC_COMPARE:
1053a15f7c96SJohn Baldwin 	case NVME_OPC_WRITE_ZEROES:
1054a15f7c96SJohn Baldwin 	case NVME_OPC_DATASET_MANAGEMENT:
1055a15f7c96SJohn Baldwin 	case NVME_OPC_VERIFY:
1056a15f7c96SJohn Baldwin 		nvmft_dispatch_command(qp, nc, false);
1057a15f7c96SJohn Baldwin 		break;
1058a15f7c96SJohn Baldwin 	default:
1059a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "Unsupported I/O opcode %#x\n", cmd->opc);
1060a15f7c96SJohn Baldwin 		nvmft_send_generic_error(qp, nc,
1061a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_OPCODE);
1062a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
1063a15f7c96SJohn Baldwin 		break;
1064a15f7c96SJohn Baldwin 	}
1065a15f7c96SJohn Baldwin }
1066a15f7c96SJohn Baldwin 
1067a15f7c96SJohn Baldwin static void
nvmft_report_aer(struct nvmft_controller * ctrlr,uint32_t aer_mask,u_int type,uint8_t info,uint8_t log_page_id)1068a15f7c96SJohn Baldwin nvmft_report_aer(struct nvmft_controller *ctrlr, uint32_t aer_mask,
1069a15f7c96SJohn Baldwin     u_int type, uint8_t info, uint8_t log_page_id)
1070a15f7c96SJohn Baldwin {
1071a15f7c96SJohn Baldwin 	struct nvme_completion cpl;
1072a15f7c96SJohn Baldwin 
1073a15f7c96SJohn Baldwin 	MPASS(type <= 7);
1074a15f7c96SJohn Baldwin 
1075a15f7c96SJohn Baldwin 	/* Drop events that are not enabled. */
1076a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
1077a15f7c96SJohn Baldwin 	if ((ctrlr->aer_mask & aer_mask) == 0) {
1078a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
1079a15f7c96SJohn Baldwin 		return;
1080a15f7c96SJohn Baldwin 	}
1081a15f7c96SJohn Baldwin 
1082a15f7c96SJohn Baldwin 	/*
1083a15f7c96SJohn Baldwin 	 * If there is no pending AER command, drop it.
1084a15f7c96SJohn Baldwin 	 * XXX: Should we queue these?
1085a15f7c96SJohn Baldwin 	 */
1086a15f7c96SJohn Baldwin 	if (ctrlr->aer_pending == 0) {
1087a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
1088a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
1089a15f7c96SJohn Baldwin 		    "dropping AER type %u, info %#x, page %#x\n",
1090a15f7c96SJohn Baldwin 		    type, info, log_page_id);
1091a15f7c96SJohn Baldwin 		return;
1092a15f7c96SJohn Baldwin 	}
1093a15f7c96SJohn Baldwin 
1094a15f7c96SJohn Baldwin 	memset(&cpl, 0, sizeof(cpl));
1095a15f7c96SJohn Baldwin 	cpl.cid = ctrlr->aer_cids[ctrlr->aer_cidx];
1096a15f7c96SJohn Baldwin 	ctrlr->aer_pending--;
1097a15f7c96SJohn Baldwin 	ctrlr->aer_cidx = (ctrlr->aer_cidx + 1) % NVMFT_NUM_AER;
1098a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
1099a15f7c96SJohn Baldwin 
1100a15f7c96SJohn Baldwin 	cpl.cdw0 = htole32(NVMEF(NVME_ASYNC_EVENT_TYPE, type) |
1101a15f7c96SJohn Baldwin 	    NVMEF(NVME_ASYNC_EVENT_INFO, info) |
1102a15f7c96SJohn Baldwin 	    NVMEF(NVME_ASYNC_EVENT_LOG_PAGE_ID, log_page_id));
1103a15f7c96SJohn Baldwin 
1104a15f7c96SJohn Baldwin 	nvmft_send_response(ctrlr->admin, &cpl);
1105a15f7c96SJohn Baldwin }
1106a15f7c96SJohn Baldwin 
1107a15f7c96SJohn Baldwin void
nvmft_controller_lun_changed(struct nvmft_controller * ctrlr,int lun_id)1108a15f7c96SJohn Baldwin nvmft_controller_lun_changed(struct nvmft_controller *ctrlr, int lun_id)
1109a15f7c96SJohn Baldwin {
1110a15f7c96SJohn Baldwin 	struct nvme_ns_list *nslist;
1111a15f7c96SJohn Baldwin 	uint32_t new_nsid, nsid;
1112a15f7c96SJohn Baldwin 	u_int i;
1113a15f7c96SJohn Baldwin 
1114a15f7c96SJohn Baldwin 	new_nsid = lun_id + 1;
1115a15f7c96SJohn Baldwin 
1116a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
1117a15f7c96SJohn Baldwin 	nslist = ctrlr->changed_ns;
1118a15f7c96SJohn Baldwin 
1119a15f7c96SJohn Baldwin 	/* If the first entry is 0xffffffff, the list is already full. */
1120a15f7c96SJohn Baldwin 	if (nslist->ns[0] != 0xffffffff) {
1121a15f7c96SJohn Baldwin 		/* Find the insertion point for this namespace ID. */
1122a15f7c96SJohn Baldwin 		for (i = 0; i < nitems(nslist->ns); i++) {
1123a15f7c96SJohn Baldwin 			nsid = le32toh(nslist->ns[i]);
1124a15f7c96SJohn Baldwin 			if (nsid == new_nsid) {
1125a15f7c96SJohn Baldwin 				/* Already reported, nothing to do. */
1126a15f7c96SJohn Baldwin 				mtx_unlock(&ctrlr->lock);
1127a15f7c96SJohn Baldwin 				return;
1128a15f7c96SJohn Baldwin 			}
1129a15f7c96SJohn Baldwin 
1130a15f7c96SJohn Baldwin 			if (nsid == 0 || nsid > new_nsid)
1131a15f7c96SJohn Baldwin 				break;
1132a15f7c96SJohn Baldwin 		}
1133a15f7c96SJohn Baldwin 
1134a15f7c96SJohn Baldwin 		if (nslist->ns[nitems(nslist->ns) - 1] != htole32(0)) {
1135a15f7c96SJohn Baldwin 			/* List is full. */
1136a15f7c96SJohn Baldwin 			memset(ctrlr->changed_ns, 0,
1137a15f7c96SJohn Baldwin 			    sizeof(*ctrlr->changed_ns));
1138a15f7c96SJohn Baldwin 			ctrlr->changed_ns->ns[0] = 0xffffffff;
1139a15f7c96SJohn Baldwin 		} else if (nslist->ns[i] == htole32(0)) {
1140a15f7c96SJohn Baldwin 			/*
1141a15f7c96SJohn Baldwin 			 * Optimize case where this ID is appended to
1142a15f7c96SJohn Baldwin 			 * the end.
1143a15f7c96SJohn Baldwin 			 */
1144a15f7c96SJohn Baldwin 			nslist->ns[i] = htole32(new_nsid);
1145a15f7c96SJohn Baldwin 		} else {
1146a15f7c96SJohn Baldwin 			memmove(&nslist->ns[i + 1], &nslist->ns[i],
1147a15f7c96SJohn Baldwin 			    (nitems(nslist->ns) - i - 1) *
1148a15f7c96SJohn Baldwin 			    sizeof(nslist->ns[0]));
1149a15f7c96SJohn Baldwin 			nslist->ns[i] = htole32(new_nsid);
1150a15f7c96SJohn Baldwin 		}
1151a15f7c96SJohn Baldwin 	}
1152a15f7c96SJohn Baldwin 
1153a15f7c96SJohn Baldwin 	if (ctrlr->changed_ns_reported) {
1154a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
1155a15f7c96SJohn Baldwin 		return;
1156a15f7c96SJohn Baldwin 	}
1157a15f7c96SJohn Baldwin 	ctrlr->changed_ns_reported = true;
1158a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
1159a15f7c96SJohn Baldwin 
1160a15f7c96SJohn Baldwin 	nvmft_report_aer(ctrlr, NVME_ASYNC_EVENT_NS_ATTRIBUTE, 0x2, 0x0,
1161a15f7c96SJohn Baldwin 	    NVME_LOG_CHANGED_NAMESPACE);
1162a15f7c96SJohn Baldwin }
1163