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