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