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 145*f37c22a3SJim Harris /* 146*f37c22a3SJim Harris * If the controller is in the middle of resetting, don't 147*f37c22a3SJim Harris * try to submit queued requests here - let the reset logic 148*f37c22a3SJim Harris * handle that instead. 149*f37c22a3SJim Harris */ 150*f37c22a3SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req) && 151*f37c22a3SJim Harris !qpair->ctrlr->is_resetting) { 1520f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 1530f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 154d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 1550f71ecf7SJim Harris } 156c2e83b40SJim Harris } 157bb0ec6b3SJim Harris 158bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 1596cb06070SJim Harris } 1606cb06070SJim Harris 161b846efd7SJim Harris static void 162b846efd7SJim Harris nvme_qpair_manual_complete_tracker(struct nvme_qpair *qpair, 163b846efd7SJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, 164b846efd7SJim Harris boolean_t print_on_error) 165b846efd7SJim Harris { 166b846efd7SJim Harris struct nvme_completion cpl; 167b846efd7SJim Harris 168b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 169b846efd7SJim Harris cpl.sqid = qpair->id; 170b846efd7SJim Harris cpl.cid = tr->cid; 171b846efd7SJim Harris cpl.sf_sct = sct; 172b846efd7SJim Harris cpl.sf_sc = sc; 173b846efd7SJim Harris nvme_qpair_complete_tracker(qpair, tr, &cpl, print_on_error); 174b846efd7SJim Harris } 175b846efd7SJim Harris 1766cb06070SJim Harris void 1776cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 1786cb06070SJim Harris { 1796cb06070SJim Harris struct nvme_tracker *tr; 1806cb06070SJim Harris struct nvme_completion *cpl; 1816cb06070SJim Harris 1826cb06070SJim Harris qpair->num_intr_handler_calls++; 1836cb06070SJim Harris 184b846efd7SJim Harris if (!qpair->is_enabled) 185b846efd7SJim Harris /* 186b846efd7SJim Harris * qpair is not enabled, likely because a controller reset is 187b846efd7SJim Harris * is in progress. Ignore the interrupt - any I/O that was 188b846efd7SJim Harris * associated with this interrupt will get retried when the 189b846efd7SJim Harris * reset is complete. 190b846efd7SJim Harris */ 191b846efd7SJim Harris return; 192b846efd7SJim Harris 1936cb06070SJim Harris while (1) { 1946cb06070SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 1956cb06070SJim Harris 1966cb06070SJim Harris if (cpl->p != qpair->phase) 1976cb06070SJim Harris break; 1986cb06070SJim Harris 1996cb06070SJim Harris tr = qpair->act_tr[cpl->cid]; 2006cb06070SJim Harris 2016cb06070SJim Harris if (tr != NULL) { 2026cb06070SJim Harris nvme_qpair_complete_tracker(qpair, tr, cpl, TRUE); 2036cb06070SJim Harris qpair->sq_head = cpl->sqhd; 2046cb06070SJim Harris } else { 2056cb06070SJim Harris printf("cpl does not map to outstanding cmd\n"); 2066cb06070SJim Harris nvme_dump_completion(cpl); 2076cb06070SJim Harris KASSERT(0, ("received completion for unknown cmd\n")); 2086cb06070SJim Harris } 209bb0ec6b3SJim Harris 210bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 211bb0ec6b3SJim Harris qpair->cq_head = 0; 212bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 213bb0ec6b3SJim Harris } 214bb0ec6b3SJim Harris 215bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 216bb0ec6b3SJim Harris qpair->cq_head); 217bb0ec6b3SJim Harris } 218bb0ec6b3SJim Harris } 219bb0ec6b3SJim Harris 220bb0ec6b3SJim Harris static void 221bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 222bb0ec6b3SJim Harris { 223bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 224bb0ec6b3SJim Harris 225bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 226bb0ec6b3SJim Harris } 227bb0ec6b3SJim Harris 228bb0ec6b3SJim Harris void 229bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 23021b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 23121b6da58SJim Harris uint32_t max_xfer_size, struct nvme_controller *ctrlr) 232bb0ec6b3SJim Harris { 23321b6da58SJim Harris struct nvme_tracker *tr; 23421b6da58SJim Harris uint32_t i; 235bb0ec6b3SJim Harris 236bb0ec6b3SJim Harris qpair->id = id; 237bb0ec6b3SJim Harris qpair->vector = vector; 238bb0ec6b3SJim Harris qpair->num_entries = num_entries; 2390f71ecf7SJim Harris #ifdef CHATHAM2 2400f71ecf7SJim Harris /* 2410f71ecf7SJim Harris * Chatham prototype board starts having issues at higher queue 2420f71ecf7SJim Harris * depths. So use a conservative estimate here of no more than 64 2430f71ecf7SJim Harris * outstanding I/O per queue at any one point. 2440f71ecf7SJim Harris */ 2450f71ecf7SJim Harris if (pci_get_devid(ctrlr->dev) == CHATHAM_PCI_ID) 2460f71ecf7SJim Harris num_trackers = min(num_trackers, 64); 2470f71ecf7SJim Harris #endif 2480f71ecf7SJim Harris qpair->num_trackers = num_trackers; 249bb0ec6b3SJim Harris qpair->max_xfer_size = max_xfer_size; 250bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 251bb0ec6b3SJim Harris 252bb0ec6b3SJim Harris if (ctrlr->msix_enabled) { 253bb0ec6b3SJim Harris 254bb0ec6b3SJim Harris /* 255bb0ec6b3SJim Harris * MSI-X vector resource IDs start at 1, so we add one to 256bb0ec6b3SJim Harris * the queue's vector to get the corresponding rid to use. 257bb0ec6b3SJim Harris */ 258bb0ec6b3SJim Harris qpair->rid = vector + 1; 259bb0ec6b3SJim Harris 260bb0ec6b3SJim Harris qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 261bb0ec6b3SJim Harris &qpair->rid, RF_ACTIVE); 262bb0ec6b3SJim Harris 263bb0ec6b3SJim Harris bus_setup_intr(ctrlr->dev, qpair->res, 264bb0ec6b3SJim Harris INTR_TYPE_MISC | INTR_MPSAFE, NULL, 265bb0ec6b3SJim Harris nvme_qpair_msix_handler, qpair, &qpair->tag); 266bb0ec6b3SJim Harris } 267bb0ec6b3SJim Harris 268bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 269bb0ec6b3SJim Harris 270bb0ec6b3SJim Harris bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 271bb0ec6b3SJim Harris sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR, 272bb0ec6b3SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size, 273bb0ec6b3SJim Harris (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0, 274bb0ec6b3SJim Harris NULL, NULL, &qpair->dma_tag); 275bb0ec6b3SJim Harris 276bb0ec6b3SJim Harris qpair->num_cmds = 0; 2776568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 278bb0ec6b3SJim Harris 279bb0ec6b3SJim Harris /* TODO: error checking on contigmalloc, bus_dmamap_load calls */ 280bb0ec6b3SJim Harris qpair->cmd = contigmalloc(qpair->num_entries * 281bb0ec6b3SJim Harris sizeof(struct nvme_command), M_NVME, M_ZERO | M_NOWAIT, 282bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 283bb0ec6b3SJim Harris qpair->cpl = contigmalloc(qpair->num_entries * 284bb0ec6b3SJim Harris sizeof(struct nvme_completion), M_NVME, M_ZERO | M_NOWAIT, 285bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 286bb0ec6b3SJim Harris 287bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map); 288bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map); 289bb0ec6b3SJim Harris 290bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map, 291bb0ec6b3SJim Harris qpair->cmd, qpair->num_entries * sizeof(struct nvme_command), 292bb0ec6b3SJim Harris nvme_single_map, &qpair->cmd_bus_addr, 0); 293bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map, 294bb0ec6b3SJim Harris qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion), 295bb0ec6b3SJim Harris nvme_single_map, &qpair->cpl_bus_addr, 0); 296bb0ec6b3SJim Harris 297bb0ec6b3SJim Harris qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl); 298bb0ec6b3SJim Harris qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl); 299bb0ec6b3SJim Harris 30065c2474eSJim Harris TAILQ_INIT(&qpair->free_tr); 30165c2474eSJim Harris TAILQ_INIT(&qpair->outstanding_tr); 3020f71ecf7SJim Harris STAILQ_INIT(&qpair->queued_req); 303bb0ec6b3SJim Harris 3040f71ecf7SJim Harris for (i = 0; i < qpair->num_trackers; i++) { 30521b6da58SJim Harris tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_NOWAIT); 30621b6da58SJim Harris 30721b6da58SJim Harris if (tr == NULL) { 30821b6da58SJim Harris printf("warning: nvme tracker malloc failed\n"); 30921b6da58SJim Harris break; 31021b6da58SJim Harris } 31121b6da58SJim Harris 31221b6da58SJim Harris nvme_qpair_construct_tracker(qpair, tr, i); 31365c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 31421b6da58SJim Harris } 31521b6da58SJim Harris 316bb0ec6b3SJim Harris qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries, 317bb0ec6b3SJim Harris M_NVME, M_ZERO | M_NOWAIT); 318bb0ec6b3SJim Harris } 319bb0ec6b3SJim Harris 320bb0ec6b3SJim Harris static void 321bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 322bb0ec6b3SJim Harris { 323bb0ec6b3SJim Harris struct nvme_tracker *tr; 324bb0ec6b3SJim Harris 325bb0ec6b3SJim Harris if (qpair->tag) 326bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 327bb0ec6b3SJim Harris 328bb0ec6b3SJim Harris if (qpair->res) 329bb0ec6b3SJim Harris bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 330bb0ec6b3SJim Harris rman_get_rid(qpair->res), qpair->res); 331bb0ec6b3SJim Harris 332bb0ec6b3SJim Harris if (qpair->dma_tag) 333bb0ec6b3SJim Harris bus_dma_tag_destroy(qpair->dma_tag); 334bb0ec6b3SJim Harris 335bb0ec6b3SJim Harris if (qpair->act_tr) 336bb0ec6b3SJim Harris free(qpair->act_tr, M_NVME); 337bb0ec6b3SJim Harris 33865c2474eSJim Harris while (!TAILQ_EMPTY(&qpair->free_tr)) { 33965c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 34065c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 341f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map); 342f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map); 343bb0ec6b3SJim Harris free(tr, M_NVME); 344bb0ec6b3SJim Harris } 345bb0ec6b3SJim Harris } 346bb0ec6b3SJim Harris 347b846efd7SJim Harris static void 348b846efd7SJim Harris nvme_admin_qpair_abort_aers(struct nvme_qpair *qpair) 349b846efd7SJim Harris { 350b846efd7SJim Harris struct nvme_tracker *tr; 351b846efd7SJim Harris 352b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 353b846efd7SJim Harris while (tr != NULL) { 354b846efd7SJim Harris if (tr->req->cmd.opc == NVME_OPC_ASYNC_EVENT_REQUEST) { 355b846efd7SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, 356b846efd7SJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_SQ_DELETION, 357b846efd7SJim Harris FALSE); 358b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 359b846efd7SJim Harris } else { 360b846efd7SJim Harris tr = TAILQ_NEXT(tr, tailq); 361b846efd7SJim Harris } 362b846efd7SJim Harris } 363b846efd7SJim Harris } 364b846efd7SJim Harris 365bb0ec6b3SJim Harris void 366bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 367bb0ec6b3SJim Harris { 368bb0ec6b3SJim Harris 369b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 370b846efd7SJim Harris 371bb0ec6b3SJim Harris /* 372bb0ec6b3SJim Harris * For NVMe, you don't send delete queue commands for the admin 373bb0ec6b3SJim Harris * queue, so we just need to unload and free the cmd and cpl memory. 374bb0ec6b3SJim Harris */ 375bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 376bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 377bb0ec6b3SJim Harris 378bb0ec6b3SJim Harris contigfree(qpair->cmd, 379bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 380bb0ec6b3SJim Harris 381bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 382bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 383bb0ec6b3SJim Harris contigfree(qpair->cpl, 384bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 385bb0ec6b3SJim Harris 386bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 387bb0ec6b3SJim Harris } 388bb0ec6b3SJim Harris 389bb0ec6b3SJim Harris static void 390bb0ec6b3SJim Harris nvme_free_cmd_ring(void *arg, const struct nvme_completion *status) 391bb0ec6b3SJim Harris { 392bb0ec6b3SJim Harris struct nvme_qpair *qpair; 393bb0ec6b3SJim Harris 394bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 395bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 396bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 397bb0ec6b3SJim Harris contigfree(qpair->cmd, 398bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 399bb0ec6b3SJim Harris qpair->cmd = NULL; 400bb0ec6b3SJim Harris } 401bb0ec6b3SJim Harris 402bb0ec6b3SJim Harris static void 403bb0ec6b3SJim Harris nvme_free_cpl_ring(void *arg, const struct nvme_completion *status) 404bb0ec6b3SJim Harris { 405bb0ec6b3SJim Harris struct nvme_qpair *qpair; 406bb0ec6b3SJim Harris 407bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 408bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 409bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 410bb0ec6b3SJim Harris contigfree(qpair->cpl, 411bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 412bb0ec6b3SJim Harris qpair->cpl = NULL; 413bb0ec6b3SJim Harris } 414bb0ec6b3SJim Harris 415bb0ec6b3SJim Harris void 416bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 417bb0ec6b3SJim Harris { 418bb0ec6b3SJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 419bb0ec6b3SJim Harris 420bb0ec6b3SJim Harris if (qpair->num_entries > 0) { 421bb0ec6b3SJim Harris 422bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_free_cmd_ring, 423bb0ec6b3SJim Harris qpair); 424bb0ec6b3SJim Harris /* Spin until free_cmd_ring sets qpair->cmd to NULL. */ 425bb0ec6b3SJim Harris while (qpair->cmd) 426bb0ec6b3SJim Harris DELAY(5); 427bb0ec6b3SJim Harris 428bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_free_cpl_ring, 429bb0ec6b3SJim Harris qpair); 430bb0ec6b3SJim Harris /* Spin until free_cpl_ring sets qpair->cmd to NULL. */ 431bb0ec6b3SJim Harris while (qpair->cpl) 432bb0ec6b3SJim Harris DELAY(5); 433bb0ec6b3SJim Harris 434bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 435bb0ec6b3SJim Harris } 436bb0ec6b3SJim Harris } 437bb0ec6b3SJim Harris 438bb0ec6b3SJim Harris static void 4390a0b08ccSJim Harris nvme_abort_complete(void *arg, const struct nvme_completion *status) 4400a0b08ccSJim Harris { 441879de699SJim Harris struct nvme_tracker *tr = arg; 442879de699SJim Harris 443879de699SJim Harris /* 444879de699SJim Harris * If cdw0 == 1, the controller was not able to abort the command 445879de699SJim Harris * we requested. We still need to check the active tracker array, 446879de699SJim Harris * to cover race where I/O timed out at same time controller was 447879de699SJim Harris * completing the I/O. 448879de699SJim Harris */ 449879de699SJim Harris if (status->cdw0 == 1 && tr->qpair->act_tr[tr->cid] != NULL) { 450879de699SJim Harris /* 451879de699SJim Harris * An I/O has timed out, and the controller was unable to 452879de699SJim Harris * abort it for some reason. Construct a fake completion 453879de699SJim Harris * status, and then complete the I/O's tracker manually. 454879de699SJim Harris */ 455879de699SJim Harris printf("abort command failed, aborting command manually\n"); 456b846efd7SJim Harris nvme_qpair_manual_complete_tracker(tr->qpair, tr, 4570a0b08ccSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST, TRUE); 458879de699SJim Harris } 459879de699SJim Harris } 460879de699SJim Harris 461879de699SJim Harris static void 462bb0ec6b3SJim Harris nvme_timeout(void *arg) 463bb0ec6b3SJim Harris { 464448195e7SJim Harris struct nvme_tracker *tr = arg; 46512d191ecSJim Harris struct nvme_qpair *qpair = tr->qpair; 46612d191ecSJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 46712d191ecSJim Harris union csts_register csts; 468448195e7SJim Harris 46948ce3178SJim Harris /* Read csts to get value of cfs - controller fatal status. */ 47012d191ecSJim Harris csts.raw = nvme_mmio_read_4(ctrlr, csts); 47112d191ecSJim Harris 47248ce3178SJim Harris if (ctrlr->enable_aborts && csts.bits.cfs == 0) { 47348ce3178SJim Harris /* 47448ce3178SJim Harris * If aborts are enabled, only use them if the controller is 47548ce3178SJim Harris * not reporting fatal status. 47648ce3178SJim Harris */ 47712d191ecSJim Harris nvme_ctrlr_cmd_abort(ctrlr, tr->cid, qpair->id, 478879de699SJim Harris nvme_abort_complete, tr); 47948ce3178SJim Harris } else 48048ce3178SJim Harris nvme_ctrlr_reset(ctrlr); 481bb0ec6b3SJim Harris } 482bb0ec6b3SJim Harris 483bb0ec6b3SJim Harris void 484b846efd7SJim Harris nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr) 485bb0ec6b3SJim Harris { 486ad697276SJim Harris struct nvme_request *req; 48794143332SJim Harris struct nvme_controller *ctrlr; 488bb0ec6b3SJim Harris 489b846efd7SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 490b846efd7SJim Harris 491ad697276SJim Harris req = tr->req; 492ad697276SJim Harris req->cmd.cid = tr->cid; 493bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 49494143332SJim Harris ctrlr = qpair->ctrlr; 495bb0ec6b3SJim Harris 49694143332SJim Harris if (req->timeout) 497633c5729SJim Harris #if __FreeBSD_version >= 800030 49894143332SJim Harris callout_reset_curcpu(&tr->timer, ctrlr->timeout_period * hz, 499633c5729SJim Harris nvme_timeout, tr); 500633c5729SJim Harris #else 50194143332SJim Harris callout_reset(&tr->timer, ctrlr->timeout_period * hz, 50294143332SJim Harris nvme_timeout, tr); 503633c5729SJim Harris #endif 504bb0ec6b3SJim Harris 505bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 506ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 507bb0ec6b3SJim Harris 508bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 509bb0ec6b3SJim Harris qpair->sq_tail = 0; 510bb0ec6b3SJim Harris 511bb0ec6b3SJim Harris wmb(); 512bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl, 513bb0ec6b3SJim Harris qpair->sq_tail); 514bb0ec6b3SJim Harris 515bb0ec6b3SJim Harris qpair->num_cmds++; 516bb0ec6b3SJim Harris } 5175ae9ed68SJim Harris 518d6f54866SJim Harris static void 519d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 5205ae9ed68SJim Harris { 5215ae9ed68SJim Harris struct nvme_tracker *tr; 5225ae9ed68SJim Harris int err; 5235ae9ed68SJim Harris 524d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 5255ae9ed68SJim Harris 52665c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 52721b6da58SJim Harris 528b846efd7SJim Harris if (tr == NULL || !qpair->is_enabled) { 5290f71ecf7SJim Harris /* 530b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 531b846efd7SJim Harris * an in-progress controller-level reset. 532b846efd7SJim Harris * 533b846efd7SJim Harris * Put the request on the qpair's request queue to be processed 534b846efd7SJim Harris * when a tracker frees up via a command completion or when 535b846efd7SJim Harris * the controller reset is completed. 5360f71ecf7SJim Harris */ 5370f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 538d6f54866SJim Harris return; 53921b6da58SJim Harris } 54021b6da58SJim Harris 54165c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 54265c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 5435ae9ed68SJim Harris tr->req = req; 5445ae9ed68SJim Harris 5455ae9ed68SJim Harris if (req->uio == NULL) { 5465ae9ed68SJim Harris if (req->payload_size > 0) { 5475ae9ed68SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, 5485ae9ed68SJim Harris tr->payload_dma_map, req->payload, 5495ae9ed68SJim Harris req->payload_size, 5505ae9ed68SJim Harris nvme_payload_map, tr, 0); 5515ae9ed68SJim Harris if (err != 0) 5525ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5535ae9ed68SJim Harris } else 554b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 5555ae9ed68SJim Harris } else { 5565ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 5575ae9ed68SJim Harris tr->payload_dma_map, req->uio, 5585ae9ed68SJim Harris nvme_payload_map_uio, tr, 0); 5595ae9ed68SJim Harris if (err != 0) 5605ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5615ae9ed68SJim Harris } 562d6f54866SJim Harris } 5635ae9ed68SJim Harris 564d6f54866SJim Harris void 565d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 566d6f54866SJim Harris { 567d6f54866SJim Harris 568d6f54866SJim Harris mtx_lock(&qpair->lock); 569d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 5705ae9ed68SJim Harris mtx_unlock(&qpair->lock); 5715ae9ed68SJim Harris } 572b846efd7SJim Harris 573b846efd7SJim Harris static void 574b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 575b846efd7SJim Harris { 576b846efd7SJim Harris 577b846efd7SJim Harris qpair->is_enabled = TRUE; 578b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 579b846efd7SJim Harris 580b846efd7SJim Harris /* 581b846efd7SJim Harris * First time through the completion queue, HW will set phase 582b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 583b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 584b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 585b846efd7SJim Harris * rolls over. 586b846efd7SJim Harris */ 587b846efd7SJim Harris qpair->phase = 1; 588b846efd7SJim Harris 589b846efd7SJim Harris memset(qpair->cmd, 0, 590b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 591b846efd7SJim Harris memset(qpair->cpl, 0, 592b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 593b846efd7SJim Harris } 594b846efd7SJim Harris 595b846efd7SJim Harris void 596b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 597b846efd7SJim Harris { 598b846efd7SJim Harris 599b846efd7SJim Harris nvme_qpair_enable(qpair); 600b846efd7SJim Harris } 601b846efd7SJim Harris 602b846efd7SJim Harris void 603b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 604b846efd7SJim Harris { 605b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 606b846efd7SJim Harris struct nvme_tracker *tr; 607b846efd7SJim Harris struct nvme_request *req; 608b846efd7SJim Harris 609b846efd7SJim Harris mtx_lock(&qpair->lock); 610b846efd7SJim Harris 611b846efd7SJim Harris nvme_qpair_enable(qpair); 612b846efd7SJim Harris 613*f37c22a3SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) { 614*f37c22a3SJim Harris device_printf(qpair->ctrlr->dev, 615*f37c22a3SJim Harris "resubmitting outstanding i/o\n"); 616*f37c22a3SJim Harris nvme_dump_command(&tr->req->cmd); 617b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 618*f37c22a3SJim Harris } 619b846efd7SJim Harris 620b846efd7SJim Harris STAILQ_INIT(&temp); 621b846efd7SJim Harris STAILQ_SWAP(&qpair->queued_req, &temp, nvme_request); 622b846efd7SJim Harris 623b846efd7SJim Harris while (!STAILQ_EMPTY(&temp)) { 624b846efd7SJim Harris req = STAILQ_FIRST(&temp); 625b846efd7SJim Harris STAILQ_REMOVE_HEAD(&temp, stailq); 626*f37c22a3SJim Harris device_printf(qpair->ctrlr->dev, 627*f37c22a3SJim Harris "resubmitting queued i/o\n"); 628*f37c22a3SJim Harris nvme_dump_command(&req->cmd); 629b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 630b846efd7SJim Harris } 631b846efd7SJim Harris 632b846efd7SJim Harris mtx_unlock(&qpair->lock); 633b846efd7SJim Harris } 634b846efd7SJim Harris 635b846efd7SJim Harris static void 636b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 637b846efd7SJim Harris { 638b846efd7SJim Harris struct nvme_tracker *tr; 639b846efd7SJim Harris 640b846efd7SJim Harris qpair->is_enabled = FALSE; 641b846efd7SJim Harris mtx_lock(&qpair->lock); 642b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 643b846efd7SJim Harris callout_stop(&tr->timer); 644b846efd7SJim Harris mtx_unlock(&qpair->lock); 645b846efd7SJim Harris } 646b846efd7SJim Harris 647b846efd7SJim Harris void 648b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 649b846efd7SJim Harris { 650b846efd7SJim Harris 651b846efd7SJim Harris nvme_qpair_disable(qpair); 652b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 653b846efd7SJim Harris } 654b846efd7SJim Harris 655b846efd7SJim Harris void 656b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 657b846efd7SJim Harris { 658b846efd7SJim Harris 659b846efd7SJim Harris nvme_qpair_disable(qpair); 660b846efd7SJim Harris } 661