xref: /freebsd/sys/dev/nvmf/controller/ctl_frontend_nvmf.c (revision 11509c6ef2fb3e6a83211109cba98da7560b5e12)
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/dnv.h>
10a15f7c96SJohn Baldwin #include <sys/jail.h>
11a15f7c96SJohn Baldwin #include <sys/kernel.h>
12a15f7c96SJohn Baldwin #include <sys/limits.h>
13a15f7c96SJohn Baldwin #include <sys/lock.h>
14a15f7c96SJohn Baldwin #include <sys/malloc.h>
15a15f7c96SJohn Baldwin #include <sys/mbuf.h>
16a15f7c96SJohn Baldwin #include <sys/memdesc.h>
17a15f7c96SJohn Baldwin #include <sys/module.h>
18a15f7c96SJohn Baldwin #include <sys/proc.h>
19a15f7c96SJohn Baldwin #include <sys/queue.h>
20a15f7c96SJohn Baldwin #include <sys/refcount.h>
21a15f7c96SJohn Baldwin #include <sys/sbuf.h>
221b3fa1acSJohn Baldwin #include <sys/smp.h>
23a15f7c96SJohn Baldwin #include <sys/sx.h>
241b3fa1acSJohn Baldwin #include <sys/taskqueue.h>
25a15f7c96SJohn Baldwin 
26a15f7c96SJohn Baldwin #include <machine/bus.h>
27a15f7c96SJohn Baldwin #include <machine/bus_dma.h>
28a15f7c96SJohn Baldwin 
29a15f7c96SJohn Baldwin #include <dev/nvmf/nvmf.h>
30a15f7c96SJohn Baldwin #include <dev/nvmf/nvmf_transport.h>
31a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_subr.h>
32a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_var.h>
33a15f7c96SJohn Baldwin 
34a15f7c96SJohn Baldwin #include <cam/ctl/ctl.h>
35a15f7c96SJohn Baldwin #include <cam/ctl/ctl_error.h>
361b3fa1acSJohn Baldwin #include <cam/ctl/ctl_ha.h>
37a15f7c96SJohn Baldwin #include <cam/ctl/ctl_io.h>
38a15f7c96SJohn Baldwin #include <cam/ctl/ctl_frontend.h>
391b3fa1acSJohn Baldwin #include <cam/ctl/ctl_private.h>
40a15f7c96SJohn Baldwin 
41a15f7c96SJohn Baldwin /*
42a15f7c96SJohn Baldwin  * Store pointers to the capsule and qpair in the two pointer members
43a15f7c96SJohn Baldwin  * of CTL_PRIV_FRONTEND.
44a15f7c96SJohn Baldwin  */
45a15f7c96SJohn Baldwin #define	NVMFT_NC(io)	((io)->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptrs[0])
46a15f7c96SJohn Baldwin #define	NVMFT_QP(io)	((io)->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptrs[1])
47a15f7c96SJohn Baldwin 
48a15f7c96SJohn Baldwin static void	nvmft_done(union ctl_io *io);
49a15f7c96SJohn Baldwin static int	nvmft_init(void);
50a15f7c96SJohn Baldwin static int	nvmft_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
51a15f7c96SJohn Baldwin     int flag, struct thread *td);
52a15f7c96SJohn Baldwin static int	nvmft_shutdown(void);
53a15f7c96SJohn Baldwin 
541b3fa1acSJohn Baldwin static struct taskqueue *nvmft_taskq;
55a15f7c96SJohn Baldwin static TAILQ_HEAD(, nvmft_port) nvmft_ports;
56a15f7c96SJohn Baldwin static struct sx nvmft_ports_lock;
57a15f7c96SJohn Baldwin 
58a15f7c96SJohn Baldwin MALLOC_DEFINE(M_NVMFT, "nvmft", "NVMe over Fabrics controller");
59a15f7c96SJohn Baldwin 
60a15f7c96SJohn Baldwin static struct ctl_frontend nvmft_frontend = {
61a15f7c96SJohn Baldwin 	.name = "nvmf",
62a15f7c96SJohn Baldwin 	.init = nvmft_init,
63a15f7c96SJohn Baldwin 	.ioctl = nvmft_ioctl,
64a15f7c96SJohn Baldwin 	.fe_dump = NULL,
65a15f7c96SJohn Baldwin 	.shutdown = nvmft_shutdown,
66a15f7c96SJohn Baldwin };
67a15f7c96SJohn Baldwin 
68a15f7c96SJohn Baldwin static void
nvmft_online(void * arg)69a15f7c96SJohn Baldwin nvmft_online(void *arg)
70a15f7c96SJohn Baldwin {
71a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
72a15f7c96SJohn Baldwin 
7397ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
74a15f7c96SJohn Baldwin 	np->online = true;
7597ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
76a15f7c96SJohn Baldwin }
77a15f7c96SJohn Baldwin 
78a15f7c96SJohn Baldwin static void
nvmft_offline(void * arg)79a15f7c96SJohn Baldwin nvmft_offline(void *arg)
80a15f7c96SJohn Baldwin {
81a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
82a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
83a15f7c96SJohn Baldwin 
8497ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
85a15f7c96SJohn Baldwin 	np->online = false;
86a15f7c96SJohn Baldwin 
87a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
88a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr,
89a15f7c96SJohn Baldwin 		    "shutting down due to port going offline\n");
90a15f7c96SJohn Baldwin 		nvmft_controller_error(ctrlr, NULL, ENODEV);
91a15f7c96SJohn Baldwin 	}
92a15f7c96SJohn Baldwin 
93a15f7c96SJohn Baldwin 	while (!TAILQ_EMPTY(&np->controllers))
9497ca2adaSJohn Baldwin 		mtx_sleep(np, &np->lock, 0, "nvmfoff", 0);
9597ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
96a15f7c96SJohn Baldwin }
97a15f7c96SJohn Baldwin 
98a15f7c96SJohn Baldwin static int
nvmft_info(void * arg,struct sbuf * sb)99*11509c6eSJohn Baldwin nvmft_info(void *arg, struct sbuf *sb)
100*11509c6eSJohn Baldwin {
101*11509c6eSJohn Baldwin 	struct nvmft_port *np = arg;
102*11509c6eSJohn Baldwin 	struct nvmft_controller *ctrlr;
103*11509c6eSJohn Baldwin 	int retval;
104*11509c6eSJohn Baldwin 
105*11509c6eSJohn Baldwin 	mtx_lock(&np->lock);
106*11509c6eSJohn Baldwin 	retval = sbuf_printf(sb, "\t<port>%s,p,%u</port>\n", np->cdata.subnqn,
107*11509c6eSJohn Baldwin 	    np->portid);
108*11509c6eSJohn Baldwin 	if (retval != 0)
109*11509c6eSJohn Baldwin 		goto out;
110*11509c6eSJohn Baldwin 
111*11509c6eSJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
112*11509c6eSJohn Baldwin 		retval = sbuf_printf(sb, "\t<host id=\"%u\">%s</host>\n",
113*11509c6eSJohn Baldwin 		    ctrlr->cntlid, ctrlr->hostnqn);
114*11509c6eSJohn Baldwin 		if (retval != 0)
115*11509c6eSJohn Baldwin 			break;
116*11509c6eSJohn Baldwin 	}
117*11509c6eSJohn Baldwin out:
118*11509c6eSJohn Baldwin 	mtx_unlock(&np->lock);
119*11509c6eSJohn Baldwin 	return (retval);
120*11509c6eSJohn Baldwin }
121*11509c6eSJohn Baldwin 
122*11509c6eSJohn Baldwin static int
nvmft_lun_enable(void * arg,int lun_id)123a15f7c96SJohn Baldwin nvmft_lun_enable(void *arg, int lun_id)
124a15f7c96SJohn Baldwin {
125a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
126a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
127a15f7c96SJohn Baldwin 	uint32_t *old_ns, *new_ns;
128a15f7c96SJohn Baldwin 	uint32_t nsid;
12997ca2adaSJohn Baldwin 	u_int i, new_count;
130a15f7c96SJohn Baldwin 
131a15f7c96SJohn Baldwin 	if (lun_id >= le32toh(np->cdata.nn)) {
132a15f7c96SJohn Baldwin 		printf("NVMFT: %s lun %d larger than maximum nsid %u\n",
133a15f7c96SJohn Baldwin 		    np->cdata.subnqn, lun_id, le32toh(np->cdata.nn));
134a15f7c96SJohn Baldwin 		return (EOPNOTSUPP);
135a15f7c96SJohn Baldwin 	}
136a15f7c96SJohn Baldwin 	nsid = lun_id + 1;
137a15f7c96SJohn Baldwin 
13897ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
13997ca2adaSJohn Baldwin 	for (;;) {
14097ca2adaSJohn Baldwin 		new_count = np->num_ns + 1;
14197ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
14297ca2adaSJohn Baldwin 		new_ns = mallocarray(new_count, sizeof(*new_ns), M_NVMFT,
143a15f7c96SJohn Baldwin 		    M_WAITOK);
14497ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
14597ca2adaSJohn Baldwin 		if (np->num_ns + 1 <= new_count)
14697ca2adaSJohn Baldwin 			break;
14797ca2adaSJohn Baldwin 		free(new_ns, M_NVMFT);
14897ca2adaSJohn Baldwin 	}
149a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
150a15f7c96SJohn Baldwin 		if (np->active_ns[i] < nsid)
151a15f7c96SJohn Baldwin 			continue;
152a15f7c96SJohn Baldwin 		if (np->active_ns[i] == nsid) {
15397ca2adaSJohn Baldwin 			mtx_unlock(&np->lock);
154a15f7c96SJohn Baldwin 			free(new_ns, M_NVMFT);
155a15f7c96SJohn Baldwin 			printf("NVMFT: %s duplicate lun %d\n",
156a15f7c96SJohn Baldwin 			    np->cdata.subnqn, lun_id);
157a15f7c96SJohn Baldwin 			return (EINVAL);
158a15f7c96SJohn Baldwin 		}
159a15f7c96SJohn Baldwin 		break;
160a15f7c96SJohn Baldwin 	}
161a15f7c96SJohn Baldwin 
162a15f7c96SJohn Baldwin 	/* Copy over IDs smaller than nsid. */
163a15f7c96SJohn Baldwin 	memcpy(new_ns, np->active_ns, i * sizeof(*np->active_ns));
164a15f7c96SJohn Baldwin 
165a15f7c96SJohn Baldwin 	/* Insert nsid. */
166a15f7c96SJohn Baldwin 	new_ns[i] = nsid;
167a15f7c96SJohn Baldwin 
168a15f7c96SJohn Baldwin 	/* Copy over IDs greater than nsid. */
169a15f7c96SJohn Baldwin 	memcpy(new_ns + i + 1, np->active_ns + i, (np->num_ns - i) *
170a15f7c96SJohn Baldwin 	    sizeof(*np->active_ns));
171a15f7c96SJohn Baldwin 
172a15f7c96SJohn Baldwin 	np->num_ns++;
173a15f7c96SJohn Baldwin 	old_ns = np->active_ns;
174a15f7c96SJohn Baldwin 	np->active_ns = new_ns;
175a15f7c96SJohn Baldwin 
176a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
177a15f7c96SJohn Baldwin 		nvmft_controller_lun_changed(ctrlr, lun_id);
178a15f7c96SJohn Baldwin 	}
179a15f7c96SJohn Baldwin 
18097ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
181a15f7c96SJohn Baldwin 	free(old_ns, M_NVMFT);
182a15f7c96SJohn Baldwin 
183a15f7c96SJohn Baldwin 	return (0);
184a15f7c96SJohn Baldwin }
185a15f7c96SJohn Baldwin 
186a15f7c96SJohn Baldwin static int
nvmft_lun_disable(void * arg,int lun_id)187a15f7c96SJohn Baldwin nvmft_lun_disable(void *arg, int lun_id)
188a15f7c96SJohn Baldwin {
189a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
190a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
191a15f7c96SJohn Baldwin 	uint32_t nsid;
192a15f7c96SJohn Baldwin 	u_int i;
193a15f7c96SJohn Baldwin 
194a15f7c96SJohn Baldwin 	if (lun_id >= le32toh(np->cdata.nn))
195a15f7c96SJohn Baldwin 		return (0);
196a15f7c96SJohn Baldwin 	nsid = lun_id + 1;
197a15f7c96SJohn Baldwin 
19897ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
199a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
200a15f7c96SJohn Baldwin 		if (np->active_ns[i] == nsid)
201a15f7c96SJohn Baldwin 			goto found;
202a15f7c96SJohn Baldwin 	}
20397ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
204a15f7c96SJohn Baldwin 	printf("NVMFT: %s request to disable nonexistent lun %d\n",
205a15f7c96SJohn Baldwin 	    np->cdata.subnqn, lun_id);
206a15f7c96SJohn Baldwin 	return (EINVAL);
207a15f7c96SJohn Baldwin 
208a15f7c96SJohn Baldwin found:
209a15f7c96SJohn Baldwin 	/* Move down IDs greater than nsid. */
210a15f7c96SJohn Baldwin 	memmove(np->active_ns + i, np->active_ns + i + 1,
211a15f7c96SJohn Baldwin 	    (np->num_ns - (i + 1)) * sizeof(*np->active_ns));
212a15f7c96SJohn Baldwin 	np->num_ns--;
213a15f7c96SJohn Baldwin 
214a15f7c96SJohn Baldwin 	/* NB: Don't bother freeing the old active_ns array. */
215a15f7c96SJohn Baldwin 
216a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
217a15f7c96SJohn Baldwin 		nvmft_controller_lun_changed(ctrlr, lun_id);
218a15f7c96SJohn Baldwin 	}
219a15f7c96SJohn Baldwin 
22097ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
221a15f7c96SJohn Baldwin 
222a15f7c96SJohn Baldwin 	return (0);
223a15f7c96SJohn Baldwin }
224a15f7c96SJohn Baldwin 
225a15f7c96SJohn Baldwin void
nvmft_populate_active_nslist(struct nvmft_port * np,uint32_t nsid,struct nvme_ns_list * nslist)226a15f7c96SJohn Baldwin nvmft_populate_active_nslist(struct nvmft_port *np, uint32_t nsid,
227a15f7c96SJohn Baldwin     struct nvme_ns_list *nslist)
228a15f7c96SJohn Baldwin {
229a15f7c96SJohn Baldwin 	u_int i, count;
230a15f7c96SJohn Baldwin 
23197ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
232a15f7c96SJohn Baldwin 	count = 0;
233a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
234a15f7c96SJohn Baldwin 		if (np->active_ns[i] <= nsid)
235a15f7c96SJohn Baldwin 			continue;
236a15f7c96SJohn Baldwin 		nslist->ns[count] = htole32(np->active_ns[i]);
237a15f7c96SJohn Baldwin 		count++;
238a15f7c96SJohn Baldwin 		if (count == nitems(nslist->ns))
239a15f7c96SJohn Baldwin 			break;
240a15f7c96SJohn Baldwin 	}
24197ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
242a15f7c96SJohn Baldwin }
243a15f7c96SJohn Baldwin 
244a15f7c96SJohn Baldwin void
nvmft_dispatch_command(struct nvmft_qpair * qp,struct nvmf_capsule * nc,bool admin)245a15f7c96SJohn Baldwin nvmft_dispatch_command(struct nvmft_qpair *qp, struct nvmf_capsule *nc,
246a15f7c96SJohn Baldwin     bool admin)
247a15f7c96SJohn Baldwin {
248a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = nvmft_qpair_ctrlr(qp);
249a15f7c96SJohn Baldwin 	const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
250a15f7c96SJohn Baldwin 	struct nvmft_port *np = ctrlr->np;
251a15f7c96SJohn Baldwin 	union ctl_io *io;
252a15f7c96SJohn Baldwin 	int error;
253a15f7c96SJohn Baldwin 
254a15f7c96SJohn Baldwin 	if (cmd->nsid == htole32(0)) {
255a15f7c96SJohn Baldwin 		nvmft_send_generic_error(qp, nc,
256a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
257a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
258a15f7c96SJohn Baldwin 		return;
259a15f7c96SJohn Baldwin 	}
260a15f7c96SJohn Baldwin 
261a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
262a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
263a15f7c96SJohn Baldwin 		ctrlr->start_busy = sbinuptime();
264a15f7c96SJohn Baldwin 	ctrlr->pending_commands++;
265a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
266a15f7c96SJohn Baldwin 	io = ctl_alloc_io(np->port.ctl_pool_ref);
267a15f7c96SJohn Baldwin 	ctl_zero_io(io);
268a15f7c96SJohn Baldwin 	NVMFT_NC(io) = nc;
269a15f7c96SJohn Baldwin 	NVMFT_QP(io) = qp;
270a15f7c96SJohn Baldwin 	io->io_hdr.io_type = admin ? CTL_IO_NVME_ADMIN : CTL_IO_NVME;
271a15f7c96SJohn Baldwin 	io->io_hdr.nexus.initid = ctrlr->cntlid;
272a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_port = np->port.targ_port;
273a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_lun = le32toh(cmd->nsid) - 1;
274a15f7c96SJohn Baldwin 	io->nvmeio.cmd = *cmd;
275a15f7c96SJohn Baldwin 	error = ctl_run(io);
276a15f7c96SJohn Baldwin 	if (error != 0) {
277a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "ctl_run failed for command on %s: %d\n",
278a15f7c96SJohn Baldwin 		    nvmft_qpair_name(qp), error);
279a15f7c96SJohn Baldwin 		ctl_nvme_set_generic_error(&io->nvmeio,
280a15f7c96SJohn Baldwin 		    NVME_SC_INTERNAL_DEVICE_ERROR);
281a15f7c96SJohn Baldwin 		nvmft_done(io);
282a15f7c96SJohn Baldwin 
283a15f7c96SJohn Baldwin 		nvmft_controller_error(ctrlr, qp, ENXIO);
284a15f7c96SJohn Baldwin 	}
285a15f7c96SJohn Baldwin }
286a15f7c96SJohn Baldwin 
287a15f7c96SJohn Baldwin void
nvmft_terminate_commands(struct nvmft_controller * ctrlr)288a15f7c96SJohn Baldwin nvmft_terminate_commands(struct nvmft_controller *ctrlr)
289a15f7c96SJohn Baldwin {
290a15f7c96SJohn Baldwin 	struct nvmft_port *np = ctrlr->np;
291a15f7c96SJohn Baldwin 	union ctl_io *io;
292a15f7c96SJohn Baldwin 	int error;
293a15f7c96SJohn Baldwin 
294a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
295a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
296a15f7c96SJohn Baldwin 		ctrlr->start_busy = sbinuptime();
297a15f7c96SJohn Baldwin 	ctrlr->pending_commands++;
298a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
299a15f7c96SJohn Baldwin 	io = ctl_alloc_io(np->port.ctl_pool_ref);
300a15f7c96SJohn Baldwin 	ctl_zero_io(io);
301a15f7c96SJohn Baldwin 	NVMFT_QP(io) = ctrlr->admin;
302a15f7c96SJohn Baldwin 	io->io_hdr.io_type = CTL_IO_TASK;
303a15f7c96SJohn Baldwin 	io->io_hdr.nexus.initid = ctrlr->cntlid;
304a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_port = np->port.targ_port;
305a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_lun = 0;
306a15f7c96SJohn Baldwin 	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX: unused? */
307a15f7c96SJohn Baldwin 	io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET;
308a15f7c96SJohn Baldwin 	error = ctl_run(io);
309a15f7c96SJohn Baldwin 	if (error != CTL_RETVAL_COMPLETE) {
310a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "failed to terminate tasks: %d\n", error);
311a15f7c96SJohn Baldwin #ifdef INVARIANTS
312a15f7c96SJohn Baldwin 		io->io_hdr.status = CTL_SUCCESS;
313a15f7c96SJohn Baldwin #endif
314a15f7c96SJohn Baldwin 		nvmft_done(io);
315a15f7c96SJohn Baldwin 	}
316a15f7c96SJohn Baldwin }
317a15f7c96SJohn Baldwin 
318a15f7c96SJohn Baldwin static void
nvmft_datamove_out_cb(void * arg,size_t xfered,int error)319a15f7c96SJohn Baldwin nvmft_datamove_out_cb(void *arg, size_t xfered, int error)
320a15f7c96SJohn Baldwin {
321a15f7c96SJohn Baldwin 	struct ctl_nvmeio *ctnio = arg;
322a15f7c96SJohn Baldwin 
323a15f7c96SJohn Baldwin 	if (error != 0) {
324a15f7c96SJohn Baldwin 		ctl_nvme_set_data_transfer_error(ctnio);
325a15f7c96SJohn Baldwin 	} else {
326a15f7c96SJohn Baldwin 		MPASS(xfered == ctnio->kern_data_len);
327a15f7c96SJohn Baldwin 		ctnio->kern_data_resid -= xfered;
328a15f7c96SJohn Baldwin 	}
329a15f7c96SJohn Baldwin 
330a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries) {
331a15f7c96SJohn Baldwin 		free(ctnio->ext_data_ptr, M_NVMFT);
332a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = NULL;
333a15f7c96SJohn Baldwin 	} else
334a15f7c96SJohn Baldwin 		MPASS(ctnio->ext_data_ptr == NULL);
335a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, false);
336a15f7c96SJohn Baldwin }
337a15f7c96SJohn Baldwin 
338a15f7c96SJohn Baldwin static void
nvmft_datamove_out(struct ctl_nvmeio * ctnio,struct nvmft_qpair * qp,struct nvmf_capsule * nc)339a15f7c96SJohn Baldwin nvmft_datamove_out(struct ctl_nvmeio *ctnio, struct nvmft_qpair *qp,
340a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
341a15f7c96SJohn Baldwin {
342a15f7c96SJohn Baldwin 	struct memdesc mem;
343a15f7c96SJohn Baldwin 	int error;
344a15f7c96SJohn Baldwin 
345a15f7c96SJohn Baldwin 	MPASS(ctnio->ext_data_ptr == NULL);
346a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries > 0) {
347a15f7c96SJohn Baldwin 		struct ctl_sg_entry *sgl;
348a15f7c96SJohn Baldwin 		struct bus_dma_segment *vlist;
349a15f7c96SJohn Baldwin 
350a15f7c96SJohn Baldwin 		vlist = mallocarray(ctnio->kern_sg_entries, sizeof(*vlist),
351a15f7c96SJohn Baldwin 		    M_NVMFT, M_WAITOK);
352a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = (void *)vlist;
353a15f7c96SJohn Baldwin 		sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
354a15f7c96SJohn Baldwin 		for (u_int i = 0; i < ctnio->kern_sg_entries; i++) {
355a15f7c96SJohn Baldwin 			vlist[i].ds_addr = (uintptr_t)sgl[i].addr;
356a15f7c96SJohn Baldwin 			vlist[i].ds_len = sgl[i].len;
357a15f7c96SJohn Baldwin 		}
358a15f7c96SJohn Baldwin 		mem = memdesc_vlist(vlist, ctnio->kern_sg_entries);
359a15f7c96SJohn Baldwin 	} else
360a15f7c96SJohn Baldwin 		mem = memdesc_vaddr(ctnio->kern_data_ptr, ctnio->kern_data_len);
361a15f7c96SJohn Baldwin 
362a15f7c96SJohn Baldwin 	error = nvmf_receive_controller_data(nc, ctnio->kern_rel_offset, &mem,
363a15f7c96SJohn Baldwin 	    ctnio->kern_data_len, nvmft_datamove_out_cb, ctnio);
364a15f7c96SJohn Baldwin 	if (error == 0)
365a15f7c96SJohn Baldwin 		return;
366a15f7c96SJohn Baldwin 
367a15f7c96SJohn Baldwin 	nvmft_printf(nvmft_qpair_ctrlr(qp),
368a15f7c96SJohn Baldwin 	    "Failed to request capsule data: %d\n", error);
369a15f7c96SJohn Baldwin 	ctl_nvme_set_data_transfer_error(ctnio);
370a15f7c96SJohn Baldwin 
371a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries) {
372a15f7c96SJohn Baldwin 		free(ctnio->ext_data_ptr, M_NVMFT);
373a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = NULL;
374a15f7c96SJohn Baldwin 	} else
375a15f7c96SJohn Baldwin 		MPASS(ctnio->ext_data_ptr == NULL);
376a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, true);
377a15f7c96SJohn Baldwin }
378a15f7c96SJohn Baldwin 
379a15f7c96SJohn Baldwin static struct mbuf *
nvmft_copy_data(struct ctl_nvmeio * ctnio)380a15f7c96SJohn Baldwin nvmft_copy_data(struct ctl_nvmeio *ctnio)
381a15f7c96SJohn Baldwin {
382a15f7c96SJohn Baldwin 	struct ctl_sg_entry *sgl;
383a15f7c96SJohn Baldwin 	struct mbuf *m0, *m;
384a15f7c96SJohn Baldwin 	uint32_t resid, off, todo;
385a15f7c96SJohn Baldwin 	int mlen;
386a15f7c96SJohn Baldwin 
387a15f7c96SJohn Baldwin 	MPASS(ctnio->kern_data_len != 0);
388a15f7c96SJohn Baldwin 
389a15f7c96SJohn Baldwin 	m0 = m_getm2(NULL, ctnio->kern_data_len, M_WAITOK, MT_DATA, 0);
390a15f7c96SJohn Baldwin 
391a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries == 0) {
392a15f7c96SJohn Baldwin 		m_copyback(m0, 0, ctnio->kern_data_len, ctnio->kern_data_ptr);
393a15f7c96SJohn Baldwin 		return (m0);
394a15f7c96SJohn Baldwin 	}
395a15f7c96SJohn Baldwin 
396a15f7c96SJohn Baldwin 	resid = ctnio->kern_data_len;
397a15f7c96SJohn Baldwin 	sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
398a15f7c96SJohn Baldwin 	off = 0;
399a15f7c96SJohn Baldwin 	m = m0;
400a15f7c96SJohn Baldwin 	mlen = M_TRAILINGSPACE(m);
401a15f7c96SJohn Baldwin 	for (;;) {
402a15f7c96SJohn Baldwin 		todo = MIN(mlen, sgl->len - off);
403a15f7c96SJohn Baldwin 		memcpy(mtod(m, char *) + m->m_len, (char *)sgl->addr + off,
404a15f7c96SJohn Baldwin 		    todo);
405a15f7c96SJohn Baldwin 		m->m_len += todo;
406a15f7c96SJohn Baldwin 		resid -= todo;
407a15f7c96SJohn Baldwin 		if (resid == 0) {
408a15f7c96SJohn Baldwin 			MPASS(m->m_next == NULL);
409a15f7c96SJohn Baldwin 			break;
410a15f7c96SJohn Baldwin 		}
411a15f7c96SJohn Baldwin 
412a15f7c96SJohn Baldwin 		off += todo;
413a15f7c96SJohn Baldwin 		if (off == sgl->len) {
414a15f7c96SJohn Baldwin 			sgl++;
415a15f7c96SJohn Baldwin 			off = 0;
416a15f7c96SJohn Baldwin 		}
417a15f7c96SJohn Baldwin 		mlen -= todo;
418a15f7c96SJohn Baldwin 		if (mlen == 0) {
419a15f7c96SJohn Baldwin 			m = m->m_next;
420a15f7c96SJohn Baldwin 			mlen = M_TRAILINGSPACE(m);
421a15f7c96SJohn Baldwin 		}
422a15f7c96SJohn Baldwin 	}
423a15f7c96SJohn Baldwin 
424a15f7c96SJohn Baldwin 	return (m0);
425a15f7c96SJohn Baldwin }
426a15f7c96SJohn Baldwin 
427a15f7c96SJohn Baldwin static void
m_free_ref_data(struct mbuf * m)428a15f7c96SJohn Baldwin m_free_ref_data(struct mbuf *m)
429a15f7c96SJohn Baldwin {
430a15f7c96SJohn Baldwin 	ctl_ref kern_data_ref = m->m_ext.ext_arg1;
431a15f7c96SJohn Baldwin 
432a15f7c96SJohn Baldwin 	kern_data_ref(m->m_ext.ext_arg2, -1);
433a15f7c96SJohn Baldwin }
434a15f7c96SJohn Baldwin 
435a15f7c96SJohn Baldwin static struct mbuf *
m_get_ref_data(struct ctl_nvmeio * ctnio,void * buf,u_int size)436a15f7c96SJohn Baldwin m_get_ref_data(struct ctl_nvmeio *ctnio, void *buf, u_int size)
437a15f7c96SJohn Baldwin {
438a15f7c96SJohn Baldwin 	struct mbuf *m;
439a15f7c96SJohn Baldwin 
440a15f7c96SJohn Baldwin 	m = m_get(M_WAITOK, MT_DATA);
441a15f7c96SJohn Baldwin 	m_extadd(m, buf, size, m_free_ref_data, ctnio->kern_data_ref,
442a15f7c96SJohn Baldwin 	    ctnio->kern_data_arg, M_RDONLY, EXT_CTL);
443a15f7c96SJohn Baldwin 	m->m_len = size;
444a15f7c96SJohn Baldwin 	ctnio->kern_data_ref(ctnio->kern_data_arg, 1);
445a15f7c96SJohn Baldwin 	return (m);
446a15f7c96SJohn Baldwin }
447a15f7c96SJohn Baldwin 
448a15f7c96SJohn Baldwin static struct mbuf *
nvmft_ref_data(struct ctl_nvmeio * ctnio)449a15f7c96SJohn Baldwin nvmft_ref_data(struct ctl_nvmeio *ctnio)
450a15f7c96SJohn Baldwin {
451a15f7c96SJohn Baldwin 	struct ctl_sg_entry *sgl;
452a15f7c96SJohn Baldwin 	struct mbuf *m0, *m;
453a15f7c96SJohn Baldwin 
454a15f7c96SJohn Baldwin 	MPASS(ctnio->kern_data_len != 0);
455a15f7c96SJohn Baldwin 
456a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries == 0)
457a15f7c96SJohn Baldwin 		return (m_get_ref_data(ctnio, ctnio->kern_data_ptr,
458a15f7c96SJohn Baldwin 		    ctnio->kern_data_len));
459a15f7c96SJohn Baldwin 
460a15f7c96SJohn Baldwin 	sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
461a15f7c96SJohn Baldwin 	m0 = m_get_ref_data(ctnio, sgl[0].addr, sgl[0].len);
462a15f7c96SJohn Baldwin 	m = m0;
463a15f7c96SJohn Baldwin 	for (u_int i = 1; i < ctnio->kern_sg_entries; i++) {
464a15f7c96SJohn Baldwin 		m->m_next = m_get_ref_data(ctnio, sgl[i].addr, sgl[i].len);
465a15f7c96SJohn Baldwin 		m = m->m_next;
466a15f7c96SJohn Baldwin 	}
467a15f7c96SJohn Baldwin 	return (m0);
468a15f7c96SJohn Baldwin }
469a15f7c96SJohn Baldwin 
470a15f7c96SJohn Baldwin static void
nvmft_datamove_in(struct ctl_nvmeio * ctnio,struct nvmft_qpair * qp,struct nvmf_capsule * nc)471a15f7c96SJohn Baldwin nvmft_datamove_in(struct ctl_nvmeio *ctnio, struct nvmft_qpair *qp,
472a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
473a15f7c96SJohn Baldwin {
474a15f7c96SJohn Baldwin 	struct mbuf *m;
475a15f7c96SJohn Baldwin 	u_int status;
476a15f7c96SJohn Baldwin 
477a15f7c96SJohn Baldwin 	if (ctnio->kern_data_ref != NULL)
478a15f7c96SJohn Baldwin 		m = nvmft_ref_data(ctnio);
479a15f7c96SJohn Baldwin 	else
480a15f7c96SJohn Baldwin 		m = nvmft_copy_data(ctnio);
481a15f7c96SJohn Baldwin 	status = nvmf_send_controller_data(nc, ctnio->kern_rel_offset, m,
482a15f7c96SJohn Baldwin 	    ctnio->kern_data_len);
483a15f7c96SJohn Baldwin 	switch (status) {
484a15f7c96SJohn Baldwin 	case NVMF_SUCCESS_SENT:
485a15f7c96SJohn Baldwin 		ctnio->success_sent = true;
486a15f7c96SJohn Baldwin 		nvmft_command_completed(qp, nc);
487a15f7c96SJohn Baldwin 		/* FALLTHROUGH */
488a15f7c96SJohn Baldwin 	case NVMF_MORE:
489a15f7c96SJohn Baldwin 	case NVME_SC_SUCCESS:
490a15f7c96SJohn Baldwin 		break;
491a15f7c96SJohn Baldwin 	default:
492a15f7c96SJohn Baldwin 		ctl_nvme_set_generic_error(ctnio, status);
493a15f7c96SJohn Baldwin 		break;
494a15f7c96SJohn Baldwin 	}
495a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, true);
496a15f7c96SJohn Baldwin }
497a15f7c96SJohn Baldwin 
4981b3fa1acSJohn Baldwin void
nvmft_handle_datamove(union ctl_io * io)4991b3fa1acSJohn Baldwin nvmft_handle_datamove(union ctl_io *io)
500a15f7c96SJohn Baldwin {
501a15f7c96SJohn Baldwin 	struct nvmf_capsule *nc;
502a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
503a15f7c96SJohn Baldwin 
504a15f7c96SJohn Baldwin 	/* Some CTL commands preemptively set a success status. */
505a15f7c96SJohn Baldwin 	MPASS(io->io_hdr.status == CTL_STATUS_NONE ||
506a15f7c96SJohn Baldwin 	    io->io_hdr.status == CTL_SUCCESS);
507a15f7c96SJohn Baldwin 	MPASS(!io->nvmeio.success_sent);
508a15f7c96SJohn Baldwin 
509a15f7c96SJohn Baldwin 	nc = NVMFT_NC(io);
510a15f7c96SJohn Baldwin 	qp = NVMFT_QP(io);
511a15f7c96SJohn Baldwin 
512a15f7c96SJohn Baldwin 	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN)
513a15f7c96SJohn Baldwin 		nvmft_datamove_in(&io->nvmeio, qp, nc);
514a15f7c96SJohn Baldwin 	else
515a15f7c96SJohn Baldwin 		nvmft_datamove_out(&io->nvmeio, qp, nc);
516a15f7c96SJohn Baldwin }
517a15f7c96SJohn Baldwin 
5181b3fa1acSJohn Baldwin void
nvmft_abort_datamove(union ctl_io * io)5191b3fa1acSJohn Baldwin nvmft_abort_datamove(union ctl_io *io)
5201b3fa1acSJohn Baldwin {
5211b3fa1acSJohn Baldwin 	io->io_hdr.port_status = 1;
5221b3fa1acSJohn Baldwin 	io->io_hdr.flags |= CTL_FLAG_ABORT;
5231b3fa1acSJohn Baldwin 	ctl_datamove_done(io, true);
5241b3fa1acSJohn Baldwin }
5251b3fa1acSJohn Baldwin 
5261b3fa1acSJohn Baldwin static void
nvmft_datamove(union ctl_io * io)5271b3fa1acSJohn Baldwin nvmft_datamove(union ctl_io *io)
5281b3fa1acSJohn Baldwin {
5291b3fa1acSJohn Baldwin 	struct nvmft_qpair *qp;
5301b3fa1acSJohn Baldwin 
5311b3fa1acSJohn Baldwin 	qp = NVMFT_QP(io);
5321b3fa1acSJohn Baldwin 	nvmft_qpair_datamove(qp, io);
5331b3fa1acSJohn Baldwin }
5341b3fa1acSJohn Baldwin 
5351b3fa1acSJohn Baldwin void
nvmft_enqueue_task(struct task * task)5361b3fa1acSJohn Baldwin nvmft_enqueue_task(struct task *task)
5371b3fa1acSJohn Baldwin {
5381b3fa1acSJohn Baldwin 	taskqueue_enqueue(nvmft_taskq, task);
5391b3fa1acSJohn Baldwin }
5401b3fa1acSJohn Baldwin 
5411b3fa1acSJohn Baldwin void
nvmft_drain_task(struct task * task)5421b3fa1acSJohn Baldwin nvmft_drain_task(struct task *task)
5431b3fa1acSJohn Baldwin {
5441b3fa1acSJohn Baldwin 	taskqueue_drain(nvmft_taskq, task);
5451b3fa1acSJohn Baldwin }
5461b3fa1acSJohn Baldwin 
547a15f7c96SJohn Baldwin static void
hip_add(uint64_t pair[2],uint64_t addend)548a15f7c96SJohn Baldwin hip_add(uint64_t pair[2], uint64_t addend)
549a15f7c96SJohn Baldwin {
550a15f7c96SJohn Baldwin 	uint64_t old, new;
551a15f7c96SJohn Baldwin 
552a15f7c96SJohn Baldwin 	old = le64toh(pair[0]);
553a15f7c96SJohn Baldwin 	new = old + addend;
554a15f7c96SJohn Baldwin 	pair[0] = htole64(new);
555a15f7c96SJohn Baldwin 	if (new < old)
556a15f7c96SJohn Baldwin 		pair[1] += htole64(1);
557a15f7c96SJohn Baldwin }
558a15f7c96SJohn Baldwin 
559a15f7c96SJohn Baldwin static void
nvmft_done(union ctl_io * io)560a15f7c96SJohn Baldwin nvmft_done(union ctl_io *io)
561a15f7c96SJohn Baldwin {
562a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
563a15f7c96SJohn Baldwin 	const struct nvme_command *cmd;
564a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
565a15f7c96SJohn Baldwin 	struct nvmf_capsule *nc;
566a15f7c96SJohn Baldwin 	size_t len;
567a15f7c96SJohn Baldwin 
568a15f7c96SJohn Baldwin 	KASSERT(io->io_hdr.status == CTL_SUCCESS ||
569a15f7c96SJohn Baldwin 	    io->io_hdr.status == CTL_NVME_ERROR,
570a15f7c96SJohn Baldwin 	    ("%s: bad status %u", __func__, io->io_hdr.status));
571a15f7c96SJohn Baldwin 
572a15f7c96SJohn Baldwin 	nc = NVMFT_NC(io);
573a15f7c96SJohn Baldwin 	qp = NVMFT_QP(io);
574a15f7c96SJohn Baldwin 	ctrlr = nvmft_qpair_ctrlr(qp);
575a15f7c96SJohn Baldwin 
576a15f7c96SJohn Baldwin 	if (nc == NULL) {
577a15f7c96SJohn Baldwin 		/* Completion of nvmft_terminate_commands. */
578a15f7c96SJohn Baldwin 		goto end;
579a15f7c96SJohn Baldwin 	}
580a15f7c96SJohn Baldwin 
581a15f7c96SJohn Baldwin 	cmd = nvmf_capsule_sqe(nc);
582a15f7c96SJohn Baldwin 
583a15f7c96SJohn Baldwin 	if (io->io_hdr.status == CTL_SUCCESS)
584a15f7c96SJohn Baldwin 		len = nvmf_capsule_data_len(nc) / 512;
585a15f7c96SJohn Baldwin 	else
586a15f7c96SJohn Baldwin 		len = 0;
587a15f7c96SJohn Baldwin 	switch (cmd->opc) {
588a15f7c96SJohn Baldwin 	case NVME_OPC_WRITE:
589a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
590a15f7c96SJohn Baldwin 		hip_add(ctrlr->hip.host_write_commands, 1);
591a15f7c96SJohn Baldwin 		len += ctrlr->partial_duw;
592a15f7c96SJohn Baldwin 		if (len > 1000)
593a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.data_units_written, len / 1000);
594a15f7c96SJohn Baldwin 		ctrlr->partial_duw = len % 1000;
595a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
596a15f7c96SJohn Baldwin 		break;
597a15f7c96SJohn Baldwin 	case NVME_OPC_READ:
598a15f7c96SJohn Baldwin 	case NVME_OPC_COMPARE:
599a15f7c96SJohn Baldwin 	case NVME_OPC_VERIFY:
600a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
601a15f7c96SJohn Baldwin 		if (cmd->opc != NVME_OPC_VERIFY)
602a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.host_read_commands, 1);
603a15f7c96SJohn Baldwin 		len += ctrlr->partial_dur;
604a15f7c96SJohn Baldwin 		if (len > 1000)
605a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.data_units_read, len / 1000);
606a15f7c96SJohn Baldwin 		ctrlr->partial_dur = len % 1000;
607a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
608a15f7c96SJohn Baldwin 		break;
609a15f7c96SJohn Baldwin 	}
610a15f7c96SJohn Baldwin 
611a15f7c96SJohn Baldwin 	if (io->nvmeio.success_sent) {
612a15f7c96SJohn Baldwin 		MPASS(io->io_hdr.status == CTL_SUCCESS);
613a15f7c96SJohn Baldwin 	} else {
614a15f7c96SJohn Baldwin 		io->nvmeio.cpl.cid = cmd->cid;
615a15f7c96SJohn Baldwin 		nvmft_send_response(qp, &io->nvmeio.cpl);
616a15f7c96SJohn Baldwin 	}
617a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
618a15f7c96SJohn Baldwin end:
619a15f7c96SJohn Baldwin 	ctl_free_io(io);
620a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
621a15f7c96SJohn Baldwin 	ctrlr->pending_commands--;
622a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
623a15f7c96SJohn Baldwin 		ctrlr->busy_total += sbinuptime() - ctrlr->start_busy;
624a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
625a15f7c96SJohn Baldwin }
626a15f7c96SJohn Baldwin 
627a15f7c96SJohn Baldwin static int
nvmft_init(void)628a15f7c96SJohn Baldwin nvmft_init(void)
629a15f7c96SJohn Baldwin {
6301b3fa1acSJohn Baldwin 	int error;
6311b3fa1acSJohn Baldwin 
6321b3fa1acSJohn Baldwin 	nvmft_taskq = taskqueue_create("nvmft", M_WAITOK,
6331b3fa1acSJohn Baldwin 	    taskqueue_thread_enqueue, &nvmft_taskq);
6341b3fa1acSJohn Baldwin 	error = taskqueue_start_threads_in_proc(&nvmft_taskq, mp_ncpus, PWAIT,
6351b3fa1acSJohn Baldwin 	    control_softc->ctl_proc, "nvmft");
6361b3fa1acSJohn Baldwin 	if (error != 0) {
6371b3fa1acSJohn Baldwin 		taskqueue_free(nvmft_taskq);
6381b3fa1acSJohn Baldwin 		return (error);
6391b3fa1acSJohn Baldwin 	}
6401b3fa1acSJohn Baldwin 
641a15f7c96SJohn Baldwin 	TAILQ_INIT(&nvmft_ports);
642a15f7c96SJohn Baldwin 	sx_init(&nvmft_ports_lock, "nvmft ports");
643a15f7c96SJohn Baldwin 	return (0);
644a15f7c96SJohn Baldwin }
645a15f7c96SJohn Baldwin 
646a15f7c96SJohn Baldwin void
nvmft_port_free(struct nvmft_port * np)647a15f7c96SJohn Baldwin nvmft_port_free(struct nvmft_port *np)
648a15f7c96SJohn Baldwin {
649a15f7c96SJohn Baldwin 	KASSERT(TAILQ_EMPTY(&np->controllers),
650a15f7c96SJohn Baldwin 	    ("%s(%p): active controllers", __func__, np));
651a15f7c96SJohn Baldwin 
652a15f7c96SJohn Baldwin 	if (np->port.targ_port != -1) {
653a15f7c96SJohn Baldwin 		if (ctl_port_deregister(&np->port) != 0)
654a15f7c96SJohn Baldwin 			printf("%s: ctl_port_deregister() failed\n", __func__);
655a15f7c96SJohn Baldwin 	}
656a15f7c96SJohn Baldwin 
657a15f7c96SJohn Baldwin 	free(np->active_ns, M_NVMFT);
658a15f7c96SJohn Baldwin 	clean_unrhdr(np->ids);
659a15f7c96SJohn Baldwin 	delete_unrhdr(np->ids);
66097ca2adaSJohn Baldwin 	mtx_destroy(&np->lock);
661a15f7c96SJohn Baldwin 	free(np, M_NVMFT);
662a15f7c96SJohn Baldwin }
663a15f7c96SJohn Baldwin 
664a15f7c96SJohn Baldwin static struct nvmft_port *
nvmft_port_find(const char * subnqn)665a15f7c96SJohn Baldwin nvmft_port_find(const char *subnqn)
666a15f7c96SJohn Baldwin {
667a15f7c96SJohn Baldwin 	struct nvmft_port *np;
668a15f7c96SJohn Baldwin 
669a15f7c96SJohn Baldwin 	KASSERT(nvmf_nqn_valid(subnqn), ("%s: invalid nqn", __func__));
670a15f7c96SJohn Baldwin 
671a15f7c96SJohn Baldwin 	sx_assert(&nvmft_ports_lock, SA_LOCKED);
672a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
673a15f7c96SJohn Baldwin 		if (strcmp(np->cdata.subnqn, subnqn) == 0)
674a15f7c96SJohn Baldwin 			break;
675a15f7c96SJohn Baldwin 	}
676a15f7c96SJohn Baldwin 	return (np);
677a15f7c96SJohn Baldwin }
678a15f7c96SJohn Baldwin 
679a15f7c96SJohn Baldwin static struct nvmft_port *
nvmft_port_find_by_id(int port_id)680a15f7c96SJohn Baldwin nvmft_port_find_by_id(int port_id)
681a15f7c96SJohn Baldwin {
682a15f7c96SJohn Baldwin 	struct nvmft_port *np;
683a15f7c96SJohn Baldwin 
684a15f7c96SJohn Baldwin 	sx_assert(&nvmft_ports_lock, SA_LOCKED);
685a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
686a15f7c96SJohn Baldwin 		if (np->port.targ_port == port_id)
687a15f7c96SJohn Baldwin 			break;
688a15f7c96SJohn Baldwin 	}
689a15f7c96SJohn Baldwin 	return (np);
690a15f7c96SJohn Baldwin }
691a15f7c96SJohn Baldwin 
692a15f7c96SJohn Baldwin /*
693a15f7c96SJohn Baldwin  * Helper function to fetch a number stored as a string in an nv_list.
694a15f7c96SJohn Baldwin  * Returns false if the string was not a valid number.
695a15f7c96SJohn Baldwin  */
696a15f7c96SJohn Baldwin static bool
dnvlist_get_strnum(nvlist_t * nvl,const char * name,u_long default_value,u_long * value)697a15f7c96SJohn Baldwin dnvlist_get_strnum(nvlist_t *nvl, const char *name, u_long default_value,
698a15f7c96SJohn Baldwin 	u_long *value)
699a15f7c96SJohn Baldwin {
700a15f7c96SJohn Baldwin 	const char *str;
701a15f7c96SJohn Baldwin 	char *cp;
702a15f7c96SJohn Baldwin 
703a15f7c96SJohn Baldwin 	str = dnvlist_get_string(nvl, name, NULL);
704a15f7c96SJohn Baldwin 	if (str == NULL) {
705a15f7c96SJohn Baldwin 		*value = default_value;
706a15f7c96SJohn Baldwin 		return (true);
707a15f7c96SJohn Baldwin 	}
708a15f7c96SJohn Baldwin 	if (*str == '\0')
709a15f7c96SJohn Baldwin 		return (false);
710a15f7c96SJohn Baldwin 	*value = strtoul(str, &cp, 0);
711a15f7c96SJohn Baldwin 	if (*cp != '\0')
712a15f7c96SJohn Baldwin 		return (false);
713a15f7c96SJohn Baldwin 	return (true);
714a15f7c96SJohn Baldwin }
715a15f7c96SJohn Baldwin 
716a15f7c96SJohn Baldwin /*
717a15f7c96SJohn Baldwin  * NVMeoF ports support the following parameters:
718a15f7c96SJohn Baldwin  *
719a15f7c96SJohn Baldwin  * Mandatory:
720a15f7c96SJohn Baldwin  *
721a15f7c96SJohn Baldwin  * subnqn: subsystem NVMe Qualified Name
722a15f7c96SJohn Baldwin  * portid: integer port ID from Discovery Log Page entry
723a15f7c96SJohn Baldwin  *
724a15f7c96SJohn Baldwin  * Optional:
725a15f7c96SJohn Baldwin  * serial: Serial Number string
726a15f7c96SJohn Baldwin  * max_io_qsize: Maximum number of I/O queue entries
727a15f7c96SJohn Baldwin  * enable_timeout: Timeout for controller enable in milliseconds
728a15f7c96SJohn Baldwin  * ioccsz: Maximum command capsule size
729a15f7c96SJohn Baldwin  * iorcsz: Maximum response capsule size
730a15f7c96SJohn Baldwin  * nn: Number of namespaces
731a15f7c96SJohn Baldwin  */
732a15f7c96SJohn Baldwin static void
nvmft_port_create(struct ctl_req * req)733a15f7c96SJohn Baldwin nvmft_port_create(struct ctl_req *req)
734a15f7c96SJohn Baldwin {
735a15f7c96SJohn Baldwin 	struct nvmft_port *np;
736a15f7c96SJohn Baldwin 	struct ctl_port *port;
737a15f7c96SJohn Baldwin 	const char *serial, *subnqn;
738a15f7c96SJohn Baldwin 	char serial_buf[NVME_SERIAL_NUMBER_LENGTH];
739a15f7c96SJohn Baldwin 	u_long enable_timeout, hostid, ioccsz, iorcsz, max_io_qsize, nn, portid;
740a15f7c96SJohn Baldwin 	int error;
741a15f7c96SJohn Baldwin 
742a15f7c96SJohn Baldwin 	/* Required parameters. */
743a15f7c96SJohn Baldwin 	subnqn = dnvlist_get_string(req->args_nvl, "subnqn", NULL);
744a15f7c96SJohn Baldwin 	if (subnqn == NULL || !nvlist_exists_string(req->args_nvl, "portid")) {
745a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
746a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
747a15f7c96SJohn Baldwin 		    "Missing required argument");
748a15f7c96SJohn Baldwin 		return;
749a15f7c96SJohn Baldwin 	}
750a15f7c96SJohn Baldwin 	if (!nvmf_nqn_valid(subnqn)) {
751a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
752a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
753a15f7c96SJohn Baldwin 		    "Invalid SubNQN");
754a15f7c96SJohn Baldwin 		return;
755a15f7c96SJohn Baldwin 	}
756a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "portid", UINT16_MAX, &portid) ||
757a15f7c96SJohn Baldwin 	    portid > UINT16_MAX) {
758a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
759a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
760a15f7c96SJohn Baldwin 		    "Invalid port ID");
761a15f7c96SJohn Baldwin 		return;
762a15f7c96SJohn Baldwin 	}
763a15f7c96SJohn Baldwin 
764a15f7c96SJohn Baldwin 	/* Optional parameters. */
765a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "max_io_qsize",
766a15f7c96SJohn Baldwin 	    NVMF_MAX_IO_ENTRIES, &max_io_qsize) ||
767a15f7c96SJohn Baldwin 	    max_io_qsize < NVME_MIN_IO_ENTRIES ||
768a15f7c96SJohn Baldwin 	    max_io_qsize > NVME_MAX_IO_ENTRIES) {
769a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
770a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
771a15f7c96SJohn Baldwin 		    "Invalid maximum I/O queue size");
772a15f7c96SJohn Baldwin 		return;
773a15f7c96SJohn Baldwin 	}
774a15f7c96SJohn Baldwin 
775a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "enable_timeout",
776a15f7c96SJohn Baldwin 	    NVMF_CC_EN_TIMEOUT * 500, &enable_timeout) ||
777a15f7c96SJohn Baldwin 	    (enable_timeout % 500) != 0 || (enable_timeout / 500) > 255) {
778a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
779a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
780a15f7c96SJohn Baldwin 		    "Invalid enable timeout");
781a15f7c96SJohn Baldwin 		return;
782a15f7c96SJohn Baldwin 	}
783a15f7c96SJohn Baldwin 
784a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "ioccsz", NVMF_IOCCSZ,
785a15f7c96SJohn Baldwin 	    &ioccsz) || ioccsz < sizeof(struct nvme_command) ||
786a15f7c96SJohn Baldwin 	    (ioccsz % 16) != 0) {
787a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
788a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
789a15f7c96SJohn Baldwin 		    "Invalid Command Capsule size");
790a15f7c96SJohn Baldwin 		return;
791a15f7c96SJohn Baldwin 	}
792a15f7c96SJohn Baldwin 
793a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "iorcsz", NVMF_IORCSZ,
794a15f7c96SJohn Baldwin 	    &iorcsz) || iorcsz < sizeof(struct nvme_completion) ||
795a15f7c96SJohn Baldwin 	    (iorcsz % 16) != 0) {
796a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
797a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
798a15f7c96SJohn Baldwin 		    "Invalid Response Capsule size");
799a15f7c96SJohn Baldwin 		return;
800a15f7c96SJohn Baldwin 	}
801a15f7c96SJohn Baldwin 
802a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "nn", NVMF_NN, &nn) ||
803a15f7c96SJohn Baldwin 	    nn < 1 || nn > UINT32_MAX) {
804a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
805a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
806a15f7c96SJohn Baldwin 		    "Invalid number of namespaces");
807a15f7c96SJohn Baldwin 		return;
808a15f7c96SJohn Baldwin 	}
809a15f7c96SJohn Baldwin 
810a15f7c96SJohn Baldwin 	serial = dnvlist_get_string(req->args_nvl, "serial", NULL);
811a15f7c96SJohn Baldwin 	if (serial == NULL) {
812a15f7c96SJohn Baldwin 		getcredhostid(curthread->td_ucred, &hostid);
813a15f7c96SJohn Baldwin 		nvmf_controller_serial(serial_buf, sizeof(serial_buf), hostid);
814a15f7c96SJohn Baldwin 		serial = serial_buf;
815a15f7c96SJohn Baldwin 	}
816a15f7c96SJohn Baldwin 
817a15f7c96SJohn Baldwin 	sx_xlock(&nvmft_ports_lock);
818a15f7c96SJohn Baldwin 
819a15f7c96SJohn Baldwin 	np = nvmft_port_find(subnqn);
820a15f7c96SJohn Baldwin 	if (np != NULL) {
821a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
822a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
823a15f7c96SJohn Baldwin 		    "SubNQN \"%s\" already exists", subnqn);
824a15f7c96SJohn Baldwin 		sx_xunlock(&nvmft_ports_lock);
825a15f7c96SJohn Baldwin 		return;
826a15f7c96SJohn Baldwin 	}
827a15f7c96SJohn Baldwin 
828a15f7c96SJohn Baldwin 	np = malloc(sizeof(*np), M_NVMFT, M_WAITOK | M_ZERO);
829a15f7c96SJohn Baldwin 	refcount_init(&np->refs, 1);
830*11509c6eSJohn Baldwin 	np->portid = portid;
831a15f7c96SJohn Baldwin 	np->max_io_qsize = max_io_qsize;
832a15f7c96SJohn Baldwin 	np->cap = _nvmf_controller_cap(max_io_qsize, enable_timeout / 500);
83397ca2adaSJohn Baldwin 	mtx_init(&np->lock, "nvmft port", NULL, MTX_DEF);
834a15f7c96SJohn Baldwin 	np->ids = new_unrhdr(0, MIN(CTL_MAX_INIT_PER_PORT - 1,
835a15f7c96SJohn Baldwin 	    NVMF_CNTLID_STATIC_MAX), UNR_NO_MTX);
836a15f7c96SJohn Baldwin 	TAILQ_INIT(&np->controllers);
837a15f7c96SJohn Baldwin 
838a15f7c96SJohn Baldwin 	/* The controller ID is set later for individual controllers. */
839a15f7c96SJohn Baldwin 	_nvmf_init_io_controller_data(0, max_io_qsize, serial, ostype,
840a15f7c96SJohn Baldwin 	    osrelease, subnqn, nn, ioccsz, iorcsz, &np->cdata);
841a15f7c96SJohn Baldwin 	np->cdata.aerl = NVMFT_NUM_AER - 1;
842a15f7c96SJohn Baldwin 	np->cdata.oaes = htole32(NVME_ASYNC_EVENT_NS_ATTRIBUTE);
843a15f7c96SJohn Baldwin 	np->cdata.oncs = htole16(NVMEF(NVME_CTRLR_DATA_ONCS_VERIFY, 1) |
844a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_WRZERO, 1) |
845a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_DSM, 1) |
846a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_COMPARE, 1));
847a15f7c96SJohn Baldwin 	np->cdata.fuses = NVMEF(NVME_CTRLR_DATA_FUSES_CNW, 1);
848a15f7c96SJohn Baldwin 
849a15f7c96SJohn Baldwin 	np->fp.afi = NVMEF(NVME_FIRMWARE_PAGE_AFI_SLOT, 1);
850a15f7c96SJohn Baldwin 	memcpy(np->fp.revision[0], np->cdata.fr, sizeof(np->cdata.fr));
851a15f7c96SJohn Baldwin 
852a15f7c96SJohn Baldwin 	port = &np->port;
853a15f7c96SJohn Baldwin 
854a15f7c96SJohn Baldwin 	port->frontend = &nvmft_frontend;
855a15f7c96SJohn Baldwin 	port->port_type = CTL_PORT_NVMF;
856a15f7c96SJohn Baldwin 	port->num_requested_ctl_io = max_io_qsize;
857a15f7c96SJohn Baldwin 	port->port_name = "nvmf";
858a15f7c96SJohn Baldwin 	port->physical_port = portid;
859a15f7c96SJohn Baldwin 	port->virtual_port = 0;
860a15f7c96SJohn Baldwin 	port->port_online = nvmft_online;
861a15f7c96SJohn Baldwin 	port->port_offline = nvmft_offline;
862*11509c6eSJohn Baldwin 	port->port_info = nvmft_info;
863a15f7c96SJohn Baldwin 	port->onoff_arg = np;
864a15f7c96SJohn Baldwin 	port->lun_enable = nvmft_lun_enable;
865a15f7c96SJohn Baldwin 	port->lun_disable = nvmft_lun_disable;
866a15f7c96SJohn Baldwin 	port->targ_lun_arg = np;
867a15f7c96SJohn Baldwin 	port->fe_datamove = nvmft_datamove;
868a15f7c96SJohn Baldwin 	port->fe_done = nvmft_done;
869a15f7c96SJohn Baldwin 	port->targ_port = -1;
870a15f7c96SJohn Baldwin 	port->options = nvlist_clone(req->args_nvl);
871a15f7c96SJohn Baldwin 
872a15f7c96SJohn Baldwin 	error = ctl_port_register(port);
873a15f7c96SJohn Baldwin 	if (error != 0) {
874a15f7c96SJohn Baldwin 		sx_xunlock(&nvmft_ports_lock);
875a15f7c96SJohn Baldwin 		nvlist_destroy(port->options);
876a15f7c96SJohn Baldwin 		nvmft_port_rele(np);
877a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
878a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
879a15f7c96SJohn Baldwin 		    "Failed to register CTL port with error %d", error);
880a15f7c96SJohn Baldwin 		return;
881a15f7c96SJohn Baldwin 	}
882a15f7c96SJohn Baldwin 
883a15f7c96SJohn Baldwin 	TAILQ_INSERT_TAIL(&nvmft_ports, np, link);
884a15f7c96SJohn Baldwin 	sx_xunlock(&nvmft_ports_lock);
885a15f7c96SJohn Baldwin 
886a15f7c96SJohn Baldwin 	req->status = CTL_LUN_OK;
887a15f7c96SJohn Baldwin 	req->result_nvl = nvlist_create(0);
888a15f7c96SJohn Baldwin 	nvlist_add_number(req->result_nvl, "port_id", port->targ_port);
889a15f7c96SJohn Baldwin }
890a15f7c96SJohn Baldwin 
891a15f7c96SJohn Baldwin static void
nvmft_port_remove(struct ctl_req * req)892a15f7c96SJohn Baldwin nvmft_port_remove(struct ctl_req *req)
893a15f7c96SJohn Baldwin {
894a15f7c96SJohn Baldwin 	struct nvmft_port *np;
895a15f7c96SJohn Baldwin 	const char *subnqn;
896a15f7c96SJohn Baldwin 	u_long port_id;
897a15f7c96SJohn Baldwin 
898a15f7c96SJohn Baldwin 	/*
899a15f7c96SJohn Baldwin 	 * ctladm port -r just provides the port_id, so permit looking
900a15f7c96SJohn Baldwin 	 * up a port either by "subnqn" or "port_id".
901a15f7c96SJohn Baldwin 	 */
902a15f7c96SJohn Baldwin 	port_id = ULONG_MAX;
903a15f7c96SJohn Baldwin 	subnqn = dnvlist_get_string(req->args_nvl, "subnqn", NULL);
904a15f7c96SJohn Baldwin 	if (subnqn == NULL) {
905a15f7c96SJohn Baldwin 		if (!nvlist_exists_string(req->args_nvl, "port_id")) {
906a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
907a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
908a15f7c96SJohn Baldwin 			    "Missing required argument");
909a15f7c96SJohn Baldwin 			return;
910a15f7c96SJohn Baldwin 		}
911a15f7c96SJohn Baldwin 		if (!dnvlist_get_strnum(req->args_nvl, "port_id", ULONG_MAX,
912a15f7c96SJohn Baldwin 		    &port_id)) {
913a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
914a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
915a15f7c96SJohn Baldwin 			    "Invalid CTL port ID");
916a15f7c96SJohn Baldwin 			return;
917a15f7c96SJohn Baldwin 		}
918a15f7c96SJohn Baldwin 	} else {
919a15f7c96SJohn Baldwin 		if (nvlist_exists_string(req->args_nvl, "port_id")) {
920a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
921a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
922a15f7c96SJohn Baldwin 			    "Ambiguous port removal request");
923a15f7c96SJohn Baldwin 			return;
924a15f7c96SJohn Baldwin 		}
925a15f7c96SJohn Baldwin 	}
926a15f7c96SJohn Baldwin 
927a15f7c96SJohn Baldwin 	sx_xlock(&nvmft_ports_lock);
928a15f7c96SJohn Baldwin 
929a15f7c96SJohn Baldwin 	if (subnqn != NULL) {
930a15f7c96SJohn Baldwin 		np = nvmft_port_find(subnqn);
931a15f7c96SJohn Baldwin 		if (np == NULL) {
932a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
933a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
934a15f7c96SJohn Baldwin 			    "SubNQN \"%s\" does not exist", subnqn);
935a15f7c96SJohn Baldwin 			sx_xunlock(&nvmft_ports_lock);
936a15f7c96SJohn Baldwin 			return;
937a15f7c96SJohn Baldwin 		}
938a15f7c96SJohn Baldwin 	} else {
939a15f7c96SJohn Baldwin 		np = nvmft_port_find_by_id(port_id);
940a15f7c96SJohn Baldwin 		if (np == NULL) {
941a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
942a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
943a15f7c96SJohn Baldwin 			    "CTL port %lu is not a NVMF port", port_id);
944a15f7c96SJohn Baldwin 			sx_xunlock(&nvmft_ports_lock);
945a15f7c96SJohn Baldwin 			return;
946a15f7c96SJohn Baldwin 		}
947a15f7c96SJohn Baldwin 	}
948a15f7c96SJohn Baldwin 
949a15f7c96SJohn Baldwin 	TAILQ_REMOVE(&nvmft_ports, np, link);
950a15f7c96SJohn Baldwin 	sx_xunlock(&nvmft_ports_lock);
951a15f7c96SJohn Baldwin 
95297ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
95317b7a0c5SJohn Baldwin 	if (np->online) {
95497ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
955a15f7c96SJohn Baldwin 		ctl_port_offline(&np->port);
95617b7a0c5SJohn Baldwin 	} else
95797ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
95817b7a0c5SJohn Baldwin 
959a15f7c96SJohn Baldwin 	nvmft_port_rele(np);
960a15f7c96SJohn Baldwin 	req->status = CTL_LUN_OK;
961a15f7c96SJohn Baldwin }
962a15f7c96SJohn Baldwin 
963a15f7c96SJohn Baldwin static void
nvmft_handoff(struct ctl_nvmf * cn)964a15f7c96SJohn Baldwin nvmft_handoff(struct ctl_nvmf *cn)
965a15f7c96SJohn Baldwin {
966365b89e8SJohn Baldwin 	const struct nvmf_fabric_connect_cmd *cmd;
967365b89e8SJohn Baldwin 	const struct nvmf_fabric_connect_data *data;
968365b89e8SJohn Baldwin 	const nvlist_t *params;
969a15f7c96SJohn Baldwin 	struct nvmft_port *np;
970365b89e8SJohn Baldwin 	nvlist_t *nvl;
971365b89e8SJohn Baldwin 	size_t len;
972365b89e8SJohn Baldwin 	enum nvmf_trtype trtype;
973a15f7c96SJohn Baldwin 	int error;
974a15f7c96SJohn Baldwin 
975a15f7c96SJohn Baldwin 	np = NULL;
976365b89e8SJohn Baldwin 	error = nvmf_unpack_ioc_nvlist(&cn->data.handoff, &nvl);
977a15f7c96SJohn Baldwin 	if (error != 0) {
978a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
979a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
980365b89e8SJohn Baldwin 		    "Failed to copyin and unpack handoff arguments");
981a15f7c96SJohn Baldwin 		return;
982a15f7c96SJohn Baldwin 	}
983a15f7c96SJohn Baldwin 
984365b89e8SJohn Baldwin 	if (!nvlist_exists_number(nvl, "trtype") ||
985365b89e8SJohn Baldwin 	    !nvlist_exists_nvlist(nvl, "params") ||
986365b89e8SJohn Baldwin 	    !nvlist_exists_binary(nvl, "cmd") ||
987365b89e8SJohn Baldwin 	    !nvlist_exists_binary(nvl, "data")) {
988a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
989a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
990365b89e8SJohn Baldwin 		    "Handoff arguments missing required value");
991365b89e8SJohn Baldwin 		goto out;
992365b89e8SJohn Baldwin 	}
993365b89e8SJohn Baldwin 
994365b89e8SJohn Baldwin 	params = nvlist_get_nvlist(nvl, "params");
995365b89e8SJohn Baldwin 	if (!nvmf_validate_qpair_nvlist(params, true)) {
996365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
997365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
998365b89e8SJohn Baldwin 		    "Invalid queue pair parameters");
999365b89e8SJohn Baldwin 		goto out;
1000365b89e8SJohn Baldwin 	}
1001365b89e8SJohn Baldwin 
1002365b89e8SJohn Baldwin 	cmd = nvlist_get_binary(nvl, "cmd", &len);
1003365b89e8SJohn Baldwin 	if (len != sizeof(*cmd)) {
1004365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1005365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1006365b89e8SJohn Baldwin 		    "Wrong size for CONNECT SQE");
1007365b89e8SJohn Baldwin 		goto out;
1008365b89e8SJohn Baldwin 	}
1009365b89e8SJohn Baldwin 
1010365b89e8SJohn Baldwin 	data = nvlist_get_binary(nvl, "data", &len);
1011365b89e8SJohn Baldwin 	if (len != sizeof(*data)) {
1012365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1013365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1014365b89e8SJohn Baldwin 		    "Wrong size for CONNECT data");
1015a15f7c96SJohn Baldwin 		goto out;
1016a15f7c96SJohn Baldwin 	}
1017a15f7c96SJohn Baldwin 
1018a15f7c96SJohn Baldwin 	if (!nvmf_nqn_valid(data->subnqn)) {
1019a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1020a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1021a15f7c96SJohn Baldwin 		    "Invalid SubNQN");
1022a15f7c96SJohn Baldwin 		goto out;
1023a15f7c96SJohn Baldwin 	}
1024a15f7c96SJohn Baldwin 
1025a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1026a15f7c96SJohn Baldwin 	np = nvmft_port_find(data->subnqn);
1027a15f7c96SJohn Baldwin 	if (np == NULL) {
1028a15f7c96SJohn Baldwin 		sx_sunlock(&nvmft_ports_lock);
1029a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1030a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1031a15f7c96SJohn Baldwin 		    "Unknown SubNQN");
1032a15f7c96SJohn Baldwin 		goto out;
1033a15f7c96SJohn Baldwin 	}
1034a15f7c96SJohn Baldwin 	if (!np->online) {
1035a15f7c96SJohn Baldwin 		sx_sunlock(&nvmft_ports_lock);
1036a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1037a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1038a15f7c96SJohn Baldwin 		    "CTL port offline");
1039a15f7c96SJohn Baldwin 		np = NULL;
1040a15f7c96SJohn Baldwin 		goto out;
1041a15f7c96SJohn Baldwin 	}
1042a15f7c96SJohn Baldwin 	nvmft_port_ref(np);
1043a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1044a15f7c96SJohn Baldwin 
1045365b89e8SJohn Baldwin 	trtype = nvlist_get_number(nvl, "trtype");
1046365b89e8SJohn Baldwin 	if (nvlist_get_bool(params, "admin")) {
1047365b89e8SJohn Baldwin 		error = nvmft_handoff_admin_queue(np, trtype, params, cmd,
1048365b89e8SJohn Baldwin 		    data);
1049a15f7c96SJohn Baldwin 		if (error != 0) {
1050a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1051a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1052a15f7c96SJohn Baldwin 			    "Failed to handoff admin queue: %d", error);
1053a15f7c96SJohn Baldwin 			goto out;
1054a15f7c96SJohn Baldwin 		}
1055a15f7c96SJohn Baldwin 	} else {
1056365b89e8SJohn Baldwin 		error = nvmft_handoff_io_queue(np, trtype, params, cmd, data);
1057a15f7c96SJohn Baldwin 		if (error != 0) {
1058a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1059a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1060225c5e53SJohn Baldwin 			    "Failed to handoff I/O queue: %d", error);
1061a15f7c96SJohn Baldwin 			goto out;
1062a15f7c96SJohn Baldwin 		}
1063a15f7c96SJohn Baldwin 	}
1064a15f7c96SJohn Baldwin 
1065a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1066a15f7c96SJohn Baldwin out:
1067a15f7c96SJohn Baldwin 	if (np != NULL)
1068a15f7c96SJohn Baldwin 		nvmft_port_rele(np);
1069365b89e8SJohn Baldwin 	nvlist_destroy(nvl);
1070a15f7c96SJohn Baldwin }
1071a15f7c96SJohn Baldwin 
1072a15f7c96SJohn Baldwin static void
nvmft_list(struct ctl_nvmf * cn)1073a15f7c96SJohn Baldwin nvmft_list(struct ctl_nvmf *cn)
1074a15f7c96SJohn Baldwin {
1075a15f7c96SJohn Baldwin 	struct ctl_nvmf_list_params *lp;
1076a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
1077a15f7c96SJohn Baldwin 	struct nvmft_port *np;
1078a15f7c96SJohn Baldwin 	struct sbuf *sb;
1079a15f7c96SJohn Baldwin 	int error;
1080a15f7c96SJohn Baldwin 
1081a15f7c96SJohn Baldwin 	lp = &cn->data.list;
1082a15f7c96SJohn Baldwin 
1083a15f7c96SJohn Baldwin 	sb = sbuf_new(NULL, NULL, lp->alloc_len, SBUF_FIXEDLEN |
1084a15f7c96SJohn Baldwin 	    SBUF_INCLUDENUL);
1085a15f7c96SJohn Baldwin 	if (sb == NULL) {
1086a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1087a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1088a15f7c96SJohn Baldwin 		    "Failed to allocate NVMeoF session list");
1089a15f7c96SJohn Baldwin 		return;
1090a15f7c96SJohn Baldwin 	}
1091a15f7c96SJohn Baldwin 
1092a15f7c96SJohn Baldwin 	sbuf_printf(sb, "<ctlnvmflist>\n");
1093a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1094a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
109597ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
1096a15f7c96SJohn Baldwin 		TAILQ_FOREACH(ctrlr, &np->controllers, link) {
1097a15f7c96SJohn Baldwin 			sbuf_printf(sb, "<connection id=\"%d\">"
1098a15f7c96SJohn Baldwin 			    "<hostnqn>%s</hostnqn>"
1099a15f7c96SJohn Baldwin 			    "<subnqn>%s</subnqn>"
1100a15f7c96SJohn Baldwin 			    "<trtype>%u</trtype>"
1101a15f7c96SJohn Baldwin 			    "</connection>\n",
1102a15f7c96SJohn Baldwin 			    ctrlr->cntlid,
1103a15f7c96SJohn Baldwin 			    ctrlr->hostnqn,
1104a15f7c96SJohn Baldwin 			    np->cdata.subnqn,
1105a15f7c96SJohn Baldwin 			    ctrlr->trtype);
1106a15f7c96SJohn Baldwin 		}
110797ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
1108a15f7c96SJohn Baldwin 	}
1109a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1110a15f7c96SJohn Baldwin 	sbuf_printf(sb, "</ctlnvmflist>\n");
1111a15f7c96SJohn Baldwin 	if (sbuf_finish(sb) != 0) {
1112a15f7c96SJohn Baldwin 		sbuf_delete(sb);
1113a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_LIST_NEED_MORE_SPACE;
1114a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1115a15f7c96SJohn Baldwin 		    "Out of space, %d bytes is too small", lp->alloc_len);
1116a15f7c96SJohn Baldwin 		return;
1117a15f7c96SJohn Baldwin 	}
1118a15f7c96SJohn Baldwin 
1119a15f7c96SJohn Baldwin 	error = copyout(sbuf_data(sb), lp->conn_xml, sbuf_len(sb));
1120a15f7c96SJohn Baldwin 	if (error != 0) {
1121a15f7c96SJohn Baldwin 		sbuf_delete(sb);
1122a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1123a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1124a15f7c96SJohn Baldwin 		    "Failed to copyout session list: %d", error);
1125a15f7c96SJohn Baldwin 		return;
1126a15f7c96SJohn Baldwin 	}
1127a15f7c96SJohn Baldwin 	lp->fill_len = sbuf_len(sb);
1128a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1129a15f7c96SJohn Baldwin 	sbuf_delete(sb);
1130a15f7c96SJohn Baldwin }
1131a15f7c96SJohn Baldwin 
1132a15f7c96SJohn Baldwin static void
nvmft_terminate(struct ctl_nvmf * cn)1133a15f7c96SJohn Baldwin nvmft_terminate(struct ctl_nvmf *cn)
1134a15f7c96SJohn Baldwin {
1135a15f7c96SJohn Baldwin 	struct ctl_nvmf_terminate_params *tp;
1136a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
1137a15f7c96SJohn Baldwin 	struct nvmft_port *np;
1138a15f7c96SJohn Baldwin 	bool found, match;
1139a15f7c96SJohn Baldwin 
1140a15f7c96SJohn Baldwin 	tp = &cn->data.terminate;
1141a15f7c96SJohn Baldwin 
1142a15f7c96SJohn Baldwin 	found = false;
1143a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1144a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
114597ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
1146a15f7c96SJohn Baldwin 		TAILQ_FOREACH(ctrlr, &np->controllers, link) {
1147a15f7c96SJohn Baldwin 			if (tp->all != 0)
1148a15f7c96SJohn Baldwin 				match = true;
1149a15f7c96SJohn Baldwin 			else if (tp->cntlid != -1)
1150a15f7c96SJohn Baldwin 				match = tp->cntlid == ctrlr->cntlid;
1151a15f7c96SJohn Baldwin 			else if (tp->hostnqn[0] != '\0')
1152a15f7c96SJohn Baldwin 				match = strncmp(tp->hostnqn, ctrlr->hostnqn,
1153a15f7c96SJohn Baldwin 				    sizeof(tp->hostnqn)) == 0;
1154a15f7c96SJohn Baldwin 			else
1155a15f7c96SJohn Baldwin 				match = false;
1156a15f7c96SJohn Baldwin 			if (!match)
1157a15f7c96SJohn Baldwin 				continue;
1158a15f7c96SJohn Baldwin 			nvmft_printf(ctrlr,
1159a15f7c96SJohn Baldwin 			    "disconnecting due to administrative request\n");
1160a15f7c96SJohn Baldwin 			nvmft_controller_error(ctrlr, NULL, ECONNABORTED);
1161a15f7c96SJohn Baldwin 			found = true;
1162a15f7c96SJohn Baldwin 		}
116397ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
1164a15f7c96SJohn Baldwin 	}
1165a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1166a15f7c96SJohn Baldwin 
1167a15f7c96SJohn Baldwin 	if (!found) {
1168a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ASSOCIATION_NOT_FOUND;
1169a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1170a15f7c96SJohn Baldwin 		    "No matching associations found");
1171a15f7c96SJohn Baldwin 		return;
1172a15f7c96SJohn Baldwin 	}
1173a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1174a15f7c96SJohn Baldwin }
1175a15f7c96SJohn Baldwin 
1176a15f7c96SJohn Baldwin static int
nvmft_ioctl(struct cdev * cdev,u_long cmd,caddr_t data,int flag,struct thread * td)1177a15f7c96SJohn Baldwin nvmft_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flag,
1178a15f7c96SJohn Baldwin     struct thread *td)
1179a15f7c96SJohn Baldwin {
1180a15f7c96SJohn Baldwin 	struct ctl_nvmf *cn;
1181a15f7c96SJohn Baldwin 	struct ctl_req *req;
1182a15f7c96SJohn Baldwin 
1183a15f7c96SJohn Baldwin 	switch (cmd) {
1184a15f7c96SJohn Baldwin 	case CTL_PORT_REQ:
1185a15f7c96SJohn Baldwin 		req = (struct ctl_req *)data;
1186a15f7c96SJohn Baldwin 		switch (req->reqtype) {
1187a15f7c96SJohn Baldwin 		case CTL_REQ_CREATE:
1188a15f7c96SJohn Baldwin 			nvmft_port_create(req);
1189a15f7c96SJohn Baldwin 			break;
1190a15f7c96SJohn Baldwin 		case CTL_REQ_REMOVE:
1191a15f7c96SJohn Baldwin 			nvmft_port_remove(req);
1192a15f7c96SJohn Baldwin 			break;
1193a15f7c96SJohn Baldwin 		default:
1194a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
1195a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
1196a15f7c96SJohn Baldwin 			    "Unsupported request type %d", req->reqtype);
1197a15f7c96SJohn Baldwin 			break;
1198a15f7c96SJohn Baldwin 		}
1199a15f7c96SJohn Baldwin 		return (0);
1200a15f7c96SJohn Baldwin 	case CTL_NVMF:
1201a15f7c96SJohn Baldwin 		cn = (struct ctl_nvmf *)data;
1202a15f7c96SJohn Baldwin 		switch (cn->type) {
1203a15f7c96SJohn Baldwin 		case CTL_NVMF_HANDOFF:
1204a15f7c96SJohn Baldwin 			nvmft_handoff(cn);
1205a15f7c96SJohn Baldwin 			break;
1206a15f7c96SJohn Baldwin 		case CTL_NVMF_LIST:
1207a15f7c96SJohn Baldwin 			nvmft_list(cn);
1208a15f7c96SJohn Baldwin 			break;
1209a15f7c96SJohn Baldwin 		case CTL_NVMF_TERMINATE:
1210a15f7c96SJohn Baldwin 			nvmft_terminate(cn);
1211a15f7c96SJohn Baldwin 			break;
1212a15f7c96SJohn Baldwin 		default:
1213a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1214a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1215a15f7c96SJohn Baldwin 			    "Invalid NVMeoF request type %d", cn->type);
1216a15f7c96SJohn Baldwin 			break;
1217a15f7c96SJohn Baldwin 		}
1218a15f7c96SJohn Baldwin 		return (0);
1219a15f7c96SJohn Baldwin 	default:
1220a15f7c96SJohn Baldwin 		return (ENOTTY);
1221a15f7c96SJohn Baldwin 	}
1222a15f7c96SJohn Baldwin }
1223a15f7c96SJohn Baldwin 
1224a15f7c96SJohn Baldwin static int
nvmft_shutdown(void)1225a15f7c96SJohn Baldwin nvmft_shutdown(void)
1226a15f7c96SJohn Baldwin {
1227a15f7c96SJohn Baldwin 	/* TODO: Need to check for active controllers. */
1228a15f7c96SJohn Baldwin 	if (!TAILQ_EMPTY(&nvmft_ports))
1229a15f7c96SJohn Baldwin 		return (EBUSY);
1230a15f7c96SJohn Baldwin 
12311b3fa1acSJohn Baldwin 	taskqueue_free(nvmft_taskq);
1232a15f7c96SJohn Baldwin 	sx_destroy(&nvmft_ports_lock);
1233a15f7c96SJohn Baldwin 	return (0);
1234a15f7c96SJohn Baldwin }
1235a15f7c96SJohn Baldwin 
1236a15f7c96SJohn Baldwin CTL_FRONTEND_DECLARE(nvmft, nvmft_frontend);
1237a15f7c96SJohn Baldwin MODULE_DEPEND(nvmft, nvmf_transport, 1, 1, 1);
1238