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