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/callout.h> 10a15f7c96SJohn Baldwin #include <sys/kernel.h> 11a15f7c96SJohn Baldwin #include <sys/lock.h> 12a15f7c96SJohn Baldwin #include <sys/malloc.h> 13a15f7c96SJohn Baldwin #include <sys/mbuf.h> 14a15f7c96SJohn Baldwin #include <sys/memdesc.h> 15a15f7c96SJohn Baldwin #include <sys/mutex.h> 16a15f7c96SJohn Baldwin #include <sys/sbuf.h> 17a15f7c96SJohn Baldwin #include <sys/sx.h> 18a15f7c96SJohn Baldwin #include <sys/taskqueue.h> 19a15f7c96SJohn Baldwin 20a15f7c96SJohn Baldwin #include <dev/nvmf/nvmf_transport.h> 21a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_subr.h> 22a15f7c96SJohn Baldwin #include <dev/nvmf/controller/nvmft_var.h> 23a15f7c96SJohn Baldwin 24a15f7c96SJohn Baldwin static void nvmft_controller_shutdown(void *arg, int pending); 25a15f7c96SJohn Baldwin static void nvmft_controller_terminate(void *arg, int pending); 26a15f7c96SJohn Baldwin 27a15f7c96SJohn Baldwin int 28a15f7c96SJohn Baldwin nvmft_printf(struct nvmft_controller *ctrlr, const char *fmt, ...) 29a15f7c96SJohn Baldwin { 30a15f7c96SJohn Baldwin char buf[128]; 31a15f7c96SJohn Baldwin struct sbuf sb; 32a15f7c96SJohn Baldwin va_list ap; 33a15f7c96SJohn Baldwin size_t retval; 34a15f7c96SJohn Baldwin 35a15f7c96SJohn Baldwin sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); 36a15f7c96SJohn Baldwin sbuf_set_drain(&sb, sbuf_printf_drain, &retval); 37a15f7c96SJohn Baldwin 38a15f7c96SJohn Baldwin sbuf_printf(&sb, "nvmft%u: ", ctrlr->cntlid); 39a15f7c96SJohn Baldwin 40a15f7c96SJohn Baldwin va_start(ap, fmt); 41a15f7c96SJohn Baldwin sbuf_vprintf(&sb, fmt, ap); 42a15f7c96SJohn Baldwin va_end(ap); 43a15f7c96SJohn Baldwin 44a15f7c96SJohn Baldwin sbuf_finish(&sb); 45a15f7c96SJohn Baldwin sbuf_delete(&sb); 46a15f7c96SJohn Baldwin 47a15f7c96SJohn Baldwin return (retval); 48a15f7c96SJohn Baldwin } 49a15f7c96SJohn Baldwin 50a15f7c96SJohn Baldwin static struct nvmft_controller * 51a15f7c96SJohn Baldwin nvmft_controller_alloc(struct nvmft_port *np, uint16_t cntlid, 52a15f7c96SJohn Baldwin const struct nvmf_fabric_connect_data *data) 53a15f7c96SJohn Baldwin { 54a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr; 55a15f7c96SJohn Baldwin 56a15f7c96SJohn Baldwin ctrlr = malloc(sizeof(*ctrlr), M_NVMFT, M_WAITOK | M_ZERO); 57a15f7c96SJohn Baldwin ctrlr->cntlid = cntlid; 58a15f7c96SJohn Baldwin nvmft_port_ref(np); 59a15f7c96SJohn Baldwin TAILQ_INSERT_TAIL(&np->controllers, ctrlr, link); 60a15f7c96SJohn Baldwin ctrlr->np = np; 61a15f7c96SJohn Baldwin mtx_init(&ctrlr->lock, "nvmft controller", NULL, MTX_DEF); 62a15f7c96SJohn Baldwin callout_init(&ctrlr->ka_timer, 1); 63a15f7c96SJohn Baldwin TASK_INIT(&ctrlr->shutdown_task, 0, nvmft_controller_shutdown, ctrlr); 64a15f7c96SJohn Baldwin TIMEOUT_TASK_INIT(taskqueue_thread, &ctrlr->terminate_task, 0, 65a15f7c96SJohn Baldwin nvmft_controller_terminate, ctrlr); 66a15f7c96SJohn Baldwin 67a15f7c96SJohn Baldwin ctrlr->cdata = np->cdata; 68a15f7c96SJohn Baldwin ctrlr->cdata.ctrlr_id = htole16(cntlid); 69a15f7c96SJohn Baldwin memcpy(ctrlr->hostid, data->hostid, sizeof(ctrlr->hostid)); 70a15f7c96SJohn Baldwin memcpy(ctrlr->hostnqn, data->hostnqn, sizeof(ctrlr->hostnqn)); 71a15f7c96SJohn Baldwin ctrlr->hip.power_cycles[0] = 1; 72a15f7c96SJohn Baldwin ctrlr->create_time = sbinuptime(); 73a15f7c96SJohn Baldwin 74a15f7c96SJohn Baldwin ctrlr->changed_ns = malloc(sizeof(*ctrlr->changed_ns), M_NVMFT, 75a15f7c96SJohn Baldwin M_WAITOK | M_ZERO); 76a15f7c96SJohn Baldwin 77a15f7c96SJohn Baldwin return (ctrlr); 78a15f7c96SJohn Baldwin } 79a15f7c96SJohn Baldwin 80a15f7c96SJohn Baldwin static void 81a15f7c96SJohn Baldwin nvmft_controller_free(struct nvmft_controller *ctrlr) 82a15f7c96SJohn Baldwin { 83a15f7c96SJohn Baldwin mtx_destroy(&ctrlr->lock); 84a15f7c96SJohn Baldwin MPASS(ctrlr->io_qpairs == NULL); 85a15f7c96SJohn Baldwin free(ctrlr->changed_ns, M_NVMFT); 86a15f7c96SJohn Baldwin free(ctrlr, M_NVMFT); 87a15f7c96SJohn Baldwin } 88a15f7c96SJohn Baldwin 89a15f7c96SJohn Baldwin static void 90a15f7c96SJohn Baldwin nvmft_keep_alive_timer(void *arg) 91a15f7c96SJohn Baldwin { 92a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr = arg; 93a15f7c96SJohn Baldwin int traffic; 94a15f7c96SJohn Baldwin 95a15f7c96SJohn Baldwin if (ctrlr->shutdown) 96a15f7c96SJohn Baldwin return; 97a15f7c96SJohn Baldwin 98a15f7c96SJohn Baldwin traffic = atomic_readandclear_int(&ctrlr->ka_active_traffic); 99a15f7c96SJohn Baldwin if (traffic == 0) { 100a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 101a15f7c96SJohn Baldwin "disconnecting due to KeepAlive timeout\n"); 102a15f7c96SJohn Baldwin nvmft_controller_error(ctrlr, NULL, ETIMEDOUT); 103a15f7c96SJohn Baldwin return; 104a15f7c96SJohn Baldwin } 105a15f7c96SJohn Baldwin 106a15f7c96SJohn Baldwin callout_schedule_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0, C_HARDCLOCK); 107a15f7c96SJohn Baldwin } 108a15f7c96SJohn Baldwin 109a15f7c96SJohn Baldwin int 110*365b89e8SJohn Baldwin nvmft_handoff_admin_queue(struct nvmft_port *np, enum nvmf_trtype trtype, 111*365b89e8SJohn Baldwin const nvlist_t *params, const struct nvmf_fabric_connect_cmd *cmd, 112a15f7c96SJohn Baldwin const struct nvmf_fabric_connect_data *data) 113a15f7c96SJohn Baldwin { 114a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr; 115a15f7c96SJohn Baldwin struct nvmft_qpair *qp; 116a15f7c96SJohn Baldwin uint32_t kato; 117a15f7c96SJohn Baldwin int cntlid; 118a15f7c96SJohn Baldwin 119a15f7c96SJohn Baldwin if (cmd->qid != htole16(0)) 120a15f7c96SJohn Baldwin return (EINVAL); 121a15f7c96SJohn Baldwin 122*365b89e8SJohn Baldwin qp = nvmft_qpair_init(trtype, params, 0, "admin queue"); 123dcfa6669SJohn Baldwin if (qp == NULL) { 124dcfa6669SJohn Baldwin printf("NVMFT: Failed to setup admin queue from %.*s\n", 125dcfa6669SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 126dcfa6669SJohn Baldwin return (ENXIO); 127dcfa6669SJohn Baldwin } 128a15f7c96SJohn Baldwin 129a15f7c96SJohn Baldwin sx_xlock(&np->lock); 130a15f7c96SJohn Baldwin cntlid = alloc_unr(np->ids); 131a15f7c96SJohn Baldwin if (cntlid == -1) { 132a15f7c96SJohn Baldwin sx_xunlock(&np->lock); 133a15f7c96SJohn Baldwin printf("NVMFT: Unable to allocate controller for %.*s\n", 134a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 135a15f7c96SJohn Baldwin nvmft_connect_error(qp, cmd, NVME_SCT_COMMAND_SPECIFIC, 136a15f7c96SJohn Baldwin NVMF_FABRIC_SC_INVALID_HOST); 137a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 138a15f7c96SJohn Baldwin return (ENOMEM); 139a15f7c96SJohn Baldwin } 140a15f7c96SJohn Baldwin 141a15f7c96SJohn Baldwin #ifdef INVARIANTS 142a15f7c96SJohn Baldwin TAILQ_FOREACH(ctrlr, &np->controllers, link) { 143a15f7c96SJohn Baldwin KASSERT(ctrlr->cntlid != cntlid, 144a15f7c96SJohn Baldwin ("%s: duplicate controllers with id %d", __func__, cntlid)); 145a15f7c96SJohn Baldwin } 146a15f7c96SJohn Baldwin #endif 147a15f7c96SJohn Baldwin 148a15f7c96SJohn Baldwin ctrlr = nvmft_controller_alloc(np, cntlid, data); 149a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "associated with %.*s\n", 150a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 151a15f7c96SJohn Baldwin ctrlr->admin = qp; 152*365b89e8SJohn Baldwin ctrlr->trtype = trtype; 153a15f7c96SJohn Baldwin 154a15f7c96SJohn Baldwin /* 155a15f7c96SJohn Baldwin * The spec requires a non-zero KeepAlive timer, but allow a 156a15f7c96SJohn Baldwin * zero KATO value to match Linux. 157a15f7c96SJohn Baldwin */ 158a15f7c96SJohn Baldwin kato = le32toh(cmd->kato); 159a15f7c96SJohn Baldwin if (kato != 0) { 160a15f7c96SJohn Baldwin /* 161a15f7c96SJohn Baldwin * Round up to 1 second matching granularity 162a15f7c96SJohn Baldwin * advertised in cdata. 163a15f7c96SJohn Baldwin */ 164a15f7c96SJohn Baldwin ctrlr->ka_sbt = mstosbt(roundup(kato, 1000)); 165a15f7c96SJohn Baldwin callout_reset_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0, 166a15f7c96SJohn Baldwin nvmft_keep_alive_timer, ctrlr, C_HARDCLOCK); 167a15f7c96SJohn Baldwin } 168a15f7c96SJohn Baldwin 169a15f7c96SJohn Baldwin nvmft_finish_accept(qp, cmd, ctrlr); 170a15f7c96SJohn Baldwin sx_xunlock(&np->lock); 171a15f7c96SJohn Baldwin 172a15f7c96SJohn Baldwin return (0); 173a15f7c96SJohn Baldwin } 174a15f7c96SJohn Baldwin 175a15f7c96SJohn Baldwin int 176*365b89e8SJohn Baldwin nvmft_handoff_io_queue(struct nvmft_port *np, enum nvmf_trtype trtype, 177*365b89e8SJohn Baldwin const nvlist_t *params, const struct nvmf_fabric_connect_cmd *cmd, 178a15f7c96SJohn Baldwin const struct nvmf_fabric_connect_data *data) 179a15f7c96SJohn Baldwin { 180a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr; 181a15f7c96SJohn Baldwin struct nvmft_qpair *qp; 182a15f7c96SJohn Baldwin char name[16]; 183a15f7c96SJohn Baldwin uint16_t cntlid, qid; 184a15f7c96SJohn Baldwin 185a15f7c96SJohn Baldwin qid = le16toh(cmd->qid); 186a15f7c96SJohn Baldwin if (qid == 0) 187a15f7c96SJohn Baldwin return (EINVAL); 188a15f7c96SJohn Baldwin cntlid = le16toh(data->cntlid); 189a15f7c96SJohn Baldwin 190a15f7c96SJohn Baldwin snprintf(name, sizeof(name), "I/O queue %u", qid); 191*365b89e8SJohn Baldwin qp = nvmft_qpair_init(trtype, params, qid, name); 192dcfa6669SJohn Baldwin if (qp == NULL) { 193dcfa6669SJohn Baldwin printf("NVMFT: Failed to setup I/O queue %u from %.*s\n", qid, 194dcfa6669SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 195dcfa6669SJohn Baldwin return (ENXIO); 196dcfa6669SJohn Baldwin } 197a15f7c96SJohn Baldwin 198a15f7c96SJohn Baldwin sx_slock(&np->lock); 199a15f7c96SJohn Baldwin TAILQ_FOREACH(ctrlr, &np->controllers, link) { 200a15f7c96SJohn Baldwin if (ctrlr->cntlid == cntlid) 201a15f7c96SJohn Baldwin break; 202a15f7c96SJohn Baldwin } 203a15f7c96SJohn Baldwin if (ctrlr == NULL) { 204a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 205a15f7c96SJohn Baldwin printf("NVMFT: Nonexistent controller %u for I/O queue %u from %.*s\n", 206a15f7c96SJohn Baldwin ctrlr->cntlid, qid, (int)sizeof(data->hostnqn), 207a15f7c96SJohn Baldwin data->hostnqn); 208a15f7c96SJohn Baldwin nvmft_connect_invalid_parameters(qp, cmd, true, 209a15f7c96SJohn Baldwin offsetof(struct nvmf_fabric_connect_data, cntlid)); 210a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 211a15f7c96SJohn Baldwin return (ENOENT); 212a15f7c96SJohn Baldwin } 213a15f7c96SJohn Baldwin 214a15f7c96SJohn Baldwin if (memcmp(ctrlr->hostid, data->hostid, sizeof(ctrlr->hostid)) != 0) { 215a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 216a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 217a15f7c96SJohn Baldwin "hostid mismatch for I/O queue %u from %.*s\n", qid, 218a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 219a15f7c96SJohn Baldwin nvmft_connect_invalid_parameters(qp, cmd, true, 220a15f7c96SJohn Baldwin offsetof(struct nvmf_fabric_connect_data, hostid)); 221a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 222a15f7c96SJohn Baldwin return (EINVAL); 223a15f7c96SJohn Baldwin } 224a15f7c96SJohn Baldwin if (memcmp(ctrlr->hostnqn, data->hostnqn, sizeof(ctrlr->hostnqn)) != 0) { 225a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 226a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 227a15f7c96SJohn Baldwin "hostnqn mismatch for I/O queue %u from %.*s\n", qid, 228a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 229a15f7c96SJohn Baldwin nvmft_connect_invalid_parameters(qp, cmd, true, 230a15f7c96SJohn Baldwin offsetof(struct nvmf_fabric_connect_data, hostnqn)); 231a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 232a15f7c96SJohn Baldwin return (EINVAL); 233a15f7c96SJohn Baldwin } 234a15f7c96SJohn Baldwin 235*365b89e8SJohn Baldwin /* XXX: Require trtype == ctrlr->trtype? */ 236a15f7c96SJohn Baldwin 237a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 238a15f7c96SJohn Baldwin if (ctrlr->shutdown) { 239a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 240a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 241a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 242a15f7c96SJohn Baldwin "attempt to create I/O queue %u on disabled controller from %.*s\n", 243a15f7c96SJohn Baldwin qid, (int)sizeof(data->hostnqn), data->hostnqn); 244a15f7c96SJohn Baldwin nvmft_connect_invalid_parameters(qp, cmd, true, 245a15f7c96SJohn Baldwin offsetof(struct nvmf_fabric_connect_data, cntlid)); 246a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 247a15f7c96SJohn Baldwin return (EINVAL); 248a15f7c96SJohn Baldwin } 249a15f7c96SJohn Baldwin if (ctrlr->num_io_queues == 0) { 250a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 251a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 252a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 253a15f7c96SJohn Baldwin "attempt to create I/O queue %u without enabled queues from %.*s\n", 254a15f7c96SJohn Baldwin qid, (int)sizeof(data->hostnqn), data->hostnqn); 255a15f7c96SJohn Baldwin nvmft_connect_error(qp, cmd, NVME_SCT_GENERIC, 256a15f7c96SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 257a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 258a15f7c96SJohn Baldwin return (EINVAL); 259a15f7c96SJohn Baldwin } 260a15f7c96SJohn Baldwin if (cmd->qid > ctrlr->num_io_queues) { 261a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 262a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 263a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 264a15f7c96SJohn Baldwin "attempt to create invalid I/O queue %u from %.*s\n", qid, 265a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 266a15f7c96SJohn Baldwin nvmft_connect_invalid_parameters(qp, cmd, false, 267a15f7c96SJohn Baldwin offsetof(struct nvmf_fabric_connect_cmd, qid)); 268a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 269a15f7c96SJohn Baldwin return (EINVAL); 270a15f7c96SJohn Baldwin } 271a15f7c96SJohn Baldwin if (ctrlr->io_qpairs[qid - 1].qp != NULL) { 272a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 273a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 274a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 275a15f7c96SJohn Baldwin "attempt to re-create I/O queue %u from %.*s\n", qid, 276a15f7c96SJohn Baldwin (int)sizeof(data->hostnqn), data->hostnqn); 277a15f7c96SJohn Baldwin nvmft_connect_error(qp, cmd, NVME_SCT_GENERIC, 278a15f7c96SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 279a15f7c96SJohn Baldwin nvmft_qpair_destroy(qp); 280a15f7c96SJohn Baldwin return (EINVAL); 281a15f7c96SJohn Baldwin } 282a15f7c96SJohn Baldwin 283a15f7c96SJohn Baldwin ctrlr->io_qpairs[qid - 1].qp = qp; 284a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 285a15f7c96SJohn Baldwin nvmft_finish_accept(qp, cmd, ctrlr); 286a15f7c96SJohn Baldwin sx_sunlock(&np->lock); 287a15f7c96SJohn Baldwin 288a15f7c96SJohn Baldwin return (0); 289a15f7c96SJohn Baldwin } 290a15f7c96SJohn Baldwin 291a15f7c96SJohn Baldwin static void 292a15f7c96SJohn Baldwin nvmft_controller_shutdown(void *arg, int pending) 293a15f7c96SJohn Baldwin { 294a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr = arg; 295a15f7c96SJohn Baldwin 296a15f7c96SJohn Baldwin MPASS(pending == 1); 297a15f7c96SJohn Baldwin 298a15f7c96SJohn Baldwin /* 299a15f7c96SJohn Baldwin * Shutdown all I/O queues to terminate pending datamoves and 300a15f7c96SJohn Baldwin * stop receiving new commands. 301a15f7c96SJohn Baldwin */ 302a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 303a15f7c96SJohn Baldwin for (u_int i = 0; i < ctrlr->num_io_queues; i++) { 304a15f7c96SJohn Baldwin if (ctrlr->io_qpairs[i].qp != NULL) { 305a15f7c96SJohn Baldwin ctrlr->io_qpairs[i].shutdown = true; 306a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 307a15f7c96SJohn Baldwin nvmft_qpair_shutdown(ctrlr->io_qpairs[i].qp); 308a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 309a15f7c96SJohn Baldwin } 310a15f7c96SJohn Baldwin } 311a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 312a15f7c96SJohn Baldwin 313a15f7c96SJohn Baldwin /* Terminate active CTL commands. */ 314a15f7c96SJohn Baldwin nvmft_terminate_commands(ctrlr); 315a15f7c96SJohn Baldwin 316a15f7c96SJohn Baldwin /* Wait for all pending CTL commands to complete. */ 317a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 318a15f7c96SJohn Baldwin while (ctrlr->pending_commands != 0) 319a15f7c96SJohn Baldwin mtx_sleep(&ctrlr->pending_commands, &ctrlr->lock, 0, "nvmftsh", 320a15f7c96SJohn Baldwin hz / 100); 321a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 322a15f7c96SJohn Baldwin 323a15f7c96SJohn Baldwin /* Delete all of the I/O queues. */ 324a15f7c96SJohn Baldwin for (u_int i = 0; i < ctrlr->num_io_queues; i++) { 325a15f7c96SJohn Baldwin if (ctrlr->io_qpairs[i].qp != NULL) 326a15f7c96SJohn Baldwin nvmft_qpair_destroy(ctrlr->io_qpairs[i].qp); 327a15f7c96SJohn Baldwin } 328a15f7c96SJohn Baldwin free(ctrlr->io_qpairs, M_NVMFT); 329a15f7c96SJohn Baldwin ctrlr->io_qpairs = NULL; 330a15f7c96SJohn Baldwin 331a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 332a15f7c96SJohn Baldwin ctrlr->num_io_queues = 0; 333a15f7c96SJohn Baldwin 334a15f7c96SJohn Baldwin /* Mark shutdown complete. */ 335a15f7c96SJohn Baldwin if (NVMEV(NVME_CSTS_REG_SHST, ctrlr->csts) == NVME_SHST_OCCURRING) { 336a15f7c96SJohn Baldwin ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_SHST); 337a15f7c96SJohn Baldwin ctrlr->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE); 338a15f7c96SJohn Baldwin } 339a15f7c96SJohn Baldwin 340a15f7c96SJohn Baldwin if (NVMEV(NVME_CSTS_REG_CFS, ctrlr->csts) == 0) { 341a15f7c96SJohn Baldwin ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_RDY); 342a15f7c96SJohn Baldwin ctrlr->shutdown = false; 343a15f7c96SJohn Baldwin } 344a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 345a15f7c96SJohn Baldwin 346a15f7c96SJohn Baldwin /* 347a15f7c96SJohn Baldwin * If the admin queue was closed while shutting down or a 348a15f7c96SJohn Baldwin * fatal controller error has occurred, terminate the 349a15f7c96SJohn Baldwin * association immediately, otherwise wait up to 2 minutes 350a15f7c96SJohn Baldwin * (NVMe-over-Fabrics 1.1 4.6). 351a15f7c96SJohn Baldwin */ 352a15f7c96SJohn Baldwin if (ctrlr->admin_closed || NVMEV(NVME_CSTS_REG_CFS, ctrlr->csts) != 0) 353a15f7c96SJohn Baldwin nvmft_controller_terminate(ctrlr, 0); 354a15f7c96SJohn Baldwin else 355a15f7c96SJohn Baldwin taskqueue_enqueue_timeout(taskqueue_thread, 356a15f7c96SJohn Baldwin &ctrlr->terminate_task, hz * 60 * 2); 357a15f7c96SJohn Baldwin } 358a15f7c96SJohn Baldwin 359a15f7c96SJohn Baldwin static void 360a15f7c96SJohn Baldwin nvmft_controller_terminate(void *arg, int pending) 361a15f7c96SJohn Baldwin { 362a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr = arg; 363a15f7c96SJohn Baldwin struct nvmft_port *np; 364a15f7c96SJohn Baldwin bool wakeup_np; 365a15f7c96SJohn Baldwin 366a15f7c96SJohn Baldwin /* If the controller has been re-enabled, nothing to do. */ 367a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 368a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) != 0) { 369a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 370a15f7c96SJohn Baldwin 371a15f7c96SJohn Baldwin if (ctrlr->ka_sbt != 0) 372a15f7c96SJohn Baldwin callout_schedule_sbt(&ctrlr->ka_timer, ctrlr->ka_sbt, 0, 373a15f7c96SJohn Baldwin C_HARDCLOCK); 374a15f7c96SJohn Baldwin return; 375a15f7c96SJohn Baldwin } 376a15f7c96SJohn Baldwin 377a15f7c96SJohn Baldwin /* Disable updates to CC while destroying admin qpair. */ 378a15f7c96SJohn Baldwin ctrlr->shutdown = true; 379a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 380a15f7c96SJohn Baldwin 381a15f7c96SJohn Baldwin nvmft_qpair_destroy(ctrlr->admin); 382a15f7c96SJohn Baldwin 383a15f7c96SJohn Baldwin /* Remove association (CNTLID). */ 384a15f7c96SJohn Baldwin np = ctrlr->np; 385a15f7c96SJohn Baldwin sx_xlock(&np->lock); 386a15f7c96SJohn Baldwin TAILQ_REMOVE(&np->controllers, ctrlr, link); 387a15f7c96SJohn Baldwin free_unr(np->ids, ctrlr->cntlid); 388a15f7c96SJohn Baldwin wakeup_np = (!np->online && TAILQ_EMPTY(&np->controllers)); 389a15f7c96SJohn Baldwin sx_xunlock(&np->lock); 390a15f7c96SJohn Baldwin if (wakeup_np) 391a15f7c96SJohn Baldwin wakeup(np); 392a15f7c96SJohn Baldwin 393a15f7c96SJohn Baldwin callout_drain(&ctrlr->ka_timer); 394a15f7c96SJohn Baldwin 395a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "association terminated\n"); 396a15f7c96SJohn Baldwin nvmft_controller_free(ctrlr); 397a15f7c96SJohn Baldwin nvmft_port_rele(np); 398a15f7c96SJohn Baldwin } 399a15f7c96SJohn Baldwin 400a15f7c96SJohn Baldwin void 401a15f7c96SJohn Baldwin nvmft_controller_error(struct nvmft_controller *ctrlr, struct nvmft_qpair *qp, 402a15f7c96SJohn Baldwin int error) 403a15f7c96SJohn Baldwin { 404a15f7c96SJohn Baldwin /* 405a15f7c96SJohn Baldwin * If a queue pair is closed, that isn't an error per se. 406a15f7c96SJohn Baldwin * That just means additional commands cannot be received on 407a15f7c96SJohn Baldwin * that queue pair. 408a15f7c96SJohn Baldwin * 409a15f7c96SJohn Baldwin * If the admin queue pair is closed while idle or while 410a15f7c96SJohn Baldwin * shutting down, terminate the association immediately. 411a15f7c96SJohn Baldwin * 412a15f7c96SJohn Baldwin * If an I/O queue pair is closed, just ignore it. 413a15f7c96SJohn Baldwin */ 414a15f7c96SJohn Baldwin if (error == 0) { 415a15f7c96SJohn Baldwin if (qp != ctrlr->admin) 416a15f7c96SJohn Baldwin return; 417a15f7c96SJohn Baldwin 418a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 419a15f7c96SJohn Baldwin if (ctrlr->shutdown) { 420a15f7c96SJohn Baldwin ctrlr->admin_closed = true; 421a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 422a15f7c96SJohn Baldwin return; 423a15f7c96SJohn Baldwin } 424a15f7c96SJohn Baldwin 425a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) == 0) { 426a15f7c96SJohn Baldwin MPASS(ctrlr->num_io_queues == 0); 427a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 428a15f7c96SJohn Baldwin 429a15f7c96SJohn Baldwin /* 430a15f7c96SJohn Baldwin * Ok to drop lock here since ctrlr->cc can't 431a15f7c96SJohn Baldwin * change if the admin queue pair has closed. 432a15f7c96SJohn Baldwin * This also means no new queues can be handed 433a15f7c96SJohn Baldwin * off, etc. Note that since there are no I/O 434a15f7c96SJohn Baldwin * queues, only the admin queue needs to be 435a15f7c96SJohn Baldwin * destroyed, so it is safe to skip 436a15f7c96SJohn Baldwin * nvmft_controller_shutdown and just schedule 437a15f7c96SJohn Baldwin * nvmft_controller_terminate. Note that we 438a15f7c96SJohn Baldwin * cannot call nvmft_controller_terminate from 439a15f7c96SJohn Baldwin * here directly as this is called from the 440a15f7c96SJohn Baldwin * transport layer and freeing the admin qpair 441a15f7c96SJohn Baldwin * might deadlock waiting for the current 442a15f7c96SJohn Baldwin * thread to exit. 443a15f7c96SJohn Baldwin */ 444a15f7c96SJohn Baldwin if (taskqueue_cancel_timeout(taskqueue_thread, 445a15f7c96SJohn Baldwin &ctrlr->terminate_task, NULL) == 0) 446a15f7c96SJohn Baldwin taskqueue_enqueue_timeout(taskqueue_thread, 447a15f7c96SJohn Baldwin &ctrlr->terminate_task, 0); 448a15f7c96SJohn Baldwin return; 449a15f7c96SJohn Baldwin } 450a15f7c96SJohn Baldwin 451a15f7c96SJohn Baldwin /* 452a15f7c96SJohn Baldwin * Treat closing of the admin queue pair while enabled 453a15f7c96SJohn Baldwin * as a transport error. Note that the admin queue 454a15f7c96SJohn Baldwin * pair has been closed. 455a15f7c96SJohn Baldwin */ 456a15f7c96SJohn Baldwin ctrlr->admin_closed = true; 457a15f7c96SJohn Baldwin } else 458a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 459a15f7c96SJohn Baldwin 460a15f7c96SJohn Baldwin /* Ignore transport errors if we are already shutting down. */ 461a15f7c96SJohn Baldwin if (ctrlr->shutdown) { 462a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 463a15f7c96SJohn Baldwin return; 464a15f7c96SJohn Baldwin } 465a15f7c96SJohn Baldwin 466a15f7c96SJohn Baldwin ctrlr->csts |= NVMEF(NVME_CSTS_REG_CFS, 1); 467a15f7c96SJohn Baldwin ctrlr->cc &= ~NVMEM(NVME_CC_REG_EN); 468a15f7c96SJohn Baldwin ctrlr->shutdown = true; 469a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 470a15f7c96SJohn Baldwin 471a15f7c96SJohn Baldwin callout_stop(&ctrlr->ka_timer); 472a15f7c96SJohn Baldwin taskqueue_enqueue(taskqueue_thread, &ctrlr->shutdown_task); 473a15f7c96SJohn Baldwin } 474a15f7c96SJohn Baldwin 475a15f7c96SJohn Baldwin /* Wrapper around m_getm2 that also sets m_len in the mbufs in the chain. */ 476a15f7c96SJohn Baldwin static struct mbuf * 477a15f7c96SJohn Baldwin m_getml(size_t len, int how) 478a15f7c96SJohn Baldwin { 479a15f7c96SJohn Baldwin struct mbuf *m, *n; 480a15f7c96SJohn Baldwin 481a15f7c96SJohn Baldwin m = m_getm2(NULL, len, how, MT_DATA, 0); 482a15f7c96SJohn Baldwin if (m == NULL) 483a15f7c96SJohn Baldwin return (NULL); 484a15f7c96SJohn Baldwin for (n = m; len > 0; n = n->m_next) { 485a15f7c96SJohn Baldwin n->m_len = M_SIZE(n); 486a15f7c96SJohn Baldwin if (n->m_len >= len) { 487a15f7c96SJohn Baldwin n->m_len = len; 488a15f7c96SJohn Baldwin MPASS(n->m_next == NULL); 489a15f7c96SJohn Baldwin } 490a15f7c96SJohn Baldwin len -= n->m_len; 491a15f7c96SJohn Baldwin } 492a15f7c96SJohn Baldwin return (m); 493a15f7c96SJohn Baldwin } 494a15f7c96SJohn Baldwin 495a15f7c96SJohn Baldwin static void 496a15f7c96SJohn Baldwin m_zero(struct mbuf *m, u_int offset, u_int len) 497a15f7c96SJohn Baldwin { 498a15f7c96SJohn Baldwin u_int todo; 499a15f7c96SJohn Baldwin 500a15f7c96SJohn Baldwin if (len == 0) 501a15f7c96SJohn Baldwin return; 502a15f7c96SJohn Baldwin 503a15f7c96SJohn Baldwin while (m->m_len <= offset) { 504a15f7c96SJohn Baldwin offset -= m->m_len; 505a15f7c96SJohn Baldwin m = m->m_next; 506a15f7c96SJohn Baldwin } 507a15f7c96SJohn Baldwin 508a15f7c96SJohn Baldwin todo = m->m_len - offset; 509a15f7c96SJohn Baldwin if (todo > len) 510a15f7c96SJohn Baldwin todo = len; 511a15f7c96SJohn Baldwin memset(mtodo(m, offset), 0, todo); 512a15f7c96SJohn Baldwin m = m->m_next; 513a15f7c96SJohn Baldwin len -= todo; 514a15f7c96SJohn Baldwin 515a15f7c96SJohn Baldwin while (len > 0) { 516a15f7c96SJohn Baldwin todo = m->m_len; 517a15f7c96SJohn Baldwin if (todo > len) 518a15f7c96SJohn Baldwin todo = len; 519a15f7c96SJohn Baldwin memset(mtod(m, void *), 0, todo); 520a15f7c96SJohn Baldwin m = m->m_next; 521a15f7c96SJohn Baldwin len -= todo; 522a15f7c96SJohn Baldwin } 523a15f7c96SJohn Baldwin } 524a15f7c96SJohn Baldwin 525a15f7c96SJohn Baldwin static void 526a15f7c96SJohn Baldwin handle_get_log_page(struct nvmft_controller *ctrlr, 527a15f7c96SJohn Baldwin struct nvmf_capsule *nc, const struct nvme_command *cmd) 528a15f7c96SJohn Baldwin { 529a15f7c96SJohn Baldwin struct mbuf *m; 530a15f7c96SJohn Baldwin uint64_t offset; 531a15f7c96SJohn Baldwin uint32_t numd; 532a15f7c96SJohn Baldwin size_t len, todo; 533a15f7c96SJohn Baldwin u_int status; 534a15f7c96SJohn Baldwin uint8_t lid; 535a15f7c96SJohn Baldwin bool rae; 536a15f7c96SJohn Baldwin 537a15f7c96SJohn Baldwin lid = le32toh(cmd->cdw10) & 0xff; 538a15f7c96SJohn Baldwin rae = (le32toh(cmd->cdw10) & (1U << 15)) != 0; 539a15f7c96SJohn Baldwin numd = le32toh(cmd->cdw10) >> 16 | le32toh(cmd->cdw11) << 16; 540a15f7c96SJohn Baldwin offset = le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32; 541a15f7c96SJohn Baldwin 542a15f7c96SJohn Baldwin if (offset % 3 != 0) { 543a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 544a15f7c96SJohn Baldwin goto done; 545a15f7c96SJohn Baldwin } 546a15f7c96SJohn Baldwin 547a15f7c96SJohn Baldwin len = (numd + 1) * 4; 548a15f7c96SJohn Baldwin 549a15f7c96SJohn Baldwin switch (lid) { 550a15f7c96SJohn Baldwin case NVME_LOG_ERROR: 551a15f7c96SJohn Baldwin todo = 0; 552a15f7c96SJohn Baldwin 553a15f7c96SJohn Baldwin m = m_getml(len, M_WAITOK); 554a15f7c96SJohn Baldwin if (todo != len) 555a15f7c96SJohn Baldwin m_zero(m, todo, len - todo); 556a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, len); 557a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 558a15f7c96SJohn Baldwin break; 559a15f7c96SJohn Baldwin case NVME_LOG_HEALTH_INFORMATION: 560a15f7c96SJohn Baldwin { 561a15f7c96SJohn Baldwin struct nvme_health_information_page hip; 562a15f7c96SJohn Baldwin 563a15f7c96SJohn Baldwin if (offset >= sizeof(hip)) { 564a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 565a15f7c96SJohn Baldwin goto done; 566a15f7c96SJohn Baldwin } 567a15f7c96SJohn Baldwin todo = sizeof(hip) - offset; 568a15f7c96SJohn Baldwin if (todo > len) 569a15f7c96SJohn Baldwin todo = len; 570a15f7c96SJohn Baldwin 571a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 572a15f7c96SJohn Baldwin hip = ctrlr->hip; 573a15f7c96SJohn Baldwin hip.controller_busy_time[0] = 574a15f7c96SJohn Baldwin sbintime_getsec(ctrlr->busy_total) / 60; 575a15f7c96SJohn Baldwin hip.power_on_hours[0] = 576a15f7c96SJohn Baldwin sbintime_getsec(sbinuptime() - ctrlr->create_time) / 3600; 577a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 578a15f7c96SJohn Baldwin 579a15f7c96SJohn Baldwin m = m_getml(len, M_WAITOK); 580a15f7c96SJohn Baldwin m_copyback(m, 0, todo, (char *)&hip + offset); 581a15f7c96SJohn Baldwin if (todo != len) 582a15f7c96SJohn Baldwin m_zero(m, todo, len - todo); 583a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, len); 584a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 585a15f7c96SJohn Baldwin break; 586a15f7c96SJohn Baldwin } 587a15f7c96SJohn Baldwin case NVME_LOG_FIRMWARE_SLOT: 588a15f7c96SJohn Baldwin if (offset >= sizeof(ctrlr->np->fp)) { 589a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 590a15f7c96SJohn Baldwin goto done; 591a15f7c96SJohn Baldwin } 592a15f7c96SJohn Baldwin todo = sizeof(ctrlr->np->fp) - offset; 593a15f7c96SJohn Baldwin if (todo > len) 594a15f7c96SJohn Baldwin todo = len; 595a15f7c96SJohn Baldwin 596a15f7c96SJohn Baldwin m = m_getml(len, M_WAITOK); 597a15f7c96SJohn Baldwin m_copyback(m, 0, todo, (char *)&ctrlr->np->fp + offset); 598a15f7c96SJohn Baldwin if (todo != len) 599a15f7c96SJohn Baldwin m_zero(m, todo, len - todo); 600a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, len); 601a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 602a15f7c96SJohn Baldwin break; 603a15f7c96SJohn Baldwin case NVME_LOG_CHANGED_NAMESPACE: 604a15f7c96SJohn Baldwin if (offset >= sizeof(*ctrlr->changed_ns)) { 605a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 606a15f7c96SJohn Baldwin goto done; 607a15f7c96SJohn Baldwin } 608a15f7c96SJohn Baldwin todo = sizeof(*ctrlr->changed_ns) - offset; 609a15f7c96SJohn Baldwin if (todo > len) 610a15f7c96SJohn Baldwin todo = len; 611a15f7c96SJohn Baldwin 612a15f7c96SJohn Baldwin m = m_getml(len, M_WAITOK); 613a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 614a15f7c96SJohn Baldwin m_copyback(m, 0, todo, (char *)ctrlr->changed_ns + offset); 615a15f7c96SJohn Baldwin if (offset == 0 && len == sizeof(*ctrlr->changed_ns)) 616a15f7c96SJohn Baldwin memset(ctrlr->changed_ns, 0, 617a15f7c96SJohn Baldwin sizeof(*ctrlr->changed_ns)); 618a15f7c96SJohn Baldwin if (!rae) 619a15f7c96SJohn Baldwin ctrlr->changed_ns_reported = false; 620a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 621a15f7c96SJohn Baldwin if (todo != len) 622a15f7c96SJohn Baldwin m_zero(m, todo, len - todo); 623a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, len); 624a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 625a15f7c96SJohn Baldwin break; 626a15f7c96SJohn Baldwin default: 627a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "Unsupported page %#x for GET_LOG_PAGE\n", 628a15f7c96SJohn Baldwin lid); 629a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 630a15f7c96SJohn Baldwin break; 631a15f7c96SJohn Baldwin } 632a15f7c96SJohn Baldwin 633a15f7c96SJohn Baldwin done: 634a15f7c96SJohn Baldwin if (status == NVMF_SUCCESS_SENT) 635a15f7c96SJohn Baldwin nvmft_command_completed(ctrlr->admin, nc); 636a15f7c96SJohn Baldwin else 637a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, status); 638a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 639a15f7c96SJohn Baldwin } 640a15f7c96SJohn Baldwin 641a15f7c96SJohn Baldwin static void 642a15f7c96SJohn Baldwin m_free_nslist(struct mbuf *m) 643a15f7c96SJohn Baldwin { 644a15f7c96SJohn Baldwin free(m->m_ext.ext_arg1, M_NVMFT); 645a15f7c96SJohn Baldwin } 646a15f7c96SJohn Baldwin 647a15f7c96SJohn Baldwin static void 648a15f7c96SJohn Baldwin handle_identify_command(struct nvmft_controller *ctrlr, 649a15f7c96SJohn Baldwin struct nvmf_capsule *nc, const struct nvme_command *cmd) 650a15f7c96SJohn Baldwin { 651a15f7c96SJohn Baldwin struct mbuf *m; 652a15f7c96SJohn Baldwin size_t data_len; 653a15f7c96SJohn Baldwin u_int status; 654a15f7c96SJohn Baldwin uint8_t cns; 655a15f7c96SJohn Baldwin 656a15f7c96SJohn Baldwin cns = le32toh(cmd->cdw10) & 0xFF; 657a15f7c96SJohn Baldwin data_len = nvmf_capsule_data_len(nc); 658a15f7c96SJohn Baldwin if (data_len != sizeof(ctrlr->cdata)) { 659a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 660a15f7c96SJohn Baldwin "Invalid length %zu for IDENTIFY with CNS %#x\n", data_len, 661a15f7c96SJohn Baldwin cns); 662a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 663a15f7c96SJohn Baldwin NVME_SC_INVALID_OPCODE); 664a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 665a15f7c96SJohn Baldwin return; 666a15f7c96SJohn Baldwin } 667a15f7c96SJohn Baldwin 668a15f7c96SJohn Baldwin switch (cns) { 669a15f7c96SJohn Baldwin case 0: /* Namespace data. */ 670a15f7c96SJohn Baldwin case 3: /* Namespace Identification Descriptor list. */ 671a15f7c96SJohn Baldwin nvmft_dispatch_command(ctrlr->admin, nc, true); 672a15f7c96SJohn Baldwin return; 673a15f7c96SJohn Baldwin case 1: 674a15f7c96SJohn Baldwin /* Controller data. */ 675a15f7c96SJohn Baldwin m = m_getml(sizeof(ctrlr->cdata), M_WAITOK); 676a15f7c96SJohn Baldwin m_copyback(m, 0, sizeof(ctrlr->cdata), (void *)&ctrlr->cdata); 677a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, 678a15f7c96SJohn Baldwin sizeof(ctrlr->cdata)); 679a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 680a15f7c96SJohn Baldwin break; 681a15f7c96SJohn Baldwin case 2: 682a15f7c96SJohn Baldwin { 683a15f7c96SJohn Baldwin /* Active namespace list. */ 684a15f7c96SJohn Baldwin struct nvme_ns_list *nslist; 685a15f7c96SJohn Baldwin uint32_t nsid; 686a15f7c96SJohn Baldwin 687a15f7c96SJohn Baldwin nsid = le32toh(cmd->nsid); 688a15f7c96SJohn Baldwin if (nsid >= 0xfffffffe) { 689a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 690a15f7c96SJohn Baldwin break; 691a15f7c96SJohn Baldwin } 692a15f7c96SJohn Baldwin 693a15f7c96SJohn Baldwin nslist = malloc(sizeof(*nslist), M_NVMFT, M_WAITOK | M_ZERO); 694a15f7c96SJohn Baldwin nvmft_populate_active_nslist(ctrlr->np, nsid, nslist); 695a15f7c96SJohn Baldwin m = m_get(M_WAITOK, MT_DATA); 696a15f7c96SJohn Baldwin m_extadd(m, (void *)nslist, sizeof(*nslist), m_free_nslist, 697a15f7c96SJohn Baldwin nslist, NULL, 0, EXT_CTL); 698a15f7c96SJohn Baldwin m->m_len = sizeof(*nslist); 699a15f7c96SJohn Baldwin status = nvmf_send_controller_data(nc, 0, m, m->m_len); 700a15f7c96SJohn Baldwin MPASS(status != NVMF_MORE); 701a15f7c96SJohn Baldwin break; 702a15f7c96SJohn Baldwin } 703a15f7c96SJohn Baldwin default: 704a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "Unsupported CNS %#x for IDENTIFY\n", cns); 705a15f7c96SJohn Baldwin status = NVME_SC_INVALID_FIELD; 706a15f7c96SJohn Baldwin break; 707a15f7c96SJohn Baldwin } 708a15f7c96SJohn Baldwin 709a15f7c96SJohn Baldwin if (status == NVMF_SUCCESS_SENT) 710a15f7c96SJohn Baldwin nvmft_command_completed(ctrlr->admin, nc); 711a15f7c96SJohn Baldwin else 712a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, status); 713a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 714a15f7c96SJohn Baldwin } 715a15f7c96SJohn Baldwin 716a15f7c96SJohn Baldwin static void 717a15f7c96SJohn Baldwin handle_set_features(struct nvmft_controller *ctrlr, 718a15f7c96SJohn Baldwin struct nvmf_capsule *nc, const struct nvme_command *cmd) 719a15f7c96SJohn Baldwin { 720a15f7c96SJohn Baldwin struct nvme_completion cqe; 721a15f7c96SJohn Baldwin uint8_t fid; 722a15f7c96SJohn Baldwin 723a15f7c96SJohn Baldwin fid = NVMEV(NVME_FEAT_SET_FID, le32toh(cmd->cdw10)); 724a15f7c96SJohn Baldwin switch (fid) { 725a15f7c96SJohn Baldwin case NVME_FEAT_NUMBER_OF_QUEUES: 726a15f7c96SJohn Baldwin { 727a15f7c96SJohn Baldwin uint32_t num_queues; 728a15f7c96SJohn Baldwin struct nvmft_io_qpair *io_qpairs; 729a15f7c96SJohn Baldwin 730a15f7c96SJohn Baldwin num_queues = le32toh(cmd->cdw11) & 0xffff; 731a15f7c96SJohn Baldwin 732a15f7c96SJohn Baldwin /* 5.12.1.7: 65535 is invalid. */ 733a15f7c96SJohn Baldwin if (num_queues == 65535) 734a15f7c96SJohn Baldwin goto error; 735a15f7c96SJohn Baldwin 736a15f7c96SJohn Baldwin /* Fabrics requires the same number of SQs and CQs. */ 737a15f7c96SJohn Baldwin if (le32toh(cmd->cdw11) >> 16 != num_queues) 738a15f7c96SJohn Baldwin goto error; 739a15f7c96SJohn Baldwin 740a15f7c96SJohn Baldwin /* Convert to 1's based */ 741a15f7c96SJohn Baldwin num_queues++; 742a15f7c96SJohn Baldwin 743a15f7c96SJohn Baldwin io_qpairs = mallocarray(num_queues, sizeof(*io_qpairs), 744a15f7c96SJohn Baldwin M_NVMFT, M_WAITOK | M_ZERO); 745a15f7c96SJohn Baldwin 746a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 747a15f7c96SJohn Baldwin if (ctrlr->num_io_queues != 0) { 748a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 749a15f7c96SJohn Baldwin free(io_qpairs, M_NVMFT); 750a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 751a15f7c96SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 752a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 753a15f7c96SJohn Baldwin return; 754a15f7c96SJohn Baldwin } 755a15f7c96SJohn Baldwin 756a15f7c96SJohn Baldwin ctrlr->num_io_queues = num_queues; 757a15f7c96SJohn Baldwin ctrlr->io_qpairs = io_qpairs; 758a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 759a15f7c96SJohn Baldwin 760a15f7c96SJohn Baldwin nvmft_init_cqe(&cqe, nc, 0); 761a15f7c96SJohn Baldwin cqe.cdw0 = cmd->cdw11; 762a15f7c96SJohn Baldwin nvmft_send_response(ctrlr->admin, &cqe); 763a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 764a15f7c96SJohn Baldwin return; 765a15f7c96SJohn Baldwin } 766a15f7c96SJohn Baldwin case NVME_FEAT_ASYNC_EVENT_CONFIGURATION: 767a15f7c96SJohn Baldwin { 768a15f7c96SJohn Baldwin uint32_t aer_mask; 769a15f7c96SJohn Baldwin 770a15f7c96SJohn Baldwin aer_mask = le32toh(cmd->cdw11); 771a15f7c96SJohn Baldwin 772a15f7c96SJohn Baldwin /* Check for any reserved or unimplemented feature bits. */ 773a15f7c96SJohn Baldwin if ((aer_mask & 0xffffc000) != 0) 774a15f7c96SJohn Baldwin goto error; 775a15f7c96SJohn Baldwin 776a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 777a15f7c96SJohn Baldwin ctrlr->aer_mask = aer_mask; 778a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 779a15f7c96SJohn Baldwin nvmft_send_success(ctrlr->admin, nc); 780a15f7c96SJohn Baldwin return; 781a15f7c96SJohn Baldwin } 782a15f7c96SJohn Baldwin default: 783a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 784a15f7c96SJohn Baldwin "Unsupported feature ID %u for SET_FEATURES\n", fid); 785a15f7c96SJohn Baldwin goto error; 786a15f7c96SJohn Baldwin } 787a15f7c96SJohn Baldwin 788a15f7c96SJohn Baldwin error: 789a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD); 790a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 791a15f7c96SJohn Baldwin } 792a15f7c96SJohn Baldwin 793a15f7c96SJohn Baldwin static bool 794a15f7c96SJohn Baldwin update_cc(struct nvmft_controller *ctrlr, uint32_t new_cc, bool *need_shutdown) 795a15f7c96SJohn Baldwin { 796a15f7c96SJohn Baldwin struct nvmft_port *np = ctrlr->np; 797a15f7c96SJohn Baldwin uint32_t changes; 798a15f7c96SJohn Baldwin 799a15f7c96SJohn Baldwin *need_shutdown = false; 800a15f7c96SJohn Baldwin 801a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 802a15f7c96SJohn Baldwin 803a15f7c96SJohn Baldwin /* Don't allow any changes while shutting down. */ 804a15f7c96SJohn Baldwin if (ctrlr->shutdown) { 805a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 806a15f7c96SJohn Baldwin return (false); 807a15f7c96SJohn Baldwin } 808a15f7c96SJohn Baldwin 809a15f7c96SJohn Baldwin if (!_nvmf_validate_cc(np->max_io_qsize, np->cap, ctrlr->cc, new_cc)) { 810a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 811a15f7c96SJohn Baldwin return (false); 812a15f7c96SJohn Baldwin } 813a15f7c96SJohn Baldwin 814a15f7c96SJohn Baldwin changes = ctrlr->cc ^ new_cc; 815a15f7c96SJohn Baldwin ctrlr->cc = new_cc; 816a15f7c96SJohn Baldwin 817a15f7c96SJohn Baldwin /* Handle shutdown requests. */ 818a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_SHN, changes) != 0 && 819a15f7c96SJohn Baldwin NVMEV(NVME_CC_REG_SHN, new_cc) != 0) { 820a15f7c96SJohn Baldwin ctrlr->csts &= ~NVMEM(NVME_CSTS_REG_SHST); 821a15f7c96SJohn Baldwin ctrlr->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_OCCURRING); 822a15f7c96SJohn Baldwin ctrlr->cc &= ~NVMEM(NVME_CC_REG_EN); 823a15f7c96SJohn Baldwin ctrlr->shutdown = true; 824a15f7c96SJohn Baldwin *need_shutdown = true; 825a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "shutdown requested\n"); 826a15f7c96SJohn Baldwin } 827a15f7c96SJohn Baldwin 828a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, changes) != 0) { 829a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) { 830a15f7c96SJohn Baldwin /* Controller reset. */ 831a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "reset requested\n"); 832a15f7c96SJohn Baldwin ctrlr->shutdown = true; 833a15f7c96SJohn Baldwin *need_shutdown = true; 834a15f7c96SJohn Baldwin } else 835a15f7c96SJohn Baldwin ctrlr->csts |= NVMEF(NVME_CSTS_REG_RDY, 1); 836a15f7c96SJohn Baldwin } 837a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 838a15f7c96SJohn Baldwin 839a15f7c96SJohn Baldwin return (true); 840a15f7c96SJohn Baldwin } 841a15f7c96SJohn Baldwin 842a15f7c96SJohn Baldwin static void 843a15f7c96SJohn Baldwin handle_property_get(struct nvmft_controller *ctrlr, struct nvmf_capsule *nc, 844a15f7c96SJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget) 845a15f7c96SJohn Baldwin { 846a15f7c96SJohn Baldwin struct nvmf_fabric_prop_get_rsp rsp; 847a15f7c96SJohn Baldwin 848a15f7c96SJohn Baldwin nvmft_init_cqe(&rsp, nc, 0); 849a15f7c96SJohn Baldwin 850a15f7c96SJohn Baldwin switch (le32toh(pget->ofst)) { 851a15f7c96SJohn Baldwin case NVMF_PROP_CAP: 852a15f7c96SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_8) 853a15f7c96SJohn Baldwin goto error; 854a15f7c96SJohn Baldwin rsp.value.u64 = htole64(ctrlr->np->cap); 855a15f7c96SJohn Baldwin break; 856a15f7c96SJohn Baldwin case NVMF_PROP_VS: 857a15f7c96SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 858a15f7c96SJohn Baldwin goto error; 859a15f7c96SJohn Baldwin rsp.value.u32.low = ctrlr->cdata.ver; 860a15f7c96SJohn Baldwin break; 861a15f7c96SJohn Baldwin case NVMF_PROP_CC: 862a15f7c96SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 863a15f7c96SJohn Baldwin goto error; 864a15f7c96SJohn Baldwin rsp.value.u32.low = htole32(ctrlr->cc); 865a15f7c96SJohn Baldwin break; 866a15f7c96SJohn Baldwin case NVMF_PROP_CSTS: 867a15f7c96SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 868a15f7c96SJohn Baldwin goto error; 869a15f7c96SJohn Baldwin rsp.value.u32.low = htole32(ctrlr->csts); 870a15f7c96SJohn Baldwin break; 871a15f7c96SJohn Baldwin default: 872a15f7c96SJohn Baldwin goto error; 873a15f7c96SJohn Baldwin } 874a15f7c96SJohn Baldwin 875a15f7c96SJohn Baldwin nvmft_send_response(ctrlr->admin, &rsp); 876a15f7c96SJohn Baldwin return; 877a15f7c96SJohn Baldwin error: 878a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD); 879a15f7c96SJohn Baldwin } 880a15f7c96SJohn Baldwin 881a15f7c96SJohn Baldwin static void 882a15f7c96SJohn Baldwin handle_property_set(struct nvmft_controller *ctrlr, struct nvmf_capsule *nc, 883a15f7c96SJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset) 884a15f7c96SJohn Baldwin { 885a15f7c96SJohn Baldwin bool need_shutdown; 886a15f7c96SJohn Baldwin 887a15f7c96SJohn Baldwin need_shutdown = false; 888a15f7c96SJohn Baldwin switch (le32toh(pset->ofst)) { 889a15f7c96SJohn Baldwin case NVMF_PROP_CC: 890a15f7c96SJohn Baldwin if (pset->attrib.size != NVMF_PROP_SIZE_4) 891a15f7c96SJohn Baldwin goto error; 892a15f7c96SJohn Baldwin if (!update_cc(ctrlr, le32toh(pset->value.u32.low), 893a15f7c96SJohn Baldwin &need_shutdown)) 894a15f7c96SJohn Baldwin goto error; 895a15f7c96SJohn Baldwin break; 896a15f7c96SJohn Baldwin default: 897a15f7c96SJohn Baldwin goto error; 898a15f7c96SJohn Baldwin } 899a15f7c96SJohn Baldwin 900a15f7c96SJohn Baldwin nvmft_send_success(ctrlr->admin, nc); 901a15f7c96SJohn Baldwin if (need_shutdown) { 902a15f7c96SJohn Baldwin callout_stop(&ctrlr->ka_timer); 903a15f7c96SJohn Baldwin taskqueue_enqueue(taskqueue_thread, &ctrlr->shutdown_task); 904a15f7c96SJohn Baldwin } 905a15f7c96SJohn Baldwin return; 906a15f7c96SJohn Baldwin error: 907a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, NVME_SC_INVALID_FIELD); 908a15f7c96SJohn Baldwin } 909a15f7c96SJohn Baldwin 910a15f7c96SJohn Baldwin static void 911a15f7c96SJohn Baldwin handle_admin_fabrics_command(struct nvmft_controller *ctrlr, 912a15f7c96SJohn Baldwin struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc) 913a15f7c96SJohn Baldwin { 914a15f7c96SJohn Baldwin switch (fc->fctype) { 915a15f7c96SJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_GET: 916a15f7c96SJohn Baldwin handle_property_get(ctrlr, nc, 917a15f7c96SJohn Baldwin (const struct nvmf_fabric_prop_get_cmd *)fc); 918a15f7c96SJohn Baldwin break; 919a15f7c96SJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_SET: 920a15f7c96SJohn Baldwin handle_property_set(ctrlr, nc, 921a15f7c96SJohn Baldwin (const struct nvmf_fabric_prop_set_cmd *)fc); 922a15f7c96SJohn Baldwin break; 923a15f7c96SJohn Baldwin case NVMF_FABRIC_COMMAND_CONNECT: 924a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 925a15f7c96SJohn Baldwin "CONNECT command on connected admin queue\n"); 926a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 927a15f7c96SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 928a15f7c96SJohn Baldwin break; 929a15f7c96SJohn Baldwin case NVMF_FABRIC_COMMAND_DISCONNECT: 930a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "DISCONNECT command on admin queue\n"); 931a15f7c96SJohn Baldwin nvmft_send_error(ctrlr->admin, nc, NVME_SCT_COMMAND_SPECIFIC, 932a15f7c96SJohn Baldwin NVMF_FABRIC_SC_INVALID_QUEUE_TYPE); 933a15f7c96SJohn Baldwin break; 934a15f7c96SJohn Baldwin default: 935a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "Unsupported fabrics command %#x\n", 936a15f7c96SJohn Baldwin fc->fctype); 937a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 938a15f7c96SJohn Baldwin NVME_SC_INVALID_OPCODE); 939a15f7c96SJohn Baldwin break; 940a15f7c96SJohn Baldwin } 941a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 942a15f7c96SJohn Baldwin } 943a15f7c96SJohn Baldwin 944a15f7c96SJohn Baldwin void 945a15f7c96SJohn Baldwin nvmft_handle_admin_command(struct nvmft_controller *ctrlr, 946a15f7c96SJohn Baldwin struct nvmf_capsule *nc) 947a15f7c96SJohn Baldwin { 948a15f7c96SJohn Baldwin const struct nvme_command *cmd = nvmf_capsule_sqe(nc); 949a15f7c96SJohn Baldwin 950a15f7c96SJohn Baldwin /* Only permit Fabrics commands while a controller is disabled. */ 951a15f7c96SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, ctrlr->cc) == 0 && 952a15f7c96SJohn Baldwin cmd->opc != NVME_OPC_FABRICS_COMMANDS) { 953a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 954f5541f9fSJohn Baldwin "Unsupported admin opcode %#x while disabled\n", cmd->opc); 955a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 956a15f7c96SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 957a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 958a15f7c96SJohn Baldwin return; 959a15f7c96SJohn Baldwin } 960a15f7c96SJohn Baldwin 961a15f7c96SJohn Baldwin atomic_store_int(&ctrlr->ka_active_traffic, 1); 962a15f7c96SJohn Baldwin 963a15f7c96SJohn Baldwin switch (cmd->opc) { 964a15f7c96SJohn Baldwin case NVME_OPC_GET_LOG_PAGE: 965a15f7c96SJohn Baldwin handle_get_log_page(ctrlr, nc, cmd); 966a15f7c96SJohn Baldwin break; 967a15f7c96SJohn Baldwin case NVME_OPC_IDENTIFY: 968a15f7c96SJohn Baldwin handle_identify_command(ctrlr, nc, cmd); 969a15f7c96SJohn Baldwin break; 970a15f7c96SJohn Baldwin case NVME_OPC_SET_FEATURES: 971a15f7c96SJohn Baldwin handle_set_features(ctrlr, nc, cmd); 972a15f7c96SJohn Baldwin break; 973a15f7c96SJohn Baldwin case NVME_OPC_ASYNC_EVENT_REQUEST: 974a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 975a15f7c96SJohn Baldwin if (ctrlr->aer_pending == NVMFT_NUM_AER) { 976a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 977a15f7c96SJohn Baldwin nvmft_send_error(ctrlr->admin, nc, 978a15f7c96SJohn Baldwin NVME_SCT_COMMAND_SPECIFIC, 979a15f7c96SJohn Baldwin NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED); 980a15f7c96SJohn Baldwin } else { 981a15f7c96SJohn Baldwin /* NB: Store the CID without byte-swapping. */ 982a15f7c96SJohn Baldwin ctrlr->aer_cids[ctrlr->aer_pidx] = cmd->cid; 983a15f7c96SJohn Baldwin ctrlr->aer_pending++; 984a15f7c96SJohn Baldwin ctrlr->aer_pidx = (ctrlr->aer_pidx + 1) % NVMFT_NUM_AER; 985a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 986a15f7c96SJohn Baldwin } 987a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 988a15f7c96SJohn Baldwin break; 989a15f7c96SJohn Baldwin case NVME_OPC_KEEP_ALIVE: 990a15f7c96SJohn Baldwin nvmft_send_success(ctrlr->admin, nc); 991a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 992a15f7c96SJohn Baldwin break; 993a15f7c96SJohn Baldwin case NVME_OPC_FABRICS_COMMANDS: 994a15f7c96SJohn Baldwin handle_admin_fabrics_command(ctrlr, nc, 995a15f7c96SJohn Baldwin (const struct nvmf_fabric_cmd *)cmd); 996a15f7c96SJohn Baldwin break; 997a15f7c96SJohn Baldwin default: 998a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "Unsupported admin opcode %#x\n", cmd->opc); 999a15f7c96SJohn Baldwin nvmft_send_generic_error(ctrlr->admin, nc, 1000a15f7c96SJohn Baldwin NVME_SC_INVALID_OPCODE); 1001a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 1002a15f7c96SJohn Baldwin break; 1003a15f7c96SJohn Baldwin } 1004a15f7c96SJohn Baldwin } 1005a15f7c96SJohn Baldwin 1006a15f7c96SJohn Baldwin void 1007a15f7c96SJohn Baldwin nvmft_handle_io_command(struct nvmft_qpair *qp, uint16_t qid, 1008a15f7c96SJohn Baldwin struct nvmf_capsule *nc) 1009a15f7c96SJohn Baldwin { 1010a15f7c96SJohn Baldwin struct nvmft_controller *ctrlr = nvmft_qpair_ctrlr(qp); 1011a15f7c96SJohn Baldwin const struct nvme_command *cmd = nvmf_capsule_sqe(nc); 1012a15f7c96SJohn Baldwin 1013a15f7c96SJohn Baldwin atomic_store_int(&ctrlr->ka_active_traffic, 1); 1014a15f7c96SJohn Baldwin 1015a15f7c96SJohn Baldwin switch (cmd->opc) { 1016a15f7c96SJohn Baldwin case NVME_OPC_FLUSH: 1017a15f7c96SJohn Baldwin if (cmd->nsid == htole32(0xffffffff)) { 1018a15f7c96SJohn Baldwin nvmft_send_generic_error(qp, nc, 1019a15f7c96SJohn Baldwin NVME_SC_INVALID_NAMESPACE_OR_FORMAT); 1020a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 1021a15f7c96SJohn Baldwin break; 1022a15f7c96SJohn Baldwin } 1023a15f7c96SJohn Baldwin /* FALLTHROUGH */ 1024a15f7c96SJohn Baldwin case NVME_OPC_WRITE: 1025a15f7c96SJohn Baldwin case NVME_OPC_READ: 1026a15f7c96SJohn Baldwin case NVME_OPC_WRITE_UNCORRECTABLE: 1027a15f7c96SJohn Baldwin case NVME_OPC_COMPARE: 1028a15f7c96SJohn Baldwin case NVME_OPC_WRITE_ZEROES: 1029a15f7c96SJohn Baldwin case NVME_OPC_DATASET_MANAGEMENT: 1030a15f7c96SJohn Baldwin case NVME_OPC_VERIFY: 1031a15f7c96SJohn Baldwin nvmft_dispatch_command(qp, nc, false); 1032a15f7c96SJohn Baldwin break; 1033a15f7c96SJohn Baldwin default: 1034a15f7c96SJohn Baldwin nvmft_printf(ctrlr, "Unsupported I/O opcode %#x\n", cmd->opc); 1035a15f7c96SJohn Baldwin nvmft_send_generic_error(qp, nc, 1036a15f7c96SJohn Baldwin NVME_SC_INVALID_OPCODE); 1037a15f7c96SJohn Baldwin nvmf_free_capsule(nc); 1038a15f7c96SJohn Baldwin break; 1039a15f7c96SJohn Baldwin } 1040a15f7c96SJohn Baldwin } 1041a15f7c96SJohn Baldwin 1042a15f7c96SJohn Baldwin static void 1043a15f7c96SJohn Baldwin nvmft_report_aer(struct nvmft_controller *ctrlr, uint32_t aer_mask, 1044a15f7c96SJohn Baldwin u_int type, uint8_t info, uint8_t log_page_id) 1045a15f7c96SJohn Baldwin { 1046a15f7c96SJohn Baldwin struct nvme_completion cpl; 1047a15f7c96SJohn Baldwin 1048a15f7c96SJohn Baldwin MPASS(type <= 7); 1049a15f7c96SJohn Baldwin 1050a15f7c96SJohn Baldwin /* Drop events that are not enabled. */ 1051a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 1052a15f7c96SJohn Baldwin if ((ctrlr->aer_mask & aer_mask) == 0) { 1053a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1054a15f7c96SJohn Baldwin return; 1055a15f7c96SJohn Baldwin } 1056a15f7c96SJohn Baldwin 1057a15f7c96SJohn Baldwin /* 1058a15f7c96SJohn Baldwin * If there is no pending AER command, drop it. 1059a15f7c96SJohn Baldwin * XXX: Should we queue these? 1060a15f7c96SJohn Baldwin */ 1061a15f7c96SJohn Baldwin if (ctrlr->aer_pending == 0) { 1062a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1063a15f7c96SJohn Baldwin nvmft_printf(ctrlr, 1064a15f7c96SJohn Baldwin "dropping AER type %u, info %#x, page %#x\n", 1065a15f7c96SJohn Baldwin type, info, log_page_id); 1066a15f7c96SJohn Baldwin return; 1067a15f7c96SJohn Baldwin } 1068a15f7c96SJohn Baldwin 1069a15f7c96SJohn Baldwin memset(&cpl, 0, sizeof(cpl)); 1070a15f7c96SJohn Baldwin cpl.cid = ctrlr->aer_cids[ctrlr->aer_cidx]; 1071a15f7c96SJohn Baldwin ctrlr->aer_pending--; 1072a15f7c96SJohn Baldwin ctrlr->aer_cidx = (ctrlr->aer_cidx + 1) % NVMFT_NUM_AER; 1073a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1074a15f7c96SJohn Baldwin 1075a15f7c96SJohn Baldwin cpl.cdw0 = htole32(NVMEF(NVME_ASYNC_EVENT_TYPE, type) | 1076a15f7c96SJohn Baldwin NVMEF(NVME_ASYNC_EVENT_INFO, info) | 1077a15f7c96SJohn Baldwin NVMEF(NVME_ASYNC_EVENT_LOG_PAGE_ID, log_page_id)); 1078a15f7c96SJohn Baldwin 1079a15f7c96SJohn Baldwin nvmft_send_response(ctrlr->admin, &cpl); 1080a15f7c96SJohn Baldwin } 1081a15f7c96SJohn Baldwin 1082a15f7c96SJohn Baldwin void 1083a15f7c96SJohn Baldwin nvmft_controller_lun_changed(struct nvmft_controller *ctrlr, int lun_id) 1084a15f7c96SJohn Baldwin { 1085a15f7c96SJohn Baldwin struct nvme_ns_list *nslist; 1086a15f7c96SJohn Baldwin uint32_t new_nsid, nsid; 1087a15f7c96SJohn Baldwin u_int i; 1088a15f7c96SJohn Baldwin 1089a15f7c96SJohn Baldwin new_nsid = lun_id + 1; 1090a15f7c96SJohn Baldwin 1091a15f7c96SJohn Baldwin mtx_lock(&ctrlr->lock); 1092a15f7c96SJohn Baldwin nslist = ctrlr->changed_ns; 1093a15f7c96SJohn Baldwin 1094a15f7c96SJohn Baldwin /* If the first entry is 0xffffffff, the list is already full. */ 1095a15f7c96SJohn Baldwin if (nslist->ns[0] != 0xffffffff) { 1096a15f7c96SJohn Baldwin /* Find the insertion point for this namespace ID. */ 1097a15f7c96SJohn Baldwin for (i = 0; i < nitems(nslist->ns); i++) { 1098a15f7c96SJohn Baldwin nsid = le32toh(nslist->ns[i]); 1099a15f7c96SJohn Baldwin if (nsid == new_nsid) { 1100a15f7c96SJohn Baldwin /* Already reported, nothing to do. */ 1101a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1102a15f7c96SJohn Baldwin return; 1103a15f7c96SJohn Baldwin } 1104a15f7c96SJohn Baldwin 1105a15f7c96SJohn Baldwin if (nsid == 0 || nsid > new_nsid) 1106a15f7c96SJohn Baldwin break; 1107a15f7c96SJohn Baldwin } 1108a15f7c96SJohn Baldwin 1109a15f7c96SJohn Baldwin if (nslist->ns[nitems(nslist->ns) - 1] != htole32(0)) { 1110a15f7c96SJohn Baldwin /* List is full. */ 1111a15f7c96SJohn Baldwin memset(ctrlr->changed_ns, 0, 1112a15f7c96SJohn Baldwin sizeof(*ctrlr->changed_ns)); 1113a15f7c96SJohn Baldwin ctrlr->changed_ns->ns[0] = 0xffffffff; 1114a15f7c96SJohn Baldwin } else if (nslist->ns[i] == htole32(0)) { 1115a15f7c96SJohn Baldwin /* 1116a15f7c96SJohn Baldwin * Optimize case where this ID is appended to 1117a15f7c96SJohn Baldwin * the end. 1118a15f7c96SJohn Baldwin */ 1119a15f7c96SJohn Baldwin nslist->ns[i] = htole32(new_nsid); 1120a15f7c96SJohn Baldwin } else { 1121a15f7c96SJohn Baldwin memmove(&nslist->ns[i + 1], &nslist->ns[i], 1122a15f7c96SJohn Baldwin (nitems(nslist->ns) - i - 1) * 1123a15f7c96SJohn Baldwin sizeof(nslist->ns[0])); 1124a15f7c96SJohn Baldwin nslist->ns[i] = htole32(new_nsid); 1125a15f7c96SJohn Baldwin } 1126a15f7c96SJohn Baldwin } 1127a15f7c96SJohn Baldwin 1128a15f7c96SJohn Baldwin if (ctrlr->changed_ns_reported) { 1129a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1130a15f7c96SJohn Baldwin return; 1131a15f7c96SJohn Baldwin } 1132a15f7c96SJohn Baldwin ctrlr->changed_ns_reported = true; 1133a15f7c96SJohn Baldwin mtx_unlock(&ctrlr->lock); 1134a15f7c96SJohn Baldwin 1135a15f7c96SJohn Baldwin nvmft_report_aer(ctrlr, NVME_ASYNC_EVENT_NS_ATTRIBUTE, 0x2, 0x0, 1136a15f7c96SJohn Baldwin NVME_LOG_CHANGED_NAMESPACE); 1137a15f7c96SJohn Baldwin } 1138