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_retry(const struct nvme_completion *cpl) 42bb0ec6b3SJim Harris { 43bb0ec6b3SJim Harris /* 44bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 45bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 46bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 47bb0ec6b3SJim Harris * look at the DNR bit. 48bb0ec6b3SJim Harris */ 49*cf81529cSJim Harris switch (cpl->status.sct) { 50bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 51*cf81529cSJim Harris switch (cpl->status.sc) { 52448195e7SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 53448195e7SJim Harris return (1); 54bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 55*cf81529cSJim Harris if (cpl->status.dnr) 56bb0ec6b3SJim Harris return (0); 57bb0ec6b3SJim Harris else 58bb0ec6b3SJim Harris return (1); 59bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 60bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 61bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 62bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 63bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 64bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 65bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 66bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 67bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 68bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 69bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 70bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 71bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 72bb0ec6b3SJim Harris default: 73bb0ec6b3SJim Harris return (0); 74bb0ec6b3SJim Harris } 75bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 76bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 77bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 78bb0ec6b3SJim Harris default: 79bb0ec6b3SJim Harris return (0); 80bb0ec6b3SJim Harris } 81bb0ec6b3SJim Harris } 82bb0ec6b3SJim Harris 8321b6da58SJim Harris static void 8421b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 8521b6da58SJim Harris uint16_t cid) 86bb0ec6b3SJim Harris { 879eb93f29SJim Harris 88f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map); 89f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map); 90f2b19f67SJim Harris 91f2b19f67SJim Harris bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp, 92f2b19f67SJim Harris sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0); 93f2b19f67SJim Harris 9412d191ecSJim Harris callout_init(&tr->timer, 1); 9521b6da58SJim Harris tr->cid = cid; 96d281e8fbSJim Harris tr->qpair = qpair; 97bb0ec6b3SJim Harris } 98bb0ec6b3SJim Harris 996cb06070SJim Harris static void 1006cb06070SJim Harris nvme_qpair_complete_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 1016cb06070SJim Harris struct nvme_completion *cpl, boolean_t print_on_error) 102bb0ec6b3SJim Harris { 103ad697276SJim Harris struct nvme_request *req; 104bb0ec6b3SJim Harris boolean_t retry, error; 105bb0ec6b3SJim Harris 106ad697276SJim Harris req = tr->req; 1076cb06070SJim Harris error = nvme_completion_is_error(cpl); 1086cb06070SJim Harris retry = error && nvme_completion_is_retry(cpl); 109ad697276SJim Harris 1106cb06070SJim Harris if (error && print_on_error) { 111bb0ec6b3SJim Harris nvme_dump_completion(cpl); 1126cb06070SJim Harris nvme_dump_command(&req->cmd); 113bb0ec6b3SJim Harris } 114bb0ec6b3SJim Harris 115bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 116bb0ec6b3SJim Harris 1176cb06070SJim Harris KASSERT(cpl->cid == req->cmd.cid, ("cpl cid does not match cmd cid\n")); 118bb0ec6b3SJim Harris 119ad697276SJim Harris if (req->cb_fn && !retry) 120ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 121bb0ec6b3SJim Harris 122bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 123bb0ec6b3SJim Harris callout_stop(&tr->timer); 124bb0ec6b3SJim Harris 125bb0ec6b3SJim Harris if (retry) 126b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 127bb0ec6b3SJim Harris else { 1285fa5cc5fSJim Harris if (req->payload_size > 0 || req->uio != NULL) 129f2b19f67SJim Harris bus_dmamap_unload(qpair->dma_tag, 130f2b19f67SJim Harris tr->payload_dma_map); 131bb0ec6b3SJim Harris 132ad697276SJim Harris nvme_free_request(req); 1330a0b08ccSJim Harris tr->req = NULL; 13421b6da58SJim Harris 13565c2474eSJim Harris TAILQ_REMOVE(&qpair->outstanding_tr, tr, tailq); 13665c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 1370f71ecf7SJim Harris 138f37c22a3SJim Harris /* 139f37c22a3SJim Harris * If the controller is in the middle of resetting, don't 140f37c22a3SJim Harris * try to submit queued requests here - let the reset logic 141f37c22a3SJim Harris * handle that instead. 142f37c22a3SJim Harris */ 143f37c22a3SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req) && 144f37c22a3SJim Harris !qpair->ctrlr->is_resetting) { 1450f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 1460f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 147d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 1480f71ecf7SJim Harris } 149c2e83b40SJim Harris } 150bb0ec6b3SJim Harris 151bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 1526cb06070SJim Harris } 1536cb06070SJim Harris 154b846efd7SJim Harris static void 155b846efd7SJim Harris nvme_qpair_manual_complete_tracker(struct nvme_qpair *qpair, 156b846efd7SJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, 157b846efd7SJim Harris boolean_t print_on_error) 158b846efd7SJim Harris { 159b846efd7SJim Harris struct nvme_completion cpl; 160b846efd7SJim Harris 161b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 162b846efd7SJim Harris cpl.sqid = qpair->id; 163b846efd7SJim Harris cpl.cid = tr->cid; 164*cf81529cSJim Harris cpl.status.sct = sct; 165*cf81529cSJim Harris cpl.status.sc = sc; 166b846efd7SJim Harris nvme_qpair_complete_tracker(qpair, tr, &cpl, print_on_error); 167b846efd7SJim Harris } 168b846efd7SJim Harris 1696cb06070SJim Harris void 1706cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 1716cb06070SJim Harris { 1726cb06070SJim Harris struct nvme_tracker *tr; 1736cb06070SJim Harris struct nvme_completion *cpl; 1746cb06070SJim Harris 1756cb06070SJim Harris qpair->num_intr_handler_calls++; 1766cb06070SJim Harris 177b846efd7SJim Harris if (!qpair->is_enabled) 178b846efd7SJim Harris /* 179b846efd7SJim Harris * qpair is not enabled, likely because a controller reset is 180b846efd7SJim Harris * is in progress. Ignore the interrupt - any I/O that was 181b846efd7SJim Harris * associated with this interrupt will get retried when the 182b846efd7SJim Harris * reset is complete. 183b846efd7SJim Harris */ 184b846efd7SJim Harris return; 185b846efd7SJim Harris 1866cb06070SJim Harris while (1) { 1876cb06070SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 1886cb06070SJim Harris 189*cf81529cSJim Harris if (cpl->status.p != qpair->phase) 1906cb06070SJim Harris break; 1916cb06070SJim Harris 1926cb06070SJim Harris tr = qpair->act_tr[cpl->cid]; 1936cb06070SJim Harris 1946cb06070SJim Harris if (tr != NULL) { 1956cb06070SJim Harris nvme_qpair_complete_tracker(qpair, tr, cpl, TRUE); 1966cb06070SJim Harris qpair->sq_head = cpl->sqhd; 1976cb06070SJim Harris } else { 1986cb06070SJim Harris printf("cpl does not map to outstanding cmd\n"); 1996cb06070SJim Harris nvme_dump_completion(cpl); 2006cb06070SJim Harris KASSERT(0, ("received completion for unknown cmd\n")); 2016cb06070SJim Harris } 202bb0ec6b3SJim Harris 203bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 204bb0ec6b3SJim Harris qpair->cq_head = 0; 205bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 206bb0ec6b3SJim Harris } 207bb0ec6b3SJim Harris 208bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 209bb0ec6b3SJim Harris qpair->cq_head); 210bb0ec6b3SJim Harris } 211bb0ec6b3SJim Harris } 212bb0ec6b3SJim Harris 213bb0ec6b3SJim Harris static void 214bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 215bb0ec6b3SJim Harris { 216bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 217bb0ec6b3SJim Harris 218bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 219bb0ec6b3SJim Harris } 220bb0ec6b3SJim Harris 221bb0ec6b3SJim Harris void 222bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 22321b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 22421b6da58SJim Harris uint32_t max_xfer_size, struct nvme_controller *ctrlr) 225bb0ec6b3SJim Harris { 22621b6da58SJim Harris struct nvme_tracker *tr; 22721b6da58SJim Harris uint32_t i; 228bb0ec6b3SJim Harris 229bb0ec6b3SJim Harris qpair->id = id; 230bb0ec6b3SJim Harris qpair->vector = vector; 231bb0ec6b3SJim Harris qpair->num_entries = num_entries; 2320f71ecf7SJim Harris #ifdef CHATHAM2 2330f71ecf7SJim Harris /* 2340f71ecf7SJim Harris * Chatham prototype board starts having issues at higher queue 2350f71ecf7SJim Harris * depths. So use a conservative estimate here of no more than 64 2360f71ecf7SJim Harris * outstanding I/O per queue at any one point. 2370f71ecf7SJim Harris */ 2380f71ecf7SJim Harris if (pci_get_devid(ctrlr->dev) == CHATHAM_PCI_ID) 2390f71ecf7SJim Harris num_trackers = min(num_trackers, 64); 2400f71ecf7SJim Harris #endif 2410f71ecf7SJim Harris qpair->num_trackers = num_trackers; 242bb0ec6b3SJim Harris qpair->max_xfer_size = max_xfer_size; 243bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 244bb0ec6b3SJim Harris 245bb0ec6b3SJim Harris if (ctrlr->msix_enabled) { 246bb0ec6b3SJim Harris 247bb0ec6b3SJim Harris /* 248bb0ec6b3SJim Harris * MSI-X vector resource IDs start at 1, so we add one to 249bb0ec6b3SJim Harris * the queue's vector to get the corresponding rid to use. 250bb0ec6b3SJim Harris */ 251bb0ec6b3SJim Harris qpair->rid = vector + 1; 252bb0ec6b3SJim Harris 253bb0ec6b3SJim Harris qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 254bb0ec6b3SJim Harris &qpair->rid, RF_ACTIVE); 255bb0ec6b3SJim Harris 256bb0ec6b3SJim Harris bus_setup_intr(ctrlr->dev, qpair->res, 257bb0ec6b3SJim Harris INTR_TYPE_MISC | INTR_MPSAFE, NULL, 258bb0ec6b3SJim Harris nvme_qpair_msix_handler, qpair, &qpair->tag); 259bb0ec6b3SJim Harris } 260bb0ec6b3SJim Harris 261bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 262bb0ec6b3SJim Harris 263bb0ec6b3SJim Harris bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 264bb0ec6b3SJim Harris sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR, 265bb0ec6b3SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size, 266bb0ec6b3SJim Harris (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0, 267bb0ec6b3SJim Harris NULL, NULL, &qpair->dma_tag); 268bb0ec6b3SJim Harris 269bb0ec6b3SJim Harris qpair->num_cmds = 0; 2706568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 271bb0ec6b3SJim Harris 272bb0ec6b3SJim Harris /* TODO: error checking on contigmalloc, bus_dmamap_load calls */ 273bb0ec6b3SJim Harris qpair->cmd = contigmalloc(qpair->num_entries * 274bb0ec6b3SJim Harris sizeof(struct nvme_command), M_NVME, M_ZERO | M_NOWAIT, 275bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 276bb0ec6b3SJim Harris qpair->cpl = contigmalloc(qpair->num_entries * 277bb0ec6b3SJim Harris sizeof(struct nvme_completion), M_NVME, M_ZERO | M_NOWAIT, 278bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 279bb0ec6b3SJim Harris 280bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map); 281bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map); 282bb0ec6b3SJim Harris 283bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map, 284bb0ec6b3SJim Harris qpair->cmd, qpair->num_entries * sizeof(struct nvme_command), 285bb0ec6b3SJim Harris nvme_single_map, &qpair->cmd_bus_addr, 0); 286bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map, 287bb0ec6b3SJim Harris qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion), 288bb0ec6b3SJim Harris nvme_single_map, &qpair->cpl_bus_addr, 0); 289bb0ec6b3SJim Harris 290bb0ec6b3SJim Harris qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl); 291bb0ec6b3SJim Harris qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl); 292bb0ec6b3SJim Harris 29365c2474eSJim Harris TAILQ_INIT(&qpair->free_tr); 29465c2474eSJim Harris TAILQ_INIT(&qpair->outstanding_tr); 2950f71ecf7SJim Harris STAILQ_INIT(&qpair->queued_req); 296bb0ec6b3SJim Harris 2970f71ecf7SJim Harris for (i = 0; i < qpair->num_trackers; i++) { 29821b6da58SJim Harris tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_NOWAIT); 29921b6da58SJim Harris 30021b6da58SJim Harris if (tr == NULL) { 30121b6da58SJim Harris printf("warning: nvme tracker malloc failed\n"); 30221b6da58SJim Harris break; 30321b6da58SJim Harris } 30421b6da58SJim Harris 30521b6da58SJim Harris nvme_qpair_construct_tracker(qpair, tr, i); 30665c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 30721b6da58SJim Harris } 30821b6da58SJim Harris 309bb0ec6b3SJim Harris qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries, 310bb0ec6b3SJim Harris M_NVME, M_ZERO | M_NOWAIT); 311bb0ec6b3SJim Harris } 312bb0ec6b3SJim Harris 313bb0ec6b3SJim Harris static void 314bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 315bb0ec6b3SJim Harris { 316bb0ec6b3SJim Harris struct nvme_tracker *tr; 317bb0ec6b3SJim Harris 318bb0ec6b3SJim Harris if (qpair->tag) 319bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 320bb0ec6b3SJim Harris 321bb0ec6b3SJim Harris if (qpair->res) 322bb0ec6b3SJim Harris bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 323bb0ec6b3SJim Harris rman_get_rid(qpair->res), qpair->res); 324bb0ec6b3SJim Harris 325bb0ec6b3SJim Harris if (qpair->dma_tag) 326bb0ec6b3SJim Harris bus_dma_tag_destroy(qpair->dma_tag); 327bb0ec6b3SJim Harris 328bb0ec6b3SJim Harris if (qpair->act_tr) 329bb0ec6b3SJim Harris free(qpair->act_tr, M_NVME); 330bb0ec6b3SJim Harris 33165c2474eSJim Harris while (!TAILQ_EMPTY(&qpair->free_tr)) { 33265c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 33365c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 334f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map); 335f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map); 336bb0ec6b3SJim Harris free(tr, M_NVME); 337bb0ec6b3SJim Harris } 338bb0ec6b3SJim Harris } 339bb0ec6b3SJim Harris 340b846efd7SJim Harris static void 341b846efd7SJim Harris nvme_admin_qpair_abort_aers(struct nvme_qpair *qpair) 342b846efd7SJim Harris { 343b846efd7SJim Harris struct nvme_tracker *tr; 344b846efd7SJim Harris 345b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 346b846efd7SJim Harris while (tr != NULL) { 347b846efd7SJim Harris if (tr->req->cmd.opc == NVME_OPC_ASYNC_EVENT_REQUEST) { 348b846efd7SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, 349b846efd7SJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_SQ_DELETION, 350b846efd7SJim Harris FALSE); 351b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 352b846efd7SJim Harris } else { 353b846efd7SJim Harris tr = TAILQ_NEXT(tr, tailq); 354b846efd7SJim Harris } 355b846efd7SJim Harris } 356b846efd7SJim Harris } 357b846efd7SJim Harris 358bb0ec6b3SJim Harris void 359bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 360bb0ec6b3SJim Harris { 361bb0ec6b3SJim Harris 362b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 363b846efd7SJim Harris 364bb0ec6b3SJim Harris /* 365bb0ec6b3SJim Harris * For NVMe, you don't send delete queue commands for the admin 366bb0ec6b3SJim Harris * queue, so we just need to unload and free the cmd and cpl memory. 367bb0ec6b3SJim Harris */ 368bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 369bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 370bb0ec6b3SJim Harris 371bb0ec6b3SJim Harris contigfree(qpair->cmd, 372bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 373bb0ec6b3SJim Harris 374bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 375bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 376bb0ec6b3SJim Harris contigfree(qpair->cpl, 377bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 378bb0ec6b3SJim Harris 379bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 380bb0ec6b3SJim Harris } 381bb0ec6b3SJim Harris 382bb0ec6b3SJim Harris static void 383bb0ec6b3SJim Harris nvme_free_cmd_ring(void *arg, const struct nvme_completion *status) 384bb0ec6b3SJim Harris { 385bb0ec6b3SJim Harris struct nvme_qpair *qpair; 386bb0ec6b3SJim Harris 387bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 388bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 389bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 390bb0ec6b3SJim Harris contigfree(qpair->cmd, 391bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 392bb0ec6b3SJim Harris qpair->cmd = NULL; 393bb0ec6b3SJim Harris } 394bb0ec6b3SJim Harris 395bb0ec6b3SJim Harris static void 396bb0ec6b3SJim Harris nvme_free_cpl_ring(void *arg, const struct nvme_completion *status) 397bb0ec6b3SJim Harris { 398bb0ec6b3SJim Harris struct nvme_qpair *qpair; 399bb0ec6b3SJim Harris 400bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 401bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 402bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 403bb0ec6b3SJim Harris contigfree(qpair->cpl, 404bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 405bb0ec6b3SJim Harris qpair->cpl = NULL; 406bb0ec6b3SJim Harris } 407bb0ec6b3SJim Harris 408bb0ec6b3SJim Harris void 409bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 410bb0ec6b3SJim Harris { 411bb0ec6b3SJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 412bb0ec6b3SJim Harris 413bb0ec6b3SJim Harris if (qpair->num_entries > 0) { 414bb0ec6b3SJim Harris 415bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_free_cmd_ring, 416bb0ec6b3SJim Harris qpair); 417bb0ec6b3SJim Harris /* Spin until free_cmd_ring sets qpair->cmd to NULL. */ 418bb0ec6b3SJim Harris while (qpair->cmd) 419bb0ec6b3SJim Harris DELAY(5); 420bb0ec6b3SJim Harris 421bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_free_cpl_ring, 422bb0ec6b3SJim Harris qpair); 423bb0ec6b3SJim Harris /* Spin until free_cpl_ring sets qpair->cmd to NULL. */ 424bb0ec6b3SJim Harris while (qpair->cpl) 425bb0ec6b3SJim Harris DELAY(5); 426bb0ec6b3SJim Harris 427bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 428bb0ec6b3SJim Harris } 429bb0ec6b3SJim Harris } 430bb0ec6b3SJim Harris 431bb0ec6b3SJim Harris static void 4320a0b08ccSJim Harris nvme_abort_complete(void *arg, const struct nvme_completion *status) 4330a0b08ccSJim Harris { 434879de699SJim Harris struct nvme_tracker *tr = arg; 435879de699SJim Harris 436879de699SJim Harris /* 437879de699SJim Harris * If cdw0 == 1, the controller was not able to abort the command 438879de699SJim Harris * we requested. We still need to check the active tracker array, 439879de699SJim Harris * to cover race where I/O timed out at same time controller was 440879de699SJim Harris * completing the I/O. 441879de699SJim Harris */ 442879de699SJim Harris if (status->cdw0 == 1 && tr->qpair->act_tr[tr->cid] != NULL) { 443879de699SJim Harris /* 444879de699SJim Harris * An I/O has timed out, and the controller was unable to 445879de699SJim Harris * abort it for some reason. Construct a fake completion 446879de699SJim Harris * status, and then complete the I/O's tracker manually. 447879de699SJim Harris */ 448879de699SJim Harris printf("abort command failed, aborting command manually\n"); 449b846efd7SJim Harris nvme_qpair_manual_complete_tracker(tr->qpair, tr, 4500a0b08ccSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST, TRUE); 451879de699SJim Harris } 452879de699SJim Harris } 453879de699SJim Harris 454879de699SJim Harris static void 455bb0ec6b3SJim Harris nvme_timeout(void *arg) 456bb0ec6b3SJim Harris { 457448195e7SJim Harris struct nvme_tracker *tr = arg; 45812d191ecSJim Harris struct nvme_qpair *qpair = tr->qpair; 45912d191ecSJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 46012d191ecSJim Harris union csts_register csts; 461448195e7SJim Harris 46248ce3178SJim Harris /* Read csts to get value of cfs - controller fatal status. */ 46312d191ecSJim Harris csts.raw = nvme_mmio_read_4(ctrlr, csts); 46412d191ecSJim Harris 46548ce3178SJim Harris if (ctrlr->enable_aborts && csts.bits.cfs == 0) { 46648ce3178SJim Harris /* 46748ce3178SJim Harris * If aborts are enabled, only use them if the controller is 46848ce3178SJim Harris * not reporting fatal status. 46948ce3178SJim Harris */ 47012d191ecSJim Harris nvme_ctrlr_cmd_abort(ctrlr, tr->cid, qpair->id, 471879de699SJim Harris nvme_abort_complete, tr); 47248ce3178SJim Harris } else 47348ce3178SJim Harris nvme_ctrlr_reset(ctrlr); 474bb0ec6b3SJim Harris } 475bb0ec6b3SJim Harris 476bb0ec6b3SJim Harris void 477b846efd7SJim Harris nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr) 478bb0ec6b3SJim Harris { 479ad697276SJim Harris struct nvme_request *req; 48094143332SJim Harris struct nvme_controller *ctrlr; 481bb0ec6b3SJim Harris 482b846efd7SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 483b846efd7SJim Harris 484ad697276SJim Harris req = tr->req; 485ad697276SJim Harris req->cmd.cid = tr->cid; 486bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 48794143332SJim Harris ctrlr = qpair->ctrlr; 488bb0ec6b3SJim Harris 48994143332SJim Harris if (req->timeout) 490633c5729SJim Harris #if __FreeBSD_version >= 800030 49194143332SJim Harris callout_reset_curcpu(&tr->timer, ctrlr->timeout_period * hz, 492633c5729SJim Harris nvme_timeout, tr); 493633c5729SJim Harris #else 49494143332SJim Harris callout_reset(&tr->timer, ctrlr->timeout_period * hz, 49594143332SJim Harris nvme_timeout, tr); 496633c5729SJim Harris #endif 497bb0ec6b3SJim Harris 498bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 499ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 500bb0ec6b3SJim Harris 501bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 502bb0ec6b3SJim Harris qpair->sq_tail = 0; 503bb0ec6b3SJim Harris 504bb0ec6b3SJim Harris wmb(); 505bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl, 506bb0ec6b3SJim Harris qpair->sq_tail); 507bb0ec6b3SJim Harris 508bb0ec6b3SJim Harris qpair->num_cmds++; 509bb0ec6b3SJim Harris } 5105ae9ed68SJim Harris 511d6f54866SJim Harris static void 512d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 5135ae9ed68SJim Harris { 5145ae9ed68SJim Harris struct nvme_tracker *tr; 5155ae9ed68SJim Harris int err; 5165ae9ed68SJim Harris 517d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 5185ae9ed68SJim Harris 51965c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 52021b6da58SJim Harris 521b846efd7SJim Harris if (tr == NULL || !qpair->is_enabled) { 5220f71ecf7SJim Harris /* 523b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 524b846efd7SJim Harris * an in-progress controller-level reset. 525b846efd7SJim Harris * 526b846efd7SJim Harris * Put the request on the qpair's request queue to be processed 527b846efd7SJim Harris * when a tracker frees up via a command completion or when 528b846efd7SJim Harris * the controller reset is completed. 5290f71ecf7SJim Harris */ 5300f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 531d6f54866SJim Harris return; 53221b6da58SJim Harris } 53321b6da58SJim Harris 53465c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 53565c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 5365ae9ed68SJim Harris tr->req = req; 5375ae9ed68SJim Harris 5385ae9ed68SJim Harris if (req->uio == NULL) { 5395ae9ed68SJim Harris if (req->payload_size > 0) { 5405ae9ed68SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, 5415ae9ed68SJim Harris tr->payload_dma_map, req->payload, 5425ae9ed68SJim Harris req->payload_size, 5435ae9ed68SJim Harris nvme_payload_map, tr, 0); 5445ae9ed68SJim Harris if (err != 0) 5455ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5465ae9ed68SJim Harris } else 547b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 5485ae9ed68SJim Harris } else { 5495ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 5505ae9ed68SJim Harris tr->payload_dma_map, req->uio, 5515ae9ed68SJim Harris nvme_payload_map_uio, tr, 0); 5525ae9ed68SJim Harris if (err != 0) 5535ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 5545ae9ed68SJim Harris } 555d6f54866SJim Harris } 5565ae9ed68SJim Harris 557d6f54866SJim Harris void 558d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 559d6f54866SJim Harris { 560d6f54866SJim Harris 561d6f54866SJim Harris mtx_lock(&qpair->lock); 562d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 5635ae9ed68SJim Harris mtx_unlock(&qpair->lock); 5645ae9ed68SJim Harris } 565b846efd7SJim Harris 566b846efd7SJim Harris static void 567b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 568b846efd7SJim Harris { 569b846efd7SJim Harris 570b846efd7SJim Harris qpair->is_enabled = TRUE; 571b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 572b846efd7SJim Harris 573b846efd7SJim Harris /* 574b846efd7SJim Harris * First time through the completion queue, HW will set phase 575b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 576b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 577b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 578b846efd7SJim Harris * rolls over. 579b846efd7SJim Harris */ 580b846efd7SJim Harris qpair->phase = 1; 581b846efd7SJim Harris 582b846efd7SJim Harris memset(qpair->cmd, 0, 583b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 584b846efd7SJim Harris memset(qpair->cpl, 0, 585b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 586b846efd7SJim Harris } 587b846efd7SJim Harris 588b846efd7SJim Harris void 589b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 590b846efd7SJim Harris { 591b846efd7SJim Harris 592b846efd7SJim Harris nvme_qpair_enable(qpair); 593b846efd7SJim Harris } 594b846efd7SJim Harris 595b846efd7SJim Harris void 596b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 597b846efd7SJim Harris { 598b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 599b846efd7SJim Harris struct nvme_tracker *tr; 600b846efd7SJim Harris struct nvme_request *req; 601b846efd7SJim Harris 602b846efd7SJim Harris mtx_lock(&qpair->lock); 603b846efd7SJim Harris 604b846efd7SJim Harris nvme_qpair_enable(qpair); 605b846efd7SJim Harris 606f37c22a3SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) { 607f37c22a3SJim Harris device_printf(qpair->ctrlr->dev, 608f37c22a3SJim Harris "resubmitting outstanding i/o\n"); 609f37c22a3SJim Harris nvme_dump_command(&tr->req->cmd); 610b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 611f37c22a3SJim Harris } 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); 619f37c22a3SJim Harris device_printf(qpair->ctrlr->dev, 620f37c22a3SJim Harris "resubmitting queued i/o\n"); 621f37c22a3SJim Harris nvme_dump_command(&req->cmd); 622b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 623b846efd7SJim Harris } 624b846efd7SJim Harris 625b846efd7SJim Harris mtx_unlock(&qpair->lock); 626b846efd7SJim Harris } 627b846efd7SJim Harris 628b846efd7SJim Harris static void 629b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 630b846efd7SJim Harris { 631b846efd7SJim Harris struct nvme_tracker *tr; 632b846efd7SJim Harris 633b846efd7SJim Harris qpair->is_enabled = FALSE; 634b846efd7SJim Harris mtx_lock(&qpair->lock); 635b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 636b846efd7SJim Harris callout_stop(&tr->timer); 637b846efd7SJim Harris mtx_unlock(&qpair->lock); 638b846efd7SJim Harris } 639b846efd7SJim Harris 640b846efd7SJim Harris void 641b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 642b846efd7SJim Harris { 643b846efd7SJim Harris 644b846efd7SJim Harris nvme_qpair_disable(qpair); 645b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 646b846efd7SJim Harris } 647b846efd7SJim Harris 648b846efd7SJim Harris void 649b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 650b846efd7SJim Harris { 651b846efd7SJim Harris 652b846efd7SJim Harris nvme_qpair_disable(qpair); 653b846efd7SJim Harris } 654