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 40547d523eSJim Harris struct nvme_opcode_string { 41547d523eSJim Harris 42547d523eSJim Harris uint16_t opc; 43547d523eSJim Harris const char * str; 44547d523eSJim Harris }; 45547d523eSJim Harris 46547d523eSJim Harris static struct nvme_opcode_string admin_opcode[] = { 47547d523eSJim Harris { NVME_OPC_DELETE_IO_SQ, "DELETE IO SQ" }, 48547d523eSJim Harris { NVME_OPC_CREATE_IO_SQ, "CREATE IO SQ" }, 49547d523eSJim Harris { NVME_OPC_GET_LOG_PAGE, "GET LOG PAGE" }, 50547d523eSJim Harris { NVME_OPC_DELETE_IO_CQ, "DELETE IO CQ" }, 51547d523eSJim Harris { NVME_OPC_CREATE_IO_CQ, "CREATE IO CQ" }, 52547d523eSJim Harris { NVME_OPC_IDENTIFY, "IDENTIFY" }, 53547d523eSJim Harris { NVME_OPC_ABORT, "ABORT" }, 54547d523eSJim Harris { NVME_OPC_SET_FEATURES, "SET FEATURES" }, 55547d523eSJim Harris { NVME_OPC_GET_FEATURES, "GET FEATURES" }, 56547d523eSJim Harris { NVME_OPC_ASYNC_EVENT_REQUEST, "ASYNC EVENT REQUEST" }, 57547d523eSJim Harris { NVME_OPC_FIRMWARE_ACTIVATE, "FIRMWARE ACTIVATE" }, 58547d523eSJim Harris { NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD, "FIRMWARE IMAGE DOWNLOAD" }, 59547d523eSJim Harris { NVME_OPC_FORMAT_NVM, "FORMAT NVM" }, 60547d523eSJim Harris { NVME_OPC_SECURITY_SEND, "SECURITY SEND" }, 61547d523eSJim Harris { NVME_OPC_SECURITY_RECEIVE, "SECURITY RECEIVE" }, 62547d523eSJim Harris { 0xFFFF, "ADMIN COMMAND" } 63547d523eSJim Harris }; 64547d523eSJim Harris 65547d523eSJim Harris static struct nvme_opcode_string io_opcode[] = { 66547d523eSJim Harris { NVME_OPC_FLUSH, "FLUSH" }, 67547d523eSJim Harris { NVME_OPC_WRITE, "WRITE" }, 68547d523eSJim Harris { NVME_OPC_READ, "READ" }, 69547d523eSJim Harris { NVME_OPC_WRITE_UNCORRECTABLE, "WRITE UNCORRECTABLE" }, 70547d523eSJim Harris { NVME_OPC_COMPARE, "COMPARE" }, 71547d523eSJim Harris { NVME_OPC_DATASET_MANAGEMENT, "DATASET MANAGEMENT" }, 72547d523eSJim Harris { 0xFFFF, "IO COMMAND" } 73547d523eSJim Harris }; 74547d523eSJim Harris 75547d523eSJim Harris static const char * 76547d523eSJim Harris get_admin_opcode_string(uint16_t opc) 77547d523eSJim Harris { 78547d523eSJim Harris struct nvme_opcode_string *entry; 79547d523eSJim Harris 80547d523eSJim Harris entry = admin_opcode; 81547d523eSJim Harris 82547d523eSJim Harris while (entry->opc != 0xFFFF) { 83547d523eSJim Harris if (entry->opc == opc) 84547d523eSJim Harris return (entry->str); 85547d523eSJim Harris entry++; 86547d523eSJim Harris } 87547d523eSJim Harris return (entry->str); 88547d523eSJim Harris } 89547d523eSJim Harris 90547d523eSJim Harris static const char * 91547d523eSJim Harris get_io_opcode_string(uint16_t opc) 92547d523eSJim Harris { 93547d523eSJim Harris struct nvme_opcode_string *entry; 94547d523eSJim Harris 95547d523eSJim Harris entry = io_opcode; 96547d523eSJim Harris 97547d523eSJim Harris while (entry->opc != 0xFFFF) { 98547d523eSJim Harris if (entry->opc == opc) 99547d523eSJim Harris return (entry->str); 100547d523eSJim Harris entry++; 101547d523eSJim Harris } 102547d523eSJim Harris return (entry->str); 103547d523eSJim Harris } 104547d523eSJim Harris 105547d523eSJim Harris 106547d523eSJim Harris static void 107547d523eSJim Harris nvme_admin_qpair_print_command(struct nvme_qpair *qpair, 108547d523eSJim Harris struct nvme_command *cmd) 109547d523eSJim Harris { 110547d523eSJim Harris 111547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%x " 112547d523eSJim Harris "cdw10:%08x cdw11:%08x\n", 113547d523eSJim Harris get_admin_opcode_string(cmd->opc), cmd->opc, qpair->id, cmd->cid, 114547d523eSJim Harris cmd->nsid, cmd->cdw10, cmd->cdw11); 115547d523eSJim Harris } 116547d523eSJim Harris 117547d523eSJim Harris static void 118547d523eSJim Harris nvme_io_qpair_print_command(struct nvme_qpair *qpair, 119547d523eSJim Harris struct nvme_command *cmd) 120547d523eSJim Harris { 121547d523eSJim Harris 122547d523eSJim Harris switch (cmd->opc) { 123547d523eSJim Harris case NVME_OPC_WRITE: 124547d523eSJim Harris case NVME_OPC_READ: 125547d523eSJim Harris case NVME_OPC_WRITE_UNCORRECTABLE: 126547d523eSJim Harris case NVME_OPC_COMPARE: 127547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d " 128bdd1fd40SJim Harris "lba:%llu len:%d\n", 129547d523eSJim Harris get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, 130bdd1fd40SJim Harris cmd->nsid, 131bdd1fd40SJim Harris ((unsigned long long)cmd->cdw11 << 32) + cmd->cdw10, 132547d523eSJim Harris (cmd->cdw12 & 0xFFFF) + 1); 133547d523eSJim Harris break; 134547d523eSJim Harris case NVME_OPC_FLUSH: 135547d523eSJim Harris case NVME_OPC_DATASET_MANAGEMENT: 136547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d\n", 137547d523eSJim Harris get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, 138547d523eSJim Harris cmd->nsid); 139547d523eSJim Harris break; 140547d523eSJim Harris default: 141547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%d\n", 142547d523eSJim Harris get_io_opcode_string(cmd->opc), cmd->opc, qpair->id, 143547d523eSJim Harris cmd->cid, cmd->nsid); 144547d523eSJim Harris break; 145547d523eSJim Harris } 146547d523eSJim Harris } 147547d523eSJim Harris 148547d523eSJim Harris static void 149547d523eSJim Harris nvme_qpair_print_command(struct nvme_qpair *qpair, struct nvme_command *cmd) 150547d523eSJim Harris { 151547d523eSJim Harris if (qpair->id == 0) 152547d523eSJim Harris nvme_admin_qpair_print_command(qpair, cmd); 153547d523eSJim Harris else 154547d523eSJim Harris nvme_io_qpair_print_command(qpair, cmd); 155547d523eSJim Harris } 156547d523eSJim Harris 157547d523eSJim Harris struct nvme_status_string { 158547d523eSJim Harris 159547d523eSJim Harris uint16_t sc; 160547d523eSJim Harris const char * str; 161547d523eSJim Harris }; 162547d523eSJim Harris 163547d523eSJim Harris static struct nvme_status_string generic_status[] = { 164547d523eSJim Harris { NVME_SC_SUCCESS, "SUCCESS" }, 165547d523eSJim Harris { NVME_SC_INVALID_OPCODE, "INVALID OPCODE" }, 166547d523eSJim Harris { NVME_SC_INVALID_FIELD, "INVALID_FIELD" }, 167547d523eSJim Harris { NVME_SC_COMMAND_ID_CONFLICT, "COMMAND ID CONFLICT" }, 168547d523eSJim Harris { NVME_SC_DATA_TRANSFER_ERROR, "DATA TRANSFER ERROR" }, 169547d523eSJim Harris { NVME_SC_ABORTED_POWER_LOSS, "ABORTED - POWER LOSS" }, 170547d523eSJim Harris { NVME_SC_INTERNAL_DEVICE_ERROR, "INTERNAL DEVICE ERROR" }, 171547d523eSJim Harris { NVME_SC_ABORTED_BY_REQUEST, "ABORTED - BY REQUEST" }, 172547d523eSJim Harris { NVME_SC_ABORTED_SQ_DELETION, "ABORTED - SQ DELETION" }, 173547d523eSJim Harris { NVME_SC_ABORTED_FAILED_FUSED, "ABORTED - FAILED FUSED" }, 174547d523eSJim Harris { NVME_SC_ABORTED_MISSING_FUSED, "ABORTED - MISSING FUSED" }, 175547d523eSJim Harris { NVME_SC_INVALID_NAMESPACE_OR_FORMAT, "INVALID NAMESPACE OR FORMAT" }, 176547d523eSJim Harris { NVME_SC_COMMAND_SEQUENCE_ERROR, "COMMAND SEQUENCE ERROR" }, 177547d523eSJim Harris { NVME_SC_LBA_OUT_OF_RANGE, "LBA OUT OF RANGE" }, 178547d523eSJim Harris { NVME_SC_CAPACITY_EXCEEDED, "CAPACITY EXCEEDED" }, 179547d523eSJim Harris { NVME_SC_NAMESPACE_NOT_READY, "NAMESPACE NOT READY" }, 180547d523eSJim Harris { 0xFFFF, "GENERIC" } 181547d523eSJim Harris }; 182547d523eSJim Harris 183547d523eSJim Harris static struct nvme_status_string command_specific_status[] = { 184547d523eSJim Harris { NVME_SC_COMPLETION_QUEUE_INVALID, "INVALID COMPLETION QUEUE" }, 185547d523eSJim Harris { NVME_SC_INVALID_QUEUE_IDENTIFIER, "INVALID QUEUE IDENTIFIER" }, 186547d523eSJim Harris { NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED, "MAX QUEUE SIZE EXCEEDED" }, 187547d523eSJim Harris { NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED, "ABORT CMD LIMIT EXCEEDED" }, 188547d523eSJim Harris { NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED, "ASYNC LIMIT EXCEEDED" }, 189547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_SLOT, "INVALID FIRMWARE SLOT" }, 190547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_IMAGE, "INVALID FIRMWARE IMAGE" }, 191547d523eSJim Harris { NVME_SC_INVALID_INTERRUPT_VECTOR, "INVALID INTERRUPT VECTOR" }, 192547d523eSJim Harris { NVME_SC_INVALID_LOG_PAGE, "INVALID LOG PAGE" }, 193547d523eSJim Harris { NVME_SC_INVALID_FORMAT, "INVALID FORMAT" }, 194547d523eSJim Harris { NVME_SC_FIRMWARE_REQUIRES_RESET, "FIRMWARE REQUIRES RESET" }, 195547d523eSJim Harris { NVME_SC_CONFLICTING_ATTRIBUTES, "CONFLICTING ATTRIBUTES" }, 196547d523eSJim Harris { NVME_SC_INVALID_PROTECTION_INFO, "INVALID PROTECTION INFO" }, 197547d523eSJim Harris { NVME_SC_ATTEMPTED_WRITE_TO_RO_PAGE, "WRITE TO RO PAGE" }, 198547d523eSJim Harris { 0xFFFF, "COMMAND SPECIFIC" } 199547d523eSJim Harris }; 200547d523eSJim Harris 201547d523eSJim Harris static struct nvme_status_string media_error_status[] = { 202547d523eSJim Harris { NVME_SC_WRITE_FAULTS, "WRITE FAULTS" }, 203547d523eSJim Harris { NVME_SC_UNRECOVERED_READ_ERROR, "UNRECOVERED READ ERROR" }, 204547d523eSJim Harris { NVME_SC_GUARD_CHECK_ERROR, "GUARD CHECK ERROR" }, 205547d523eSJim Harris { NVME_SC_APPLICATION_TAG_CHECK_ERROR, "APPLICATION TAG CHECK ERROR" }, 206547d523eSJim Harris { NVME_SC_REFERENCE_TAG_CHECK_ERROR, "REFERENCE TAG CHECK ERROR" }, 207547d523eSJim Harris { NVME_SC_COMPARE_FAILURE, "COMPARE FAILURE" }, 208547d523eSJim Harris { NVME_SC_ACCESS_DENIED, "ACCESS DENIED" }, 209547d523eSJim Harris { 0xFFFF, "MEDIA ERROR" } 210547d523eSJim Harris }; 211547d523eSJim Harris 212547d523eSJim Harris static const char * 213547d523eSJim Harris get_status_string(uint16_t sct, uint16_t sc) 214547d523eSJim Harris { 215547d523eSJim Harris struct nvme_status_string *entry; 216547d523eSJim Harris 217547d523eSJim Harris switch (sct) { 218547d523eSJim Harris case NVME_SCT_GENERIC: 219547d523eSJim Harris entry = generic_status; 220547d523eSJim Harris break; 221547d523eSJim Harris case NVME_SCT_COMMAND_SPECIFIC: 222547d523eSJim Harris entry = command_specific_status; 223547d523eSJim Harris break; 224547d523eSJim Harris case NVME_SCT_MEDIA_ERROR: 225547d523eSJim Harris entry = media_error_status; 226547d523eSJim Harris break; 227547d523eSJim Harris case NVME_SCT_VENDOR_SPECIFIC: 228547d523eSJim Harris return ("VENDOR SPECIFIC"); 229547d523eSJim Harris default: 230547d523eSJim Harris return ("RESERVED"); 231547d523eSJim Harris } 232547d523eSJim Harris 233547d523eSJim Harris while (entry->sc != 0xFFFF) { 234547d523eSJim Harris if (entry->sc == sc) 235547d523eSJim Harris return (entry->str); 236547d523eSJim Harris entry++; 237547d523eSJim Harris } 238547d523eSJim Harris return (entry->str); 239547d523eSJim Harris } 240547d523eSJim Harris 241547d523eSJim Harris static void 242547d523eSJim Harris nvme_qpair_print_completion(struct nvme_qpair *qpair, 243547d523eSJim Harris struct nvme_completion *cpl) 244547d523eSJim Harris { 245547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x/%02x) sqid:%d cid:%d cdw0:%x\n", 246547d523eSJim Harris get_status_string(cpl->status.sct, cpl->status.sc), 247547d523eSJim Harris cpl->status.sct, cpl->status.sc, cpl->sqid, cpl->cid, cpl->cdw0); 248547d523eSJim Harris } 249547d523eSJim Harris 250bb0ec6b3SJim Harris static boolean_t 2516cb06070SJim Harris nvme_completion_is_retry(const struct nvme_completion *cpl) 252bb0ec6b3SJim Harris { 253bb0ec6b3SJim Harris /* 254bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 255bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 256bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 257bb0ec6b3SJim Harris * look at the DNR bit. 258bb0ec6b3SJim Harris */ 259cf81529cSJim Harris switch (cpl->status.sct) { 260bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 261cf81529cSJim Harris switch (cpl->status.sc) { 262448195e7SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 263bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 264cf81529cSJim Harris if (cpl->status.dnr) 265bb0ec6b3SJim Harris return (0); 266bb0ec6b3SJim Harris else 267bb0ec6b3SJim Harris return (1); 268bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 269bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 270bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 271bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 272bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 273bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 274bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 275bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 276bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 277bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 278bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 279bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 280bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 281bb0ec6b3SJim Harris default: 282bb0ec6b3SJim Harris return (0); 283bb0ec6b3SJim Harris } 284bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 285bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 286bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 287bb0ec6b3SJim Harris default: 288bb0ec6b3SJim Harris return (0); 289bb0ec6b3SJim Harris } 290bb0ec6b3SJim Harris } 291bb0ec6b3SJim Harris 29221b6da58SJim Harris static void 29321b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 29421b6da58SJim Harris uint16_t cid) 295bb0ec6b3SJim Harris { 2969eb93f29SJim Harris 297f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map); 298f2b19f67SJim Harris bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map); 299f2b19f67SJim Harris 300f2b19f67SJim Harris bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp, 301f2b19f67SJim Harris sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0); 302f2b19f67SJim Harris 30312d191ecSJim Harris callout_init(&tr->timer, 1); 30421b6da58SJim Harris tr->cid = cid; 305d281e8fbSJim Harris tr->qpair = qpair; 306bb0ec6b3SJim Harris } 307bb0ec6b3SJim Harris 3086cb06070SJim Harris static void 3096cb06070SJim Harris nvme_qpair_complete_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr, 3106cb06070SJim Harris struct nvme_completion *cpl, boolean_t print_on_error) 311bb0ec6b3SJim Harris { 312ad697276SJim Harris struct nvme_request *req; 313bb0ec6b3SJim Harris boolean_t retry, error; 314bb0ec6b3SJim Harris 315ad697276SJim Harris req = tr->req; 3166cb06070SJim Harris error = nvme_completion_is_error(cpl); 317cb5b7c13SJim Harris retry = error && nvme_completion_is_retry(cpl) && 318cb5b7c13SJim Harris req->retries < nvme_retry_count; 319ad697276SJim Harris 3206cb06070SJim Harris if (error && print_on_error) { 321547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 322547d523eSJim Harris nvme_qpair_print_completion(qpair, cpl); 323bb0ec6b3SJim Harris } 324bb0ec6b3SJim Harris 325bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 326bb0ec6b3SJim Harris 3276cb06070SJim Harris KASSERT(cpl->cid == req->cmd.cid, ("cpl cid does not match cmd cid\n")); 328bb0ec6b3SJim Harris 329ad697276SJim Harris if (req->cb_fn && !retry) 330ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 331bb0ec6b3SJim Harris 332bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 333bb0ec6b3SJim Harris callout_stop(&tr->timer); 334bb0ec6b3SJim Harris 335cb5b7c13SJim Harris if (retry) { 336cb5b7c13SJim Harris req->retries++; 337b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 338cb5b7c13SJim Harris } else { 3391e526bc4SJim Harris if (req->type != NVME_REQUEST_NULL) 340f2b19f67SJim Harris bus_dmamap_unload(qpair->dma_tag, 341f2b19f67SJim Harris tr->payload_dma_map); 342bb0ec6b3SJim Harris 343ad697276SJim Harris nvme_free_request(req); 3440a0b08ccSJim Harris tr->req = NULL; 34521b6da58SJim Harris 34665c2474eSJim Harris TAILQ_REMOVE(&qpair->outstanding_tr, tr, tailq); 34765c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 3480f71ecf7SJim Harris 349f37c22a3SJim Harris /* 350f37c22a3SJim Harris * If the controller is in the middle of resetting, don't 351f37c22a3SJim Harris * try to submit queued requests here - let the reset logic 352f37c22a3SJim Harris * handle that instead. 353f37c22a3SJim Harris */ 354f37c22a3SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req) && 355f37c22a3SJim Harris !qpair->ctrlr->is_resetting) { 3560f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 3570f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 358d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 3590f71ecf7SJim Harris } 360c2e83b40SJim Harris } 361bb0ec6b3SJim Harris 362bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 3636cb06070SJim Harris } 3646cb06070SJim Harris 365b846efd7SJim Harris static void 366b846efd7SJim Harris nvme_qpair_manual_complete_tracker(struct nvme_qpair *qpair, 367232e2edbSJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr, 368b846efd7SJim Harris boolean_t print_on_error) 369b846efd7SJim Harris { 370b846efd7SJim Harris struct nvme_completion cpl; 371b846efd7SJim Harris 372b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 373b846efd7SJim Harris cpl.sqid = qpair->id; 374b846efd7SJim Harris cpl.cid = tr->cid; 375cf81529cSJim Harris cpl.status.sct = sct; 376cf81529cSJim Harris cpl.status.sc = sc; 377232e2edbSJim Harris cpl.status.dnr = dnr; 378b846efd7SJim Harris nvme_qpair_complete_tracker(qpair, tr, &cpl, print_on_error); 379b846efd7SJim Harris } 380b846efd7SJim Harris 3816cb06070SJim Harris void 382232e2edbSJim Harris nvme_qpair_manual_complete_request(struct nvme_qpair *qpair, 383232e2edbSJim Harris struct nvme_request *req, uint32_t sct, uint32_t sc, 384232e2edbSJim Harris boolean_t print_on_error) 385232e2edbSJim Harris { 386232e2edbSJim Harris struct nvme_completion cpl; 387232e2edbSJim Harris boolean_t error; 388232e2edbSJim Harris 389232e2edbSJim Harris memset(&cpl, 0, sizeof(cpl)); 390232e2edbSJim Harris cpl.sqid = qpair->id; 391232e2edbSJim Harris cpl.status.sct = sct; 392232e2edbSJim Harris cpl.status.sc = sc; 393232e2edbSJim Harris 394232e2edbSJim Harris error = nvme_completion_is_error(&cpl); 395232e2edbSJim Harris 396232e2edbSJim Harris if (error && print_on_error) { 397547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 398547d523eSJim Harris nvme_qpair_print_completion(qpair, &cpl); 399232e2edbSJim Harris } 400232e2edbSJim Harris 401232e2edbSJim Harris if (req->cb_fn) 402232e2edbSJim Harris req->cb_fn(req->cb_arg, &cpl); 403232e2edbSJim Harris 404232e2edbSJim Harris nvme_free_request(req); 405232e2edbSJim Harris } 406232e2edbSJim Harris 407232e2edbSJim Harris void 4086cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 4096cb06070SJim Harris { 4106cb06070SJim Harris struct nvme_tracker *tr; 4116cb06070SJim Harris struct nvme_completion *cpl; 4126cb06070SJim Harris 4136cb06070SJim Harris qpair->num_intr_handler_calls++; 4146cb06070SJim Harris 415b846efd7SJim Harris if (!qpair->is_enabled) 416b846efd7SJim Harris /* 417b846efd7SJim Harris * qpair is not enabled, likely because a controller reset is 418b846efd7SJim Harris * is in progress. Ignore the interrupt - any I/O that was 419b846efd7SJim Harris * associated with this interrupt will get retried when the 420b846efd7SJim Harris * reset is complete. 421b846efd7SJim Harris */ 422b846efd7SJim Harris return; 423b846efd7SJim Harris 4246cb06070SJim Harris while (1) { 4256cb06070SJim Harris cpl = &qpair->cpl[qpair->cq_head]; 4266cb06070SJim Harris 427cf81529cSJim Harris if (cpl->status.p != qpair->phase) 4286cb06070SJim Harris break; 4296cb06070SJim Harris 4306cb06070SJim Harris tr = qpair->act_tr[cpl->cid]; 4316cb06070SJim Harris 4326cb06070SJim Harris if (tr != NULL) { 4336cb06070SJim Harris nvme_qpair_complete_tracker(qpair, tr, cpl, TRUE); 4346cb06070SJim Harris qpair->sq_head = cpl->sqhd; 4356cb06070SJim Harris } else { 436547d523eSJim Harris nvme_printf(qpair->ctrlr, 437547d523eSJim Harris "cpl does not map to outstanding cmd\n"); 4386cb06070SJim Harris nvme_dump_completion(cpl); 4396cb06070SJim Harris KASSERT(0, ("received completion for unknown cmd\n")); 4406cb06070SJim Harris } 441bb0ec6b3SJim Harris 442bb0ec6b3SJim Harris if (++qpair->cq_head == qpair->num_entries) { 443bb0ec6b3SJim Harris qpair->cq_head = 0; 444bb0ec6b3SJim Harris qpair->phase = !qpair->phase; 445bb0ec6b3SJim Harris } 446bb0ec6b3SJim Harris 447bb0ec6b3SJim Harris nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl, 448bb0ec6b3SJim Harris qpair->cq_head); 449bb0ec6b3SJim Harris } 450bb0ec6b3SJim Harris } 451bb0ec6b3SJim Harris 452bb0ec6b3SJim Harris static void 453bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg) 454bb0ec6b3SJim Harris { 455bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 456bb0ec6b3SJim Harris 457bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 458bb0ec6b3SJim Harris } 459bb0ec6b3SJim Harris 460bb0ec6b3SJim Harris void 461bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, 46221b6da58SJim Harris uint16_t vector, uint32_t num_entries, uint32_t num_trackers, 4638d09e3c4SJim Harris struct nvme_controller *ctrlr) 464bb0ec6b3SJim Harris { 46521b6da58SJim Harris struct nvme_tracker *tr; 46621b6da58SJim Harris uint32_t i; 467bb0ec6b3SJim Harris 468bb0ec6b3SJim Harris qpair->id = id; 469bb0ec6b3SJim Harris qpair->vector = vector; 470bb0ec6b3SJim Harris qpair->num_entries = num_entries; 4710f71ecf7SJim Harris #ifdef CHATHAM2 4720f71ecf7SJim Harris /* 4730f71ecf7SJim Harris * Chatham prototype board starts having issues at higher queue 4740f71ecf7SJim Harris * depths. So use a conservative estimate here of no more than 64 4750f71ecf7SJim Harris * outstanding I/O per queue at any one point. 4760f71ecf7SJim Harris */ 4770f71ecf7SJim Harris if (pci_get_devid(ctrlr->dev) == CHATHAM_PCI_ID) 4780f71ecf7SJim Harris num_trackers = min(num_trackers, 64); 4790f71ecf7SJim Harris #endif 4800f71ecf7SJim Harris qpair->num_trackers = num_trackers; 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, 5038d09e3c4SJim Harris BUS_SPACE_MAXADDR, NULL, NULL, NVME_MAX_XFER_SIZE, 5048d09e3c4SJim Harris (NVME_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 */ 636547d523eSJim Harris nvme_printf(tr->qpair->ctrlr, 637547d523eSJim 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 701ca269f32SJim Harris nvme_payload_map(void *arg, bus_dma_segment_t *seg, int nseg, int error) 702ca269f32SJim Harris { 703ca269f32SJim Harris struct nvme_tracker *tr = arg; 704ca269f32SJim Harris uint32_t cur_nseg; 705ca269f32SJim Harris 706ca269f32SJim Harris /* 707ca269f32SJim Harris * If the mapping operation failed, return immediately. The caller 708ca269f32SJim Harris * is responsible for detecting the error status and failing the 709ca269f32SJim Harris * tracker manually. 710ca269f32SJim Harris */ 711ca269f32SJim Harris if (error != 0) 712ca269f32SJim Harris return; 713ca269f32SJim Harris 714ca269f32SJim Harris /* 715ca269f32SJim Harris * Note that we specified PAGE_SIZE for alignment and max 716ca269f32SJim Harris * segment size when creating the bus dma tags. So here 717ca269f32SJim Harris * we can safely just transfer each segment to its 718ca269f32SJim Harris * associated PRP entry. 719ca269f32SJim Harris */ 720ca269f32SJim Harris tr->req->cmd.prp1 = seg[0].ds_addr; 721ca269f32SJim Harris 722ca269f32SJim Harris if (nseg == 2) { 723ca269f32SJim Harris tr->req->cmd.prp2 = seg[1].ds_addr; 724ca269f32SJim Harris } else if (nseg > 2) { 725ca269f32SJim Harris cur_nseg = 1; 726ca269f32SJim Harris tr->req->cmd.prp2 = (uint64_t)tr->prp_bus_addr; 727ca269f32SJim Harris while (cur_nseg < nseg) { 728ca269f32SJim Harris tr->prp[cur_nseg-1] = 729ca269f32SJim Harris (uint64_t)seg[cur_nseg].ds_addr; 730ca269f32SJim Harris cur_nseg++; 731ca269f32SJim Harris } 732ca269f32SJim Harris } 733ca269f32SJim Harris 734ca269f32SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 735ca269f32SJim Harris } 736ca269f32SJim Harris 737ca269f32SJim Harris static void 738ca269f32SJim Harris nvme_payload_map_uio(void *arg, bus_dma_segment_t *seg, int nseg, 739ca269f32SJim Harris bus_size_t mapsize, int error) 740ca269f32SJim Harris { 741ca269f32SJim Harris 742ca269f32SJim Harris nvme_payload_map(arg, seg, nseg, error); 743ca269f32SJim Harris } 744ca269f32SJim Harris 745ca269f32SJim Harris static void 746d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 7475ae9ed68SJim Harris { 7485ae9ed68SJim Harris struct nvme_tracker *tr; 749e2b99004SJim Harris int err = 0; 7505ae9ed68SJim Harris 751d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 7525ae9ed68SJim Harris 75365c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 754232e2edbSJim Harris req->qpair = qpair; 75521b6da58SJim Harris 756b846efd7SJim Harris if (tr == NULL || !qpair->is_enabled) { 7570f71ecf7SJim Harris /* 758b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 759232e2edbSJim Harris * an in-progress controller-level reset or controller 760232e2edbSJim Harris * failure. 761232e2edbSJim Harris */ 762232e2edbSJim Harris 763232e2edbSJim Harris if (qpair->ctrlr->is_failed) { 764232e2edbSJim Harris /* 765232e2edbSJim Harris * The controller has failed. Post the request to a 766232e2edbSJim Harris * task where it will be aborted, so that we do not 767232e2edbSJim Harris * invoke the request's callback in the context 768232e2edbSJim Harris * of the submission. 769232e2edbSJim Harris */ 770232e2edbSJim Harris nvme_ctrlr_post_failed_request(qpair->ctrlr, req); 771232e2edbSJim Harris } else { 772232e2edbSJim Harris /* 773232e2edbSJim Harris * Put the request on the qpair's request queue to be 774232e2edbSJim Harris * processed when a tracker frees up via a command 775232e2edbSJim Harris * completion or when the controller reset is 776232e2edbSJim Harris * completed. 7770f71ecf7SJim Harris */ 7780f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 779232e2edbSJim Harris } 780d6f54866SJim Harris return; 78121b6da58SJim Harris } 78221b6da58SJim Harris 78365c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 78465c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 7855ae9ed68SJim Harris tr->req = req; 7865ae9ed68SJim Harris 7871e526bc4SJim Harris switch (req->type) { 7881e526bc4SJim Harris case NVME_REQUEST_VADDR: 789*7b68ae1eSJim Harris KASSERT(req->payload_size <= qpair->ctrlr->max_xfer_size, 790*7b68ae1eSJim Harris ("payload_size (%d) exceeds max_xfer_size (%d)\n", 791*7b68ae1eSJim Harris req->payload_size, qpair->ctrlr->max_xfer_size)); 7921e526bc4SJim Harris err = bus_dmamap_load(tr->qpair->dma_tag, tr->payload_dma_map, 7931e526bc4SJim Harris req->u.payload, req->payload_size, nvme_payload_map, tr, 0); 7945ae9ed68SJim Harris if (err != 0) 795e2b99004SJim Harris nvme_printf(qpair->ctrlr, 796e2b99004SJim Harris "bus_dmamap_load returned 0x%x!\n", err); 7971e526bc4SJim Harris break; 7981e526bc4SJim Harris case NVME_REQUEST_NULL: 799b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 8001e526bc4SJim Harris break; 8011e526bc4SJim Harris case NVME_REQUEST_UIO: 8025ae9ed68SJim Harris err = bus_dmamap_load_uio(tr->qpair->dma_tag, 8031e526bc4SJim Harris tr->payload_dma_map, req->u.uio, nvme_payload_map_uio, 8041e526bc4SJim Harris tr, 0); 8055ae9ed68SJim Harris if (err != 0) 806e2b99004SJim Harris nvme_printf(qpair->ctrlr, 807e2b99004SJim Harris "bus_dmamap_load_uio returned 0x%x!\n", err); 8081e526bc4SJim Harris break; 8095fdf9c3cSJim Harris #ifdef NVME_UNMAPPED_BIO_SUPPORT 8105fdf9c3cSJim Harris case NVME_REQUEST_BIO: 811*7b68ae1eSJim Harris KASSERT(req->u.bio->bio_bcount <= qpair->ctrlr->max_xfer_size, 812*7b68ae1eSJim Harris ("bio->bio_bcount (%jd) exceeds max_xfer_size (%d)\n", 813*7b68ae1eSJim Harris (intmax_t)req->u.bio->bio_bcount, 814*7b68ae1eSJim Harris qpair->ctrlr->max_xfer_size)); 8155fdf9c3cSJim Harris err = bus_dmamap_load_bio(tr->qpair->dma_tag, 8165fdf9c3cSJim Harris tr->payload_dma_map, req->u.bio, nvme_payload_map, tr, 0); 8175fdf9c3cSJim Harris if (err != 0) 818e2b99004SJim Harris nvme_printf(qpair->ctrlr, 819e2b99004SJim Harris "bus_dmamap_load_bio returned 0x%x!\n", err); 8205fdf9c3cSJim Harris break; 8215fdf9c3cSJim Harris #endif 8221e526bc4SJim Harris default: 8231e526bc4SJim Harris panic("unknown nvme request type 0x%x\n", req->type); 8241e526bc4SJim Harris break; 8255ae9ed68SJim Harris } 826e2b99004SJim Harris 827e2b99004SJim Harris if (err != 0) { 828e2b99004SJim Harris /* 829e2b99004SJim Harris * The dmamap operation failed, so we manually fail the 830e2b99004SJim Harris * tracker here with DATA_TRANSFER_ERROR status. 831e2b99004SJim Harris * 832e2b99004SJim Harris * nvme_qpair_manual_complete_tracker must not be called 833e2b99004SJim Harris * with the qpair lock held. 834e2b99004SJim Harris */ 835e2b99004SJim Harris mtx_unlock(&qpair->lock); 836e2b99004SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 837e2b99004SJim Harris NVME_SC_DATA_TRANSFER_ERROR, 1 /* do not retry */, TRUE); 838e2b99004SJim Harris mtx_lock(&qpair->lock); 839e2b99004SJim Harris } 840d6f54866SJim Harris } 8415ae9ed68SJim Harris 842d6f54866SJim Harris void 843d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 844d6f54866SJim Harris { 845d6f54866SJim Harris 846d6f54866SJim Harris mtx_lock(&qpair->lock); 847d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 8485ae9ed68SJim Harris mtx_unlock(&qpair->lock); 8495ae9ed68SJim Harris } 850b846efd7SJim Harris 851b846efd7SJim Harris static void 852b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 853b846efd7SJim Harris { 854b846efd7SJim Harris 855b846efd7SJim Harris qpair->is_enabled = TRUE; 856cb5b7c13SJim Harris } 857cb5b7c13SJim Harris 858cb5b7c13SJim Harris void 859cb5b7c13SJim Harris nvme_qpair_reset(struct nvme_qpair *qpair) 860cb5b7c13SJim Harris { 861cb5b7c13SJim Harris 862b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 863b846efd7SJim Harris 864b846efd7SJim Harris /* 865b846efd7SJim Harris * First time through the completion queue, HW will set phase 866b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 867b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 868b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 869b846efd7SJim Harris * rolls over. 870b846efd7SJim Harris */ 871b846efd7SJim Harris qpair->phase = 1; 872b846efd7SJim Harris 873b846efd7SJim Harris memset(qpair->cmd, 0, 874b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 875b846efd7SJim Harris memset(qpair->cpl, 0, 876b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 877b846efd7SJim Harris } 878b846efd7SJim Harris 879b846efd7SJim Harris void 880b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 881b846efd7SJim Harris { 88243a37256SJim Harris struct nvme_tracker *tr; 88343a37256SJim Harris struct nvme_tracker *tr_temp; 88443a37256SJim Harris 88543a37256SJim Harris /* 88643a37256SJim Harris * Manually abort each outstanding admin command. Do not retry 88743a37256SJim Harris * admin commands found here, since they will be left over from 88843a37256SJim Harris * a controller reset and its likely the context in which the 88943a37256SJim Harris * command was issued no longer applies. 89043a37256SJim Harris */ 89143a37256SJim Harris TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 892547d523eSJim Harris nvme_printf(qpair->ctrlr, 89343a37256SJim Harris "aborting outstanding admin command\n"); 89443a37256SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 89543a37256SJim Harris NVME_SC_ABORTED_BY_REQUEST, 1 /* do not retry */, TRUE); 89643a37256SJim Harris } 897b846efd7SJim Harris 898b846efd7SJim Harris nvme_qpair_enable(qpair); 899b846efd7SJim Harris } 900b846efd7SJim Harris 901b846efd7SJim Harris void 902b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 903b846efd7SJim Harris { 904b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 905b846efd7SJim Harris struct nvme_tracker *tr; 906cb5b7c13SJim Harris struct nvme_tracker *tr_temp; 907b846efd7SJim Harris struct nvme_request *req; 908b846efd7SJim Harris 909cb5b7c13SJim Harris /* 910cb5b7c13SJim Harris * Manually abort each outstanding I/O. This normally results in a 911cb5b7c13SJim Harris * retry, unless the retry count on the associated request has 912cb5b7c13SJim Harris * reached its limit. 913cb5b7c13SJim Harris */ 914cb5b7c13SJim Harris TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 915547d523eSJim Harris nvme_printf(qpair->ctrlr, "aborting outstanding i/o\n"); 916cb5b7c13SJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 917232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, 0, TRUE); 918cb5b7c13SJim Harris } 919cb5b7c13SJim Harris 920b846efd7SJim Harris mtx_lock(&qpair->lock); 921b846efd7SJim Harris 922b846efd7SJim Harris nvme_qpair_enable(qpair); 923b846efd7SJim Harris 924b846efd7SJim Harris STAILQ_INIT(&temp); 925b846efd7SJim Harris STAILQ_SWAP(&qpair->queued_req, &temp, nvme_request); 926b846efd7SJim Harris 927b846efd7SJim Harris while (!STAILQ_EMPTY(&temp)) { 928b846efd7SJim Harris req = STAILQ_FIRST(&temp); 929b846efd7SJim Harris STAILQ_REMOVE_HEAD(&temp, stailq); 930547d523eSJim Harris nvme_printf(qpair->ctrlr, "resubmitting queued i/o\n"); 931547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 932b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 933b846efd7SJim Harris } 934b846efd7SJim Harris 935b846efd7SJim Harris mtx_unlock(&qpair->lock); 936b846efd7SJim Harris } 937b846efd7SJim Harris 938b846efd7SJim Harris static void 939b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 940b846efd7SJim Harris { 941b846efd7SJim Harris struct nvme_tracker *tr; 942b846efd7SJim Harris 943b846efd7SJim Harris qpair->is_enabled = FALSE; 944b846efd7SJim Harris mtx_lock(&qpair->lock); 945b846efd7SJim Harris TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) 946b846efd7SJim Harris callout_stop(&tr->timer); 947b846efd7SJim Harris mtx_unlock(&qpair->lock); 948b846efd7SJim Harris } 949b846efd7SJim Harris 950b846efd7SJim Harris void 951b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 952b846efd7SJim Harris { 953b846efd7SJim Harris 954b846efd7SJim Harris nvme_qpair_disable(qpair); 955b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 956b846efd7SJim Harris } 957b846efd7SJim Harris 958b846efd7SJim Harris void 959b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 960b846efd7SJim Harris { 961b846efd7SJim Harris 962b846efd7SJim Harris nvme_qpair_disable(qpair); 963b846efd7SJim Harris } 964232e2edbSJim Harris 965232e2edbSJim Harris void 966232e2edbSJim Harris nvme_qpair_fail(struct nvme_qpair *qpair) 967232e2edbSJim Harris { 968232e2edbSJim Harris struct nvme_tracker *tr; 969232e2edbSJim Harris struct nvme_request *req; 970232e2edbSJim Harris 971232e2edbSJim Harris mtx_lock(&qpair->lock); 972232e2edbSJim Harris 973232e2edbSJim Harris while (!STAILQ_EMPTY(&qpair->queued_req)) { 974232e2edbSJim Harris req = STAILQ_FIRST(&qpair->queued_req); 975232e2edbSJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 976547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing queued i/o\n"); 977232e2edbSJim Harris mtx_unlock(&qpair->lock); 978232e2edbSJim Harris nvme_qpair_manual_complete_request(qpair, req, NVME_SCT_GENERIC, 979232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, TRUE); 980232e2edbSJim Harris mtx_lock(&qpair->lock); 981232e2edbSJim Harris } 982232e2edbSJim Harris 983232e2edbSJim Harris /* Manually abort each outstanding I/O. */ 984232e2edbSJim Harris while (!TAILQ_EMPTY(&qpair->outstanding_tr)) { 985232e2edbSJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 986232e2edbSJim Harris /* 987232e2edbSJim Harris * Do not remove the tracker. The abort_tracker path will 988232e2edbSJim Harris * do that for us. 989232e2edbSJim Harris */ 990547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing outstanding i/o\n"); 991232e2edbSJim Harris mtx_unlock(&qpair->lock); 992232e2edbSJim Harris nvme_qpair_manual_complete_tracker(qpair, tr, NVME_SCT_GENERIC, 993232e2edbSJim Harris NVME_SC_ABORTED_BY_REQUEST, 1 /* do not retry */, TRUE); 994232e2edbSJim Harris mtx_lock(&qpair->lock); 995232e2edbSJim Harris } 996232e2edbSJim Harris 997232e2edbSJim Harris mtx_unlock(&qpair->lock); 998232e2edbSJim Harris } 999232e2edbSJim Harris 1000