xref: /freebsd/sys/dev/nvmf/controller/ctl_frontend_nvmf.c (revision 97ca2ada80b870edbbb4f66b26e274cf8e55e0bc)
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
69a15f7c96SJohn Baldwin nvmft_online(void *arg)
70a15f7c96SJohn Baldwin {
71a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
72a15f7c96SJohn Baldwin 
73*97ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
74a15f7c96SJohn Baldwin 	np->online = true;
75*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
76a15f7c96SJohn Baldwin }
77a15f7c96SJohn Baldwin 
78a15f7c96SJohn Baldwin static void
79a15f7c96SJohn Baldwin nvmft_offline(void *arg)
80a15f7c96SJohn Baldwin {
81a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
82a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
83a15f7c96SJohn Baldwin 
84*97ca2adaSJohn 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))
94*97ca2adaSJohn Baldwin 		mtx_sleep(np, &np->lock, 0, "nvmfoff", 0);
95*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
96a15f7c96SJohn Baldwin }
97a15f7c96SJohn Baldwin 
98a15f7c96SJohn Baldwin static int
99a15f7c96SJohn Baldwin nvmft_lun_enable(void *arg, int lun_id)
100a15f7c96SJohn Baldwin {
101a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
102a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
103a15f7c96SJohn Baldwin 	uint32_t *old_ns, *new_ns;
104a15f7c96SJohn Baldwin 	uint32_t nsid;
105*97ca2adaSJohn Baldwin 	u_int i, new_count;
106a15f7c96SJohn Baldwin 
107a15f7c96SJohn Baldwin 	if (lun_id >= le32toh(np->cdata.nn)) {
108a15f7c96SJohn Baldwin 		printf("NVMFT: %s lun %d larger than maximum nsid %u\n",
109a15f7c96SJohn Baldwin 		    np->cdata.subnqn, lun_id, le32toh(np->cdata.nn));
110a15f7c96SJohn Baldwin 		return (EOPNOTSUPP);
111a15f7c96SJohn Baldwin 	}
112a15f7c96SJohn Baldwin 	nsid = lun_id + 1;
113a15f7c96SJohn Baldwin 
114*97ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
115*97ca2adaSJohn Baldwin 	for (;;) {
116*97ca2adaSJohn Baldwin 		new_count = np->num_ns + 1;
117*97ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
118*97ca2adaSJohn Baldwin 		new_ns = mallocarray(new_count, sizeof(*new_ns), M_NVMFT,
119a15f7c96SJohn Baldwin 		    M_WAITOK);
120*97ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
121*97ca2adaSJohn Baldwin 		if (np->num_ns + 1 <= new_count)
122*97ca2adaSJohn Baldwin 			break;
123*97ca2adaSJohn Baldwin 		free(new_ns, M_NVMFT);
124*97ca2adaSJohn Baldwin 	}
125a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
126a15f7c96SJohn Baldwin 		if (np->active_ns[i] < nsid)
127a15f7c96SJohn Baldwin 			continue;
128a15f7c96SJohn Baldwin 		if (np->active_ns[i] == nsid) {
129*97ca2adaSJohn Baldwin 			mtx_unlock(&np->lock);
130a15f7c96SJohn Baldwin 			free(new_ns, M_NVMFT);
131a15f7c96SJohn Baldwin 			printf("NVMFT: %s duplicate lun %d\n",
132a15f7c96SJohn Baldwin 			    np->cdata.subnqn, lun_id);
133a15f7c96SJohn Baldwin 			return (EINVAL);
134a15f7c96SJohn Baldwin 		}
135a15f7c96SJohn Baldwin 		break;
136a15f7c96SJohn Baldwin 	}
137a15f7c96SJohn Baldwin 
138a15f7c96SJohn Baldwin 	/* Copy over IDs smaller than nsid. */
139a15f7c96SJohn Baldwin 	memcpy(new_ns, np->active_ns, i * sizeof(*np->active_ns));
140a15f7c96SJohn Baldwin 
141a15f7c96SJohn Baldwin 	/* Insert nsid. */
142a15f7c96SJohn Baldwin 	new_ns[i] = nsid;
143a15f7c96SJohn Baldwin 
144a15f7c96SJohn Baldwin 	/* Copy over IDs greater than nsid. */
145a15f7c96SJohn Baldwin 	memcpy(new_ns + i + 1, np->active_ns + i, (np->num_ns - i) *
146a15f7c96SJohn Baldwin 	    sizeof(*np->active_ns));
147a15f7c96SJohn Baldwin 
148a15f7c96SJohn Baldwin 	np->num_ns++;
149a15f7c96SJohn Baldwin 	old_ns = np->active_ns;
150a15f7c96SJohn Baldwin 	np->active_ns = new_ns;
151a15f7c96SJohn Baldwin 
152a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
153a15f7c96SJohn Baldwin 		nvmft_controller_lun_changed(ctrlr, lun_id);
154a15f7c96SJohn Baldwin 	}
155a15f7c96SJohn Baldwin 
156*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
157a15f7c96SJohn Baldwin 	free(old_ns, M_NVMFT);
158a15f7c96SJohn Baldwin 
159a15f7c96SJohn Baldwin 	return (0);
160a15f7c96SJohn Baldwin }
161a15f7c96SJohn Baldwin 
162a15f7c96SJohn Baldwin static int
163a15f7c96SJohn Baldwin nvmft_lun_disable(void *arg, int lun_id)
164a15f7c96SJohn Baldwin {
165a15f7c96SJohn Baldwin 	struct nvmft_port *np = arg;
166a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
167a15f7c96SJohn Baldwin 	uint32_t nsid;
168a15f7c96SJohn Baldwin 	u_int i;
169a15f7c96SJohn Baldwin 
170a15f7c96SJohn Baldwin 	if (lun_id >= le32toh(np->cdata.nn))
171a15f7c96SJohn Baldwin 		return (0);
172a15f7c96SJohn Baldwin 	nsid = lun_id + 1;
173a15f7c96SJohn Baldwin 
174*97ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
175a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
176a15f7c96SJohn Baldwin 		if (np->active_ns[i] == nsid)
177a15f7c96SJohn Baldwin 			goto found;
178a15f7c96SJohn Baldwin 	}
179*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
180a15f7c96SJohn Baldwin 	printf("NVMFT: %s request to disable nonexistent lun %d\n",
181a15f7c96SJohn Baldwin 	    np->cdata.subnqn, lun_id);
182a15f7c96SJohn Baldwin 	return (EINVAL);
183a15f7c96SJohn Baldwin 
184a15f7c96SJohn Baldwin found:
185a15f7c96SJohn Baldwin 	/* Move down IDs greater than nsid. */
186a15f7c96SJohn Baldwin 	memmove(np->active_ns + i, np->active_ns + i + 1,
187a15f7c96SJohn Baldwin 	    (np->num_ns - (i + 1)) * sizeof(*np->active_ns));
188a15f7c96SJohn Baldwin 	np->num_ns--;
189a15f7c96SJohn Baldwin 
190a15f7c96SJohn Baldwin 	/* NB: Don't bother freeing the old active_ns array. */
191a15f7c96SJohn Baldwin 
192a15f7c96SJohn Baldwin 	TAILQ_FOREACH(ctrlr, &np->controllers, link) {
193a15f7c96SJohn Baldwin 		nvmft_controller_lun_changed(ctrlr, lun_id);
194a15f7c96SJohn Baldwin 	}
195a15f7c96SJohn Baldwin 
196*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
197a15f7c96SJohn Baldwin 
198a15f7c96SJohn Baldwin 	return (0);
199a15f7c96SJohn Baldwin }
200a15f7c96SJohn Baldwin 
201a15f7c96SJohn Baldwin void
202a15f7c96SJohn Baldwin nvmft_populate_active_nslist(struct nvmft_port *np, uint32_t nsid,
203a15f7c96SJohn Baldwin     struct nvme_ns_list *nslist)
204a15f7c96SJohn Baldwin {
205a15f7c96SJohn Baldwin 	u_int i, count;
206a15f7c96SJohn Baldwin 
207*97ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
208a15f7c96SJohn Baldwin 	count = 0;
209a15f7c96SJohn Baldwin 	for (i = 0; i < np->num_ns; i++) {
210a15f7c96SJohn Baldwin 		if (np->active_ns[i] <= nsid)
211a15f7c96SJohn Baldwin 			continue;
212a15f7c96SJohn Baldwin 		nslist->ns[count] = htole32(np->active_ns[i]);
213a15f7c96SJohn Baldwin 		count++;
214a15f7c96SJohn Baldwin 		if (count == nitems(nslist->ns))
215a15f7c96SJohn Baldwin 			break;
216a15f7c96SJohn Baldwin 	}
217*97ca2adaSJohn Baldwin 	mtx_unlock(&np->lock);
218a15f7c96SJohn Baldwin }
219a15f7c96SJohn Baldwin 
220a15f7c96SJohn Baldwin void
221a15f7c96SJohn Baldwin nvmft_dispatch_command(struct nvmft_qpair *qp, struct nvmf_capsule *nc,
222a15f7c96SJohn Baldwin     bool admin)
223a15f7c96SJohn Baldwin {
224a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr = nvmft_qpair_ctrlr(qp);
225a15f7c96SJohn Baldwin 	const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
226a15f7c96SJohn Baldwin 	struct nvmft_port *np = ctrlr->np;
227a15f7c96SJohn Baldwin 	union ctl_io *io;
228a15f7c96SJohn Baldwin 	int error;
229a15f7c96SJohn Baldwin 
230a15f7c96SJohn Baldwin 	if (cmd->nsid == htole32(0)) {
231a15f7c96SJohn Baldwin 		nvmft_send_generic_error(qp, nc,
232a15f7c96SJohn Baldwin 		    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
233a15f7c96SJohn Baldwin 		nvmf_free_capsule(nc);
234a15f7c96SJohn Baldwin 		return;
235a15f7c96SJohn Baldwin 	}
236a15f7c96SJohn Baldwin 
237a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
238a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
239a15f7c96SJohn Baldwin 		ctrlr->start_busy = sbinuptime();
240a15f7c96SJohn Baldwin 	ctrlr->pending_commands++;
241a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
242a15f7c96SJohn Baldwin 	io = ctl_alloc_io(np->port.ctl_pool_ref);
243a15f7c96SJohn Baldwin 	ctl_zero_io(io);
244a15f7c96SJohn Baldwin 	NVMFT_NC(io) = nc;
245a15f7c96SJohn Baldwin 	NVMFT_QP(io) = qp;
246a15f7c96SJohn Baldwin 	io->io_hdr.io_type = admin ? CTL_IO_NVME_ADMIN : CTL_IO_NVME;
247a15f7c96SJohn Baldwin 	io->io_hdr.nexus.initid = ctrlr->cntlid;
248a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_port = np->port.targ_port;
249a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_lun = le32toh(cmd->nsid) - 1;
250a15f7c96SJohn Baldwin 	io->nvmeio.cmd = *cmd;
251a15f7c96SJohn Baldwin 	error = ctl_run(io);
252a15f7c96SJohn Baldwin 	if (error != 0) {
253a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "ctl_run failed for command on %s: %d\n",
254a15f7c96SJohn Baldwin 		    nvmft_qpair_name(qp), error);
255a15f7c96SJohn Baldwin 		ctl_nvme_set_generic_error(&io->nvmeio,
256a15f7c96SJohn Baldwin 		    NVME_SC_INTERNAL_DEVICE_ERROR);
257a15f7c96SJohn Baldwin 		nvmft_done(io);
258a15f7c96SJohn Baldwin 
259a15f7c96SJohn Baldwin 		nvmft_controller_error(ctrlr, qp, ENXIO);
260a15f7c96SJohn Baldwin 	}
261a15f7c96SJohn Baldwin }
262a15f7c96SJohn Baldwin 
263a15f7c96SJohn Baldwin void
264a15f7c96SJohn Baldwin nvmft_terminate_commands(struct nvmft_controller *ctrlr)
265a15f7c96SJohn Baldwin {
266a15f7c96SJohn Baldwin 	struct nvmft_port *np = ctrlr->np;
267a15f7c96SJohn Baldwin 	union ctl_io *io;
268a15f7c96SJohn Baldwin 	int error;
269a15f7c96SJohn Baldwin 
270a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
271a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
272a15f7c96SJohn Baldwin 		ctrlr->start_busy = sbinuptime();
273a15f7c96SJohn Baldwin 	ctrlr->pending_commands++;
274a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
275a15f7c96SJohn Baldwin 	io = ctl_alloc_io(np->port.ctl_pool_ref);
276a15f7c96SJohn Baldwin 	ctl_zero_io(io);
277a15f7c96SJohn Baldwin 	NVMFT_QP(io) = ctrlr->admin;
278a15f7c96SJohn Baldwin 	io->io_hdr.io_type = CTL_IO_TASK;
279a15f7c96SJohn Baldwin 	io->io_hdr.nexus.initid = ctrlr->cntlid;
280a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_port = np->port.targ_port;
281a15f7c96SJohn Baldwin 	io->io_hdr.nexus.targ_lun = 0;
282a15f7c96SJohn Baldwin 	io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX: unused? */
283a15f7c96SJohn Baldwin 	io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET;
284a15f7c96SJohn Baldwin 	error = ctl_run(io);
285a15f7c96SJohn Baldwin 	if (error != CTL_RETVAL_COMPLETE) {
286a15f7c96SJohn Baldwin 		nvmft_printf(ctrlr, "failed to terminate tasks: %d\n", error);
287a15f7c96SJohn Baldwin #ifdef INVARIANTS
288a15f7c96SJohn Baldwin 		io->io_hdr.status = CTL_SUCCESS;
289a15f7c96SJohn Baldwin #endif
290a15f7c96SJohn Baldwin 		nvmft_done(io);
291a15f7c96SJohn Baldwin 	}
292a15f7c96SJohn Baldwin }
293a15f7c96SJohn Baldwin 
294a15f7c96SJohn Baldwin static void
295a15f7c96SJohn Baldwin nvmft_datamove_out_cb(void *arg, size_t xfered, int error)
296a15f7c96SJohn Baldwin {
297a15f7c96SJohn Baldwin 	struct ctl_nvmeio *ctnio = arg;
298a15f7c96SJohn Baldwin 
299a15f7c96SJohn Baldwin 	if (error != 0) {
300a15f7c96SJohn Baldwin 		ctl_nvme_set_data_transfer_error(ctnio);
301a15f7c96SJohn Baldwin 	} else {
302a15f7c96SJohn Baldwin 		MPASS(xfered == ctnio->kern_data_len);
303a15f7c96SJohn Baldwin 		ctnio->kern_data_resid -= xfered;
304a15f7c96SJohn Baldwin 	}
305a15f7c96SJohn Baldwin 
306a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries) {
307a15f7c96SJohn Baldwin 		free(ctnio->ext_data_ptr, M_NVMFT);
308a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = NULL;
309a15f7c96SJohn Baldwin 	} else
310a15f7c96SJohn Baldwin 		MPASS(ctnio->ext_data_ptr == NULL);
311a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, false);
312a15f7c96SJohn Baldwin }
313a15f7c96SJohn Baldwin 
314a15f7c96SJohn Baldwin static void
315a15f7c96SJohn Baldwin nvmft_datamove_out(struct ctl_nvmeio *ctnio, struct nvmft_qpair *qp,
316a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
317a15f7c96SJohn Baldwin {
318a15f7c96SJohn Baldwin 	struct memdesc mem;
319a15f7c96SJohn Baldwin 	int error;
320a15f7c96SJohn Baldwin 
321a15f7c96SJohn Baldwin 	MPASS(ctnio->ext_data_ptr == NULL);
322a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries > 0) {
323a15f7c96SJohn Baldwin 		struct ctl_sg_entry *sgl;
324a15f7c96SJohn Baldwin 		struct bus_dma_segment *vlist;
325a15f7c96SJohn Baldwin 
326a15f7c96SJohn Baldwin 		vlist = mallocarray(ctnio->kern_sg_entries, sizeof(*vlist),
327a15f7c96SJohn Baldwin 		    M_NVMFT, M_WAITOK);
328a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = (void *)vlist;
329a15f7c96SJohn Baldwin 		sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
330a15f7c96SJohn Baldwin 		for (u_int i = 0; i < ctnio->kern_sg_entries; i++) {
331a15f7c96SJohn Baldwin 			vlist[i].ds_addr = (uintptr_t)sgl[i].addr;
332a15f7c96SJohn Baldwin 			vlist[i].ds_len = sgl[i].len;
333a15f7c96SJohn Baldwin 		}
334a15f7c96SJohn Baldwin 		mem = memdesc_vlist(vlist, ctnio->kern_sg_entries);
335a15f7c96SJohn Baldwin 	} else
336a15f7c96SJohn Baldwin 		mem = memdesc_vaddr(ctnio->kern_data_ptr, ctnio->kern_data_len);
337a15f7c96SJohn Baldwin 
338a15f7c96SJohn Baldwin 	error = nvmf_receive_controller_data(nc, ctnio->kern_rel_offset, &mem,
339a15f7c96SJohn Baldwin 	    ctnio->kern_data_len, nvmft_datamove_out_cb, ctnio);
340a15f7c96SJohn Baldwin 	if (error == 0)
341a15f7c96SJohn Baldwin 		return;
342a15f7c96SJohn Baldwin 
343a15f7c96SJohn Baldwin 	nvmft_printf(nvmft_qpair_ctrlr(qp),
344a15f7c96SJohn Baldwin 	    "Failed to request capsule data: %d\n", error);
345a15f7c96SJohn Baldwin 	ctl_nvme_set_data_transfer_error(ctnio);
346a15f7c96SJohn Baldwin 
347a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries) {
348a15f7c96SJohn Baldwin 		free(ctnio->ext_data_ptr, M_NVMFT);
349a15f7c96SJohn Baldwin 		ctnio->ext_data_ptr = NULL;
350a15f7c96SJohn Baldwin 	} else
351a15f7c96SJohn Baldwin 		MPASS(ctnio->ext_data_ptr == NULL);
352a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, true);
353a15f7c96SJohn Baldwin }
354a15f7c96SJohn Baldwin 
355a15f7c96SJohn Baldwin static struct mbuf *
356a15f7c96SJohn Baldwin nvmft_copy_data(struct ctl_nvmeio *ctnio)
357a15f7c96SJohn Baldwin {
358a15f7c96SJohn Baldwin 	struct ctl_sg_entry *sgl;
359a15f7c96SJohn Baldwin 	struct mbuf *m0, *m;
360a15f7c96SJohn Baldwin 	uint32_t resid, off, todo;
361a15f7c96SJohn Baldwin 	int mlen;
362a15f7c96SJohn Baldwin 
363a15f7c96SJohn Baldwin 	MPASS(ctnio->kern_data_len != 0);
364a15f7c96SJohn Baldwin 
365a15f7c96SJohn Baldwin 	m0 = m_getm2(NULL, ctnio->kern_data_len, M_WAITOK, MT_DATA, 0);
366a15f7c96SJohn Baldwin 
367a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries == 0) {
368a15f7c96SJohn Baldwin 		m_copyback(m0, 0, ctnio->kern_data_len, ctnio->kern_data_ptr);
369a15f7c96SJohn Baldwin 		return (m0);
370a15f7c96SJohn Baldwin 	}
371a15f7c96SJohn Baldwin 
372a15f7c96SJohn Baldwin 	resid = ctnio->kern_data_len;
373a15f7c96SJohn Baldwin 	sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
374a15f7c96SJohn Baldwin 	off = 0;
375a15f7c96SJohn Baldwin 	m = m0;
376a15f7c96SJohn Baldwin 	mlen = M_TRAILINGSPACE(m);
377a15f7c96SJohn Baldwin 	for (;;) {
378a15f7c96SJohn Baldwin 		todo = MIN(mlen, sgl->len - off);
379a15f7c96SJohn Baldwin 		memcpy(mtod(m, char *) + m->m_len, (char *)sgl->addr + off,
380a15f7c96SJohn Baldwin 		    todo);
381a15f7c96SJohn Baldwin 		m->m_len += todo;
382a15f7c96SJohn Baldwin 		resid -= todo;
383a15f7c96SJohn Baldwin 		if (resid == 0) {
384a15f7c96SJohn Baldwin 			MPASS(m->m_next == NULL);
385a15f7c96SJohn Baldwin 			break;
386a15f7c96SJohn Baldwin 		}
387a15f7c96SJohn Baldwin 
388a15f7c96SJohn Baldwin 		off += todo;
389a15f7c96SJohn Baldwin 		if (off == sgl->len) {
390a15f7c96SJohn Baldwin 			sgl++;
391a15f7c96SJohn Baldwin 			off = 0;
392a15f7c96SJohn Baldwin 		}
393a15f7c96SJohn Baldwin 		mlen -= todo;
394a15f7c96SJohn Baldwin 		if (mlen == 0) {
395a15f7c96SJohn Baldwin 			m = m->m_next;
396a15f7c96SJohn Baldwin 			mlen = M_TRAILINGSPACE(m);
397a15f7c96SJohn Baldwin 		}
398a15f7c96SJohn Baldwin 	}
399a15f7c96SJohn Baldwin 
400a15f7c96SJohn Baldwin 	return (m0);
401a15f7c96SJohn Baldwin }
402a15f7c96SJohn Baldwin 
403a15f7c96SJohn Baldwin static void
404a15f7c96SJohn Baldwin m_free_ref_data(struct mbuf *m)
405a15f7c96SJohn Baldwin {
406a15f7c96SJohn Baldwin 	ctl_ref kern_data_ref = m->m_ext.ext_arg1;
407a15f7c96SJohn Baldwin 
408a15f7c96SJohn Baldwin 	kern_data_ref(m->m_ext.ext_arg2, -1);
409a15f7c96SJohn Baldwin }
410a15f7c96SJohn Baldwin 
411a15f7c96SJohn Baldwin static struct mbuf *
412a15f7c96SJohn Baldwin m_get_ref_data(struct ctl_nvmeio *ctnio, void *buf, u_int size)
413a15f7c96SJohn Baldwin {
414a15f7c96SJohn Baldwin 	struct mbuf *m;
415a15f7c96SJohn Baldwin 
416a15f7c96SJohn Baldwin 	m = m_get(M_WAITOK, MT_DATA);
417a15f7c96SJohn Baldwin 	m_extadd(m, buf, size, m_free_ref_data, ctnio->kern_data_ref,
418a15f7c96SJohn Baldwin 	    ctnio->kern_data_arg, M_RDONLY, EXT_CTL);
419a15f7c96SJohn Baldwin 	m->m_len = size;
420a15f7c96SJohn Baldwin 	ctnio->kern_data_ref(ctnio->kern_data_arg, 1);
421a15f7c96SJohn Baldwin 	return (m);
422a15f7c96SJohn Baldwin }
423a15f7c96SJohn Baldwin 
424a15f7c96SJohn Baldwin static struct mbuf *
425a15f7c96SJohn Baldwin nvmft_ref_data(struct ctl_nvmeio *ctnio)
426a15f7c96SJohn Baldwin {
427a15f7c96SJohn Baldwin 	struct ctl_sg_entry *sgl;
428a15f7c96SJohn Baldwin 	struct mbuf *m0, *m;
429a15f7c96SJohn Baldwin 
430a15f7c96SJohn Baldwin 	MPASS(ctnio->kern_data_len != 0);
431a15f7c96SJohn Baldwin 
432a15f7c96SJohn Baldwin 	if (ctnio->kern_sg_entries == 0)
433a15f7c96SJohn Baldwin 		return (m_get_ref_data(ctnio, ctnio->kern_data_ptr,
434a15f7c96SJohn Baldwin 		    ctnio->kern_data_len));
435a15f7c96SJohn Baldwin 
436a15f7c96SJohn Baldwin 	sgl = (struct ctl_sg_entry *)ctnio->kern_data_ptr;
437a15f7c96SJohn Baldwin 	m0 = m_get_ref_data(ctnio, sgl[0].addr, sgl[0].len);
438a15f7c96SJohn Baldwin 	m = m0;
439a15f7c96SJohn Baldwin 	for (u_int i = 1; i < ctnio->kern_sg_entries; i++) {
440a15f7c96SJohn Baldwin 		m->m_next = m_get_ref_data(ctnio, sgl[i].addr, sgl[i].len);
441a15f7c96SJohn Baldwin 		m = m->m_next;
442a15f7c96SJohn Baldwin 	}
443a15f7c96SJohn Baldwin 	return (m0);
444a15f7c96SJohn Baldwin }
445a15f7c96SJohn Baldwin 
446a15f7c96SJohn Baldwin static void
447a15f7c96SJohn Baldwin nvmft_datamove_in(struct ctl_nvmeio *ctnio, struct nvmft_qpair *qp,
448a15f7c96SJohn Baldwin     struct nvmf_capsule *nc)
449a15f7c96SJohn Baldwin {
450a15f7c96SJohn Baldwin 	struct mbuf *m;
451a15f7c96SJohn Baldwin 	u_int status;
452a15f7c96SJohn Baldwin 
453a15f7c96SJohn Baldwin 	if (ctnio->kern_data_ref != NULL)
454a15f7c96SJohn Baldwin 		m = nvmft_ref_data(ctnio);
455a15f7c96SJohn Baldwin 	else
456a15f7c96SJohn Baldwin 		m = nvmft_copy_data(ctnio);
457a15f7c96SJohn Baldwin 	status = nvmf_send_controller_data(nc, ctnio->kern_rel_offset, m,
458a15f7c96SJohn Baldwin 	    ctnio->kern_data_len);
459a15f7c96SJohn Baldwin 	switch (status) {
460a15f7c96SJohn Baldwin 	case NVMF_SUCCESS_SENT:
461a15f7c96SJohn Baldwin 		ctnio->success_sent = true;
462a15f7c96SJohn Baldwin 		nvmft_command_completed(qp, nc);
463a15f7c96SJohn Baldwin 		/* FALLTHROUGH */
464a15f7c96SJohn Baldwin 	case NVMF_MORE:
465a15f7c96SJohn Baldwin 	case NVME_SC_SUCCESS:
466a15f7c96SJohn Baldwin 		break;
467a15f7c96SJohn Baldwin 	default:
468a15f7c96SJohn Baldwin 		ctl_nvme_set_generic_error(ctnio, status);
469a15f7c96SJohn Baldwin 		break;
470a15f7c96SJohn Baldwin 	}
471a15f7c96SJohn Baldwin 	ctl_datamove_done((union ctl_io *)ctnio, true);
472a15f7c96SJohn Baldwin }
473a15f7c96SJohn Baldwin 
4741b3fa1acSJohn Baldwin void
4751b3fa1acSJohn Baldwin nvmft_handle_datamove(union ctl_io *io)
476a15f7c96SJohn Baldwin {
477a15f7c96SJohn Baldwin 	struct nvmf_capsule *nc;
478a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
479a15f7c96SJohn Baldwin 
480a15f7c96SJohn Baldwin 	/* Some CTL commands preemptively set a success status. */
481a15f7c96SJohn Baldwin 	MPASS(io->io_hdr.status == CTL_STATUS_NONE ||
482a15f7c96SJohn Baldwin 	    io->io_hdr.status == CTL_SUCCESS);
483a15f7c96SJohn Baldwin 	MPASS(!io->nvmeio.success_sent);
484a15f7c96SJohn Baldwin 
485a15f7c96SJohn Baldwin 	nc = NVMFT_NC(io);
486a15f7c96SJohn Baldwin 	qp = NVMFT_QP(io);
487a15f7c96SJohn Baldwin 
488a15f7c96SJohn Baldwin 	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN)
489a15f7c96SJohn Baldwin 		nvmft_datamove_in(&io->nvmeio, qp, nc);
490a15f7c96SJohn Baldwin 	else
491a15f7c96SJohn Baldwin 		nvmft_datamove_out(&io->nvmeio, qp, nc);
492a15f7c96SJohn Baldwin }
493a15f7c96SJohn Baldwin 
4941b3fa1acSJohn Baldwin void
4951b3fa1acSJohn Baldwin nvmft_abort_datamove(union ctl_io *io)
4961b3fa1acSJohn Baldwin {
4971b3fa1acSJohn Baldwin 	io->io_hdr.port_status = 1;
4981b3fa1acSJohn Baldwin 	io->io_hdr.flags |= CTL_FLAG_ABORT;
4991b3fa1acSJohn Baldwin 	ctl_datamove_done(io, true);
5001b3fa1acSJohn Baldwin }
5011b3fa1acSJohn Baldwin 
5021b3fa1acSJohn Baldwin static void
5031b3fa1acSJohn Baldwin nvmft_datamove(union ctl_io *io)
5041b3fa1acSJohn Baldwin {
5051b3fa1acSJohn Baldwin 	struct nvmft_qpair *qp;
5061b3fa1acSJohn Baldwin 
5071b3fa1acSJohn Baldwin 	qp = NVMFT_QP(io);
5081b3fa1acSJohn Baldwin 	nvmft_qpair_datamove(qp, io);
5091b3fa1acSJohn Baldwin }
5101b3fa1acSJohn Baldwin 
5111b3fa1acSJohn Baldwin void
5121b3fa1acSJohn Baldwin nvmft_enqueue_task(struct task *task)
5131b3fa1acSJohn Baldwin {
5141b3fa1acSJohn Baldwin 	taskqueue_enqueue(nvmft_taskq, task);
5151b3fa1acSJohn Baldwin }
5161b3fa1acSJohn Baldwin 
5171b3fa1acSJohn Baldwin void
5181b3fa1acSJohn Baldwin nvmft_drain_task(struct task *task)
5191b3fa1acSJohn Baldwin {
5201b3fa1acSJohn Baldwin 	taskqueue_drain(nvmft_taskq, task);
5211b3fa1acSJohn Baldwin }
5221b3fa1acSJohn Baldwin 
523a15f7c96SJohn Baldwin static void
524a15f7c96SJohn Baldwin hip_add(uint64_t pair[2], uint64_t addend)
525a15f7c96SJohn Baldwin {
526a15f7c96SJohn Baldwin 	uint64_t old, new;
527a15f7c96SJohn Baldwin 
528a15f7c96SJohn Baldwin 	old = le64toh(pair[0]);
529a15f7c96SJohn Baldwin 	new = old + addend;
530a15f7c96SJohn Baldwin 	pair[0] = htole64(new);
531a15f7c96SJohn Baldwin 	if (new < old)
532a15f7c96SJohn Baldwin 		pair[1] += htole64(1);
533a15f7c96SJohn Baldwin }
534a15f7c96SJohn Baldwin 
535a15f7c96SJohn Baldwin static void
536a15f7c96SJohn Baldwin nvmft_done(union ctl_io *io)
537a15f7c96SJohn Baldwin {
538a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
539a15f7c96SJohn Baldwin 	const struct nvme_command *cmd;
540a15f7c96SJohn Baldwin 	struct nvmft_qpair *qp;
541a15f7c96SJohn Baldwin 	struct nvmf_capsule *nc;
542a15f7c96SJohn Baldwin 	size_t len;
543a15f7c96SJohn Baldwin 
544a15f7c96SJohn Baldwin 	KASSERT(io->io_hdr.status == CTL_SUCCESS ||
545a15f7c96SJohn Baldwin 	    io->io_hdr.status == CTL_NVME_ERROR,
546a15f7c96SJohn Baldwin 	    ("%s: bad status %u", __func__, io->io_hdr.status));
547a15f7c96SJohn Baldwin 
548a15f7c96SJohn Baldwin 	nc = NVMFT_NC(io);
549a15f7c96SJohn Baldwin 	qp = NVMFT_QP(io);
550a15f7c96SJohn Baldwin 	ctrlr = nvmft_qpair_ctrlr(qp);
551a15f7c96SJohn Baldwin 
552a15f7c96SJohn Baldwin 	if (nc == NULL) {
553a15f7c96SJohn Baldwin 		/* Completion of nvmft_terminate_commands. */
554a15f7c96SJohn Baldwin 		goto end;
555a15f7c96SJohn Baldwin 	}
556a15f7c96SJohn Baldwin 
557a15f7c96SJohn Baldwin 	cmd = nvmf_capsule_sqe(nc);
558a15f7c96SJohn Baldwin 
559a15f7c96SJohn Baldwin 	if (io->io_hdr.status == CTL_SUCCESS)
560a15f7c96SJohn Baldwin 		len = nvmf_capsule_data_len(nc) / 512;
561a15f7c96SJohn Baldwin 	else
562a15f7c96SJohn Baldwin 		len = 0;
563a15f7c96SJohn Baldwin 	switch (cmd->opc) {
564a15f7c96SJohn Baldwin 	case NVME_OPC_WRITE:
565a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
566a15f7c96SJohn Baldwin 		hip_add(ctrlr->hip.host_write_commands, 1);
567a15f7c96SJohn Baldwin 		len += ctrlr->partial_duw;
568a15f7c96SJohn Baldwin 		if (len > 1000)
569a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.data_units_written, len / 1000);
570a15f7c96SJohn Baldwin 		ctrlr->partial_duw = len % 1000;
571a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
572a15f7c96SJohn Baldwin 		break;
573a15f7c96SJohn Baldwin 	case NVME_OPC_READ:
574a15f7c96SJohn Baldwin 	case NVME_OPC_COMPARE:
575a15f7c96SJohn Baldwin 	case NVME_OPC_VERIFY:
576a15f7c96SJohn Baldwin 		mtx_lock(&ctrlr->lock);
577a15f7c96SJohn Baldwin 		if (cmd->opc != NVME_OPC_VERIFY)
578a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.host_read_commands, 1);
579a15f7c96SJohn Baldwin 		len += ctrlr->partial_dur;
580a15f7c96SJohn Baldwin 		if (len > 1000)
581a15f7c96SJohn Baldwin 			hip_add(ctrlr->hip.data_units_read, len / 1000);
582a15f7c96SJohn Baldwin 		ctrlr->partial_dur = len % 1000;
583a15f7c96SJohn Baldwin 		mtx_unlock(&ctrlr->lock);
584a15f7c96SJohn Baldwin 		break;
585a15f7c96SJohn Baldwin 	}
586a15f7c96SJohn Baldwin 
587a15f7c96SJohn Baldwin 	if (io->nvmeio.success_sent) {
588a15f7c96SJohn Baldwin 		MPASS(io->io_hdr.status == CTL_SUCCESS);
589a15f7c96SJohn Baldwin 	} else {
590a15f7c96SJohn Baldwin 		io->nvmeio.cpl.cid = cmd->cid;
591a15f7c96SJohn Baldwin 		nvmft_send_response(qp, &io->nvmeio.cpl);
592a15f7c96SJohn Baldwin 	}
593a15f7c96SJohn Baldwin 	nvmf_free_capsule(nc);
594a15f7c96SJohn Baldwin end:
595a15f7c96SJohn Baldwin 	ctl_free_io(io);
596a15f7c96SJohn Baldwin 	mtx_lock(&ctrlr->lock);
597a15f7c96SJohn Baldwin 	ctrlr->pending_commands--;
598a15f7c96SJohn Baldwin 	if (ctrlr->pending_commands == 0)
599a15f7c96SJohn Baldwin 		ctrlr->busy_total += sbinuptime() - ctrlr->start_busy;
600a15f7c96SJohn Baldwin 	mtx_unlock(&ctrlr->lock);
601a15f7c96SJohn Baldwin }
602a15f7c96SJohn Baldwin 
603a15f7c96SJohn Baldwin static int
604a15f7c96SJohn Baldwin nvmft_init(void)
605a15f7c96SJohn Baldwin {
6061b3fa1acSJohn Baldwin 	int error;
6071b3fa1acSJohn Baldwin 
6081b3fa1acSJohn Baldwin 	nvmft_taskq = taskqueue_create("nvmft", M_WAITOK,
6091b3fa1acSJohn Baldwin 	    taskqueue_thread_enqueue, &nvmft_taskq);
6101b3fa1acSJohn Baldwin 	error = taskqueue_start_threads_in_proc(&nvmft_taskq, mp_ncpus, PWAIT,
6111b3fa1acSJohn Baldwin 	    control_softc->ctl_proc, "nvmft");
6121b3fa1acSJohn Baldwin 	if (error != 0) {
6131b3fa1acSJohn Baldwin 		taskqueue_free(nvmft_taskq);
6141b3fa1acSJohn Baldwin 		return (error);
6151b3fa1acSJohn Baldwin 	}
6161b3fa1acSJohn Baldwin 
617a15f7c96SJohn Baldwin 	TAILQ_INIT(&nvmft_ports);
618a15f7c96SJohn Baldwin 	sx_init(&nvmft_ports_lock, "nvmft ports");
619a15f7c96SJohn Baldwin 	return (0);
620a15f7c96SJohn Baldwin }
621a15f7c96SJohn Baldwin 
622a15f7c96SJohn Baldwin void
623a15f7c96SJohn Baldwin nvmft_port_free(struct nvmft_port *np)
624a15f7c96SJohn Baldwin {
625a15f7c96SJohn Baldwin 	KASSERT(TAILQ_EMPTY(&np->controllers),
626a15f7c96SJohn Baldwin 	    ("%s(%p): active controllers", __func__, np));
627a15f7c96SJohn Baldwin 
628a15f7c96SJohn Baldwin 	if (np->port.targ_port != -1) {
629a15f7c96SJohn Baldwin 		if (ctl_port_deregister(&np->port) != 0)
630a15f7c96SJohn Baldwin 			printf("%s: ctl_port_deregister() failed\n", __func__);
631a15f7c96SJohn Baldwin 	}
632a15f7c96SJohn Baldwin 
633a15f7c96SJohn Baldwin 	free(np->active_ns, M_NVMFT);
634a15f7c96SJohn Baldwin 	clean_unrhdr(np->ids);
635a15f7c96SJohn Baldwin 	delete_unrhdr(np->ids);
636*97ca2adaSJohn Baldwin 	mtx_destroy(&np->lock);
637a15f7c96SJohn Baldwin 	free(np, M_NVMFT);
638a15f7c96SJohn Baldwin }
639a15f7c96SJohn Baldwin 
640a15f7c96SJohn Baldwin static struct nvmft_port *
641a15f7c96SJohn Baldwin nvmft_port_find(const char *subnqn)
642a15f7c96SJohn Baldwin {
643a15f7c96SJohn Baldwin 	struct nvmft_port *np;
644a15f7c96SJohn Baldwin 
645a15f7c96SJohn Baldwin 	KASSERT(nvmf_nqn_valid(subnqn), ("%s: invalid nqn", __func__));
646a15f7c96SJohn Baldwin 
647a15f7c96SJohn Baldwin 	sx_assert(&nvmft_ports_lock, SA_LOCKED);
648a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
649a15f7c96SJohn Baldwin 		if (strcmp(np->cdata.subnqn, subnqn) == 0)
650a15f7c96SJohn Baldwin 			break;
651a15f7c96SJohn Baldwin 	}
652a15f7c96SJohn Baldwin 	return (np);
653a15f7c96SJohn Baldwin }
654a15f7c96SJohn Baldwin 
655a15f7c96SJohn Baldwin static struct nvmft_port *
656a15f7c96SJohn Baldwin nvmft_port_find_by_id(int port_id)
657a15f7c96SJohn Baldwin {
658a15f7c96SJohn Baldwin 	struct nvmft_port *np;
659a15f7c96SJohn Baldwin 
660a15f7c96SJohn Baldwin 	sx_assert(&nvmft_ports_lock, SA_LOCKED);
661a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
662a15f7c96SJohn Baldwin 		if (np->port.targ_port == port_id)
663a15f7c96SJohn Baldwin 			break;
664a15f7c96SJohn Baldwin 	}
665a15f7c96SJohn Baldwin 	return (np);
666a15f7c96SJohn Baldwin }
667a15f7c96SJohn Baldwin 
668a15f7c96SJohn Baldwin /*
669a15f7c96SJohn Baldwin  * Helper function to fetch a number stored as a string in an nv_list.
670a15f7c96SJohn Baldwin  * Returns false if the string was not a valid number.
671a15f7c96SJohn Baldwin  */
672a15f7c96SJohn Baldwin static bool
673a15f7c96SJohn Baldwin dnvlist_get_strnum(nvlist_t *nvl, const char *name, u_long default_value,
674a15f7c96SJohn Baldwin 	u_long *value)
675a15f7c96SJohn Baldwin {
676a15f7c96SJohn Baldwin 	const char *str;
677a15f7c96SJohn Baldwin 	char *cp;
678a15f7c96SJohn Baldwin 
679a15f7c96SJohn Baldwin 	str = dnvlist_get_string(nvl, name, NULL);
680a15f7c96SJohn Baldwin 	if (str == NULL) {
681a15f7c96SJohn Baldwin 		*value = default_value;
682a15f7c96SJohn Baldwin 		return (true);
683a15f7c96SJohn Baldwin 	}
684a15f7c96SJohn Baldwin 	if (*str == '\0')
685a15f7c96SJohn Baldwin 		return (false);
686a15f7c96SJohn Baldwin 	*value = strtoul(str, &cp, 0);
687a15f7c96SJohn Baldwin 	if (*cp != '\0')
688a15f7c96SJohn Baldwin 		return (false);
689a15f7c96SJohn Baldwin 	return (true);
690a15f7c96SJohn Baldwin }
691a15f7c96SJohn Baldwin 
692a15f7c96SJohn Baldwin /*
693a15f7c96SJohn Baldwin  * NVMeoF ports support the following parameters:
694a15f7c96SJohn Baldwin  *
695a15f7c96SJohn Baldwin  * Mandatory:
696a15f7c96SJohn Baldwin  *
697a15f7c96SJohn Baldwin  * subnqn: subsystem NVMe Qualified Name
698a15f7c96SJohn Baldwin  * portid: integer port ID from Discovery Log Page entry
699a15f7c96SJohn Baldwin  *
700a15f7c96SJohn Baldwin  * Optional:
701a15f7c96SJohn Baldwin  * serial: Serial Number string
702a15f7c96SJohn Baldwin  * max_io_qsize: Maximum number of I/O queue entries
703a15f7c96SJohn Baldwin  * enable_timeout: Timeout for controller enable in milliseconds
704a15f7c96SJohn Baldwin  * ioccsz: Maximum command capsule size
705a15f7c96SJohn Baldwin  * iorcsz: Maximum response capsule size
706a15f7c96SJohn Baldwin  * nn: Number of namespaces
707a15f7c96SJohn Baldwin  */
708a15f7c96SJohn Baldwin static void
709a15f7c96SJohn Baldwin nvmft_port_create(struct ctl_req *req)
710a15f7c96SJohn Baldwin {
711a15f7c96SJohn Baldwin 	struct nvmft_port *np;
712a15f7c96SJohn Baldwin 	struct ctl_port *port;
713a15f7c96SJohn Baldwin 	const char *serial, *subnqn;
714a15f7c96SJohn Baldwin 	char serial_buf[NVME_SERIAL_NUMBER_LENGTH];
715a15f7c96SJohn Baldwin 	u_long enable_timeout, hostid, ioccsz, iorcsz, max_io_qsize, nn, portid;
716a15f7c96SJohn Baldwin 	int error;
717a15f7c96SJohn Baldwin 
718a15f7c96SJohn Baldwin 	/* Required parameters. */
719a15f7c96SJohn Baldwin 	subnqn = dnvlist_get_string(req->args_nvl, "subnqn", NULL);
720a15f7c96SJohn Baldwin 	if (subnqn == NULL || !nvlist_exists_string(req->args_nvl, "portid")) {
721a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
722a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
723a15f7c96SJohn Baldwin 		    "Missing required argument");
724a15f7c96SJohn Baldwin 		return;
725a15f7c96SJohn Baldwin 	}
726a15f7c96SJohn Baldwin 	if (!nvmf_nqn_valid(subnqn)) {
727a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
728a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
729a15f7c96SJohn Baldwin 		    "Invalid SubNQN");
730a15f7c96SJohn Baldwin 		return;
731a15f7c96SJohn Baldwin 	}
732a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "portid", UINT16_MAX, &portid) ||
733a15f7c96SJohn Baldwin 	    portid > UINT16_MAX) {
734a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
735a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
736a15f7c96SJohn Baldwin 		    "Invalid port ID");
737a15f7c96SJohn Baldwin 		return;
738a15f7c96SJohn Baldwin 	}
739a15f7c96SJohn Baldwin 
740a15f7c96SJohn Baldwin 	/* Optional parameters. */
741a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "max_io_qsize",
742a15f7c96SJohn Baldwin 	    NVMF_MAX_IO_ENTRIES, &max_io_qsize) ||
743a15f7c96SJohn Baldwin 	    max_io_qsize < NVME_MIN_IO_ENTRIES ||
744a15f7c96SJohn Baldwin 	    max_io_qsize > NVME_MAX_IO_ENTRIES) {
745a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
746a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
747a15f7c96SJohn Baldwin 		    "Invalid maximum I/O queue size");
748a15f7c96SJohn Baldwin 		return;
749a15f7c96SJohn Baldwin 	}
750a15f7c96SJohn Baldwin 
751a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "enable_timeout",
752a15f7c96SJohn Baldwin 	    NVMF_CC_EN_TIMEOUT * 500, &enable_timeout) ||
753a15f7c96SJohn Baldwin 	    (enable_timeout % 500) != 0 || (enable_timeout / 500) > 255) {
754a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
755a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
756a15f7c96SJohn Baldwin 		    "Invalid enable timeout");
757a15f7c96SJohn Baldwin 		return;
758a15f7c96SJohn Baldwin 	}
759a15f7c96SJohn Baldwin 
760a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "ioccsz", NVMF_IOCCSZ,
761a15f7c96SJohn Baldwin 	    &ioccsz) || ioccsz < sizeof(struct nvme_command) ||
762a15f7c96SJohn Baldwin 	    (ioccsz % 16) != 0) {
763a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
764a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
765a15f7c96SJohn Baldwin 		    "Invalid Command Capsule size");
766a15f7c96SJohn Baldwin 		return;
767a15f7c96SJohn Baldwin 	}
768a15f7c96SJohn Baldwin 
769a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "iorcsz", NVMF_IORCSZ,
770a15f7c96SJohn Baldwin 	    &iorcsz) || iorcsz < sizeof(struct nvme_completion) ||
771a15f7c96SJohn Baldwin 	    (iorcsz % 16) != 0) {
772a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
773a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
774a15f7c96SJohn Baldwin 		    "Invalid Response Capsule size");
775a15f7c96SJohn Baldwin 		return;
776a15f7c96SJohn Baldwin 	}
777a15f7c96SJohn Baldwin 
778a15f7c96SJohn Baldwin 	if (!dnvlist_get_strnum(req->args_nvl, "nn", NVMF_NN, &nn) ||
779a15f7c96SJohn Baldwin 	    nn < 1 || nn > UINT32_MAX) {
780a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
781a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
782a15f7c96SJohn Baldwin 		    "Invalid number of namespaces");
783a15f7c96SJohn Baldwin 		return;
784a15f7c96SJohn Baldwin 	}
785a15f7c96SJohn Baldwin 
786a15f7c96SJohn Baldwin 	serial = dnvlist_get_string(req->args_nvl, "serial", NULL);
787a15f7c96SJohn Baldwin 	if (serial == NULL) {
788a15f7c96SJohn Baldwin 		getcredhostid(curthread->td_ucred, &hostid);
789a15f7c96SJohn Baldwin 		nvmf_controller_serial(serial_buf, sizeof(serial_buf), hostid);
790a15f7c96SJohn Baldwin 		serial = serial_buf;
791a15f7c96SJohn Baldwin 	}
792a15f7c96SJohn Baldwin 
793a15f7c96SJohn Baldwin 	sx_xlock(&nvmft_ports_lock);
794a15f7c96SJohn Baldwin 
795a15f7c96SJohn Baldwin 	np = nvmft_port_find(subnqn);
796a15f7c96SJohn Baldwin 	if (np != NULL) {
797a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
798a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
799a15f7c96SJohn Baldwin 		    "SubNQN \"%s\" already exists", subnqn);
800a15f7c96SJohn Baldwin 		sx_xunlock(&nvmft_ports_lock);
801a15f7c96SJohn Baldwin 		return;
802a15f7c96SJohn Baldwin 	}
803a15f7c96SJohn Baldwin 
804a15f7c96SJohn Baldwin 	np = malloc(sizeof(*np), M_NVMFT, M_WAITOK | M_ZERO);
805a15f7c96SJohn Baldwin 	refcount_init(&np->refs, 1);
806a15f7c96SJohn Baldwin 	np->max_io_qsize = max_io_qsize;
807a15f7c96SJohn Baldwin 	np->cap = _nvmf_controller_cap(max_io_qsize, enable_timeout / 500);
808*97ca2adaSJohn Baldwin 	mtx_init(&np->lock, "nvmft port", NULL, MTX_DEF);
809a15f7c96SJohn Baldwin 	np->ids = new_unrhdr(0, MIN(CTL_MAX_INIT_PER_PORT - 1,
810a15f7c96SJohn Baldwin 	    NVMF_CNTLID_STATIC_MAX), UNR_NO_MTX);
811a15f7c96SJohn Baldwin 	TAILQ_INIT(&np->controllers);
812a15f7c96SJohn Baldwin 
813a15f7c96SJohn Baldwin 	/* The controller ID is set later for individual controllers. */
814a15f7c96SJohn Baldwin 	_nvmf_init_io_controller_data(0, max_io_qsize, serial, ostype,
815a15f7c96SJohn Baldwin 	    osrelease, subnqn, nn, ioccsz, iorcsz, &np->cdata);
816a15f7c96SJohn Baldwin 	np->cdata.aerl = NVMFT_NUM_AER - 1;
817a15f7c96SJohn Baldwin 	np->cdata.oaes = htole32(NVME_ASYNC_EVENT_NS_ATTRIBUTE);
818a15f7c96SJohn Baldwin 	np->cdata.oncs = htole16(NVMEF(NVME_CTRLR_DATA_ONCS_VERIFY, 1) |
819a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_WRZERO, 1) |
820a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_DSM, 1) |
821a15f7c96SJohn Baldwin 	    NVMEF(NVME_CTRLR_DATA_ONCS_COMPARE, 1));
822a15f7c96SJohn Baldwin 	np->cdata.fuses = NVMEF(NVME_CTRLR_DATA_FUSES_CNW, 1);
823a15f7c96SJohn Baldwin 
824a15f7c96SJohn Baldwin 	np->fp.afi = NVMEF(NVME_FIRMWARE_PAGE_AFI_SLOT, 1);
825a15f7c96SJohn Baldwin 	memcpy(np->fp.revision[0], np->cdata.fr, sizeof(np->cdata.fr));
826a15f7c96SJohn Baldwin 
827a15f7c96SJohn Baldwin 	port = &np->port;
828a15f7c96SJohn Baldwin 
829a15f7c96SJohn Baldwin 	port->frontend = &nvmft_frontend;
830a15f7c96SJohn Baldwin 	port->port_type = CTL_PORT_NVMF;
831a15f7c96SJohn Baldwin 	port->num_requested_ctl_io = max_io_qsize;
832a15f7c96SJohn Baldwin 	port->port_name = "nvmf";
833a15f7c96SJohn Baldwin 	port->physical_port = portid;
834a15f7c96SJohn Baldwin 	port->virtual_port = 0;
835a15f7c96SJohn Baldwin 	port->port_online = nvmft_online;
836a15f7c96SJohn Baldwin 	port->port_offline = nvmft_offline;
837a15f7c96SJohn Baldwin 	port->onoff_arg = np;
838a15f7c96SJohn Baldwin 	port->lun_enable = nvmft_lun_enable;
839a15f7c96SJohn Baldwin 	port->lun_disable = nvmft_lun_disable;
840a15f7c96SJohn Baldwin 	port->targ_lun_arg = np;
841a15f7c96SJohn Baldwin 	port->fe_datamove = nvmft_datamove;
842a15f7c96SJohn Baldwin 	port->fe_done = nvmft_done;
843a15f7c96SJohn Baldwin 	port->targ_port = -1;
844a15f7c96SJohn Baldwin 	port->options = nvlist_clone(req->args_nvl);
845a15f7c96SJohn Baldwin 
846a15f7c96SJohn Baldwin 	error = ctl_port_register(port);
847a15f7c96SJohn Baldwin 	if (error != 0) {
848a15f7c96SJohn Baldwin 		sx_xunlock(&nvmft_ports_lock);
849a15f7c96SJohn Baldwin 		nvlist_destroy(port->options);
850a15f7c96SJohn Baldwin 		nvmft_port_rele(np);
851a15f7c96SJohn Baldwin 		req->status = CTL_LUN_ERROR;
852a15f7c96SJohn Baldwin 		snprintf(req->error_str, sizeof(req->error_str),
853a15f7c96SJohn Baldwin 		    "Failed to register CTL port with error %d", error);
854a15f7c96SJohn Baldwin 		return;
855a15f7c96SJohn Baldwin 	}
856a15f7c96SJohn Baldwin 
857a15f7c96SJohn Baldwin 	TAILQ_INSERT_TAIL(&nvmft_ports, np, link);
858a15f7c96SJohn Baldwin 	sx_xunlock(&nvmft_ports_lock);
859a15f7c96SJohn Baldwin 
860a15f7c96SJohn Baldwin 	req->status = CTL_LUN_OK;
861a15f7c96SJohn Baldwin 	req->result_nvl = nvlist_create(0);
862a15f7c96SJohn Baldwin 	nvlist_add_number(req->result_nvl, "port_id", port->targ_port);
863a15f7c96SJohn Baldwin }
864a15f7c96SJohn Baldwin 
865a15f7c96SJohn Baldwin static void
866a15f7c96SJohn Baldwin nvmft_port_remove(struct ctl_req *req)
867a15f7c96SJohn Baldwin {
868a15f7c96SJohn Baldwin 	struct nvmft_port *np;
869a15f7c96SJohn Baldwin 	const char *subnqn;
870a15f7c96SJohn Baldwin 	u_long port_id;
871a15f7c96SJohn Baldwin 
872a15f7c96SJohn Baldwin 	/*
873a15f7c96SJohn Baldwin 	 * ctladm port -r just provides the port_id, so permit looking
874a15f7c96SJohn Baldwin 	 * up a port either by "subnqn" or "port_id".
875a15f7c96SJohn Baldwin 	 */
876a15f7c96SJohn Baldwin 	port_id = ULONG_MAX;
877a15f7c96SJohn Baldwin 	subnqn = dnvlist_get_string(req->args_nvl, "subnqn", NULL);
878a15f7c96SJohn Baldwin 	if (subnqn == NULL) {
879a15f7c96SJohn Baldwin 		if (!nvlist_exists_string(req->args_nvl, "port_id")) {
880a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
881a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
882a15f7c96SJohn Baldwin 			    "Missing required argument");
883a15f7c96SJohn Baldwin 			return;
884a15f7c96SJohn Baldwin 		}
885a15f7c96SJohn Baldwin 		if (!dnvlist_get_strnum(req->args_nvl, "port_id", ULONG_MAX,
886a15f7c96SJohn Baldwin 		    &port_id)) {
887a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
888a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
889a15f7c96SJohn Baldwin 			    "Invalid CTL port ID");
890a15f7c96SJohn Baldwin 			return;
891a15f7c96SJohn Baldwin 		}
892a15f7c96SJohn Baldwin 	} else {
893a15f7c96SJohn Baldwin 		if (nvlist_exists_string(req->args_nvl, "port_id")) {
894a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
895a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
896a15f7c96SJohn Baldwin 			    "Ambiguous port removal request");
897a15f7c96SJohn Baldwin 			return;
898a15f7c96SJohn Baldwin 		}
899a15f7c96SJohn Baldwin 	}
900a15f7c96SJohn Baldwin 
901a15f7c96SJohn Baldwin 	sx_xlock(&nvmft_ports_lock);
902a15f7c96SJohn Baldwin 
903a15f7c96SJohn Baldwin 	if (subnqn != NULL) {
904a15f7c96SJohn Baldwin 		np = nvmft_port_find(subnqn);
905a15f7c96SJohn Baldwin 		if (np == NULL) {
906a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
907a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
908a15f7c96SJohn Baldwin 			    "SubNQN \"%s\" does not exist", subnqn);
909a15f7c96SJohn Baldwin 			sx_xunlock(&nvmft_ports_lock);
910a15f7c96SJohn Baldwin 			return;
911a15f7c96SJohn Baldwin 		}
912a15f7c96SJohn Baldwin 	} else {
913a15f7c96SJohn Baldwin 		np = nvmft_port_find_by_id(port_id);
914a15f7c96SJohn Baldwin 		if (np == NULL) {
915a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
916a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
917a15f7c96SJohn Baldwin 			    "CTL port %lu is not a NVMF port", port_id);
918a15f7c96SJohn Baldwin 			sx_xunlock(&nvmft_ports_lock);
919a15f7c96SJohn Baldwin 			return;
920a15f7c96SJohn Baldwin 		}
921a15f7c96SJohn Baldwin 	}
922a15f7c96SJohn Baldwin 
923a15f7c96SJohn Baldwin 	TAILQ_REMOVE(&nvmft_ports, np, link);
924a15f7c96SJohn Baldwin 	sx_xunlock(&nvmft_ports_lock);
925a15f7c96SJohn Baldwin 
926*97ca2adaSJohn Baldwin 	mtx_lock(&np->lock);
92717b7a0c5SJohn Baldwin 	if (np->online) {
928*97ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
929a15f7c96SJohn Baldwin 		ctl_port_offline(&np->port);
93017b7a0c5SJohn Baldwin 	} else
931*97ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
93217b7a0c5SJohn Baldwin 
933a15f7c96SJohn Baldwin 	nvmft_port_rele(np);
934a15f7c96SJohn Baldwin 	req->status = CTL_LUN_OK;
935a15f7c96SJohn Baldwin }
936a15f7c96SJohn Baldwin 
937a15f7c96SJohn Baldwin static void
938a15f7c96SJohn Baldwin nvmft_handoff(struct ctl_nvmf *cn)
939a15f7c96SJohn Baldwin {
940365b89e8SJohn Baldwin 	const struct nvmf_fabric_connect_cmd *cmd;
941365b89e8SJohn Baldwin 	const struct nvmf_fabric_connect_data *data;
942365b89e8SJohn Baldwin 	const nvlist_t *params;
943a15f7c96SJohn Baldwin 	struct nvmft_port *np;
944365b89e8SJohn Baldwin 	nvlist_t *nvl;
945365b89e8SJohn Baldwin 	size_t len;
946365b89e8SJohn Baldwin 	enum nvmf_trtype trtype;
947a15f7c96SJohn Baldwin 	int error;
948a15f7c96SJohn Baldwin 
949a15f7c96SJohn Baldwin 	np = NULL;
950365b89e8SJohn Baldwin 	error = nvmf_unpack_ioc_nvlist(&cn->data.handoff, &nvl);
951a15f7c96SJohn Baldwin 	if (error != 0) {
952a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
953a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
954365b89e8SJohn Baldwin 		    "Failed to copyin and unpack handoff arguments");
955a15f7c96SJohn Baldwin 		return;
956a15f7c96SJohn Baldwin 	}
957a15f7c96SJohn Baldwin 
958365b89e8SJohn Baldwin 	if (!nvlist_exists_number(nvl, "trtype") ||
959365b89e8SJohn Baldwin 	    !nvlist_exists_nvlist(nvl, "params") ||
960365b89e8SJohn Baldwin 	    !nvlist_exists_binary(nvl, "cmd") ||
961365b89e8SJohn Baldwin 	    !nvlist_exists_binary(nvl, "data")) {
962a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
963a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
964365b89e8SJohn Baldwin 		    "Handoff arguments missing required value");
965365b89e8SJohn Baldwin 		goto out;
966365b89e8SJohn Baldwin 	}
967365b89e8SJohn Baldwin 
968365b89e8SJohn Baldwin 	params = nvlist_get_nvlist(nvl, "params");
969365b89e8SJohn Baldwin 	if (!nvmf_validate_qpair_nvlist(params, true)) {
970365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
971365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
972365b89e8SJohn Baldwin 		    "Invalid queue pair parameters");
973365b89e8SJohn Baldwin 		goto out;
974365b89e8SJohn Baldwin 	}
975365b89e8SJohn Baldwin 
976365b89e8SJohn Baldwin 	cmd = nvlist_get_binary(nvl, "cmd", &len);
977365b89e8SJohn Baldwin 	if (len != sizeof(*cmd)) {
978365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
979365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
980365b89e8SJohn Baldwin 		    "Wrong size for CONNECT SQE");
981365b89e8SJohn Baldwin 		goto out;
982365b89e8SJohn Baldwin 	}
983365b89e8SJohn Baldwin 
984365b89e8SJohn Baldwin 	data = nvlist_get_binary(nvl, "data", &len);
985365b89e8SJohn Baldwin 	if (len != sizeof(*data)) {
986365b89e8SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
987365b89e8SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
988365b89e8SJohn Baldwin 		    "Wrong size for CONNECT data");
989a15f7c96SJohn Baldwin 		goto out;
990a15f7c96SJohn Baldwin 	}
991a15f7c96SJohn Baldwin 
992a15f7c96SJohn Baldwin 	if (!nvmf_nqn_valid(data->subnqn)) {
993a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
994a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
995a15f7c96SJohn Baldwin 		    "Invalid SubNQN");
996a15f7c96SJohn Baldwin 		goto out;
997a15f7c96SJohn Baldwin 	}
998a15f7c96SJohn Baldwin 
999a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1000a15f7c96SJohn Baldwin 	np = nvmft_port_find(data->subnqn);
1001a15f7c96SJohn Baldwin 	if (np == NULL) {
1002a15f7c96SJohn Baldwin 		sx_sunlock(&nvmft_ports_lock);
1003a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1004a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1005a15f7c96SJohn Baldwin 		    "Unknown SubNQN");
1006a15f7c96SJohn Baldwin 		goto out;
1007a15f7c96SJohn Baldwin 	}
1008a15f7c96SJohn Baldwin 	if (!np->online) {
1009a15f7c96SJohn Baldwin 		sx_sunlock(&nvmft_ports_lock);
1010a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1011a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1012a15f7c96SJohn Baldwin 		    "CTL port offline");
1013a15f7c96SJohn Baldwin 		np = NULL;
1014a15f7c96SJohn Baldwin 		goto out;
1015a15f7c96SJohn Baldwin 	}
1016a15f7c96SJohn Baldwin 	nvmft_port_ref(np);
1017a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1018a15f7c96SJohn Baldwin 
1019365b89e8SJohn Baldwin 	trtype = nvlist_get_number(nvl, "trtype");
1020365b89e8SJohn Baldwin 	if (nvlist_get_bool(params, "admin")) {
1021365b89e8SJohn Baldwin 		error = nvmft_handoff_admin_queue(np, trtype, params, cmd,
1022365b89e8SJohn Baldwin 		    data);
1023a15f7c96SJohn Baldwin 		if (error != 0) {
1024a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1025a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1026a15f7c96SJohn Baldwin 			    "Failed to handoff admin queue: %d", error);
1027a15f7c96SJohn Baldwin 			goto out;
1028a15f7c96SJohn Baldwin 		}
1029a15f7c96SJohn Baldwin 	} else {
1030365b89e8SJohn Baldwin 		error = nvmft_handoff_io_queue(np, trtype, params, cmd, data);
1031a15f7c96SJohn Baldwin 		if (error != 0) {
1032a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1033a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1034225c5e53SJohn Baldwin 			    "Failed to handoff I/O queue: %d", error);
1035a15f7c96SJohn Baldwin 			goto out;
1036a15f7c96SJohn Baldwin 		}
1037a15f7c96SJohn Baldwin 	}
1038a15f7c96SJohn Baldwin 
1039a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1040a15f7c96SJohn Baldwin out:
1041a15f7c96SJohn Baldwin 	if (np != NULL)
1042a15f7c96SJohn Baldwin 		nvmft_port_rele(np);
1043365b89e8SJohn Baldwin 	nvlist_destroy(nvl);
1044a15f7c96SJohn Baldwin }
1045a15f7c96SJohn Baldwin 
1046a15f7c96SJohn Baldwin static void
1047a15f7c96SJohn Baldwin nvmft_list(struct ctl_nvmf *cn)
1048a15f7c96SJohn Baldwin {
1049a15f7c96SJohn Baldwin 	struct ctl_nvmf_list_params *lp;
1050a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
1051a15f7c96SJohn Baldwin 	struct nvmft_port *np;
1052a15f7c96SJohn Baldwin 	struct sbuf *sb;
1053a15f7c96SJohn Baldwin 	int error;
1054a15f7c96SJohn Baldwin 
1055a15f7c96SJohn Baldwin 	lp = &cn->data.list;
1056a15f7c96SJohn Baldwin 
1057a15f7c96SJohn Baldwin 	sb = sbuf_new(NULL, NULL, lp->alloc_len, SBUF_FIXEDLEN |
1058a15f7c96SJohn Baldwin 	    SBUF_INCLUDENUL);
1059a15f7c96SJohn Baldwin 	if (sb == NULL) {
1060a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1061a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1062a15f7c96SJohn Baldwin 		    "Failed to allocate NVMeoF session list");
1063a15f7c96SJohn Baldwin 		return;
1064a15f7c96SJohn Baldwin 	}
1065a15f7c96SJohn Baldwin 
1066a15f7c96SJohn Baldwin 	sbuf_printf(sb, "<ctlnvmflist>\n");
1067a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1068a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
1069*97ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
1070a15f7c96SJohn Baldwin 		TAILQ_FOREACH(ctrlr, &np->controllers, link) {
1071a15f7c96SJohn Baldwin 			sbuf_printf(sb, "<connection id=\"%d\">"
1072a15f7c96SJohn Baldwin 			    "<hostnqn>%s</hostnqn>"
1073a15f7c96SJohn Baldwin 			    "<subnqn>%s</subnqn>"
1074a15f7c96SJohn Baldwin 			    "<trtype>%u</trtype>"
1075a15f7c96SJohn Baldwin 			    "</connection>\n",
1076a15f7c96SJohn Baldwin 			    ctrlr->cntlid,
1077a15f7c96SJohn Baldwin 			    ctrlr->hostnqn,
1078a15f7c96SJohn Baldwin 			    np->cdata.subnqn,
1079a15f7c96SJohn Baldwin 			    ctrlr->trtype);
1080a15f7c96SJohn Baldwin 		}
1081*97ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
1082a15f7c96SJohn Baldwin 	}
1083a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1084a15f7c96SJohn Baldwin 	sbuf_printf(sb, "</ctlnvmflist>\n");
1085a15f7c96SJohn Baldwin 	if (sbuf_finish(sb) != 0) {
1086a15f7c96SJohn Baldwin 		sbuf_delete(sb);
1087a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_LIST_NEED_MORE_SPACE;
1088a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1089a15f7c96SJohn Baldwin 		    "Out of space, %d bytes is too small", lp->alloc_len);
1090a15f7c96SJohn Baldwin 		return;
1091a15f7c96SJohn Baldwin 	}
1092a15f7c96SJohn Baldwin 
1093a15f7c96SJohn Baldwin 	error = copyout(sbuf_data(sb), lp->conn_xml, sbuf_len(sb));
1094a15f7c96SJohn Baldwin 	if (error != 0) {
1095a15f7c96SJohn Baldwin 		sbuf_delete(sb);
1096a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ERROR;
1097a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1098a15f7c96SJohn Baldwin 		    "Failed to copyout session list: %d", error);
1099a15f7c96SJohn Baldwin 		return;
1100a15f7c96SJohn Baldwin 	}
1101a15f7c96SJohn Baldwin 	lp->fill_len = sbuf_len(sb);
1102a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1103a15f7c96SJohn Baldwin 	sbuf_delete(sb);
1104a15f7c96SJohn Baldwin }
1105a15f7c96SJohn Baldwin 
1106a15f7c96SJohn Baldwin static void
1107a15f7c96SJohn Baldwin nvmft_terminate(struct ctl_nvmf *cn)
1108a15f7c96SJohn Baldwin {
1109a15f7c96SJohn Baldwin 	struct ctl_nvmf_terminate_params *tp;
1110a15f7c96SJohn Baldwin 	struct nvmft_controller *ctrlr;
1111a15f7c96SJohn Baldwin 	struct nvmft_port *np;
1112a15f7c96SJohn Baldwin 	bool found, match;
1113a15f7c96SJohn Baldwin 
1114a15f7c96SJohn Baldwin 	tp = &cn->data.terminate;
1115a15f7c96SJohn Baldwin 
1116a15f7c96SJohn Baldwin 	found = false;
1117a15f7c96SJohn Baldwin 	sx_slock(&nvmft_ports_lock);
1118a15f7c96SJohn Baldwin 	TAILQ_FOREACH(np, &nvmft_ports, link) {
1119*97ca2adaSJohn Baldwin 		mtx_lock(&np->lock);
1120a15f7c96SJohn Baldwin 		TAILQ_FOREACH(ctrlr, &np->controllers, link) {
1121a15f7c96SJohn Baldwin 			if (tp->all != 0)
1122a15f7c96SJohn Baldwin 				match = true;
1123a15f7c96SJohn Baldwin 			else if (tp->cntlid != -1)
1124a15f7c96SJohn Baldwin 				match = tp->cntlid == ctrlr->cntlid;
1125a15f7c96SJohn Baldwin 			else if (tp->hostnqn[0] != '\0')
1126a15f7c96SJohn Baldwin 				match = strncmp(tp->hostnqn, ctrlr->hostnqn,
1127a15f7c96SJohn Baldwin 				    sizeof(tp->hostnqn)) == 0;
1128a15f7c96SJohn Baldwin 			else
1129a15f7c96SJohn Baldwin 				match = false;
1130a15f7c96SJohn Baldwin 			if (!match)
1131a15f7c96SJohn Baldwin 				continue;
1132a15f7c96SJohn Baldwin 			nvmft_printf(ctrlr,
1133a15f7c96SJohn Baldwin 			    "disconnecting due to administrative request\n");
1134a15f7c96SJohn Baldwin 			nvmft_controller_error(ctrlr, NULL, ECONNABORTED);
1135a15f7c96SJohn Baldwin 			found = true;
1136a15f7c96SJohn Baldwin 		}
1137*97ca2adaSJohn Baldwin 		mtx_unlock(&np->lock);
1138a15f7c96SJohn Baldwin 	}
1139a15f7c96SJohn Baldwin 	sx_sunlock(&nvmft_ports_lock);
1140a15f7c96SJohn Baldwin 
1141a15f7c96SJohn Baldwin 	if (!found) {
1142a15f7c96SJohn Baldwin 		cn->status = CTL_NVMF_ASSOCIATION_NOT_FOUND;
1143a15f7c96SJohn Baldwin 		snprintf(cn->error_str, sizeof(cn->error_str),
1144a15f7c96SJohn Baldwin 		    "No matching associations found");
1145a15f7c96SJohn Baldwin 		return;
1146a15f7c96SJohn Baldwin 	}
1147a15f7c96SJohn Baldwin 	cn->status = CTL_NVMF_OK;
1148a15f7c96SJohn Baldwin }
1149a15f7c96SJohn Baldwin 
1150a15f7c96SJohn Baldwin static int
1151a15f7c96SJohn Baldwin nvmft_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flag,
1152a15f7c96SJohn Baldwin     struct thread *td)
1153a15f7c96SJohn Baldwin {
1154a15f7c96SJohn Baldwin 	struct ctl_nvmf *cn;
1155a15f7c96SJohn Baldwin 	struct ctl_req *req;
1156a15f7c96SJohn Baldwin 
1157a15f7c96SJohn Baldwin 	switch (cmd) {
1158a15f7c96SJohn Baldwin 	case CTL_PORT_REQ:
1159a15f7c96SJohn Baldwin 		req = (struct ctl_req *)data;
1160a15f7c96SJohn Baldwin 		switch (req->reqtype) {
1161a15f7c96SJohn Baldwin 		case CTL_REQ_CREATE:
1162a15f7c96SJohn Baldwin 			nvmft_port_create(req);
1163a15f7c96SJohn Baldwin 			break;
1164a15f7c96SJohn Baldwin 		case CTL_REQ_REMOVE:
1165a15f7c96SJohn Baldwin 			nvmft_port_remove(req);
1166a15f7c96SJohn Baldwin 			break;
1167a15f7c96SJohn Baldwin 		default:
1168a15f7c96SJohn Baldwin 			req->status = CTL_LUN_ERROR;
1169a15f7c96SJohn Baldwin 			snprintf(req->error_str, sizeof(req->error_str),
1170a15f7c96SJohn Baldwin 			    "Unsupported request type %d", req->reqtype);
1171a15f7c96SJohn Baldwin 			break;
1172a15f7c96SJohn Baldwin 		}
1173a15f7c96SJohn Baldwin 		return (0);
1174a15f7c96SJohn Baldwin 	case CTL_NVMF:
1175a15f7c96SJohn Baldwin 		cn = (struct ctl_nvmf *)data;
1176a15f7c96SJohn Baldwin 		switch (cn->type) {
1177a15f7c96SJohn Baldwin 		case CTL_NVMF_HANDOFF:
1178a15f7c96SJohn Baldwin 			nvmft_handoff(cn);
1179a15f7c96SJohn Baldwin 			break;
1180a15f7c96SJohn Baldwin 		case CTL_NVMF_LIST:
1181a15f7c96SJohn Baldwin 			nvmft_list(cn);
1182a15f7c96SJohn Baldwin 			break;
1183a15f7c96SJohn Baldwin 		case CTL_NVMF_TERMINATE:
1184a15f7c96SJohn Baldwin 			nvmft_terminate(cn);
1185a15f7c96SJohn Baldwin 			break;
1186a15f7c96SJohn Baldwin 		default:
1187a15f7c96SJohn Baldwin 			cn->status = CTL_NVMF_ERROR;
1188a15f7c96SJohn Baldwin 			snprintf(cn->error_str, sizeof(cn->error_str),
1189a15f7c96SJohn Baldwin 			    "Invalid NVMeoF request type %d", cn->type);
1190a15f7c96SJohn Baldwin 			break;
1191a15f7c96SJohn Baldwin 		}
1192a15f7c96SJohn Baldwin 		return (0);
1193a15f7c96SJohn Baldwin 	default:
1194a15f7c96SJohn Baldwin 		return (ENOTTY);
1195a15f7c96SJohn Baldwin 	}
1196a15f7c96SJohn Baldwin }
1197a15f7c96SJohn Baldwin 
1198a15f7c96SJohn Baldwin static int
1199a15f7c96SJohn Baldwin nvmft_shutdown(void)
1200a15f7c96SJohn Baldwin {
1201a15f7c96SJohn Baldwin 	/* TODO: Need to check for active controllers. */
1202a15f7c96SJohn Baldwin 	if (!TAILQ_EMPTY(&nvmft_ports))
1203a15f7c96SJohn Baldwin 		return (EBUSY);
1204a15f7c96SJohn Baldwin 
12051b3fa1acSJohn Baldwin 	taskqueue_free(nvmft_taskq);
1206a15f7c96SJohn Baldwin 	sx_destroy(&nvmft_ports_lock);
1207a15f7c96SJohn Baldwin 	return (0);
1208a15f7c96SJohn Baldwin }
1209a15f7c96SJohn Baldwin 
1210a15f7c96SJohn Baldwin CTL_FRONTEND_DECLARE(nvmft, nvmft_frontend);
1211a15f7c96SJohn Baldwin MODULE_DEPEND(nvmft, nvmf_transport, 1, 1, 1);
1212