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