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