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 40*547d523eSJim Harris struct nvme_opcode_string { 41*547d523eSJim Harris 42*547d523eSJim Harris uint16_t opc; 43*547d523eSJim Harris const char * str; 44*547d523eSJim Harris }; 45*547d523eSJim Harris 46*547d523eSJim Harris static struct nvme_opcode_string admin_opcode[] = { 47*547d523eSJim Harris { NVME_OPC_DELETE_IO_SQ, "DELETE IO SQ" }, 48*547d523eSJim Harris { NVME_OPC_CREATE_IO_SQ, "CREATE IO SQ" }, 49*547d523eSJim Harris { NVME_OPC_GET_LOG_PAGE, "GET LOG PAGE" }, 50*547d523eSJim Harris { NVME_OPC_DELETE_IO_CQ, "DELETE IO CQ" }, 51*547d523eSJim Harris { NVME_OPC_CREATE_IO_CQ, "CREATE IO CQ" }, 52*547d523eSJim Harris { NVME_OPC_IDENTIFY, "IDENTIFY" }, 53*547d523eSJim Harris { NVME_OPC_ABORT, "ABORT" }, 54*547d523eSJim Harris { NVME_OPC_SET_FEATURES, "SET FEATURES" }, 55*547d523eSJim Harris { NVME_OPC_GET_FEATURES, "GET FEATURES" }, 56*547d523eSJim Harris { NVME_OPC_ASYNC_EVENT_REQUEST, "ASYNC EVENT REQUEST" }, 57*547d523eSJim Harris { NVME_OPC_FIRMWARE_ACTIVATE, "FIRMWARE ACTIVATE" }, 58*547d523eSJim Harris { NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD, "FIRMWARE IMAGE DOWNLOAD" }, 59*547d523eSJim Harris { NVME_OPC_FORMAT_NVM, "FORMAT NVM" }, 60*547d523eSJim Harris { NVME_OPC_SECURITY_SEND, "SECURITY SEND" }, 61*547d523eSJim Harris { NVME_OPC_SECURITY_RECEIVE, "SECURITY RECEIVE" }, 62*547d523eSJim Harris { 0xFFFF, "ADMIN COMMAND" } 63*547d523eSJim Harris }; 64*547d523eSJim Harris 65*547d523eSJim Harris static struct nvme_opcode_string io_opcode[] = { 66*547d523eSJim Harris { NVME_OPC_FLUSH, "FLUSH" }, 67*547d523eSJim Harris { NVME_OPC_WRITE, "WRITE" }, 68*547d523eSJim Harris { NVME_OPC_READ, "READ" }, 69*547d523eSJim Harris { NVME_OPC_WRITE_UNCORRECTABLE, "WRITE UNCORRECTABLE" }, 70*547d523eSJim Harris { NVME_OPC_COMPARE, "COMPARE" }, 71*547d523eSJim Harris { NVME_OPC_DATASET_MANAGEMENT, "DATASET MANAGEMENT" }, 72*547d523eSJim Harris { 0xFFFF, "IO COMMAND" } 73*547d523eSJim Harris }; 74*547d523eSJim Harris 75*547d523eSJim Harris static const char * 76*547d523eSJim Harris get_admin_opcode_string(uint16_t opc) 77*547d523eSJim Harris { 78*547d523eSJim Harris struct nvme_opcode_string *entry; 79*547d523eSJim Harris 80*547d523eSJim Harris entry = admin_opcode; 81*547d523eSJim Harris 82*547d523eSJim Harris while (entry->opc != 0xFFFF) { 83*547d523eSJim Harris if (entry->opc == opc) 84*547d523eSJim Harris return (entry->str); 85*547d523eSJim Harris entry++; 86*547d523eSJim Harris } 87*547d523eSJim Harris return (entry->str); 88*547d523eSJim Harris } 89*547d523eSJim Harris 90*547d523eSJim Harris static const char * 91*547d523eSJim Harris get_io_opcode_string(uint16_t opc) 92*547d523eSJim Harris { 93*547d523eSJim Harris struct nvme_opcode_string *entry; 94*547d523eSJim Harris 95*547d523eSJim Harris entry = io_opcode; 96*547d523eSJim Harris 97*547d523eSJim Harris while (entry->opc != 0xFFFF) { 98*547d523eSJim Harris if (entry->opc == opc) 99*547d523eSJim Harris return (entry->str); 100*547d523eSJim Harris entry++; 101*547d523eSJim Harris } 102*547d523eSJim Harris return (entry->str); 103*547d523eSJim Harris } 104*547d523eSJim Harris 105*547d523eSJim Harris 106*547d523eSJim Harris static void 107*547d523eSJim Harris nvme_admin_qpair_print_command(struct nvme_qpair *qpair, 108*547d523eSJim Harris struct nvme_command *cmd) 109*547d523eSJim Harris { 110*547d523eSJim Harris 111*547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%x " 112*547d523eSJim Harris "cdw10:%08x cdw11:%08x\n", 113*547d523eSJim Harris get_admin_opcode_string(cmd->opc), cmd->opc, qpair->id, cmd->cid, 114*547d523eSJim Harris cmd->nsid, cmd->cdw10, cmd->cdw11); 115*547d523eSJim Harris } 116*547d523eSJim Harris 117*547d523eSJim Harris static void 118*547d523eSJim Harris nvme_io_qpair_print_command(struct nvme_qpair *qpair, 119*547d523eSJim Harris struct nvme_command *cmd) 120*547d523eSJim Harris { 121*547d523eSJim Harris 122*547d523eSJim Harris switch (cmd->opc) { 123*547d523eSJim Harris case NVME_OPC_WRITE: 124*547d523eSJim Harris case NVME_OPC_READ: 125*547d523eSJim Harris case NVME_OPC_WRITE_UNCORRECTABLE: 126*547d523eSJim Harris case NVME_OPC_COMPARE: 127*547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d " 128*547d523eSJim Harris "lba:%lu len:%d\n", 129*547d523eSJim Harris get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, 130*547d523eSJim Harris cmd->nsid, ((uint64_t)cmd->cdw11 << 32) | cmd->cdw10, 131*547d523eSJim Harris (cmd->cdw12 & 0xFFFF) + 1); 132*547d523eSJim Harris break; 133*547d523eSJim Harris case NVME_OPC_FLUSH: 134*547d523eSJim Harris case NVME_OPC_DATASET_MANAGEMENT: 135*547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d\n", 136*547d523eSJim Harris get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, 137*547d523eSJim Harris cmd->nsid); 138*547d523eSJim Harris break; 139*547d523eSJim Harris default: 140*547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%d\n", 141*547d523eSJim Harris get_io_opcode_string(cmd->opc), cmd->opc, qpair->id, 142*547d523eSJim Harris cmd->cid, cmd->nsid); 143*547d523eSJim Harris break; 144*547d523eSJim Harris } 145*547d523eSJim Harris } 146*547d523eSJim Harris 147*547d523eSJim Harris static void 148*547d523eSJim Harris nvme_qpair_print_command(struct nvme_qpair *qpair, struct nvme_command *cmd) 149*547d523eSJim Harris { 150*547d523eSJim Harris if (qpair->id == 0) 151*547d523eSJim Harris nvme_admin_qpair_print_command(qpair, cmd); 152*547d523eSJim Harris else 153*547d523eSJim Harris nvme_io_qpair_print_command(qpair, cmd); 154*547d523eSJim Harris } 155*547d523eSJim Harris 156*547d523eSJim Harris struct nvme_status_string { 157*547d523eSJim Harris 158*547d523eSJim Harris uint16_t sc; 159*547d523eSJim Harris const char * str; 160*547d523eSJim Harris }; 161*547d523eSJim Harris 162*547d523eSJim Harris static struct nvme_status_string generic_status[] = { 163*547d523eSJim Harris { NVME_SC_SUCCESS, "SUCCESS" }, 164*547d523eSJim Harris { NVME_SC_INVALID_OPCODE, "INVALID OPCODE" }, 165*547d523eSJim Harris { NVME_SC_INVALID_FIELD, "INVALID_FIELD" }, 166*547d523eSJim Harris { NVME_SC_COMMAND_ID_CONFLICT, "COMMAND ID CONFLICT" }, 167*547d523eSJim Harris { NVME_SC_DATA_TRANSFER_ERROR, "DATA TRANSFER ERROR" }, 168*547d523eSJim Harris { NVME_SC_ABORTED_POWER_LOSS, "ABORTED - POWER LOSS" }, 169*547d523eSJim Harris { NVME_SC_INTERNAL_DEVICE_ERROR, "INTERNAL DEVICE ERROR" }, 170*547d523eSJim Harris { NVME_SC_ABORTED_BY_REQUEST, "ABORTED - BY REQUEST" }, 171*547d523eSJim Harris { NVME_SC_ABORTED_SQ_DELETION, "ABORTED - SQ DELETION" }, 172*547d523eSJim Harris { NVME_SC_ABORTED_FAILED_FUSED, "ABORTED - FAILED FUSED" }, 173*547d523eSJim Harris { NVME_SC_ABORTED_MISSING_FUSED, "ABORTED - MISSING FUSED" }, 174*547d523eSJim Harris { NVME_SC_INVALID_NAMESPACE_OR_FORMAT, "INVALID NAMESPACE OR FORMAT" }, 175*547d523eSJim Harris { NVME_SC_COMMAND_SEQUENCE_ERROR, "COMMAND SEQUENCE ERROR" }, 176*547d523eSJim Harris { NVME_SC_LBA_OUT_OF_RANGE, "LBA OUT OF RANGE" }, 177*547d523eSJim Harris { NVME_SC_CAPACITY_EXCEEDED, "CAPACITY EXCEEDED" }, 178*547d523eSJim Harris { NVME_SC_NAMESPACE_NOT_READY, "NAMESPACE NOT READY" }, 179*547d523eSJim Harris { 0xFFFF, "GENERIC" } 180*547d523eSJim Harris }; 181*547d523eSJim Harris 182*547d523eSJim Harris static struct nvme_status_string command_specific_status[] = { 183*547d523eSJim Harris { NVME_SC_COMPLETION_QUEUE_INVALID, "INVALID COMPLETION QUEUE" }, 184*547d523eSJim Harris { NVME_SC_INVALID_QUEUE_IDENTIFIER, "INVALID QUEUE IDENTIFIER" }, 185*547d523eSJim Harris { NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED, "MAX QUEUE SIZE EXCEEDED" }, 186*547d523eSJim Harris { NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED, "ABORT CMD LIMIT EXCEEDED" }, 187*547d523eSJim Harris { NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED, "ASYNC LIMIT EXCEEDED" }, 188*547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_SLOT, "INVALID FIRMWARE SLOT" }, 189*547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_IMAGE, "INVALID FIRMWARE IMAGE" }, 190*547d523eSJim Harris { NVME_SC_INVALID_INTERRUPT_VECTOR, "INVALID INTERRUPT VECTOR" }, 191*547d523eSJim Harris { NVME_SC_INVALID_LOG_PAGE, "INVALID LOG PAGE" }, 192*547d523eSJim Harris { NVME_SC_INVALID_FORMAT, "INVALID FORMAT" }, 193*547d523eSJim Harris { NVME_SC_FIRMWARE_REQUIRES_RESET, "FIRMWARE REQUIRES RESET" }, 194*547d523eSJim Harris { NVME_SC_CONFLICTING_ATTRIBUTES, "CONFLICTING ATTRIBUTES" }, 195*547d523eSJim Harris { NVME_SC_INVALID_PROTECTION_INFO, "INVALID PROTECTION INFO" }, 196*547d523eSJim Harris { NVME_SC_ATTEMPTED_WRITE_TO_RO_PAGE, "WRITE TO RO PAGE" }, 197*547d523eSJim Harris { 0xFFFF, "COMMAND SPECIFIC" } 198*547d523eSJim Harris }; 199*547d523eSJim Harris 200*547d523eSJim Harris static struct nvme_status_string media_error_status[] = { 201*547d523eSJim Harris { NVME_SC_WRITE_FAULTS, "WRITE FAULTS" }, 202*547d523eSJim Harris { NVME_SC_UNRECOVERED_READ_ERROR, "UNRECOVERED READ ERROR" }, 203*547d523eSJim Harris { NVME_SC_GUARD_CHECK_ERROR, "GUARD CHECK ERROR" }, 204*547d523eSJim Harris { NVME_SC_APPLICATION_TAG_CHECK_ERROR, "APPLICATION TAG CHECK ERROR" }, 205*547d523eSJim Harris { NVME_SC_REFERENCE_TAG_CHECK_ERROR, "REFERENCE TAG CHECK ERROR" }, 206*547d523eSJim Harris { NVME_SC_COMPARE_FAILURE, "COMPARE FAILURE" }, 207*547d523eSJim Harris { NVME_SC_ACCESS_DENIED, "ACCESS DENIED" }, 208*547d523eSJim Harris { 0xFFFF, "MEDIA ERROR" } 209*547d523eSJim Harris }; 210*547d523eSJim Harris 211*547d523eSJim Harris static const char * 212*547d523eSJim Harris get_status_string(uint16_t sct, uint16_t sc) 213*547d523eSJim Harris { 214*547d523eSJim Harris struct nvme_status_string *entry; 215*547d523eSJim Harris 216*547d523eSJim Harris switch (sct) { 217*547d523eSJim Harris case NVME_SCT_GENERIC: 218*547d523eSJim Harris entry = generic_status; 219*547d523eSJim Harris break; 220*547d523eSJim Harris case NVME_SCT_COMMAND_SPECIFIC: 221*547d523eSJim Harris entry = command_specific_status; 222*547d523eSJim Harris break; 223*547d523eSJim Harris case NVME_SCT_MEDIA_ERROR: 224*547d523eSJim Harris entry = media_error_status; 225*547d523eSJim Harris break; 226*547d523eSJim Harris case NVME_SCT_VENDOR_SPECIFIC: 227*547d523eSJim Harris return ("VENDOR SPECIFIC"); 228*547d523eSJim Harris default: 229*547d523eSJim Harris return ("RESERVED"); 230*547d523eSJim Harris } 231*547d523eSJim Harris 232*547d523eSJim Harris while (entry->sc != 0xFFFF) { 233*547d523eSJim Harris if (entry->sc == sc) 234*547d523eSJim Harris return (entry->str); 235*547d523eSJim Harris entry++; 236*547d523eSJim Harris } 237*547d523eSJim Harris return (entry->str); 238*547d523eSJim Harris } 239*547d523eSJim Harris 240*547d523eSJim Harris static void 241*547d523eSJim Harris nvme_qpair_print_completion(struct nvme_qpair *qpair, 242*547d523eSJim Harris struct nvme_completion *cpl) 243*547d523eSJim Harris { 244*547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x/%02x) sqid:%d cid:%d cdw0:%x\n", 245*547d523eSJim Harris get_status_string(cpl->status.sct, cpl->status.sc), 246*547d523eSJim Harris cpl->status.sct, cpl->status.sc, cpl->sqid, cpl->cid, cpl->cdw0); 247*547d523eSJim Harris } 248*547d523eSJim Harris 249bb0ec6b3SJim Harris static boolean_t 2506cb06070SJim Harris nvme_completion_is_retry(const struct nvme_completion *cpl) 251bb0ec6b3SJim Harris { 252bb0ec6b3SJim Harris /* 253bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 254bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 255bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 256bb0ec6b3SJim Harris * look at the DNR bit. 257bb0ec6b3SJim Harris */ 258cf81529cSJim Harris switch (cpl->status.sct) { 259bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 260cf81529cSJim Harris switch (cpl->status.sc) { 261448195e7SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 262bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 263cf81529cSJim Harris if (cpl->status.dnr) 264bb0ec6b3SJim Harris return (0); 265bb0ec6b3SJim Harris else 266bb0ec6b3SJim Harris return (1); 267bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 268bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 269bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 270bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 271bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 272bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 273bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 274bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 275bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 276bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 277bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 278bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 279bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 280bb0ec6b3SJim Harris default: 281bb0ec6b3SJim Harris return (0); 282bb0ec6b3SJim Harris } 283bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 284bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 285bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 286bb0ec6b3SJim Harris default: 287bb0ec6b3SJim Harris return (0); 288bb0ec6b3SJim Harris } 289bb0ec6b3SJim Harris } 290bb0ec6b3SJim Harris 29121b6da58SJim Harris static void 29221b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 29321b6da58SJim Harris uint16_t cid) 294bb0ec6b3SJim Harris { 2959eb93f29SJim Harris 296f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map); 297f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map); 298f2b19f67SJim Harris 299f2b19f67SJim Harris bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp, 300f2b19f67SJim Harris sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0); 301f2b19f67SJim Harris 30212d191ecSJim Harris callout_init(&tr->timer, 1); 30321b6da58SJim Harris tr->cid = cid; 304d281e8fbSJim Harris tr->qpair = qpair; 305bb0ec6b3SJim Harris } 306bb0ec6b3SJim Harris 3076cb06070SJim Harris static void 3086cb06070SJim Harris nvme_qpair_complete_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 3096cb06070SJim Harris struct nvme_completion *cpl, boolean_t print_on_error) 310bb0ec6b3SJim Harris { 311ad697276SJim Harris struct nvme_request *req; 312bb0ec6b3SJim Harris boolean_t retry, error; 313bb0ec6b3SJim Harris 314ad697276SJim Harris req = tr->req; 3156cb06070SJim Harris error = nvme_completion_is_error(cpl); 316cb5b7c13SJim Harris retry = error && nvme_completion_is_retry(cpl) && 317cb5b7c13SJim Harris req->retries < nvme_retry_count; 318ad697276SJim Harris 3196cb06070SJim Harris if (error && print_on_error) { 320*547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 321*547d523eSJim Harris nvme_qpair_print_completion(qpair, cpl); 322bb0ec6b3SJim Harris } 323bb0ec6b3SJim Harris 324bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 325bb0ec6b3SJim Harris 3266cb06070SJim Harris KASSERT(cpl->cid == req->cmd.cid, ("cpl cid does not match cmd cid\n")); 327bb0ec6b3SJim Harris 328ad697276SJim Harris if (req->cb_fn && !retry) 329ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 330bb0ec6b3SJim Harris 331bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 332bb0ec6b3SJim Harris callout_stop(&tr->timer); 333bb0ec6b3SJim Harris 334cb5b7c13SJim Harris if (retry) { 335cb5b7c13SJim Harris req->retries++; 336b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 337cb5b7c13SJim Harris } else { 3385fa5cc5fSJim Harris if (req->payload_size > 0 || req->uio != NULL) 339f2b19f67SJim Harris bus_dmamap_unload(qpair->dma_tag, 340f2b19f67SJim Harris tr->payload_dma_map); 341bb0ec6b3SJim Harris 342ad697276SJim Harris nvme_free_request(req); 3430a0b08ccSJim Harris tr->req = NULL; 34421b6da58SJim Harris 34565c2474eSJim Harris TAILQ_REMOVE(&qpair->outstanding_tr, tr, tailq); 34665c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 3470f71ecf7SJim Harris 348f37c22a3SJim Harris /* 349f37c22a3SJim Harris * If the controller is in the middle of resetting, don't 350f37c22a3SJim Harris * try to submit queued requests here - let the reset logic 351f37c22a3SJim Harris * handle that instead. 352f37c22a3SJim Harris */ 353f37c22a3SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req) && 354f37c22a3SJim Harris !qpair->ctrlr->is_resetting) { 3550f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 3560f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 357d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 3580f71ecf7SJim Harris } 359c2e83b40SJim Harris } 360bb0ec6b3SJim Harris 361bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 3626cb06070SJim Harris } 3636cb06070SJim Harris 364b846efd7SJim Harris static void 365b846efd7SJim Harris nvme_qpair_manual_complete_tracker(struct nvme_qpair *qpair, 366232e2edbSJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr, 367b846efd7SJim Harris boolean_t print_on_error) 368b846efd7SJim Harris { 369b846efd7SJim Harris struct nvme_completion cpl; 370b846efd7SJim Harris 371b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 372b846efd7SJim Harris cpl.sqid = qpair->id; 373b846efd7SJim Harris cpl.cid = tr->cid; 374cf81529cSJim Harris cpl.status.sct = sct; 375cf81529cSJim Harris cpl.status.sc = sc; 376232e2edbSJim Harris cpl.status.dnr = dnr; 377b846efd7SJim Harris nvme_qpair_complete_tracker(qpair, tr, &cpl, print_on_error); 378b846efd7SJim Harris } 379b846efd7SJim Harris 3806cb06070SJim Harris void 381232e2edbSJim Harris nvme_qpair_manual_complete_request(struct nvme_qpair *qpair, 382232e2edbSJim Harris struct nvme_request *req, uint32_t sct, uint32_t sc, 383232e2edbSJim Harris boolean_t print_on_error) 384232e2edbSJim Harris { 385232e2edbSJim Harris struct nvme_completion cpl; 386232e2edbSJim Harris boolean_t error; 387232e2edbSJim Harris 388232e2edbSJim Harris memset(&cpl, 0, sizeof(cpl)); 389232e2edbSJim Harris cpl.sqid = qpair->id; 390232e2edbSJim Harris cpl.status.sct = sct; 391232e2edbSJim Harris cpl.status.sc = sc; 392232e2edbSJim Harris 393232e2edbSJim Harris error = nvme_completion_is_error(&cpl); 394232e2edbSJim Harris 395232e2edbSJim Harris if (error && print_on_error) { 396*547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 397*547d523eSJim Harris nvme_qpair_print_completion(qpair, &cpl); 398232e2edbSJim Harris } 399232e2edbSJim Harris 400232e2edbSJim Harris if (req->cb_fn) 401232e2edbSJim Harris req->cb_fn(req->cb_arg, &cpl); 402232e2edbSJim Harris 403232e2edbSJim Harris nvme_free_request(req); 404232e2edbSJim Harris } 405232e2edbSJim Harris 406232e2edbSJim Harris void 4076cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 4086cb06070SJim Harris { 4096cb06070SJim Harris struct nvme_tracker *tr; 4106cb06070SJim Harris struct nvme_completion *cpl; 4116cb06070SJim Harris 4126cb06070SJim Harris qpair->num_intr_handler_calls++; 4136cb06070SJim Harris 414b846efd7SJim Harris if (!qpair->is_enabled) 415b846efd7SJim Harris /* 416b846efd7SJim Harris * qpair is not enabled, likely because a controller reset is 417b846efd7SJim Harris * is in progress. Ignore the interrupt - any I/O that was 418b846efd7SJim Harris * associated with this interrupt will get retried when the 419b846efd7SJim Harris * reset is complete. 420b846efd7SJim Harris */ 421b846efd7SJim Harris return; 422b846efd7SJim Harris 4236cb06070SJim Harris while (1) { 4246cb06070SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 4256cb06070SJim Harris 426cf81529cSJim Harris if (cpl->status.p != qpair->phase) 4276cb06070SJim Harris break; 4286cb06070SJim Harris 4296cb06070SJim Harris tr = qpair->act_tr[cpl->cid]; 4306cb06070SJim Harris 4316cb06070SJim Harris if (tr != NULL) { 4326cb06070SJim Harris nvme_qpair_complete_tracker(qpair, tr, cpl, TRUE); 4336cb06070SJim Harris qpair->sq_head = cpl->sqhd; 4346cb06070SJim Harris } else { 435*547d523eSJim Harris nvme_printf(qpair->ctrlr, 436*547d523eSJim Harris "cpl does not map to outstanding cmd\n"); 4376cb06070SJim Harris nvme_dump_completion(cpl); 4386cb06070SJim Harris KASSERT(0, ("received completion for unknown cmd\n")); 4396cb06070SJim Harris } 440bb0ec6b3SJim Harris 441bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 442bb0ec6b3SJim Harris qpair->cq_head = 0; 443bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 444bb0ec6b3SJim Harris } 445bb0ec6b3SJim Harris 446bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 447bb0ec6b3SJim Harris qpair->cq_head); 448bb0ec6b3SJim Harris } 449bb0ec6b3SJim Harris } 450bb0ec6b3SJim Harris 451bb0ec6b3SJim Harris static void 452bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 453bb0ec6b3SJim Harris { 454bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 455bb0ec6b3SJim Harris 456bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 457bb0ec6b3SJim Harris } 458bb0ec6b3SJim Harris 459bb0ec6b3SJim Harris void 460bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 46121b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 46221b6da58SJim Harris uint32_t max_xfer_size, struct nvme_controller *ctrlr) 463bb0ec6b3SJim Harris { 46421b6da58SJim Harris struct nvme_tracker *tr; 46521b6da58SJim Harris uint32_t i; 466bb0ec6b3SJim Harris 467bb0ec6b3SJim Harris qpair->id = id; 468bb0ec6b3SJim Harris qpair->vector = vector; 469bb0ec6b3SJim Harris qpair->num_entries = num_entries; 4700f71ecf7SJim Harris #ifdef CHATHAM2 4710f71ecf7SJim Harris /* 4720f71ecf7SJim Harris * Chatham prototype board starts having issues at higher queue 4730f71ecf7SJim Harris * depths. So use a conservative estimate here of no more than 64 4740f71ecf7SJim Harris * outstanding I/O per queue at any one point. 4750f71ecf7SJim Harris */ 4760f71ecf7SJim Harris if (pci_get_devid(ctrlr->dev) == CHATHAM_PCI_ID) 4770f71ecf7SJim Harris num_trackers = min(num_trackers, 64); 4780f71ecf7SJim Harris #endif 4790f71ecf7SJim Harris qpair->num_trackers = num_trackers; 480bb0ec6b3SJim Harris qpair->max_xfer_size = max_xfer_size; 481bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 482bb0ec6b3SJim Harris 483bb0ec6b3SJim Harris if (ctrlr->msix_enabled) { 484bb0ec6b3SJim Harris 485bb0ec6b3SJim Harris /* 486bb0ec6b3SJim Harris * MSI-X vector resource IDs start at 1, so we add one to 487bb0ec6b3SJim Harris * the queue's vector to get the corresponding rid to use. 488bb0ec6b3SJim Harris */ 489bb0ec6b3SJim Harris qpair->rid = vector + 1; 490bb0ec6b3SJim Harris 491bb0ec6b3SJim Harris qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 492bb0ec6b3SJim Harris &qpair->rid, RF_ACTIVE); 493bb0ec6b3SJim Harris 494bb0ec6b3SJim Harris bus_setup_intr(ctrlr->dev, qpair->res, 495bb0ec6b3SJim Harris INTR_TYPE_MISC | INTR_MPSAFE, NULL, 496bb0ec6b3SJim Harris nvme_qpair_msix_handler, qpair, &qpair->tag); 497bb0ec6b3SJim Harris } 498bb0ec6b3SJim Harris 499bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 500bb0ec6b3SJim Harris 501bb0ec6b3SJim Harris bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 502bb0ec6b3SJim Harris sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR, 503bb0ec6b3SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size, 504bb0ec6b3SJim Harris (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0, 505bb0ec6b3SJim Harris NULL, NULL, &qpair->dma_tag); 506bb0ec6b3SJim Harris 507bb0ec6b3SJim Harris qpair->num_cmds = 0; 5086568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 509bb0ec6b3SJim Harris 510bb0ec6b3SJim Harris qpair->cmd = contigmalloc(qpair->num_entries * 511237d2019SJim Harris sizeof(struct nvme_command), M_NVME, M_ZERO, 512bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 513bb0ec6b3SJim Harris qpair->cpl = contigmalloc(qpair->num_entries * 514237d2019SJim Harris sizeof(struct nvme_completion), M_NVME, M_ZERO, 515bb0ec6b3SJim Harris 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 516bb0ec6b3SJim Harris 517bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map); 518bb0ec6b3SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map); 519bb0ec6b3SJim Harris 520bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map, 521bb0ec6b3SJim Harris qpair->cmd, qpair->num_entries * sizeof(struct nvme_command), 522bb0ec6b3SJim Harris nvme_single_map, &qpair->cmd_bus_addr, 0); 523bb0ec6b3SJim Harris bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map, 524bb0ec6b3SJim Harris qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion), 525bb0ec6b3SJim Harris nvme_single_map, &qpair->cpl_bus_addr, 0); 526bb0ec6b3SJim Harris 527bb0ec6b3SJim Harris qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl); 528bb0ec6b3SJim Harris qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl); 529bb0ec6b3SJim Harris 53065c2474eSJim Harris TAILQ_INIT(&qpair->free_tr); 53165c2474eSJim Harris TAILQ_INIT(&qpair->outstanding_tr); 5320f71ecf7SJim Harris STAILQ_INIT(&qpair->queued_req); 533bb0ec6b3SJim Harris 5340f71ecf7SJim Harris for (i = 0; i < qpair->num_trackers; i++) { 535237d2019SJim Harris tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_WAITOK); 53621b6da58SJim Harris nvme_qpair_construct_tracker(qpair, tr, i); 53765c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 53821b6da58SJim Harris } 53921b6da58SJim Harris 540bb0ec6b3SJim Harris qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries, 541237d2019SJim Harris M_NVME, M_ZERO | M_WAITOK); 542bb0ec6b3SJim Harris } 543bb0ec6b3SJim Harris 544bb0ec6b3SJim Harris static void 545bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 546bb0ec6b3SJim Harris { 547bb0ec6b3SJim Harris struct nvme_tracker *tr; 548bb0ec6b3SJim Harris 549bb0ec6b3SJim Harris if (qpair->tag) 550bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 551bb0ec6b3SJim Harris 552bb0ec6b3SJim Harris if (qpair->res) 553bb0ec6b3SJim Harris bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 554bb0ec6b3SJim Harris rman_get_rid(qpair->res), qpair->res); 555bb0ec6b3SJim Harris 5563d7eb41cSJim Harris if (qpair->cmd) { 5573d7eb41cSJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map); 5583d7eb41cSJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map); 5593d7eb41cSJim Harris contigfree(qpair->cmd, 5603d7eb41cSJim Harris qpair->num_entries * sizeof(struct nvme_command), M_NVME); 5613d7eb41cSJim Harris } 5623d7eb41cSJim Harris 5633d7eb41cSJim Harris if (qpair->cpl) { 5643d7eb41cSJim Harris bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map); 5653d7eb41cSJim Harris bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map); 5663d7eb41cSJim Harris contigfree(qpair->cpl, 5673d7eb41cSJim Harris qpair->num_entries * sizeof(struct nvme_completion), 5683d7eb41cSJim Harris M_NVME); 5693d7eb41cSJim Harris } 5703d7eb41cSJim Harris 571bb0ec6b3SJim Harris if (qpair->dma_tag) 572bb0ec6b3SJim Harris bus_dma_tag_destroy(qpair->dma_tag); 573bb0ec6b3SJim Harris 574bb0ec6b3SJim Harris if (qpair->act_tr) 575bb0ec6b3SJim Harris free(qpair->act_tr, M_NVME); 576bb0ec6b3SJim Harris 57765c2474eSJim Harris while (!TAILQ_EMPTY(&qpair->free_tr)) { 57865c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 57965c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 580f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map); 581f2b19f67SJim Harris bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map); 582bb0ec6b3SJim Harris free(tr, M_NVME); 583bb0ec6b3SJim Harris } 584bb0ec6b3SJim Harris } 585bb0ec6b3SJim Harris 586b846efd7SJim Harris static void 587b846efd7SJim Harris nvme_admin_qpair_abort_aers(struct nvme_qpair *qpair) 588b846efd7SJim Harris { 589b846efd7SJim Harris struct nvme_tracker *tr; 590b846efd7SJim Harris 591b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 592b846efd7SJim Harris while (tr != NULL) { 593b846efd7SJim Harris if (tr->req->cmd.opc == NVME_OPC_ASYNC_EVENT_REQUEST) { 594b846efd7SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, 595232e2edbSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_SQ_DELETION, 0, 596b846efd7SJim Harris FALSE); 597b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 598b846efd7SJim Harris } else { 599b846efd7SJim Harris tr = TAILQ_NEXT(tr, tailq); 600b846efd7SJim Harris } 601b846efd7SJim Harris } 602b846efd7SJim Harris } 603b846efd7SJim Harris 604bb0ec6b3SJim Harris void 605bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 606bb0ec6b3SJim Harris { 607bb0ec6b3SJim Harris 608b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 609bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 610bb0ec6b3SJim Harris } 611bb0ec6b3SJim Harris 612bb0ec6b3SJim Harris void 613bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 614bb0ec6b3SJim Harris { 615bb0ec6b3SJim Harris 616bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 617bb0ec6b3SJim Harris } 618bb0ec6b3SJim Harris 619bb0ec6b3SJim Harris static void 6200a0b08ccSJim Harris nvme_abort_complete(void *arg, const struct nvme_completion *status) 6210a0b08ccSJim Harris { 622879de699SJim Harris struct nvme_tracker *tr = arg; 623879de699SJim Harris 624879de699SJim Harris /* 625879de699SJim Harris * If cdw0 == 1, the controller was not able to abort the command 626879de699SJim Harris * we requested. We still need to check the active tracker array, 627879de699SJim Harris * to cover race where I/O timed out at same time controller was 628879de699SJim Harris * completing the I/O. 629879de699SJim Harris */ 630879de699SJim Harris if (status->cdw0 == 1 && tr->qpair->act_tr[tr->cid] != NULL) { 631879de699SJim Harris /* 632879de699SJim Harris * An I/O has timed out, and the controller was unable to 633879de699SJim Harris * abort it for some reason. Construct a fake completion 634879de699SJim Harris * status, and then complete the I/O's tracker manually. 635879de699SJim Harris */ 636*547d523eSJim Harris nvme_printf(tr->qpair->ctrlr, 637*547d523eSJim Harris "abort command failed, aborting command manually\n"); 638b846efd7SJim Harris nvme_qpair_manual_complete_tracker(tr->qpair, tr, 639232e2edbSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST, 0, TRUE); 640879de699SJim Harris } 641879de699SJim Harris } 642879de699SJim Harris 643879de699SJim Harris static void 644bb0ec6b3SJim Harris nvme_timeout(void *arg) 645bb0ec6b3SJim Harris { 646448195e7SJim Harris struct nvme_tracker *tr = arg; 64712d191ecSJim Harris struct nvme_qpair *qpair = tr->qpair; 64812d191ecSJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 64912d191ecSJim Harris union csts_register csts; 650448195e7SJim Harris 65148ce3178SJim Harris /* Read csts to get value of cfs - controller fatal status. */ 65212d191ecSJim Harris csts.raw = nvme_mmio_read_4(ctrlr, csts); 65312d191ecSJim Harris 65448ce3178SJim Harris if (ctrlr->enable_aborts && csts.bits.cfs == 0) { 65548ce3178SJim Harris /* 65648ce3178SJim Harris * If aborts are enabled, only use them if the controller is 65748ce3178SJim Harris * not reporting fatal status. 65848ce3178SJim Harris */ 65912d191ecSJim Harris nvme_ctrlr_cmd_abort(ctrlr, tr->cid, qpair->id, 660879de699SJim Harris nvme_abort_complete, tr); 66148ce3178SJim Harris } else 66248ce3178SJim Harris nvme_ctrlr_reset(ctrlr); 663bb0ec6b3SJim Harris } 664bb0ec6b3SJim Harris 665bb0ec6b3SJim Harris void 666b846efd7SJim Harris nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr) 667bb0ec6b3SJim Harris { 668ad697276SJim Harris struct nvme_request *req; 66994143332SJim Harris struct nvme_controller *ctrlr; 670bb0ec6b3SJim Harris 671b846efd7SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 672b846efd7SJim Harris 673ad697276SJim Harris req = tr->req; 674ad697276SJim Harris req->cmd.cid = tr->cid; 675bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 67694143332SJim Harris ctrlr = qpair->ctrlr; 677bb0ec6b3SJim Harris 67894143332SJim Harris if (req->timeout) 679633c5729SJim Harris #if __FreeBSD_version >= 800030 68094143332SJim Harris callout_reset_curcpu(&tr->timer, ctrlr->timeout_period * hz, 681633c5729SJim Harris nvme_timeout, tr); 682633c5729SJim Harris #else 68394143332SJim Harris callout_reset(&tr->timer, ctrlr->timeout_period * hz, 68494143332SJim Harris nvme_timeout, tr); 685633c5729SJim Harris #endif 686bb0ec6b3SJim Harris 687bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 688ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 689bb0ec6b3SJim Harris 690bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 691bb0ec6b3SJim Harris qpair->sq_tail = 0; 692bb0ec6b3SJim Harris 693bb0ec6b3SJim Harris wmb(); 694bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl, 695bb0ec6b3SJim Harris qpair->sq_tail); 696bb0ec6b3SJim Harris 697bb0ec6b3SJim Harris qpair->num_cmds++; 698bb0ec6b3SJim Harris } 6995ae9ed68SJim Harris 700d6f54866SJim Harris static void 701d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 7025ae9ed68SJim Harris { 7035ae9ed68SJim Harris struct nvme_tracker *tr; 7045ae9ed68SJim Harris int err; 7055ae9ed68SJim Harris 706d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 7075ae9ed68SJim Harris 70865c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 709232e2edbSJim Harris req->qpair = qpair; 71021b6da58SJim Harris 711b846efd7SJim Harris if (tr == NULL || !qpair->is_enabled) { 7120f71ecf7SJim Harris /* 713b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 714232e2edbSJim Harris * an in-progress controller-level reset or controller 715232e2edbSJim Harris * failure. 716232e2edbSJim Harris */ 717232e2edbSJim Harris 718232e2edbSJim Harris if (qpair->ctrlr->is_failed) { 719232e2edbSJim Harris /* 720232e2edbSJim Harris * The controller has failed. Post the request to a 721232e2edbSJim Harris * task where it will be aborted, so that we do not 722232e2edbSJim Harris * invoke the request's callback in the context 723232e2edbSJim Harris * of the submission. 724232e2edbSJim Harris */ 725232e2edbSJim Harris nvme_ctrlr_post_failed_request(qpair->ctrlr, req); 726232e2edbSJim Harris } else { 727232e2edbSJim Harris /* 728232e2edbSJim Harris * Put the request on the qpair's request queue to be 729232e2edbSJim Harris * processed when a tracker frees up via a command 730232e2edbSJim Harris * completion or when the controller reset is 731232e2edbSJim Harris * completed. 7320f71ecf7SJim Harris */ 7330f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 734232e2edbSJim Harris } 735d6f54866SJim Harris return; 73621b6da58SJim Harris } 73721b6da58SJim Harris 73865c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 73965c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 7405ae9ed68SJim Harris tr->req = req; 7415ae9ed68SJim Harris 7425ae9ed68SJim Harris if (req->uio == NULL) { 7435ae9ed68SJim Harris if (req->payload_size > 0) { 7445ae9ed68SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, 7455ae9ed68SJim Harris tr->payload_dma_map, req->payload, 7465ae9ed68SJim Harris req->payload_size, 7475ae9ed68SJim Harris nvme_payload_map, tr, 0); 7485ae9ed68SJim Harris if (err != 0) 7495ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 7505ae9ed68SJim Harris } else 751b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 7525ae9ed68SJim Harris } else { 7535ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 7545ae9ed68SJim Harris tr->payload_dma_map, req->uio, 7555ae9ed68SJim Harris nvme_payload_map_uio, tr, 0); 7565ae9ed68SJim Harris if (err != 0) 7575ae9ed68SJim Harris panic("bus_dmamap_load returned non-zero!\n"); 7585ae9ed68SJim Harris } 759d6f54866SJim Harris } 7605ae9ed68SJim Harris 761d6f54866SJim Harris void 762d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 763d6f54866SJim Harris { 764d6f54866SJim Harris 765d6f54866SJim Harris mtx_lock(&qpair->lock); 766d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 7675ae9ed68SJim Harris mtx_unlock(&qpair->lock); 7685ae9ed68SJim Harris } 769b846efd7SJim Harris 770b846efd7SJim Harris static void 771b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 772b846efd7SJim Harris { 773b846efd7SJim Harris 774b846efd7SJim Harris qpair->is_enabled = TRUE; 775cb5b7c13SJim Harris } 776cb5b7c13SJim Harris 777cb5b7c13SJim Harris void 778cb5b7c13SJim Harris nvme_qpair_reset(struct nvme_qpair *qpair) 779cb5b7c13SJim Harris { 780cb5b7c13SJim Harris 781b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 782b846efd7SJim Harris 783b846efd7SJim Harris /* 784b846efd7SJim Harris * First time through the completion queue, HW will set phase 785b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 786b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 787b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 788b846efd7SJim Harris * rolls over. 789b846efd7SJim Harris */ 790b846efd7SJim Harris qpair->phase = 1; 791b846efd7SJim Harris 792b846efd7SJim Harris memset(qpair->cmd, 0, 793b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 794b846efd7SJim Harris memset(qpair->cpl, 0, 795b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 796b846efd7SJim Harris } 797b846efd7SJim Harris 798b846efd7SJim Harris void 799b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 800b846efd7SJim Harris { 80143a37256SJim Harris struct nvme_tracker *tr; 80243a37256SJim Harris struct nvme_tracker *tr_temp; 80343a37256SJim Harris 80443a37256SJim Harris /* 80543a37256SJim Harris * Manually abort each outstanding admin command. Do not retry 80643a37256SJim Harris * admin commands found here, since they will be left over from 80743a37256SJim Harris * a controller reset and its likely the context in which the 80843a37256SJim Harris * command was issued no longer applies. 80943a37256SJim Harris */ 81043a37256SJim Harris TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 811*547d523eSJim Harris nvme_printf(qpair->ctrlr, 81243a37256SJim Harris "aborting outstanding admin command\n"); 81343a37256SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 81443a37256SJim Harris NVME_SC_ABORTED_BY_REQUEST, 1 /* do not retry */, TRUE); 81543a37256SJim Harris } 816b846efd7SJim Harris 817b846efd7SJim Harris nvme_qpair_enable(qpair); 818b846efd7SJim Harris } 819b846efd7SJim Harris 820b846efd7SJim Harris void 821b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 822b846efd7SJim Harris { 823b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 824b846efd7SJim Harris struct nvme_tracker *tr; 825cb5b7c13SJim Harris struct nvme_tracker *tr_temp; 826b846efd7SJim Harris struct nvme_request *req; 827b846efd7SJim Harris 828cb5b7c13SJim Harris /* 829cb5b7c13SJim Harris * Manually abort each outstanding I/O. This normally results in a 830cb5b7c13SJim Harris * retry, unless the retry count on the associated request has 831cb5b7c13SJim Harris * reached its limit. 832cb5b7c13SJim Harris */ 833cb5b7c13SJim Harris TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 834*547d523eSJim Harris nvme_printf(qpair->ctrlr, "aborting outstanding i/o\n"); 835cb5b7c13SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 836232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, 0, TRUE); 837cb5b7c13SJim Harris } 838cb5b7c13SJim Harris 839b846efd7SJim Harris mtx_lock(&qpair->lock); 840b846efd7SJim Harris 841b846efd7SJim Harris nvme_qpair_enable(qpair); 842b846efd7SJim Harris 843b846efd7SJim Harris STAILQ_INIT(&temp); 844b846efd7SJim Harris STAILQ_SWAP(&qpair->queued_req, &temp, nvme_request); 845b846efd7SJim Harris 846b846efd7SJim Harris while (!STAILQ_EMPTY(&temp)) { 847b846efd7SJim Harris req = STAILQ_FIRST(&temp); 848b846efd7SJim Harris STAILQ_REMOVE_HEAD(&temp, stailq); 849*547d523eSJim Harris nvme_printf(qpair->ctrlr, "resubmitting queued i/o\n"); 850*547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 851b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 852b846efd7SJim Harris } 853b846efd7SJim Harris 854b846efd7SJim Harris mtx_unlock(&qpair->lock); 855b846efd7SJim Harris } 856b846efd7SJim Harris 857b846efd7SJim Harris static void 858b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 859b846efd7SJim Harris { 860b846efd7SJim Harris struct nvme_tracker *tr; 861b846efd7SJim Harris 862b846efd7SJim Harris qpair->is_enabled = FALSE; 863b846efd7SJim Harris mtx_lock(&qpair->lock); 864b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 865b846efd7SJim Harris callout_stop(&tr->timer); 866b846efd7SJim Harris mtx_unlock(&qpair->lock); 867b846efd7SJim Harris } 868b846efd7SJim Harris 869b846efd7SJim Harris void 870b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 871b846efd7SJim Harris { 872b846efd7SJim Harris 873b846efd7SJim Harris nvme_qpair_disable(qpair); 874b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 875b846efd7SJim Harris } 876b846efd7SJim Harris 877b846efd7SJim Harris void 878b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 879b846efd7SJim Harris { 880b846efd7SJim Harris 881b846efd7SJim Harris nvme_qpair_disable(qpair); 882b846efd7SJim Harris } 883232e2edbSJim Harris 884232e2edbSJim Harris void 885232e2edbSJim Harris nvme_qpair_fail(struct nvme_qpair *qpair) 886232e2edbSJim Harris { 887232e2edbSJim Harris struct nvme_tracker *tr; 888232e2edbSJim Harris struct nvme_request *req; 889232e2edbSJim Harris 890232e2edbSJim Harris mtx_lock(&qpair->lock); 891232e2edbSJim Harris 892232e2edbSJim Harris while (!STAILQ_EMPTY(&qpair->queued_req)) { 893232e2edbSJim Harris req = STAILQ_FIRST(&qpair->queued_req); 894232e2edbSJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 895*547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing queued i/o\n"); 896232e2edbSJim Harris mtx_unlock(&qpair->lock); 897232e2edbSJim Harris nvme_qpair_manual_complete_request(qpair, req, NVME_SCT_GENERIC, 898232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, TRUE); 899232e2edbSJim Harris mtx_lock(&qpair->lock); 900232e2edbSJim Harris } 901232e2edbSJim Harris 902232e2edbSJim Harris /* Manually abort each outstanding I/O. */ 903232e2edbSJim Harris while (!TAILQ_EMPTY(&qpair->outstanding_tr)) { 904232e2edbSJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 905232e2edbSJim Harris /* 906232e2edbSJim Harris * Do not remove the tracker. The abort_tracker path will 907232e2edbSJim Harris * do that for us. 908232e2edbSJim Harris */ 909*547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing outstanding i/o\n"); 910232e2edbSJim Harris mtx_unlock(&qpair->lock); 911232e2edbSJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 912232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, 1 /* do not retry */, TRUE); 913232e2edbSJim Harris mtx_lock(&qpair->lock); 914232e2edbSJim Harris } 915232e2edbSJim Harris 916232e2edbSJim Harris mtx_unlock(&qpair->lock); 917232e2edbSJim Harris } 918232e2edbSJim Harris 919