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 33bb0ec6b3SJim Harris #include "nvme_private.h" 34bb0ec6b3SJim Harris 35bb0ec6b3SJim Harris static boolean_t 36bb0ec6b3SJim Harris nvme_completion_check_retry(const struct nvme_completion *cpl) 37bb0ec6b3SJim Harris { 38bb0ec6b3SJim Harris /* 39bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 40bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 41bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 42bb0ec6b3SJim Harris * look at the DNR bit. 43bb0ec6b3SJim Harris */ 44bb0ec6b3SJim Harris switch (cpl->sf_sct) { 45bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 46bb0ec6b3SJim Harris switch (cpl->sf_sc) { 47bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 48bb0ec6b3SJim Harris if (cpl->sf_dnr) 49bb0ec6b3SJim Harris return (0); 50bb0ec6b3SJim Harris else 51bb0ec6b3SJim Harris return (1); 52bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 53bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 54bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 55bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 56bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 57bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 58bb0ec6b3SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 59bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 60bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 61bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 62bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 63bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 64bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 65bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 66bb0ec6b3SJim Harris default: 67bb0ec6b3SJim Harris return (0); 68bb0ec6b3SJim Harris } 69bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 70bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 71bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 72bb0ec6b3SJim Harris default: 73bb0ec6b3SJim Harris return (0); 74bb0ec6b3SJim Harris } 75bb0ec6b3SJim Harris } 76bb0ec6b3SJim Harris 77*21b6da58SJim Harris static void 78*21b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 79*21b6da58SJim Harris uint16_t cid) 80bb0ec6b3SJim Harris { 819eb93f29SJim Harris 82f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map); 83f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map); 84f2b19f67SJim Harris 85f2b19f67SJim Harris bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp, 86f2b19f67SJim Harris sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0); 87f2b19f67SJim Harris 88bb0ec6b3SJim Harris callout_init_mtx(&tr->timer, &qpair->lock, 0); 89*21b6da58SJim Harris tr->cid = cid; 90d281e8fbSJim Harris tr->qpair = qpair; 91bb0ec6b3SJim Harris } 92bb0ec6b3SJim Harris 93bb0ec6b3SJim Harris void 94bb0ec6b3SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 95bb0ec6b3SJim Harris { 96bb0ec6b3SJim Harris struct nvme_tracker *tr; 97ad697276SJim Harris struct nvme_request *req; 98bb0ec6b3SJim Harris struct nvme_completion *cpl; 99bb0ec6b3SJim Harris boolean_t retry, error; 100bb0ec6b3SJim Harris 1016568ebfcSJim Harris qpair->num_intr_handler_calls++; 1026568ebfcSJim Harris 103bb0ec6b3SJim Harris while (1) { 104bb0ec6b3SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 105bb0ec6b3SJim Harris 106bb0ec6b3SJim Harris if (cpl->p != qpair->phase) 107bb0ec6b3SJim Harris break; 108bb0ec6b3SJim Harris 109bb0ec6b3SJim Harris tr = qpair->act_tr[cpl->cid]; 110ad697276SJim Harris req = tr->req; 111ad697276SJim Harris 112bb0ec6b3SJim Harris KASSERT(tr, 113bb0ec6b3SJim Harris ("completion queue has entries but no active trackers\n")); 114bb0ec6b3SJim Harris 115bb0ec6b3SJim Harris error = cpl->sf_sc || cpl->sf_sct; 116bb0ec6b3SJim Harris retry = error && nvme_completion_check_retry(cpl); 117bb0ec6b3SJim Harris 118bb0ec6b3SJim Harris if (error) { 119bb0ec6b3SJim Harris nvme_dump_completion(cpl); 120ad697276SJim Harris nvme_dump_command(&tr->req->cmd); 121bb0ec6b3SJim Harris } 122bb0ec6b3SJim Harris 123bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 124bb0ec6b3SJim Harris 125bb0ec6b3SJim Harris KASSERT(cpl->cid == tr->cmd.cid, 126bb0ec6b3SJim Harris ("cpl cid does not match cmd cid\n")); 127bb0ec6b3SJim Harris 128ad697276SJim Harris if (req->cb_fn && !retry) 129ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 130bb0ec6b3SJim Harris 131bb0ec6b3SJim Harris qpair->sq_head = cpl->sqhd; 132bb0ec6b3SJim Harris 133bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 134bb0ec6b3SJim Harris callout_stop(&tr->timer); 135bb0ec6b3SJim Harris 136bb0ec6b3SJim Harris if (retry) 137bb0ec6b3SJim Harris nvme_qpair_submit_cmd(qpair, tr); 138bb0ec6b3SJim Harris else { 1395fa5cc5fSJim Harris if (req->payload_size > 0 || req->uio != NULL) 140f2b19f67SJim Harris bus_dmamap_unload(qpair->dma_tag, 141f2b19f67SJim Harris tr->payload_dma_map); 142bb0ec6b3SJim Harris 143ad697276SJim Harris nvme_free_request(req); 144*21b6da58SJim Harris 145*21b6da58SJim Harris if (SLIST_EMPTY(&qpair->free_tr)) 146*21b6da58SJim Harris wakeup(qpair); 147*21b6da58SJim Harris 148bb0ec6b3SJim Harris SLIST_INSERT_HEAD(&qpair->free_tr, tr, slist); 149c2e83b40SJim Harris } 150bb0ec6b3SJim Harris 151bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 152bb0ec6b3SJim Harris 153bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 154bb0ec6b3SJim Harris qpair->cq_head = 0; 155bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 156bb0ec6b3SJim Harris } 157bb0ec6b3SJim Harris 158bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 159bb0ec6b3SJim Harris qpair->cq_head); 160bb0ec6b3SJim Harris } 161bb0ec6b3SJim Harris } 162bb0ec6b3SJim Harris 163bb0ec6b3SJim Harris static void 164bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 165bb0ec6b3SJim Harris { 166bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 167bb0ec6b3SJim Harris 168bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 169bb0ec6b3SJim Harris } 170bb0ec6b3SJim Harris 171bb0ec6b3SJim Harris void 172bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 173*21b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 174*21b6da58SJim Harris uint32_t max_xfer_size, struct nvme_controller *ctrlr) 175bb0ec6b3SJim Harris { 176*21b6da58SJim Harris struct nvme_tracker *tr; 177*21b6da58SJim Harris uint32_t i; 178bb0ec6b3SJim Harris 179bb0ec6b3SJim Harris qpair->id = id; 180bb0ec6b3SJim Harris qpair->vector = vector; 181bb0ec6b3SJim Harris qpair->num_entries = num_entries; 182bb0ec6b3SJim Harris qpair->max_xfer_size = max_xfer_size; 183bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 184bb0ec6b3SJim Harris 185bb0ec6b3SJim Harris /* 186bb0ec6b3SJim Harris * First time through the completion queue, HW will set phase 187bb0ec6b3SJim Harris * bit on completions to 1. So set this to 1 here, indicating 188bb0ec6b3SJim Harris * we're looking for a 1 to know which entries have completed. 189bb0ec6b3SJim Harris * we'll toggle the bit each time when the completion queue 190bb0ec6b3SJim Harris * rolls over. 191bb0ec6b3SJim Harris */ 192bb0ec6b3SJim Harris qpair->phase = 1; 193bb0ec6b3SJim Harris 194bb0ec6b3SJim Harris if (ctrlr->msix_enabled) { 195bb0ec6b3SJim Harris 196bb0ec6b3SJim Harris /* 197bb0ec6b3SJim Harris * MSI-X vector resource IDs start at 1, so we add one to 198bb0ec6b3SJim Harris * the queue's vector to get the corresponding rid to use. 199bb0ec6b3SJim Harris */ 200bb0ec6b3SJim Harris qpair->rid = vector + 1; 201bb0ec6b3SJim Harris 202bb0ec6b3SJim Harris qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 203bb0ec6b3SJim Harris &qpair->rid, RF_ACTIVE); 204bb0ec6b3SJim Harris 205bb0ec6b3SJim Harris bus_setup_intr(ctrlr->dev, qpair->res, 206bb0ec6b3SJim Harris INTR_TYPE_MISC | INTR_MPSAFE, NULL, 207bb0ec6b3SJim Harris nvme_qpair_msix_handler, qpair, &qpair->tag); 208bb0ec6b3SJim Harris } 209bb0ec6b3SJim Harris 210bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 211bb0ec6b3SJim Harris 212bb0ec6b3SJim Harris bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 213bb0ec6b3SJim Harris sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR, 214bb0ec6b3SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size, 215bb0ec6b3SJim Harris (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0, 216bb0ec6b3SJim Harris NULL, NULL, &qpair->dma_tag); 217bb0ec6b3SJim Harris 218bb0ec6b3SJim Harris qpair->num_cmds = 0; 2196568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 220*21b6da58SJim Harris qpair->num_trackers = num_trackers; 221bb0ec6b3SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 222bb0ec6b3SJim Harris 223bb0ec6b3SJim Harris /* TODO: error checking on contigmalloc, bus_dmamap_load calls */ 224bb0ec6b3SJim Harris qpair->cmd = contigmalloc(qpair->num_entries * 225bb0ec6b3SJim Harris sizeof(struct nvme_command), M_NVME, M_ZERO | M_NOWAIT, 226bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 227bb0ec6b3SJim Harris qpair->cpl = contigmalloc(qpair->num_entries * 228bb0ec6b3SJim Harris sizeof(struct nvme_completion), M_NVME, M_ZERO | M_NOWAIT, 229bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 230bb0ec6b3SJim Harris 231bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map); 232bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map); 233bb0ec6b3SJim Harris 234bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map, 235bb0ec6b3SJim Harris qpair->cmd, qpair->num_entries * sizeof(struct nvme_command), 236bb0ec6b3SJim Harris nvme_single_map, &qpair->cmd_bus_addr, 0); 237bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map, 238bb0ec6b3SJim Harris qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion), 239bb0ec6b3SJim Harris nvme_single_map, &qpair->cpl_bus_addr, 0); 240bb0ec6b3SJim Harris 241bb0ec6b3SJim Harris qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl); 242bb0ec6b3SJim Harris qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl); 243bb0ec6b3SJim Harris 244bb0ec6b3SJim Harris SLIST_INIT(&qpair->free_tr); 245bb0ec6b3SJim Harris 246*21b6da58SJim Harris for (i = 0; i < num_trackers; i++) { 247*21b6da58SJim Harris tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_NOWAIT); 248*21b6da58SJim Harris 249*21b6da58SJim Harris if (tr == NULL) { 250*21b6da58SJim Harris printf("warning: nvme tracker malloc failed\n"); 251*21b6da58SJim Harris break; 252*21b6da58SJim Harris } 253*21b6da58SJim Harris 254*21b6da58SJim Harris nvme_qpair_construct_tracker(qpair, tr, i); 255*21b6da58SJim Harris SLIST_INSERT_HEAD(&qpair->free_tr, tr, slist); 256*21b6da58SJim Harris } 257*21b6da58SJim Harris 258bb0ec6b3SJim Harris qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries, 259bb0ec6b3SJim Harris M_NVME, M_ZERO | M_NOWAIT); 260bb0ec6b3SJim Harris } 261bb0ec6b3SJim Harris 262bb0ec6b3SJim Harris static void 263bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 264bb0ec6b3SJim Harris { 265bb0ec6b3SJim Harris struct nvme_tracker *tr; 266bb0ec6b3SJim Harris 267bb0ec6b3SJim Harris if (qpair->tag) 268bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 269bb0ec6b3SJim Harris 270bb0ec6b3SJim Harris if (qpair->res) 271bb0ec6b3SJim Harris bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 272bb0ec6b3SJim Harris rman_get_rid(qpair->res), qpair->res); 273bb0ec6b3SJim Harris 274bb0ec6b3SJim Harris if (qpair->dma_tag) 275bb0ec6b3SJim Harris bus_dma_tag_destroy(qpair->dma_tag); 276bb0ec6b3SJim Harris 277bb0ec6b3SJim Harris if (qpair->act_tr) 278bb0ec6b3SJim Harris free(qpair->act_tr, M_NVME); 279bb0ec6b3SJim Harris 280bb0ec6b3SJim Harris while (!SLIST_EMPTY(&qpair->free_tr)) { 281bb0ec6b3SJim Harris tr = SLIST_FIRST(&qpair->free_tr); 282bb0ec6b3SJim Harris SLIST_REMOVE_HEAD(&qpair->free_tr, slist); 283f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map); 284f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map); 285bb0ec6b3SJim Harris free(tr, M_NVME); 286bb0ec6b3SJim Harris } 287bb0ec6b3SJim Harris } 288bb0ec6b3SJim Harris 289bb0ec6b3SJim Harris void 290bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 291bb0ec6b3SJim Harris { 292bb0ec6b3SJim Harris 293bb0ec6b3SJim Harris /* 294bb0ec6b3SJim Harris * For NVMe, you don't send delete queue commands for the admin 295bb0ec6b3SJim Harris * queue, so we just need to unload and free the cmd and cpl memory. 296bb0ec6b3SJim Harris */ 297bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 298bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 299bb0ec6b3SJim Harris 300bb0ec6b3SJim Harris contigfree(qpair->cmd, 301bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 302bb0ec6b3SJim Harris 303bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 304bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 305bb0ec6b3SJim Harris contigfree(qpair->cpl, 306bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 307bb0ec6b3SJim Harris 308bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 309bb0ec6b3SJim Harris } 310bb0ec6b3SJim Harris 311bb0ec6b3SJim Harris static void 312bb0ec6b3SJim Harris nvme_free_cmd_ring(void *arg, const struct nvme_completion *status) 313bb0ec6b3SJim Harris { 314bb0ec6b3SJim Harris struct nvme_qpair *qpair; 315bb0ec6b3SJim Harris 316bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 317bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 318bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 319bb0ec6b3SJim Harris contigfree(qpair->cmd, 320bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 321bb0ec6b3SJim Harris qpair->cmd = NULL; 322bb0ec6b3SJim Harris } 323bb0ec6b3SJim Harris 324bb0ec6b3SJim Harris static void 325bb0ec6b3SJim Harris nvme_free_cpl_ring(void *arg, const struct nvme_completion *status) 326bb0ec6b3SJim Harris { 327bb0ec6b3SJim Harris struct nvme_qpair *qpair; 328bb0ec6b3SJim Harris 329bb0ec6b3SJim Harris qpair = (struct nvme_qpair *)arg; 330bb0ec6b3SJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 331bb0ec6b3SJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 332bb0ec6b3SJim Harris contigfree(qpair->cpl, 333bb0ec6b3SJim Harris qpair->num_entries * sizeof(struct nvme_completion), M_NVME); 334bb0ec6b3SJim Harris qpair->cpl = NULL; 335bb0ec6b3SJim Harris } 336bb0ec6b3SJim Harris 337bb0ec6b3SJim Harris void 338bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 339bb0ec6b3SJim Harris { 340bb0ec6b3SJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 341bb0ec6b3SJim Harris 342bb0ec6b3SJim Harris if (qpair->num_entries > 0) { 343bb0ec6b3SJim Harris 344bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_free_cmd_ring, 345bb0ec6b3SJim Harris qpair); 346bb0ec6b3SJim Harris /* Spin until free_cmd_ring sets qpair->cmd to NULL. */ 347bb0ec6b3SJim Harris while (qpair->cmd) 348bb0ec6b3SJim Harris DELAY(5); 349bb0ec6b3SJim Harris 350bb0ec6b3SJim Harris nvme_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_free_cpl_ring, 351bb0ec6b3SJim Harris qpair); 352bb0ec6b3SJim Harris /* Spin until free_cpl_ring sets qpair->cmd to NULL. */ 353bb0ec6b3SJim Harris while (qpair->cpl) 354bb0ec6b3SJim Harris DELAY(5); 355bb0ec6b3SJim Harris 356bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 357bb0ec6b3SJim Harris } 358bb0ec6b3SJim Harris } 359bb0ec6b3SJim Harris 360bb0ec6b3SJim Harris static void 361bb0ec6b3SJim Harris nvme_timeout(void *arg) 362bb0ec6b3SJim Harris { 363bb0ec6b3SJim Harris /* 364bb0ec6b3SJim Harris * TODO: Add explicit abort operation here, once nvme(4) supports 365bb0ec6b3SJim Harris * abort commands. 366bb0ec6b3SJim Harris */ 367bb0ec6b3SJim Harris } 368bb0ec6b3SJim Harris 369bb0ec6b3SJim Harris void 370bb0ec6b3SJim Harris nvme_qpair_submit_cmd(struct nvme_qpair *qpair, struct nvme_tracker *tr) 371bb0ec6b3SJim Harris { 372ad697276SJim Harris struct nvme_request *req; 373bb0ec6b3SJim Harris 374ad697276SJim Harris req = tr->req; 375ad697276SJim Harris req->cmd.cid = tr->cid; 376bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 377bb0ec6b3SJim Harris 378bb0ec6b3SJim Harris callout_reset(&tr->timer, NVME_TIMEOUT_IN_SEC * hz, nvme_timeout, tr); 379bb0ec6b3SJim Harris 380bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 381ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 382bb0ec6b3SJim Harris 383bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 384bb0ec6b3SJim Harris qpair->sq_tail = 0; 385bb0ec6b3SJim Harris 386bb0ec6b3SJim Harris wmb(); 387bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl, 388bb0ec6b3SJim Harris qpair->sq_tail); 389bb0ec6b3SJim Harris 390bb0ec6b3SJim Harris qpair->num_cmds++; 391bb0ec6b3SJim Harris } 3925ae9ed68SJim Harris 3935ae9ed68SJim Harris void 3945ae9ed68SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 3955ae9ed68SJim Harris { 3965ae9ed68SJim Harris struct nvme_tracker *tr; 3975ae9ed68SJim Harris int err; 3985ae9ed68SJim Harris 3995ae9ed68SJim Harris mtx_lock(&qpair->lock); 4005ae9ed68SJim Harris 401*21b6da58SJim Harris tr = SLIST_FIRST(&qpair->free_tr); 402*21b6da58SJim Harris 403*21b6da58SJim Harris while (tr == NULL) { 404*21b6da58SJim Harris msleep(qpair, &qpair->lock, PRIBIO, "qpair_tr", 0); 405*21b6da58SJim Harris printf("msleep\n"); 406*21b6da58SJim Harris tr = SLIST_FIRST(&qpair->free_tr); 407*21b6da58SJim Harris } 408*21b6da58SJim Harris 409*21b6da58SJim Harris SLIST_REMOVE_HEAD(&qpair->free_tr, slist); 4105ae9ed68SJim Harris tr->req = req; 4115ae9ed68SJim Harris 4125ae9ed68SJim Harris if (req->uio == NULL) { 4135ae9ed68SJim Harris if (req->payload_size > 0) { 4145ae9ed68SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, 4155ae9ed68SJim Harris tr->payload_dma_map, req->payload, 4165ae9ed68SJim Harris req->payload_size, 4175ae9ed68SJim Harris nvme_payload_map, tr, 0); 4185ae9ed68SJim Harris if (err != 0) 4195ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 4205ae9ed68SJim Harris } else 4215ae9ed68SJim Harris nvme_qpair_submit_cmd(tr->qpair, tr); 4225ae9ed68SJim Harris } else { 4235ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 4245ae9ed68SJim Harris tr->payload_dma_map, req->uio, 4255ae9ed68SJim Harris nvme_payload_map_uio, tr, 0); 4265ae9ed68SJim Harris if (err != 0) 4275ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 4285ae9ed68SJim Harris } 4295ae9ed68SJim Harris 4305ae9ed68SJim Harris mtx_unlock(&qpair->lock); 4315ae9ed68SJim Harris } 432