1bb0ec6b3SJim Harris /*- 2bb0ec6b3SJim Harris * Copyright (C) 2012 Intel Corporation 3bb0ec6b3SJim Harris * All rights reserved. 4bb0ec6b3SJim Harris * 5bb0ec6b3SJim Harris * Redistribution and use in source and binary forms, with or without 6bb0ec6b3SJim Harris * modification, are permitted provided that the following conditions 7bb0ec6b3SJim Harris * are met: 8bb0ec6b3SJim Harris * 1. Redistributions of source code must retain the above copyright 9bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer. 10bb0ec6b3SJim Harris * 2. Redistributions in binary form must reproduce the above copyright 11bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer in the 12bb0ec6b3SJim Harris * documentation and/or other materials provided with the distribution. 13bb0ec6b3SJim Harris * 14bb0ec6b3SJim Harris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15bb0ec6b3SJim Harris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16bb0ec6b3SJim Harris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17bb0ec6b3SJim Harris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18bb0ec6b3SJim Harris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19bb0ec6b3SJim Harris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20bb0ec6b3SJim Harris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21bb0ec6b3SJim Harris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22bb0ec6b3SJim Harris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23bb0ec6b3SJim Harris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24bb0ec6b3SJim Harris * SUCH DAMAGE. 25bb0ec6b3SJim Harris */ 26bb0ec6b3SJim Harris 27bb0ec6b3SJim Harris #include <sys/cdefs.h> 28bb0ec6b3SJim Harris __FBSDID("$FreeBSD$"); 29bb0ec6b3SJim Harris 30bb0ec6b3SJim Harris #include <sys/param.h> 31bb0ec6b3SJim Harris #include <sys/bus.h> 32bb0ec6b3SJim Harris 330f71ecf7SJim Harris #include <dev/pci/pcivar.h> 340f71ecf7SJim Harris 35bb0ec6b3SJim Harris #include "nvme_private.h" 36bb0ec6b3SJim Harris 37d6f54866SJim Harris static void _nvme_qpair_submit_request(struct nvme_qpair *qpair, 38d6f54866SJim Harris struct nvme_request *req); 39d6f54866SJim Harris 40bb0ec6b3SJim Harris static boolean_t 416cb06070SJim Harris nvme_completion_is_error(struct nvme_completion *cpl) 426cb06070SJim Harris { 436cb06070SJim Harris 446cb06070SJim Harris return (cpl->sf_sc != 0 || cpl->sf_sct != 0); 456cb06070SJim Harris } 466cb06070SJim Harris 476cb06070SJim Harris static boolean_t 486cb06070SJim Harris nvme_completion_is_retry(const struct nvme_completion *cpl) 49bb0ec6b3SJim Harris { 50bb0ec6b3SJim Harris /* 51bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 52bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 53bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 54bb0ec6b3SJim Harris * look at the DNR bit. 55bb0ec6b3SJim Harris */ 56bb0ec6b3SJim Harris switch (cpl->sf_sct) { 57bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 58bb0ec6b3SJim Harris switch (cpl->sf_sc) { 59448195e7SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 60448195e7SJim Harris return (1); 61bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 62bb0ec6b3SJim Harris if (cpl->sf_dnr) 63bb0ec6b3SJim Harris return (0); 64bb0ec6b3SJim Harris else 65bb0ec6b3SJim Harris return (1); 66bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 67bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 68bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 69bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 70bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 71bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 72bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 73bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 74bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 75bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 76bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 77bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 78bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 79bb0ec6b3SJim Harris default: 80bb0ec6b3SJim Harris return (0); 81bb0ec6b3SJim Harris } 82bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 83bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 84bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 85bb0ec6b3SJim Harris default: 86bb0ec6b3SJim Harris return (0); 87bb0ec6b3SJim Harris } 88bb0ec6b3SJim Harris } 89bb0ec6b3SJim Harris 9021b6da58SJim Harris static void 9121b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 9221b6da58SJim Harris uint16_t cid) 93bb0ec6b3SJim Harris { 949eb93f29SJim Harris 95f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map); 96f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map); 97f2b19f67SJim Harris 98f2b19f67SJim Harris bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp, 99f2b19f67SJim Harris sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0); 100f2b19f67SJim Harris 10112d191ecSJim Harris callout_init(&tr->timer, 1); 10221b6da58SJim Harris tr->cid = cid; 103d281e8fbSJim Harris tr->qpair = qpair; 104bb0ec6b3SJim Harris } 105bb0ec6b3SJim Harris 1066cb06070SJim Harris static void 1076cb06070SJim Harris nvme_qpair_complete_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 1086cb06070SJim Harris struct nvme_completion *cpl, boolean_t print_on_error) 109bb0ec6b3SJim Harris { 110ad697276SJim Harris struct nvme_request *req; 111bb0ec6b3SJim Harris boolean_t retry, error; 112bb0ec6b3SJim Harris 113ad697276SJim Harris req = tr->req; 1146cb06070SJim Harris error = nvme_completion_is_error(cpl); 1156cb06070SJim Harris retry = error && nvme_completion_is_retry(cpl); 116ad697276SJim Harris 1176cb06070SJim Harris if (error && print_on_error) { 118bb0ec6b3SJim Harris nvme_dump_completion(cpl); 1196cb06070SJim Harris nvme_dump_command(&req->cmd); 120bb0ec6b3SJim Harris } 121bb0ec6b3SJim Harris 122bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 123bb0ec6b3SJim Harris 1246cb06070SJim Harris KASSERT(cpl->cid == req->cmd.cid, ("cpl cid does not match cmd cid\n")); 125bb0ec6b3SJim Harris 126ad697276SJim Harris if (req->cb_fn && !retry) 127ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 128bb0ec6b3SJim Harris 129bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 130bb0ec6b3SJim Harris callout_stop(&tr->timer); 131bb0ec6b3SJim Harris 132bb0ec6b3SJim Harris if (retry) 133b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 134bb0ec6b3SJim Harris else { 1355fa5cc5fSJim Harris if (req->payload_size > 0 || req->uio != NULL) 136f2b19f67SJim Harris bus_dmamap_unload(qpair->dma_tag, 137f2b19f67SJim Harris tr->payload_dma_map); 138bb0ec6b3SJim Harris 139ad697276SJim Harris nvme_free_request(req); 1400a0b08ccSJim Harris tr->req = NULL; 14121b6da58SJim Harris 14265c2474eSJim Harris TAILQ_REMOVE(&qpair->outstanding_tr, tr, tailq); 14365c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 1440f71ecf7SJim Harris 1450f71ecf7SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req)) { 1460f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 1470f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 148d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 1490f71ecf7SJim Harris } 150c2e83b40SJim Harris } 151bb0ec6b3SJim Harris 152bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 1536cb06070SJim Harris } 1546cb06070SJim Harris 155b846efd7SJim Harris static void 156b846efd7SJim Harris nvme_qpair_manual_complete_tracker(struct nvme_qpair *qpair, 157b846efd7SJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, 158b846efd7SJim Harris boolean_t print_on_error) 159b846efd7SJim Harris { 160b846efd7SJim Harris struct nvme_completion cpl; 161b846efd7SJim Harris 162b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 163b846efd7SJim Harris cpl.sqid = qpair->id; 164b846efd7SJim Harris cpl.cid = tr->cid; 165b846efd7SJim Harris cpl.sf_sct = sct; 166b846efd7SJim Harris cpl.sf_sc = sc; 167b846efd7SJim Harris nvme_qpair_complete_tracker(qpair, tr, &cpl, print_on_error); 168b846efd7SJim Harris } 169b846efd7SJim Harris 1706cb06070SJim Harris void 1716cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 1726cb06070SJim Harris { 1736cb06070SJim Harris struct nvme_tracker *tr; 1746cb06070SJim Harris struct nvme_completion *cpl; 1756cb06070SJim Harris 1766cb06070SJim Harris qpair->num_intr_handler_calls++; 1776cb06070SJim Harris 178b846efd7SJim Harris if (!qpair->is_enabled) 179b846efd7SJim Harris /* 180b846efd7SJim Harris * qpair is not enabled, likely because a controller reset is 181b846efd7SJim Harris * is in progress. Ignore the interrupt - any I/O that was 182b846efd7SJim Harris * associated with this interrupt will get retried when the 183b846efd7SJim Harris * reset is complete. 184b846efd7SJim Harris */ 185b846efd7SJim Harris return; 186b846efd7SJim Harris 1876cb06070SJim Harris while (1) { 1886cb06070SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 1896cb06070SJim Harris 1906cb06070SJim Harris if (cpl->p != qpair->phase) 1916cb06070SJim Harris break; 1926cb06070SJim Harris 1936cb06070SJim Harris tr = qpair->act_tr[cpl->cid]; 1946cb06070SJim Harris 1956cb06070SJim Harris if (tr != NULL) { 1966cb06070SJim Harris nvme_qpair_complete_tracker(qpair, tr, cpl, TRUE); 1976cb06070SJim Harris qpair->sq_head = cpl->sqhd; 1986cb06070SJim Harris } else { 1996cb06070SJim Harris printf("cpl does not map to outstanding cmd\n"); 2006cb06070SJim Harris nvme_dump_completion(cpl); 2016cb06070SJim Harris KASSERT(0, ("received completion for unknown cmd\n")); 2026cb06070SJim Harris } 203bb0ec6b3SJim Harris 204bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 205bb0ec6b3SJim Harris qpair->cq_head = 0; 206bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 207bb0ec6b3SJim Harris } 208bb0ec6b3SJim Harris 209bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 210bb0ec6b3SJim Harris qpair->cq_head); 211bb0ec6b3SJim Harris } 212bb0ec6b3SJim Harris } 213bb0ec6b3SJim Harris 214bb0ec6b3SJim Harris static void 215bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 216bb0ec6b3SJim Harris { 217bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 218bb0ec6b3SJim Harris 219bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 220bb0ec6b3SJim Harris } 221bb0ec6b3SJim Harris 222bb0ec6b3SJim Harris void 223bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 22421b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 22521b6da58SJim Harris uint32_t max_xfer_size, struct nvme_controller *ctrlr) 226bb0ec6b3SJim Harris { 22721b6da58SJim Harris struct nvme_tracker *tr; 22821b6da58SJim Harris uint32_t i; 229bb0ec6b3SJim Harris 230bb0ec6b3SJim Harris qpair->id = id; 231bb0ec6b3SJim Harris qpair->vector = vector; 232bb0ec6b3SJim Harris qpair->num_entries = num_entries; 2330f71ecf7SJim Harris #ifdef CHATHAM2 2340f71ecf7SJim Harris /* 2350f71ecf7SJim Harris * Chatham prototype board starts having issues at higher queue 2360f71ecf7SJim Harris * depths. So use a conservative estimate here of no more than 64 2370f71ecf7SJim Harris * outstanding I/O per queue at any one point. 2380f71ecf7SJim Harris */ 2390f71ecf7SJim Harris if (pci_get_devid(ctrlr->dev) == CHATHAM_PCI_ID) 2400f71ecf7SJim Harris num_trackers = min(num_trackers, 64); 2410f71ecf7SJim Harris #endif 2420f71ecf7SJim Harris qpair->num_trackers = num_trackers; 243bb0ec6b3SJim Harris qpair->max_xfer_size = max_xfer_size; 244bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 245bb0ec6b3SJim Harris 246bb0ec6b3SJim Harris if (ctrlr->msix_enabled) { 247bb0ec6b3SJim Harris 248bb0ec6b3SJim Harris /* 249bb0ec6b3SJim Harris * MSI-X vector resource IDs start at 1, so we add one to 250bb0ec6b3SJim Harris * the queue's vector to get the corresponding rid to use. 251bb0ec6b3SJim Harris */ 252bb0ec6b3SJim Harris qpair->rid = vector + 1; 253bb0ec6b3SJim Harris 254bb0ec6b3SJim Harris qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 255bb0ec6b3SJim Harris &qpair->rid, RF_ACTIVE); 256bb0ec6b3SJim Harris 257bb0ec6b3SJim Harris bus_setup_intr(ctrlr->dev, qpair->res, 258bb0ec6b3SJim Harris INTR_TYPE_MISC | INTR_MPSAFE, NULL, 259bb0ec6b3SJim Harris nvme_qpair_msix_handler, qpair, &qpair->tag); 260bb0ec6b3SJim Harris } 261bb0ec6b3SJim Harris 262bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 263bb0ec6b3SJim Harris 264bb0ec6b3SJim Harris bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 265bb0ec6b3SJim Harris sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR, 266bb0ec6b3SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size, 267bb0ec6b3SJim Harris (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0, 268bb0ec6b3SJim Harris NULL, NULL, &qpair->dma_tag); 269bb0ec6b3SJim Harris 270bb0ec6b3SJim Harris qpair->num_cmds = 0; 2716568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 272bb0ec6b3SJim Harris 273bb0ec6b3SJim Harris /* TODO: error checking on contigmalloc, bus_dmamap_load calls */ 274bb0ec6b3SJim Harris qpair->cmd = contigmalloc(qpair->num_entries * 275bb0ec6b3SJim Harris sizeof(struct nvme_command), M_NVME, M_ZERO | M_NOWAIT, 276bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 277bb0ec6b3SJim Harris qpair->cpl = contigmalloc(qpair->num_entries * 278bb0ec6b3SJim Harris sizeof(struct nvme_completion), M_NVME, M_ZERO | M_NOWAIT, 279bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 280bb0ec6b3SJim Harris 281bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map); 282bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map); 283bb0ec6b3SJim Harris 284bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map, 285bb0ec6b3SJim Harris qpair->cmd, qpair->num_entries * sizeof(struct nvme_command), 286bb0ec6b3SJim Harris nvme_single_map, &qpair->cmd_bus_addr, 0); 287bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map, 288bb0ec6b3SJim Harris qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion), 289bb0ec6b3SJim Harris nvme_single_map, &qpair->cpl_bus_addr, 0); 290bb0ec6b3SJim Harris 291bb0ec6b3SJim Harris qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl); 292bb0ec6b3SJim Harris qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl); 293bb0ec6b3SJim Harris 29465c2474eSJim Harris TAILQ_INIT(&qpair->free_tr); 29565c2474eSJim Harris TAILQ_INIT(&qpair->outstanding_tr); 2960f71ecf7SJim Harris STAILQ_INIT(&qpair->queued_req); 297bb0ec6b3SJim Harris 2980f71ecf7SJim Harris for (i = 0; i < qpair->num_trackers; i++) { 29921b6da58SJim Harris tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_NOWAIT); 30021b6da58SJim Harris 30121b6da58SJim Harris if (tr == NULL) { 30221b6da58SJim Harris printf("warning: nvme tracker malloc failed\n"); 30321b6da58SJim Harris break; 30421b6da58SJim Harris } 30521b6da58SJim Harris 30621b6da58SJim Harris nvme_qpair_construct_tracker(qpair, tr, i); 30765c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 30821b6da58SJim Harris } 30921b6da58SJim Harris 310bb0ec6b3SJim Harris qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries, 311bb0ec6b3SJim Harris M_NVME, M_ZERO | M_NOWAIT); 312bb0ec6b3SJim Harris } 313bb0ec6b3SJim Harris 314bb0ec6b3SJim Harris static void 315bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 316bb0ec6b3SJim Harris { 317bb0ec6b3SJim Harris struct nvme_tracker *tr; 318bb0ec6b3SJim Harris 319bb0ec6b3SJim Harris if (qpair->tag) 320bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 321bb0ec6b3SJim Harris 322bb0ec6b3SJim Harris if (qpair->res) 323bb0ec6b3SJim Harris bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 324bb0ec6b3SJim Harris rman_get_rid(qpair->res), qpair->res); 325bb0ec6b3SJim Harris 326bb0ec6b3SJim Harris if (qpair->dma_tag) 327bb0ec6b3SJim Harris bus_dma_tag_destroy(qpair->dma_tag); 328bb0ec6b3SJim Harris 329bb0ec6b3SJim Harris if (qpair->act_tr) 330bb0ec6b3SJim Harris free(qpair->act_tr, M_NVME); 331bb0ec6b3SJim Harris 33265c2474eSJim Harris while (!TAILQ_EMPTY(&qpair->free_tr)) { 33365c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 33465c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 335f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map); 336f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map); 337bb0ec6b3SJim Harris free(tr, M_NVME); 338bb0ec6b3SJim Harris } 339bb0ec6b3SJim Harris } 340bb0ec6b3SJim Harris 341b846efd7SJim Harris static void 342b846efd7SJim Harris nvme_admin_qpair_abort_aers(struct nvme_qpair *qpair) 343b846efd7SJim Harris { 344b846efd7SJim Harris struct nvme_tracker *tr; 345b846efd7SJim Harris 346b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 347b846efd7SJim Harris while (tr != NULL) { 348b846efd7SJim Harris if (tr->req->cmd.opc == NVME_OPC_ASYNC_EVENT_REQUEST) { 349b846efd7SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, 350b846efd7SJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_SQ_DELETION, 351b846efd7SJim Harris FALSE); 352b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 353b846efd7SJim Harris } else { 354b846efd7SJim Harris tr = TAILQ_NEXT(tr, tailq); 355b846efd7SJim Harris } 356b846efd7SJim Harris } 357b846efd7SJim Harris } 358b846efd7SJim Harris 359bb0ec6b3SJim Harris void 360bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 361bb0ec6b3SJim Harris { 362bb0ec6b3SJim Harris 363b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 364b846efd7SJim Harris 365bb0ec6b3SJim Harris /* 366bb0ec6b3SJim Harris * For NVMe, you don't send delete queue commands for the admin 367bb0ec6b3SJim Harris * queue, so we just need to unload and free the cmd and cpl memory. 368bb0ec6b3SJim Harris */ 369bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 370bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 371bb0ec6b3SJim Harris 372bb0ec6b3SJim Harris contigfree(qpair->cmd, 373bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 374bb0ec6b3SJim Harris 375bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 376bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 377bb0ec6b3SJim Harris contigfree(qpair->cpl, 378bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 379bb0ec6b3SJim Harris 380bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 381bb0ec6b3SJim Harris } 382bb0ec6b3SJim Harris 383bb0ec6b3SJim Harris static void 384bb0ec6b3SJim Harris nvme_free_cmd_ring(void *arg, const struct nvme_completion *status) 385bb0ec6b3SJim Harris { 386bb0ec6b3SJim Harris struct nvme_qpair *qpair; 387bb0ec6b3SJim Harris 388bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 389bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 390bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 391bb0ec6b3SJim Harris contigfree(qpair->cmd, 392bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 393bb0ec6b3SJim Harris qpair->cmd = NULL; 394bb0ec6b3SJim Harris } 395bb0ec6b3SJim Harris 396bb0ec6b3SJim Harris static void 397bb0ec6b3SJim Harris nvme_free_cpl_ring(void *arg, const struct nvme_completion *status) 398bb0ec6b3SJim Harris { 399bb0ec6b3SJim Harris struct nvme_qpair *qpair; 400bb0ec6b3SJim Harris 401bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 402bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 403bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 404bb0ec6b3SJim Harris contigfree(qpair->cpl, 405bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 406bb0ec6b3SJim Harris qpair->cpl = NULL; 407bb0ec6b3SJim Harris } 408bb0ec6b3SJim Harris 409bb0ec6b3SJim Harris void 410bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 411bb0ec6b3SJim Harris { 412bb0ec6b3SJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 413bb0ec6b3SJim Harris 414bb0ec6b3SJim Harris if (qpair->num_entries > 0) { 415bb0ec6b3SJim Harris 416bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_free_cmd_ring, 417bb0ec6b3SJim Harris qpair); 418bb0ec6b3SJim Harris /* Spin until free_cmd_ring sets qpair->cmd to NULL. */ 419bb0ec6b3SJim Harris while (qpair->cmd) 420bb0ec6b3SJim Harris DELAY(5); 421bb0ec6b3SJim Harris 422bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_free_cpl_ring, 423bb0ec6b3SJim Harris qpair); 424bb0ec6b3SJim Harris /* Spin until free_cpl_ring sets qpair->cmd to NULL. */ 425bb0ec6b3SJim Harris while (qpair->cpl) 426bb0ec6b3SJim Harris DELAY(5); 427bb0ec6b3SJim Harris 428bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 429bb0ec6b3SJim Harris } 430bb0ec6b3SJim Harris } 431bb0ec6b3SJim Harris 432bb0ec6b3SJim Harris static void 4330a0b08ccSJim Harris nvme_abort_complete(void *arg, const struct nvme_completion *status) 4340a0b08ccSJim Harris { 435879de699SJim Harris struct nvme_tracker *tr = arg; 436879de699SJim Harris 437879de699SJim Harris /* 438879de699SJim Harris * If cdw0 == 1, the controller was not able to abort the command 439879de699SJim Harris * we requested. We still need to check the active tracker array, 440879de699SJim Harris * to cover race where I/O timed out at same time controller was 441879de699SJim Harris * completing the I/O. 442879de699SJim Harris */ 443879de699SJim Harris if (status->cdw0 == 1 && tr->qpair->act_tr[tr->cid] != NULL) { 444879de699SJim Harris /* 445879de699SJim Harris * An I/O has timed out, and the controller was unable to 446879de699SJim Harris * abort it for some reason. Construct a fake completion 447879de699SJim Harris * status, and then complete the I/O's tracker manually. 448879de699SJim Harris */ 449879de699SJim Harris printf("abort command failed, aborting command manually\n"); 450b846efd7SJim Harris nvme_qpair_manual_complete_tracker(tr->qpair, tr, 4510a0b08ccSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST, TRUE); 452879de699SJim Harris } 453879de699SJim Harris } 454879de699SJim Harris 455879de699SJim Harris static void 456bb0ec6b3SJim Harris nvme_timeout(void *arg) 457bb0ec6b3SJim Harris { 458448195e7SJim Harris struct nvme_tracker *tr = arg; 45912d191ecSJim Harris struct nvme_qpair *qpair = tr->qpair; 46012d191ecSJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 46112d191ecSJim Harris union csts_register csts; 462448195e7SJim Harris 46312d191ecSJim Harris csts.raw = nvme_mmio_read_4(ctrlr, csts); 46412d191ecSJim Harris if (csts.bits.cfs == 1) { 46512d191ecSJim Harris /* 46612d191ecSJim Harris * The controller is reporting fatal status. Don't bother 46712d191ecSJim Harris * trying to abort the timed out command - proceed 46812d191ecSJim Harris * immediately to a controller-level reset. 46912d191ecSJim Harris */ 47012d191ecSJim Harris device_printf(ctrlr->dev, 47112d191ecSJim Harris "controller reports fatal status, resetting...\n"); 47212d191ecSJim Harris nvme_ctrlr_reset(ctrlr); 47312d191ecSJim Harris return; 47412d191ecSJim Harris } 47512d191ecSJim Harris 47612d191ecSJim Harris nvme_ctrlr_cmd_abort(ctrlr, tr->cid, qpair->id, 477879de699SJim Harris nvme_abort_complete, tr); 478bb0ec6b3SJim Harris } 479bb0ec6b3SJim Harris 480bb0ec6b3SJim Harris void 481b846efd7SJim Harris nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr) 482bb0ec6b3SJim Harris { 483ad697276SJim Harris struct nvme_request *req; 484*94143332SJim Harris struct nvme_controller *ctrlr; 485bb0ec6b3SJim Harris 486b846efd7SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 487b846efd7SJim Harris 488ad697276SJim Harris req = tr->req; 489ad697276SJim Harris req->cmd.cid = tr->cid; 490bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 491*94143332SJim Harris ctrlr = qpair->ctrlr; 492bb0ec6b3SJim Harris 493*94143332SJim Harris if (req->timeout) 494633c5729SJim Harris #if __FreeBSD_version >= 800030 495*94143332SJim Harris callout_reset_curcpu(&tr->timer, ctrlr->timeout_period * hz, 496633c5729SJim Harris nvme_timeout, tr); 497633c5729SJim Harris #else 498*94143332SJim Harris callout_reset(&tr->timer, ctrlr->timeout_period * hz, 499*94143332SJim Harris nvme_timeout, tr); 500633c5729SJim Harris #endif 501bb0ec6b3SJim Harris 502bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 503ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 504bb0ec6b3SJim Harris 505bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 506bb0ec6b3SJim Harris qpair->sq_tail = 0; 507bb0ec6b3SJim Harris 508bb0ec6b3SJim Harris wmb(); 509bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl, 510bb0ec6b3SJim Harris qpair->sq_tail); 511bb0ec6b3SJim Harris 512bb0ec6b3SJim Harris qpair->num_cmds++; 513bb0ec6b3SJim Harris } 5145ae9ed68SJim Harris 515d6f54866SJim Harris static void 516d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 5175ae9ed68SJim Harris { 5185ae9ed68SJim Harris struct nvme_tracker *tr; 5195ae9ed68SJim Harris int err; 5205ae9ed68SJim Harris 521d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 5225ae9ed68SJim Harris 52365c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 52421b6da58SJim Harris 525b846efd7SJim Harris if (tr == NULL || !qpair->is_enabled) { 5260f71ecf7SJim Harris /* 527b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 528b846efd7SJim Harris * an in-progress controller-level reset. 529b846efd7SJim Harris * 530b846efd7SJim Harris * Put the request on the qpair's request queue to be processed 531b846efd7SJim Harris * when a tracker frees up via a command completion or when 532b846efd7SJim Harris * the controller reset is completed. 5330f71ecf7SJim Harris */ 5340f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 535d6f54866SJim Harris return; 53621b6da58SJim Harris } 53721b6da58SJim Harris 53865c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 53965c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 5405ae9ed68SJim Harris tr->req = req; 5415ae9ed68SJim Harris 5425ae9ed68SJim Harris if (req->uio == NULL) { 5435ae9ed68SJim Harris if (req->payload_size > 0) { 5445ae9ed68SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, 5455ae9ed68SJim Harris tr->payload_dma_map, req->payload, 5465ae9ed68SJim Harris req->payload_size, 5475ae9ed68SJim Harris nvme_payload_map, tr, 0); 5485ae9ed68SJim Harris if (err != 0) 5495ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5505ae9ed68SJim Harris } else 551b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 5525ae9ed68SJim Harris } else { 5535ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 5545ae9ed68SJim Harris tr->payload_dma_map, req->uio, 5555ae9ed68SJim Harris nvme_payload_map_uio, tr, 0); 5565ae9ed68SJim Harris if (err != 0) 5575ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5585ae9ed68SJim Harris } 559d6f54866SJim Harris } 5605ae9ed68SJim Harris 561d6f54866SJim Harris void 562d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 563d6f54866SJim Harris { 564d6f54866SJim Harris 565d6f54866SJim Harris mtx_lock(&qpair->lock); 566d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 5675ae9ed68SJim Harris mtx_unlock(&qpair->lock); 5685ae9ed68SJim Harris } 569b846efd7SJim Harris 570b846efd7SJim Harris static void 571b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 572b846efd7SJim Harris { 573b846efd7SJim Harris 574b846efd7SJim Harris qpair->is_enabled = TRUE; 575b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 576b846efd7SJim Harris 577b846efd7SJim Harris /* 578b846efd7SJim Harris * First time through the completion queue, HW will set phase 579b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 580b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 581b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 582b846efd7SJim Harris * rolls over. 583b846efd7SJim Harris */ 584b846efd7SJim Harris qpair->phase = 1; 585b846efd7SJim Harris 586b846efd7SJim Harris memset(qpair->cmd, 0, 587b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 588b846efd7SJim Harris memset(qpair->cpl, 0, 589b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 590b846efd7SJim Harris } 591b846efd7SJim Harris 592b846efd7SJim Harris void 593b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 594b846efd7SJim Harris { 595b846efd7SJim Harris 596b846efd7SJim Harris nvme_qpair_enable(qpair); 597b846efd7SJim Harris } 598b846efd7SJim Harris 599b846efd7SJim Harris void 600b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 601b846efd7SJim Harris { 602b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 603b846efd7SJim Harris struct nvme_tracker *tr; 604b846efd7SJim Harris struct nvme_request *req; 605b846efd7SJim Harris 606b846efd7SJim Harris mtx_lock(&qpair->lock); 607b846efd7SJim Harris 608b846efd7SJim Harris nvme_qpair_enable(qpair); 609b846efd7SJim Harris 610b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 611b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 612b846efd7SJim Harris 613b846efd7SJim Harris STAILQ_INIT(&temp); 614b846efd7SJim Harris STAILQ_SWAP(&qpair->queued_req, &temp, nvme_request); 615b846efd7SJim Harris 616b846efd7SJim Harris while (!STAILQ_EMPTY(&temp)) { 617b846efd7SJim Harris req = STAILQ_FIRST(&temp); 618b846efd7SJim Harris STAILQ_REMOVE_HEAD(&temp, stailq); 619b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 620b846efd7SJim Harris } 621b846efd7SJim Harris 622b846efd7SJim Harris mtx_unlock(&qpair->lock); 623b846efd7SJim Harris } 624b846efd7SJim Harris 625b846efd7SJim Harris static void 626b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 627b846efd7SJim Harris { 628b846efd7SJim Harris struct nvme_tracker *tr; 629b846efd7SJim Harris 630b846efd7SJim Harris qpair->is_enabled = FALSE; 631b846efd7SJim Harris mtx_lock(&qpair->lock); 632b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 633b846efd7SJim Harris callout_stop(&tr->timer); 634b846efd7SJim Harris mtx_unlock(&qpair->lock); 635b846efd7SJim Harris } 636b846efd7SJim Harris 637b846efd7SJim Harris void 638b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 639b846efd7SJim Harris { 640b846efd7SJim Harris 641b846efd7SJim Harris nvme_qpair_disable(qpair); 642b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 643b846efd7SJim Harris } 644b846efd7SJim Harris 645b846efd7SJim Harris void 646b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 647b846efd7SJim Harris { 648b846efd7SJim Harris 649b846efd7SJim Harris nvme_qpair_disable(qpair); 650b846efd7SJim Harris } 651