1bb0ec6b3SJim Harris /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4f42ca756SJim Harris * Copyright (C) 2012-2014 Intel Corporation 5bb0ec6b3SJim Harris * All rights reserved. 6bb0ec6b3SJim Harris * 7bb0ec6b3SJim Harris * Redistribution and use in source and binary forms, with or without 8bb0ec6b3SJim Harris * modification, are permitted provided that the following conditions 9bb0ec6b3SJim Harris * are met: 10bb0ec6b3SJim Harris * 1. Redistributions of source code must retain the above copyright 11bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer. 12bb0ec6b3SJim Harris * 2. Redistributions in binary form must reproduce the above copyright 13bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer in the 14bb0ec6b3SJim Harris * documentation and/or other materials provided with the distribution. 15bb0ec6b3SJim Harris * 16bb0ec6b3SJim Harris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17bb0ec6b3SJim Harris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18bb0ec6b3SJim Harris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19bb0ec6b3SJim Harris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20bb0ec6b3SJim Harris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21bb0ec6b3SJim Harris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22bb0ec6b3SJim Harris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23bb0ec6b3SJim Harris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24bb0ec6b3SJim Harris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25bb0ec6b3SJim Harris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26bb0ec6b3SJim Harris * SUCH DAMAGE. 27bb0ec6b3SJim Harris */ 28bb0ec6b3SJim Harris 29bb0ec6b3SJim Harris #include <sys/cdefs.h> 30bb0ec6b3SJim Harris #include <sys/param.h> 31bb0ec6b3SJim Harris #include <sys/bus.h> 32d0aaeffdSWarner Losh #include <sys/conf.h> 331eab19cbSAlexander Motin #include <sys/domainset.h> 34d0aaeffdSWarner Losh #include <sys/proc.h> 35bb0ec6b3SJim Harris 360f71ecf7SJim Harris #include <dev/pci/pcivar.h> 370f71ecf7SJim Harris 38bb0ec6b3SJim Harris #include "nvme_private.h" 39bb0ec6b3SJim Harris 402ffd6fceSWarner Losh typedef enum error_print { ERROR_PRINT_NONE, ERROR_PRINT_NO_RETRY, ERROR_PRINT_ALL } error_print_t; 412ffd6fceSWarner Losh #define DO_NOT_RETRY 1 422ffd6fceSWarner Losh 43d6f54866SJim Harris static void _nvme_qpair_submit_request(struct nvme_qpair *qpair, 44d6f54866SJim Harris struct nvme_request *req); 45a965389bSScott Long static void nvme_qpair_destroy(struct nvme_qpair *qpair); 46d6f54866SJim Harris 472ad9a815SWarner Losh #define DEFAULT_INDEX 256 482ad9a815SWarner Losh #define DEFAULT_ENTRY(x) [DEFAULT_INDEX] = x 492ad9a815SWarner Losh #define OPC_ENTRY(x) [NVME_OPC_ ## x] = #x 502ad9a815SWarner Losh 512ad9a815SWarner Losh static const char *admin_opcode[DEFAULT_INDEX + 1] = { 522ad9a815SWarner Losh OPC_ENTRY(DELETE_IO_SQ), 532ad9a815SWarner Losh OPC_ENTRY(CREATE_IO_SQ), 542ad9a815SWarner Losh OPC_ENTRY(GET_LOG_PAGE), 552ad9a815SWarner Losh OPC_ENTRY(DELETE_IO_CQ), 562ad9a815SWarner Losh OPC_ENTRY(CREATE_IO_CQ), 572ad9a815SWarner Losh OPC_ENTRY(IDENTIFY), 582ad9a815SWarner Losh OPC_ENTRY(ABORT), 592ad9a815SWarner Losh OPC_ENTRY(SET_FEATURES), 602ad9a815SWarner Losh OPC_ENTRY(GET_FEATURES), 612ad9a815SWarner Losh OPC_ENTRY(ASYNC_EVENT_REQUEST), 622ad9a815SWarner Losh OPC_ENTRY(NAMESPACE_MANAGEMENT), 632ad9a815SWarner Losh OPC_ENTRY(FIRMWARE_ACTIVATE), 642ad9a815SWarner Losh OPC_ENTRY(FIRMWARE_IMAGE_DOWNLOAD), 652ad9a815SWarner Losh OPC_ENTRY(DEVICE_SELF_TEST), 662ad9a815SWarner Losh OPC_ENTRY(NAMESPACE_ATTACHMENT), 672ad9a815SWarner Losh OPC_ENTRY(KEEP_ALIVE), 682ad9a815SWarner Losh OPC_ENTRY(DIRECTIVE_SEND), 692ad9a815SWarner Losh OPC_ENTRY(DIRECTIVE_RECEIVE), 702ad9a815SWarner Losh OPC_ENTRY(VIRTUALIZATION_MANAGEMENT), 712ad9a815SWarner Losh OPC_ENTRY(NVME_MI_SEND), 722ad9a815SWarner Losh OPC_ENTRY(NVME_MI_RECEIVE), 732ad9a815SWarner Losh OPC_ENTRY(CAPACITY_MANAGEMENT), 742ad9a815SWarner Losh OPC_ENTRY(LOCKDOWN), 752ad9a815SWarner Losh OPC_ENTRY(DOORBELL_BUFFER_CONFIG), 762ad9a815SWarner Losh OPC_ENTRY(FABRICS_COMMANDS), 772ad9a815SWarner Losh OPC_ENTRY(FORMAT_NVM), 782ad9a815SWarner Losh OPC_ENTRY(SECURITY_SEND), 792ad9a815SWarner Losh OPC_ENTRY(SECURITY_RECEIVE), 802ad9a815SWarner Losh OPC_ENTRY(SANITIZE), 812ad9a815SWarner Losh OPC_ENTRY(GET_LBA_STATUS), 822ad9a815SWarner Losh DEFAULT_ENTRY("ADMIN COMMAND"), 83547d523eSJim Harris }; 84547d523eSJim Harris 852ad9a815SWarner Losh static const char *io_opcode[DEFAULT_INDEX + 1] = { 862ad9a815SWarner Losh OPC_ENTRY(FLUSH), 872ad9a815SWarner Losh OPC_ENTRY(WRITE), 882ad9a815SWarner Losh OPC_ENTRY(READ), 892ad9a815SWarner Losh OPC_ENTRY(WRITE_UNCORRECTABLE), 902ad9a815SWarner Losh OPC_ENTRY(COMPARE), 912ad9a815SWarner Losh OPC_ENTRY(WRITE_ZEROES), 922ad9a815SWarner Losh OPC_ENTRY(DATASET_MANAGEMENT), 932ad9a815SWarner Losh OPC_ENTRY(VERIFY), 942ad9a815SWarner Losh OPC_ENTRY(RESERVATION_REGISTER), 952ad9a815SWarner Losh OPC_ENTRY(RESERVATION_REPORT), 962ad9a815SWarner Losh OPC_ENTRY(RESERVATION_ACQUIRE), 972ad9a815SWarner Losh OPC_ENTRY(RESERVATION_RELEASE), 982ad9a815SWarner Losh OPC_ENTRY(COPY), 992ad9a815SWarner Losh DEFAULT_ENTRY("IO COMMAND"), 100547d523eSJim Harris }; 101547d523eSJim Harris 102547d523eSJim Harris static const char * 1032ad9a815SWarner Losh get_opcode_string(const char *op[DEFAULT_INDEX + 1], uint16_t opc) 104547d523eSJim Harris { 1052ad9a815SWarner Losh const char *nm = opc < DEFAULT_INDEX ? op[opc] : op[DEFAULT_INDEX]; 1062ad9a815SWarner Losh 1072ad9a815SWarner Losh return (nm != NULL ? nm : op[DEFAULT_INDEX]); 108547d523eSJim Harris } 109547d523eSJim Harris 110547d523eSJim Harris static const char * 111edd23e4dSWarner Losh get_admin_opcode_string(uint16_t opc) 112edd23e4dSWarner Losh { 113edd23e4dSWarner Losh return (get_opcode_string(admin_opcode, opc)); 114edd23e4dSWarner Losh } 115edd23e4dSWarner Losh 116edd23e4dSWarner Losh static const char * 117547d523eSJim Harris get_io_opcode_string(uint16_t opc) 118547d523eSJim Harris { 119edd23e4dSWarner Losh return (get_opcode_string(io_opcode, opc)); 120547d523eSJim Harris } 121547d523eSJim Harris 122547d523eSJim Harris static void 123547d523eSJim Harris nvme_admin_qpair_print_command(struct nvme_qpair *qpair, 124547d523eSJim Harris struct nvme_command *cmd) 125547d523eSJim Harris { 126547d523eSJim Harris 127547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%x " 128547d523eSJim Harris "cdw10:%08x cdw11:%08x\n", 1299544e6dcSChuck Tuffli get_admin_opcode_string(cmd->opc), cmd->opc, qpair->id, cmd->cid, 1300d787e9bSWojciech Macek le32toh(cmd->nsid), le32toh(cmd->cdw10), le32toh(cmd->cdw11)); 131547d523eSJim Harris } 132547d523eSJim Harris 133547d523eSJim Harris static void 134547d523eSJim Harris nvme_io_qpair_print_command(struct nvme_qpair *qpair, 135547d523eSJim Harris struct nvme_command *cmd) 136547d523eSJim Harris { 137547d523eSJim Harris 1389544e6dcSChuck Tuffli switch (cmd->opc) { 139547d523eSJim Harris case NVME_OPC_WRITE: 140547d523eSJim Harris case NVME_OPC_READ: 141547d523eSJim Harris case NVME_OPC_WRITE_UNCORRECTABLE: 142547d523eSJim Harris case NVME_OPC_COMPARE: 1436b1a96b1SAlexander Motin case NVME_OPC_WRITE_ZEROES: 14490dfa8f0SAlexander Motin case NVME_OPC_VERIFY: 145547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d " 146bdd1fd40SJim Harris "lba:%llu len:%d\n", 1479544e6dcSChuck Tuffli get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, le32toh(cmd->nsid), 1480d787e9bSWojciech Macek ((unsigned long long)le32toh(cmd->cdw11) << 32) + le32toh(cmd->cdw10), 1490d787e9bSWojciech Macek (le32toh(cmd->cdw12) & 0xFFFF) + 1); 150547d523eSJim Harris break; 151547d523eSJim Harris case NVME_OPC_FLUSH: 152547d523eSJim Harris case NVME_OPC_DATASET_MANAGEMENT: 1536b1a96b1SAlexander Motin case NVME_OPC_RESERVATION_REGISTER: 1546b1a96b1SAlexander Motin case NVME_OPC_RESERVATION_REPORT: 1556b1a96b1SAlexander Motin case NVME_OPC_RESERVATION_ACQUIRE: 1566b1a96b1SAlexander Motin case NVME_OPC_RESERVATION_RELEASE: 157547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s sqid:%d cid:%d nsid:%d\n", 1589544e6dcSChuck Tuffli get_io_opcode_string(cmd->opc), qpair->id, cmd->cid, le32toh(cmd->nsid)); 159547d523eSJim Harris break; 160547d523eSJim Harris default: 161547d523eSJim Harris nvme_printf(qpair->ctrlr, "%s (%02x) sqid:%d cid:%d nsid:%d\n", 1629544e6dcSChuck Tuffli get_io_opcode_string(cmd->opc), cmd->opc, qpair->id, 1630d787e9bSWojciech Macek cmd->cid, le32toh(cmd->nsid)); 164547d523eSJim Harris break; 165547d523eSJim Harris } 166547d523eSJim Harris } 167547d523eSJim Harris 1687be0b068SWarner Losh void 169547d523eSJim Harris nvme_qpair_print_command(struct nvme_qpair *qpair, struct nvme_command *cmd) 170547d523eSJim Harris { 171547d523eSJim Harris if (qpair->id == 0) 172547d523eSJim Harris nvme_admin_qpair_print_command(qpair, cmd); 173547d523eSJim Harris else 174547d523eSJim Harris nvme_io_qpair_print_command(qpair, cmd); 175c75bdc04SWarner Losh if (nvme_verbose_cmd_dump) { 176c75bdc04SWarner Losh nvme_printf(qpair->ctrlr, 177c75bdc04SWarner Losh "nsid:%#x rsvd2:%#x rsvd3:%#x mptr:%#jx prp1:%#jx prp2:%#jx\n", 178c75bdc04SWarner Losh cmd->nsid, cmd->rsvd2, cmd->rsvd3, (uintmax_t)cmd->mptr, 179c75bdc04SWarner Losh (uintmax_t)cmd->prp1, (uintmax_t)cmd->prp2); 180c75bdc04SWarner Losh nvme_printf(qpair->ctrlr, 181c75bdc04SWarner Losh "cdw10: %#x cdw11:%#x cdw12:%#x cdw13:%#x cdw14:%#x cdw15:%#x\n", 182c75bdc04SWarner Losh cmd->cdw10, cmd->cdw11, cmd->cdw12, cmd->cdw13, cmd->cdw14, 183c75bdc04SWarner Losh cmd->cdw15); 184c75bdc04SWarner Losh } 185547d523eSJim Harris } 186547d523eSJim Harris 187547d523eSJim Harris struct nvme_status_string { 188547d523eSJim Harris uint16_t sc; 189547d523eSJim Harris const char * str; 190547d523eSJim Harris }; 191547d523eSJim Harris 192547d523eSJim Harris static struct nvme_status_string generic_status[] = { 193547d523eSJim Harris { NVME_SC_SUCCESS, "SUCCESS" }, 194547d523eSJim Harris { NVME_SC_INVALID_OPCODE, "INVALID OPCODE" }, 195547d523eSJim Harris { NVME_SC_INVALID_FIELD, "INVALID_FIELD" }, 196547d523eSJim Harris { NVME_SC_COMMAND_ID_CONFLICT, "COMMAND ID CONFLICT" }, 197547d523eSJim Harris { NVME_SC_DATA_TRANSFER_ERROR, "DATA TRANSFER ERROR" }, 198547d523eSJim Harris { NVME_SC_ABORTED_POWER_LOSS, "ABORTED - POWER LOSS" }, 199547d523eSJim Harris { NVME_SC_INTERNAL_DEVICE_ERROR, "INTERNAL DEVICE ERROR" }, 200547d523eSJim Harris { NVME_SC_ABORTED_BY_REQUEST, "ABORTED - BY REQUEST" }, 201547d523eSJim Harris { NVME_SC_ABORTED_SQ_DELETION, "ABORTED - SQ DELETION" }, 202547d523eSJim Harris { NVME_SC_ABORTED_FAILED_FUSED, "ABORTED - FAILED FUSED" }, 203547d523eSJim Harris { NVME_SC_ABORTED_MISSING_FUSED, "ABORTED - MISSING FUSED" }, 204547d523eSJim Harris { NVME_SC_INVALID_NAMESPACE_OR_FORMAT, "INVALID NAMESPACE OR FORMAT" }, 205547d523eSJim Harris { NVME_SC_COMMAND_SEQUENCE_ERROR, "COMMAND SEQUENCE ERROR" }, 2066b1a96b1SAlexander Motin { NVME_SC_INVALID_SGL_SEGMENT_DESCR, "INVALID SGL SEGMENT DESCRIPTOR" }, 2076b1a96b1SAlexander Motin { NVME_SC_INVALID_NUMBER_OF_SGL_DESCR, "INVALID NUMBER OF SGL DESCRIPTORS" }, 2086b1a96b1SAlexander Motin { NVME_SC_DATA_SGL_LENGTH_INVALID, "DATA SGL LENGTH INVALID" }, 2096b1a96b1SAlexander Motin { NVME_SC_METADATA_SGL_LENGTH_INVALID, "METADATA SGL LENGTH INVALID" }, 2106b1a96b1SAlexander Motin { NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID, "SGL DESCRIPTOR TYPE INVALID" }, 2116b1a96b1SAlexander Motin { NVME_SC_INVALID_USE_OF_CMB, "INVALID USE OF CONTROLLER MEMORY BUFFER" }, 2126b1a96b1SAlexander Motin { NVME_SC_PRP_OFFET_INVALID, "PRP OFFET INVALID" }, 2136b1a96b1SAlexander Motin { NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED, "ATOMIC WRITE UNIT EXCEEDED" }, 2146b1a96b1SAlexander Motin { NVME_SC_OPERATION_DENIED, "OPERATION DENIED" }, 2156b1a96b1SAlexander Motin { NVME_SC_SGL_OFFSET_INVALID, "SGL OFFSET INVALID" }, 2166b1a96b1SAlexander Motin { NVME_SC_HOST_ID_INCONSISTENT_FORMAT, "HOST IDENTIFIER INCONSISTENT FORMAT" }, 2176b1a96b1SAlexander Motin { NVME_SC_KEEP_ALIVE_TIMEOUT_EXPIRED, "KEEP ALIVE TIMEOUT EXPIRED" }, 2186b1a96b1SAlexander Motin { NVME_SC_KEEP_ALIVE_TIMEOUT_INVALID, "KEEP ALIVE TIMEOUT INVALID" }, 2196b1a96b1SAlexander Motin { NVME_SC_ABORTED_DUE_TO_PREEMPT, "COMMAND ABORTED DUE TO PREEMPT AND ABORT" }, 2206b1a96b1SAlexander Motin { NVME_SC_SANITIZE_FAILED, "SANITIZE FAILED" }, 2216b1a96b1SAlexander Motin { NVME_SC_SANITIZE_IN_PROGRESS, "SANITIZE IN PROGRESS" }, 2226b1a96b1SAlexander Motin { NVME_SC_SGL_DATA_BLOCK_GRAN_INVALID, "SGL_DATA_BLOCK_GRANULARITY_INVALID" }, 2236b1a96b1SAlexander Motin { NVME_SC_NOT_SUPPORTED_IN_CMB, "COMMAND NOT SUPPORTED FOR QUEUE IN CMB" }, 22490dfa8f0SAlexander Motin { NVME_SC_NAMESPACE_IS_WRITE_PROTECTED, "NAMESPACE IS WRITE PROTECTED" }, 22590dfa8f0SAlexander Motin { NVME_SC_COMMAND_INTERRUPTED, "COMMAND INTERRUPTED" }, 22690dfa8f0SAlexander Motin { NVME_SC_TRANSIENT_TRANSPORT_ERROR, "TRANSIENT TRANSPORT ERROR" }, 2276b1a96b1SAlexander Motin 228547d523eSJim Harris { NVME_SC_LBA_OUT_OF_RANGE, "LBA OUT OF RANGE" }, 229547d523eSJim Harris { NVME_SC_CAPACITY_EXCEEDED, "CAPACITY EXCEEDED" }, 230547d523eSJim Harris { NVME_SC_NAMESPACE_NOT_READY, "NAMESPACE NOT READY" }, 2316b1a96b1SAlexander Motin { NVME_SC_RESERVATION_CONFLICT, "RESERVATION CONFLICT" }, 2326b1a96b1SAlexander Motin { NVME_SC_FORMAT_IN_PROGRESS, "FORMAT IN PROGRESS" }, 233547d523eSJim Harris { 0xFFFF, "GENERIC" } 234547d523eSJim Harris }; 235547d523eSJim Harris 236547d523eSJim Harris static struct nvme_status_string command_specific_status[] = { 237547d523eSJim Harris { NVME_SC_COMPLETION_QUEUE_INVALID, "INVALID COMPLETION QUEUE" }, 238547d523eSJim Harris { NVME_SC_INVALID_QUEUE_IDENTIFIER, "INVALID QUEUE IDENTIFIER" }, 239547d523eSJim Harris { NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED, "MAX QUEUE SIZE EXCEEDED" }, 240547d523eSJim Harris { NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED, "ABORT CMD LIMIT EXCEEDED" }, 241547d523eSJim Harris { NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED, "ASYNC LIMIT EXCEEDED" }, 242547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_SLOT, "INVALID FIRMWARE SLOT" }, 243547d523eSJim Harris { NVME_SC_INVALID_FIRMWARE_IMAGE, "INVALID FIRMWARE IMAGE" }, 244547d523eSJim Harris { NVME_SC_INVALID_INTERRUPT_VECTOR, "INVALID INTERRUPT VECTOR" }, 245547d523eSJim Harris { NVME_SC_INVALID_LOG_PAGE, "INVALID LOG PAGE" }, 246547d523eSJim Harris { NVME_SC_INVALID_FORMAT, "INVALID FORMAT" }, 247547d523eSJim Harris { NVME_SC_FIRMWARE_REQUIRES_RESET, "FIRMWARE REQUIRES RESET" }, 2486b1a96b1SAlexander Motin { NVME_SC_INVALID_QUEUE_DELETION, "INVALID QUEUE DELETION" }, 2496b1a96b1SAlexander Motin { NVME_SC_FEATURE_NOT_SAVEABLE, "FEATURE IDENTIFIER NOT SAVEABLE" }, 2506b1a96b1SAlexander Motin { NVME_SC_FEATURE_NOT_CHANGEABLE, "FEATURE NOT CHANGEABLE" }, 2516b1a96b1SAlexander Motin { NVME_SC_FEATURE_NOT_NS_SPECIFIC, "FEATURE NOT NAMESPACE SPECIFIC" }, 2526b1a96b1SAlexander Motin { NVME_SC_FW_ACT_REQUIRES_NVMS_RESET, "FIRMWARE ACTIVATION REQUIRES NVM SUBSYSTEM RESET" }, 2536b1a96b1SAlexander Motin { NVME_SC_FW_ACT_REQUIRES_RESET, "FIRMWARE ACTIVATION REQUIRES RESET" }, 2546b1a96b1SAlexander Motin { NVME_SC_FW_ACT_REQUIRES_TIME, "FIRMWARE ACTIVATION REQUIRES MAXIMUM TIME VIOLATION" }, 2556b1a96b1SAlexander Motin { NVME_SC_FW_ACT_PROHIBITED, "FIRMWARE ACTIVATION PROHIBITED" }, 2566b1a96b1SAlexander Motin { NVME_SC_OVERLAPPING_RANGE, "OVERLAPPING RANGE" }, 2576b1a96b1SAlexander Motin { NVME_SC_NS_INSUFFICIENT_CAPACITY, "NAMESPACE INSUFFICIENT CAPACITY" }, 2586b1a96b1SAlexander Motin { NVME_SC_NS_ID_UNAVAILABLE, "NAMESPACE IDENTIFIER UNAVAILABLE" }, 2596b1a96b1SAlexander Motin { NVME_SC_NS_ALREADY_ATTACHED, "NAMESPACE ALREADY ATTACHED" }, 2606b1a96b1SAlexander Motin { NVME_SC_NS_IS_PRIVATE, "NAMESPACE IS PRIVATE" }, 2616b1a96b1SAlexander Motin { NVME_SC_NS_NOT_ATTACHED, "NS NOT ATTACHED" }, 2626b1a96b1SAlexander Motin { NVME_SC_THIN_PROV_NOT_SUPPORTED, "THIN PROVISIONING NOT SUPPORTED" }, 2636b1a96b1SAlexander Motin { NVME_SC_CTRLR_LIST_INVALID, "CONTROLLER LIST INVALID" }, 2648d08cdc7SChuck Tuffli { NVME_SC_SELF_TEST_IN_PROGRESS, "DEVICE SELF-TEST IN PROGRESS" }, 2656b1a96b1SAlexander Motin { NVME_SC_BOOT_PART_WRITE_PROHIB, "BOOT PARTITION WRITE PROHIBITED" }, 2666b1a96b1SAlexander Motin { NVME_SC_INVALID_CTRLR_ID, "INVALID CONTROLLER IDENTIFIER" }, 2676b1a96b1SAlexander Motin { NVME_SC_INVALID_SEC_CTRLR_STATE, "INVALID SECONDARY CONTROLLER STATE" }, 2686b1a96b1SAlexander Motin { NVME_SC_INVALID_NUM_OF_CTRLR_RESRC, "INVALID NUMBER OF CONTROLLER RESOURCES" }, 2696b1a96b1SAlexander Motin { NVME_SC_INVALID_RESOURCE_ID, "INVALID RESOURCE IDENTIFIER" }, 27090dfa8f0SAlexander Motin { NVME_SC_SANITIZE_PROHIBITED_WPMRE, "SANITIZE PROHIBITED WRITE PERSISTENT MEMORY REGION ENABLED" }, 27190dfa8f0SAlexander Motin { NVME_SC_ANA_GROUP_ID_INVALID, "ANA GROUP IDENTIFIED INVALID" }, 27290dfa8f0SAlexander Motin { NVME_SC_ANA_ATTACH_FAILED, "ANA ATTACH FAILED" }, 2736b1a96b1SAlexander Motin 274547d523eSJim Harris { NVME_SC_CONFLICTING_ATTRIBUTES, "CONFLICTING ATTRIBUTES" }, 275547d523eSJim Harris { NVME_SC_INVALID_PROTECTION_INFO, "INVALID PROTECTION INFO" }, 276547d523eSJim Harris { NVME_SC_ATTEMPTED_WRITE_TO_RO_PAGE, "WRITE TO RO PAGE" }, 277547d523eSJim Harris { 0xFFFF, "COMMAND SPECIFIC" } 278547d523eSJim Harris }; 279547d523eSJim Harris 280547d523eSJim Harris static struct nvme_status_string media_error_status[] = { 281547d523eSJim Harris { NVME_SC_WRITE_FAULTS, "WRITE FAULTS" }, 282547d523eSJim Harris { NVME_SC_UNRECOVERED_READ_ERROR, "UNRECOVERED READ ERROR" }, 283547d523eSJim Harris { NVME_SC_GUARD_CHECK_ERROR, "GUARD CHECK ERROR" }, 284547d523eSJim Harris { NVME_SC_APPLICATION_TAG_CHECK_ERROR, "APPLICATION TAG CHECK ERROR" }, 285547d523eSJim Harris { NVME_SC_REFERENCE_TAG_CHECK_ERROR, "REFERENCE TAG CHECK ERROR" }, 286547d523eSJim Harris { NVME_SC_COMPARE_FAILURE, "COMPARE FAILURE" }, 287547d523eSJim Harris { NVME_SC_ACCESS_DENIED, "ACCESS DENIED" }, 2886b1a96b1SAlexander Motin { NVME_SC_DEALLOCATED_OR_UNWRITTEN, "DEALLOCATED OR UNWRITTEN LOGICAL BLOCK" }, 289547d523eSJim Harris { 0xFFFF, "MEDIA ERROR" } 290547d523eSJim Harris }; 291547d523eSJim Harris 292a6d222ebSAlexander Motin static struct nvme_status_string path_related_status[] = { 293a6d222ebSAlexander Motin { NVME_SC_INTERNAL_PATH_ERROR, "INTERNAL PATH ERROR" }, 294a6d222ebSAlexander Motin { NVME_SC_ASYMMETRIC_ACCESS_PERSISTENT_LOSS, "ASYMMETRIC ACCESS PERSISTENT LOSS" }, 295a6d222ebSAlexander Motin { NVME_SC_ASYMMETRIC_ACCESS_INACCESSIBLE, "ASYMMETRIC ACCESS INACCESSIBLE" }, 296a6d222ebSAlexander Motin { NVME_SC_ASYMMETRIC_ACCESS_TRANSITION, "ASYMMETRIC ACCESS TRANSITION" }, 297a6d222ebSAlexander Motin { NVME_SC_CONTROLLER_PATHING_ERROR, "CONTROLLER PATHING ERROR" }, 298a6d222ebSAlexander Motin { NVME_SC_HOST_PATHING_ERROR, "HOST PATHING ERROR" }, 2995ae44634SJohn Baldwin { NVME_SC_COMMAND_ABORTED_BY_HOST, "COMMAND ABORTED BY HOST" }, 300a6d222ebSAlexander Motin { 0xFFFF, "PATH RELATED" }, 301a6d222ebSAlexander Motin }; 302a6d222ebSAlexander Motin 303547d523eSJim Harris static const char * 304547d523eSJim Harris get_status_string(uint16_t sct, uint16_t sc) 305547d523eSJim Harris { 306547d523eSJim Harris struct nvme_status_string *entry; 307547d523eSJim Harris 308547d523eSJim Harris switch (sct) { 309547d523eSJim Harris case NVME_SCT_GENERIC: 310547d523eSJim Harris entry = generic_status; 311547d523eSJim Harris break; 312547d523eSJim Harris case NVME_SCT_COMMAND_SPECIFIC: 313547d523eSJim Harris entry = command_specific_status; 314547d523eSJim Harris break; 315547d523eSJim Harris case NVME_SCT_MEDIA_ERROR: 316547d523eSJim Harris entry = media_error_status; 317547d523eSJim Harris break; 318a6d222ebSAlexander Motin case NVME_SCT_PATH_RELATED: 319a6d222ebSAlexander Motin entry = path_related_status; 320a6d222ebSAlexander Motin break; 321547d523eSJim Harris case NVME_SCT_VENDOR_SPECIFIC: 322547d523eSJim Harris return ("VENDOR SPECIFIC"); 323547d523eSJim Harris default: 324547d523eSJim Harris return ("RESERVED"); 325547d523eSJim Harris } 326547d523eSJim Harris 327547d523eSJim Harris while (entry->sc != 0xFFFF) { 328547d523eSJim Harris if (entry->sc == sc) 329547d523eSJim Harris return (entry->str); 330547d523eSJim Harris entry++; 331547d523eSJim Harris } 332547d523eSJim Harris return (entry->str); 333547d523eSJim Harris } 334547d523eSJim Harris 3356f76d493SWarner Losh void 336547d523eSJim Harris nvme_qpair_print_completion(struct nvme_qpair *qpair, 337547d523eSJim Harris struct nvme_completion *cpl) 338547d523eSJim Harris { 3396f76d493SWarner Losh uint8_t sct, sc, crd, m, dnr, p; 3400d787e9bSWojciech Macek 3410d787e9bSWojciech Macek sct = NVME_STATUS_GET_SCT(cpl->status); 3420d787e9bSWojciech Macek sc = NVME_STATUS_GET_SC(cpl->status); 343a69c0964SAlexander Motin crd = NVME_STATUS_GET_CRD(cpl->status); 344a69c0964SAlexander Motin m = NVME_STATUS_GET_M(cpl->status); 345a69c0964SAlexander Motin dnr = NVME_STATUS_GET_DNR(cpl->status); 3466f76d493SWarner Losh p = NVME_STATUS_GET_P(cpl->status); 3470d787e9bSWojciech Macek 3486f76d493SWarner Losh nvme_printf(qpair->ctrlr, "%s (%02x/%02x) crd:%x m:%x dnr:%x p:%d " 349a69c0964SAlexander Motin "sqid:%d cid:%d cdw0:%x\n", 3506f76d493SWarner Losh get_status_string(sct, sc), sct, sc, crd, m, dnr, p, 351a69c0964SAlexander Motin cpl->sqid, cpl->cid, cpl->cdw0); 352547d523eSJim Harris } 353547d523eSJim Harris 3547588c6ccSWarner Losh static bool 3556cb06070SJim Harris nvme_completion_is_retry(const struct nvme_completion *cpl) 356bb0ec6b3SJim Harris { 3570d787e9bSWojciech Macek uint8_t sct, sc, dnr; 3580d787e9bSWojciech Macek 3590d787e9bSWojciech Macek sct = NVME_STATUS_GET_SCT(cpl->status); 3600d787e9bSWojciech Macek sc = NVME_STATUS_GET_SC(cpl->status); 3612ffd6fceSWarner Losh dnr = NVME_STATUS_GET_DNR(cpl->status); /* Do Not Retry Bit */ 3620d787e9bSWojciech Macek 363bb0ec6b3SJim Harris /* 364bb0ec6b3SJim Harris * TODO: spec is not clear how commands that are aborted due 365bb0ec6b3SJim Harris * to TLER will be marked. So for now, it seems 366bb0ec6b3SJim Harris * NAMESPACE_NOT_READY is the only case where we should 36795108cadSWarner Losh * look at the DNR bit. Requests failed with ABORTED_BY_REQUEST 36895108cadSWarner Losh * set the DNR bit correctly since the driver controls that. 369bb0ec6b3SJim Harris */ 3700d787e9bSWojciech Macek switch (sct) { 371bb0ec6b3SJim Harris case NVME_SCT_GENERIC: 3720d787e9bSWojciech Macek switch (sc) { 373448195e7SJim Harris case NVME_SC_ABORTED_BY_REQUEST: 374bb0ec6b3SJim Harris case NVME_SC_NAMESPACE_NOT_READY: 3750d787e9bSWojciech Macek if (dnr) 376bb0ec6b3SJim Harris return (0); 377bb0ec6b3SJim Harris else 378bb0ec6b3SJim Harris return (1); 379bb0ec6b3SJim Harris case NVME_SC_INVALID_OPCODE: 380bb0ec6b3SJim Harris case NVME_SC_INVALID_FIELD: 381bb0ec6b3SJim Harris case NVME_SC_COMMAND_ID_CONFLICT: 382bb0ec6b3SJim Harris case NVME_SC_DATA_TRANSFER_ERROR: 383bb0ec6b3SJim Harris case NVME_SC_ABORTED_POWER_LOSS: 384bb0ec6b3SJim Harris case NVME_SC_INTERNAL_DEVICE_ERROR: 385bb0ec6b3SJim Harris case NVME_SC_ABORTED_SQ_DELETION: 386bb0ec6b3SJim Harris case NVME_SC_ABORTED_FAILED_FUSED: 387bb0ec6b3SJim Harris case NVME_SC_ABORTED_MISSING_FUSED: 388bb0ec6b3SJim Harris case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 389bb0ec6b3SJim Harris case NVME_SC_COMMAND_SEQUENCE_ERROR: 390bb0ec6b3SJim Harris case NVME_SC_LBA_OUT_OF_RANGE: 391bb0ec6b3SJim Harris case NVME_SC_CAPACITY_EXCEEDED: 392bb0ec6b3SJim Harris default: 393bb0ec6b3SJim Harris return (0); 394bb0ec6b3SJim Harris } 395bb0ec6b3SJim Harris case NVME_SCT_COMMAND_SPECIFIC: 396bb0ec6b3SJim Harris case NVME_SCT_MEDIA_ERROR: 397a6d222ebSAlexander Motin return (0); 398a6d222ebSAlexander Motin case NVME_SCT_PATH_RELATED: 399a6d222ebSAlexander Motin switch (sc) { 400a6d222ebSAlexander Motin case NVME_SC_INTERNAL_PATH_ERROR: 401a6d222ebSAlexander Motin if (dnr) 402a6d222ebSAlexander Motin return (0); 403a6d222ebSAlexander Motin else 404a6d222ebSAlexander Motin return (1); 405a6d222ebSAlexander Motin default: 406a6d222ebSAlexander Motin return (0); 407a6d222ebSAlexander Motin } 408bb0ec6b3SJim Harris case NVME_SCT_VENDOR_SPECIFIC: 409bb0ec6b3SJim Harris default: 410bb0ec6b3SJim Harris return (0); 411bb0ec6b3SJim Harris } 412bb0ec6b3SJim Harris } 413bb0ec6b3SJim Harris 41421b6da58SJim Harris static void 41543393e8bSWarner Losh nvme_qpair_complete_tracker(struct nvme_tracker *tr, 4162ffd6fceSWarner Losh struct nvme_completion *cpl, error_print_t print_on_error) 417bb0ec6b3SJim Harris { 41843393e8bSWarner Losh struct nvme_qpair * qpair = tr->qpair; 419ad697276SJim Harris struct nvme_request *req; 4207588c6ccSWarner Losh bool retry, error, retriable; 421bb0ec6b3SJim Harris 422ad697276SJim Harris req = tr->req; 4236cb06070SJim Harris error = nvme_completion_is_error(cpl); 4245e83c2ffSWarner Losh retriable = nvme_completion_is_retry(cpl); 4255e83c2ffSWarner Losh retry = error && retriable && req->retries < nvme_retry_count; 426c37fc318SWarner Losh if (retry) 427c37fc318SWarner Losh qpair->num_retries++; 4285e83c2ffSWarner Losh if (error && req->retries >= nvme_retry_count && retriable) 4295e83c2ffSWarner Losh qpair->num_failures++; 430ad697276SJim Harris 4312ffd6fceSWarner Losh if (error && (print_on_error == ERROR_PRINT_ALL || 4322ffd6fceSWarner Losh (!retry && print_on_error == ERROR_PRINT_NO_RETRY))) { 433547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 434547d523eSJim Harris nvme_qpair_print_completion(qpair, cpl); 435bb0ec6b3SJim Harris } 436bb0ec6b3SJim Harris 437bb0ec6b3SJim Harris qpair->act_tr[cpl->cid] = NULL; 438bb0ec6b3SJim Harris 4396cb06070SJim Harris KASSERT(cpl->cid == req->cmd.cid, ("cpl cid does not match cmd cid\n")); 440bb0ec6b3SJim Harris 4410a4b14e8SMichal Meloun if (!retry) { 44292103adbSJohn Baldwin if (req->payload_valid) { 4430a4b14e8SMichal Meloun bus_dmamap_sync(qpair->dma_tag_payload, 4440a4b14e8SMichal Meloun tr->payload_dma_map, 4450a4b14e8SMichal Meloun BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 4460a4b14e8SMichal Meloun } 4470a4b14e8SMichal Meloun if (req->cb_fn) 448ad697276SJim Harris req->cb_fn(req->cb_arg, cpl); 4490a4b14e8SMichal Meloun } 450bb0ec6b3SJim Harris 451bb0ec6b3SJim Harris mtx_lock(&qpair->lock); 452bb0ec6b3SJim Harris 453cb5b7c13SJim Harris if (retry) { 454cb5b7c13SJim Harris req->retries++; 455b846efd7SJim Harris nvme_qpair_submit_tracker(qpair, tr); 456cb5b7c13SJim Harris } else { 45792103adbSJohn Baldwin if (req->payload_valid) { 458a6e30963SJim Harris bus_dmamap_unload(qpair->dma_tag_payload, 459f2b19f67SJim Harris tr->payload_dma_map); 4602e0090afSJustin Hibbits } 461bb0ec6b3SJim Harris 462ad697276SJim Harris nvme_free_request(req); 4630a0b08ccSJim Harris tr->req = NULL; 46421b6da58SJim Harris 46565c2474eSJim Harris TAILQ_REMOVE(&qpair->outstanding_tr, tr, tailq); 46665c2474eSJim Harris TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 4670f71ecf7SJim Harris 468f37c22a3SJim Harris /* 469f37c22a3SJim Harris * If the controller is in the middle of resetting, don't 470f37c22a3SJim Harris * try to submit queued requests here - let the reset logic 471f37c22a3SJim Harris * handle that instead. 472f37c22a3SJim Harris */ 473f37c22a3SJim Harris if (!STAILQ_EMPTY(&qpair->queued_req) && 474f37c22a3SJim Harris !qpair->ctrlr->is_resetting) { 4750f71ecf7SJim Harris req = STAILQ_FIRST(&qpair->queued_req); 4760f71ecf7SJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 477d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 4780f71ecf7SJim Harris } 479c2e83b40SJim Harris } 480bb0ec6b3SJim Harris 481bb0ec6b3SJim Harris mtx_unlock(&qpair->lock); 4826cb06070SJim Harris } 4836cb06070SJim Harris 484b846efd7SJim Harris static void 48543393e8bSWarner Losh nvme_qpair_manual_complete_tracker( 486232e2edbSJim Harris struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr, 4872ffd6fceSWarner Losh error_print_t print_on_error) 488b846efd7SJim Harris { 489b846efd7SJim Harris struct nvme_completion cpl; 490b846efd7SJim Harris 491b846efd7SJim Harris memset(&cpl, 0, sizeof(cpl)); 49243393e8bSWarner Losh 49343393e8bSWarner Losh struct nvme_qpair * qpair = tr->qpair; 49443393e8bSWarner Losh 495b846efd7SJim Harris cpl.sqid = qpair->id; 496b846efd7SJim Harris cpl.cid = tr->cid; 4970d787e9bSWojciech Macek cpl.status |= (sct & NVME_STATUS_SCT_MASK) << NVME_STATUS_SCT_SHIFT; 4980d787e9bSWojciech Macek cpl.status |= (sc & NVME_STATUS_SC_MASK) << NVME_STATUS_SC_SHIFT; 4990d787e9bSWojciech Macek cpl.status |= (dnr & NVME_STATUS_DNR_MASK) << NVME_STATUS_DNR_SHIFT; 50095cd10f1SWarner Losh /* M=0 : this is artificial so no data in error log page */ 50195cd10f1SWarner Losh /* CRD=0 : this is artificial and no delayed retry support anyway */ 50295cd10f1SWarner Losh /* P=0 : phase not checked */ 50343393e8bSWarner Losh nvme_qpair_complete_tracker(tr, &cpl, print_on_error); 504b846efd7SJim Harris } 505b846efd7SJim Harris 5066cb06070SJim Harris void 507232e2edbSJim Harris nvme_qpair_manual_complete_request(struct nvme_qpair *qpair, 5082ffd6fceSWarner Losh struct nvme_request *req, uint32_t sct, uint32_t sc) 509232e2edbSJim Harris { 510232e2edbSJim Harris struct nvme_completion cpl; 5117588c6ccSWarner Losh bool error; 512232e2edbSJim Harris 513232e2edbSJim Harris memset(&cpl, 0, sizeof(cpl)); 514232e2edbSJim Harris cpl.sqid = qpair->id; 5150d787e9bSWojciech Macek cpl.status |= (sct & NVME_STATUS_SCT_MASK) << NVME_STATUS_SCT_SHIFT; 5160d787e9bSWojciech Macek cpl.status |= (sc & NVME_STATUS_SC_MASK) << NVME_STATUS_SC_SHIFT; 517232e2edbSJim Harris 518232e2edbSJim Harris error = nvme_completion_is_error(&cpl); 519232e2edbSJim Harris 5202ffd6fceSWarner Losh if (error) { 521547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 522547d523eSJim Harris nvme_qpair_print_completion(qpair, &cpl); 523232e2edbSJim Harris } 524232e2edbSJim Harris 525232e2edbSJim Harris if (req->cb_fn) 526232e2edbSJim Harris req->cb_fn(req->cb_arg, &cpl); 527232e2edbSJim Harris 528232e2edbSJim Harris nvme_free_request(req); 529232e2edbSJim Harris } 530232e2edbSJim Harris 531d85d9648SWarner Losh bool 5326cb06070SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair) 5336cb06070SJim Harris { 5346cb06070SJim Harris struct nvme_tracker *tr; 5350d787e9bSWojciech Macek struct nvme_completion cpl; 536d85d9648SWarner Losh int done = 0; 537d0aaeffdSWarner Losh bool in_panic = dumping || SCHEDULER_STOPPED(); 5386cb06070SJim Harris 539b846efd7SJim Harris /* 540dfa01f4fSGordon Bergling * qpair is not enabled, likely because a controller reset is in 541d0aaeffdSWarner Losh * progress. Ignore the interrupt - any I/O that was associated with 5427d5eebe0SWarner Losh * this interrupt will get retried when the reset is complete. Any 5437d5eebe0SWarner Losh * pending completions for when we're in startup will be completed 5447d5eebe0SWarner Losh * as soon as initialization is complete and we start sending commands 5457d5eebe0SWarner Losh * to the device. 546b846efd7SJim Harris */ 547587aa255SWarner Losh if (qpair->recovery_state != RECOVERY_NONE) { 548587aa255SWarner Losh qpair->num_ignored++; 549d85d9648SWarner Losh return (false); 550587aa255SWarner Losh } 551b846efd7SJim Harris 5527d5eebe0SWarner Losh /* 5537d5eebe0SWarner Losh * Sanity check initialization. After we reset the hardware, the phase 5547d5eebe0SWarner Losh * is defined to be 1. So if we get here with zero prior calls and the 5557d5eebe0SWarner Losh * phase is 0, it means that we've lost a race between the 5567d5eebe0SWarner Losh * initialization and the ISR running. With the phase wrong, we'll 5577d5eebe0SWarner Losh * process a bunch of completions that aren't really completions leading 5587d5eebe0SWarner Losh * to a KASSERT below. 5597d5eebe0SWarner Losh */ 5607d5eebe0SWarner Losh KASSERT(!(qpair->num_intr_handler_calls == 0 && qpair->phase == 0), 5617d5eebe0SWarner Losh ("%s: Phase wrong for first interrupt call.", 5627d5eebe0SWarner Losh device_get_nameunit(qpair->ctrlr->dev))); 5637d5eebe0SWarner Losh 5647d5eebe0SWarner Losh qpair->num_intr_handler_calls++; 5657d5eebe0SWarner Losh 5668f9d5a8dSMichal Meloun bus_dmamap_sync(qpair->dma_tag, qpair->queuemem_map, 5678f9d5a8dSMichal Meloun BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 568d0aaeffdSWarner Losh /* 569d0aaeffdSWarner Losh * A panic can stop the CPU this routine is running on at any point. If 570d0aaeffdSWarner Losh * we're called during a panic, complete the sq_head wrap protocol for 571d0aaeffdSWarner Losh * the case where we are interrupted just after the increment at 1 572d0aaeffdSWarner Losh * below, but before we can reset cq_head to zero at 2. Also cope with 573d0aaeffdSWarner Losh * the case where we do the zero at 2, but may or may not have done the 574d0aaeffdSWarner Losh * phase adjustment at step 3. The panic machinery flushes all pending 575d0aaeffdSWarner Losh * memory writes, so we can make these strong ordering assumptions 576d0aaeffdSWarner Losh * that would otherwise be unwise if we were racing in real time. 577d0aaeffdSWarner Losh */ 578d0aaeffdSWarner Losh if (__predict_false(in_panic)) { 579d0aaeffdSWarner Losh if (qpair->cq_head == qpair->num_entries) { 580d0aaeffdSWarner Losh /* 581d0aaeffdSWarner Losh * Here we know that we need to zero cq_head and then negate 582d0aaeffdSWarner Losh * the phase, which hasn't been assigned if cq_head isn't 583d0aaeffdSWarner Losh * zero due to the atomic_store_rel. 584d0aaeffdSWarner Losh */ 585d0aaeffdSWarner Losh qpair->cq_head = 0; 586d0aaeffdSWarner Losh qpair->phase = !qpair->phase; 587d0aaeffdSWarner Losh } else if (qpair->cq_head == 0) { 588d0aaeffdSWarner Losh /* 589d0aaeffdSWarner Losh * In this case, we know that the assignment at 2 590d0aaeffdSWarner Losh * happened below, but we don't know if it 3 happened or 591d0aaeffdSWarner Losh * not. To do this, we look at the last completion 592d0aaeffdSWarner Losh * entry and set the phase to the opposite phase 593d0aaeffdSWarner Losh * that it has. This gets us back in sync 594d0aaeffdSWarner Losh */ 595d0aaeffdSWarner Losh cpl = qpair->cpl[qpair->num_entries - 1]; 596d0aaeffdSWarner Losh nvme_completion_swapbytes(&cpl); 597d0aaeffdSWarner Losh qpair->phase = !NVME_STATUS_GET_P(cpl.status); 598d0aaeffdSWarner Losh } 599d0aaeffdSWarner Losh } 600d0aaeffdSWarner Losh 6016cb06070SJim Harris while (1) { 602aa0ab681SWarner Losh uint16_t status; 6036cb06070SJim Harris 604aa0ab681SWarner Losh /* 605aa0ab681SWarner Losh * We need to do this dance to avoid a race between the host and 606aa0ab681SWarner Losh * the device where the device overtakes the host while the host 607aa0ab681SWarner Losh * is reading this record, leaving the status field 'new' and 608aa0ab681SWarner Losh * the sqhd and cid fields potentially stale. If the phase 609aa0ab681SWarner Losh * doesn't match, that means status hasn't yet been updated and 610aa0ab681SWarner Losh * we'll get any pending changes next time. It also means that 611aa0ab681SWarner Losh * the phase must be the same the second time. We have to sync 612aa0ab681SWarner Losh * before reading to ensure any bouncing completes. 613aa0ab681SWarner Losh */ 614aa0ab681SWarner Losh status = le16toh(qpair->cpl[qpair->cq_head].status); 615aa0ab681SWarner Losh if (NVME_STATUS_GET_P(status) != qpair->phase) 616aa0ab681SWarner Losh break; 617aa0ab681SWarner Losh 618aa0ab681SWarner Losh bus_dmamap_sync(qpair->dma_tag, qpair->queuemem_map, 619aa0ab681SWarner Losh BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 620aa0ab681SWarner Losh cpl = qpair->cpl[qpair->cq_head]; 6210d787e9bSWojciech Macek nvme_completion_swapbytes(&cpl); 6220d787e9bSWojciech Macek 623aa0ab681SWarner Losh KASSERT( 624aa0ab681SWarner Losh NVME_STATUS_GET_P(status) == NVME_STATUS_GET_P(cpl.status), 625aa0ab681SWarner Losh ("Phase unexpectedly inconsistent")); 6266cb06070SJim Harris 62736a87d0cSWarner Losh if (cpl.cid < qpair->num_trackers) 6280d787e9bSWojciech Macek tr = qpair->act_tr[cpl.cid]; 62936a87d0cSWarner Losh else 63036a87d0cSWarner Losh tr = NULL; 6316cb06070SJim Harris 6322ec165e3SWarner Losh done++; 6336cb06070SJim Harris if (tr != NULL) { 63443393e8bSWarner Losh nvme_qpair_complete_tracker(tr, &cpl, ERROR_PRINT_ALL); 6350d787e9bSWojciech Macek qpair->sq_head = cpl.sqhd; 636d0aaeffdSWarner Losh } else if (!in_panic) { 637d0aaeffdSWarner Losh /* 638d0aaeffdSWarner Losh * A missing tracker is normally an error. However, a 639d0aaeffdSWarner Losh * panic can stop the CPU this routine is running on 640d0aaeffdSWarner Losh * after completing an I/O but before updating 641d0aaeffdSWarner Losh * qpair->cq_head at 1 below. Later, we re-enter this 642d0aaeffdSWarner Losh * routine to poll I/O associated with the kernel 643d0aaeffdSWarner Losh * dump. We find that the tr has been set to null before 644d0aaeffdSWarner Losh * calling the completion routine. If it hasn't 645d0aaeffdSWarner Losh * completed (or it triggers a panic), then '1' below 646d0aaeffdSWarner Losh * won't have updated cq_head. Rather than panic again, 647d0aaeffdSWarner Losh * ignore this condition because it's not unexpected. 648d0aaeffdSWarner Losh */ 649547d523eSJim Harris nvme_printf(qpair->ctrlr, 65036a87d0cSWarner Losh "cpl (cid = %u) does not map to outstanding cmd\n", 65136a87d0cSWarner Losh cpl.cid); 6526f76d493SWarner Losh nvme_qpair_print_completion(qpair, 6536f76d493SWarner Losh &qpair->cpl[qpair->cq_head]); 654d0aaeffdSWarner Losh KASSERT(0, ("received completion for unknown cmd")); 6556cb06070SJim Harris } 656bb0ec6b3SJim Harris 657d0aaeffdSWarner Losh /* 658d0aaeffdSWarner Losh * There's a number of races with the following (see above) when 659d0aaeffdSWarner Losh * the system panics. We compensate for each one of them by 660d0aaeffdSWarner Losh * using the atomic store to force strong ordering (at least when 661d0aaeffdSWarner Losh * viewed in the aftermath of a panic). 662d0aaeffdSWarner Losh */ 663d0aaeffdSWarner Losh if (++qpair->cq_head == qpair->num_entries) { /* 1 */ 664d0aaeffdSWarner Losh atomic_store_rel_int(&qpair->cq_head, 0); /* 2 */ 665d0aaeffdSWarner Losh qpair->phase = !qpair->phase; /* 3 */ 666bb0ec6b3SJim Harris } 6672ec165e3SWarner Losh } 668bb0ec6b3SJim Harris 6692ec165e3SWarner Losh if (done != 0) { 670f93b7f95SWarner Losh bus_space_write_4(qpair->ctrlr->bus_tag, qpair->ctrlr->bus_handle, 671f93b7f95SWarner Losh qpair->cq_hdbl_off, qpair->cq_head); 672bb0ec6b3SJim Harris } 6732ec165e3SWarner Losh 674d85d9648SWarner Losh return (done != 0); 675bb0ec6b3SJim Harris } 676bb0ec6b3SJim Harris 677bb0ec6b3SJim Harris static void 678e3bdf3daSAlexander Motin nvme_qpair_msi_handler(void *arg) 679bb0ec6b3SJim Harris { 680bb0ec6b3SJim Harris struct nvme_qpair *qpair = arg; 681bb0ec6b3SJim Harris 682bb0ec6b3SJim Harris nvme_qpair_process_completions(qpair); 683bb0ec6b3SJim Harris } 684bb0ec6b3SJim Harris 685a965389bSScott Long int 6861eab19cbSAlexander Motin nvme_qpair_construct(struct nvme_qpair *qpair, 6871eab19cbSAlexander Motin uint32_t num_entries, uint32_t num_trackers, 6888d09e3c4SJim Harris struct nvme_controller *ctrlr) 689bb0ec6b3SJim Harris { 69021b6da58SJim Harris struct nvme_tracker *tr; 691a965389bSScott Long size_t cmdsz, cplsz, prpsz, allocsz, prpmemsz; 692a965389bSScott Long uint64_t queuemem_phys, prpmem_phys, list_phys; 693a965389bSScott Long uint8_t *queuemem, *prpmem, *prp_list; 694a965389bSScott Long int i, err; 695bb0ec6b3SJim Harris 696e3bdf3daSAlexander Motin qpair->vector = ctrlr->msi_count > 1 ? qpair->id : 0; 697bb0ec6b3SJim Harris qpair->num_entries = num_entries; 6980f71ecf7SJim Harris qpair->num_trackers = num_trackers; 699bb0ec6b3SJim Harris qpair->ctrlr = ctrlr; 700bb0ec6b3SJim Harris 701bb0ec6b3SJim Harris mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF); 702bb0ec6b3SJim Harris 7031416ef36SJim Harris /* Note: NVMe PRP format is restricted to 4-byte alignment. */ 704a6e30963SJim Harris err = bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 7050fd4cd40SWarner Losh 4, ctrlr->page_size, BUS_SPACE_MAXADDR, 706ac90f70dSAlexander Motin BUS_SPACE_MAXADDR, NULL, NULL, ctrlr->max_xfer_size, 7070fd4cd40SWarner Losh howmany(ctrlr->max_xfer_size, ctrlr->page_size) + 1, 7080fd4cd40SWarner Losh ctrlr->page_size, 0, 709a6e30963SJim Harris NULL, NULL, &qpair->dma_tag_payload); 710a965389bSScott Long if (err != 0) { 711a6e30963SJim Harris nvme_printf(ctrlr, "payload tag create failed %d\n", err); 712a965389bSScott Long goto out; 713a965389bSScott Long } 714a965389bSScott Long 715a965389bSScott Long /* 716a965389bSScott Long * Each component must be page aligned, and individual PRP lists 717a965389bSScott Long * cannot cross a page boundary. 718a965389bSScott Long */ 719a965389bSScott Long cmdsz = qpair->num_entries * sizeof(struct nvme_command); 7200fd4cd40SWarner Losh cmdsz = roundup2(cmdsz, ctrlr->page_size); 721a965389bSScott Long cplsz = qpair->num_entries * sizeof(struct nvme_completion); 7220fd4cd40SWarner Losh cplsz = roundup2(cplsz, ctrlr->page_size); 723ac90f70dSAlexander Motin /* 724ac90f70dSAlexander Motin * For commands requiring more than 2 PRP entries, one PRP will be 725ac90f70dSAlexander Motin * embedded in the command (prp1), and the rest of the PRP entries 726ac90f70dSAlexander Motin * will be in a list pointed to by the command (prp2). 727ac90f70dSAlexander Motin */ 7280fd4cd40SWarner Losh prpsz = sizeof(uint64_t) * 7290fd4cd40SWarner Losh howmany(ctrlr->max_xfer_size, ctrlr->page_size); 730a965389bSScott Long prpmemsz = qpair->num_trackers * prpsz; 731a965389bSScott Long allocsz = cmdsz + cplsz + prpmemsz; 732a6e30963SJim Harris 733a6e30963SJim Harris err = bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev), 7340fd4cd40SWarner Losh ctrlr->page_size, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 735a965389bSScott Long allocsz, 1, allocsz, 0, NULL, NULL, &qpair->dma_tag); 736a965389bSScott Long if (err != 0) { 737a6e30963SJim Harris nvme_printf(ctrlr, "tag create failed %d\n", err); 738a965389bSScott Long goto out; 739a965389bSScott Long } 7401eab19cbSAlexander Motin bus_dma_tag_set_domain(qpair->dma_tag, qpair->domain); 741a965389bSScott Long 742a965389bSScott Long if (bus_dmamem_alloc(qpair->dma_tag, (void **)&queuemem, 7438f9d5a8dSMichal Meloun BUS_DMA_COHERENT | BUS_DMA_NOWAIT, &qpair->queuemem_map)) { 744a965389bSScott Long nvme_printf(ctrlr, "failed to alloc qpair memory\n"); 745a965389bSScott Long goto out; 746a965389bSScott Long } 747a965389bSScott Long 748a965389bSScott Long if (bus_dmamap_load(qpair->dma_tag, qpair->queuemem_map, 749a965389bSScott Long queuemem, allocsz, nvme_single_map, &queuemem_phys, 0) != 0) { 750a965389bSScott Long nvme_printf(ctrlr, "failed to load qpair memory\n"); 751550d5d64SAlexander Motin bus_dmamem_free(qpair->dma_tag, qpair->cmd, 752550d5d64SAlexander Motin qpair->queuemem_map); 753a965389bSScott Long goto out; 754a965389bSScott Long } 755bb0ec6b3SJim Harris 756bb0ec6b3SJim Harris qpair->num_cmds = 0; 7576568ebfcSJim Harris qpair->num_intr_handler_calls = 0; 758c37fc318SWarner Losh qpair->num_retries = 0; 7595e83c2ffSWarner Losh qpair->num_failures = 0; 760587aa255SWarner Losh qpair->num_ignored = 0; 761a965389bSScott Long qpair->cmd = (struct nvme_command *)queuemem; 762a965389bSScott Long qpair->cpl = (struct nvme_completion *)(queuemem + cmdsz); 763a965389bSScott Long prpmem = (uint8_t *)(queuemem + cmdsz + cplsz); 764a965389bSScott Long qpair->cmd_bus_addr = queuemem_phys; 765a965389bSScott Long qpair->cpl_bus_addr = queuemem_phys + cmdsz; 766a965389bSScott Long prpmem_phys = queuemem_phys + cmdsz + cplsz; 767bb0ec6b3SJim Harris 768502dc84aSWarner Losh callout_init(&qpair->timer, 1); 769502dc84aSWarner Losh qpair->timer_armed = false; 770fa81f373SWarner Losh qpair->recovery_state = RECOVERY_WAITING; 771502dc84aSWarner Losh 772f93b7f95SWarner Losh /* 773f93b7f95SWarner Losh * Calcuate the stride of the doorbell register. Many emulators set this 774f93b7f95SWarner Losh * value to correspond to a cache line. However, some hardware has set 775f93b7f95SWarner Losh * it to various small values. 776f93b7f95SWarner Losh */ 777f93b7f95SWarner Losh qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[0]) + 7781eab19cbSAlexander Motin (qpair->id << (ctrlr->dstrd + 1)); 779f93b7f95SWarner Losh qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[0]) + 7801eab19cbSAlexander Motin (qpair->id << (ctrlr->dstrd + 1)) + (1 << ctrlr->dstrd); 781bb0ec6b3SJim Harris 78265c2474eSJim Harris TAILQ_INIT(&qpair->free_tr); 78365c2474eSJim Harris TAILQ_INIT(&qpair->outstanding_tr); 7840f71ecf7SJim Harris STAILQ_INIT(&qpair->queued_req); 785bb0ec6b3SJim Harris 786a965389bSScott Long list_phys = prpmem_phys; 787a965389bSScott Long prp_list = prpmem; 7880f71ecf7SJim Harris for (i = 0; i < qpair->num_trackers; i++) { 789a965389bSScott Long if (list_phys + prpsz > prpmem_phys + prpmemsz) { 790a965389bSScott Long qpair->num_trackers = i; 791a965389bSScott Long break; 79221b6da58SJim Harris } 79321b6da58SJim Harris 794a965389bSScott Long /* 795a965389bSScott Long * Make sure that the PRP list for this tracker doesn't 7960fd4cd40SWarner Losh * overflow to another nvme page. 797a965389bSScott Long */ 798a965389bSScott Long if (trunc_page(list_phys) != 799a965389bSScott Long trunc_page(list_phys + prpsz - 1)) { 8000fd4cd40SWarner Losh list_phys = roundup2(list_phys, ctrlr->page_size); 801a965389bSScott Long prp_list = 8020fd4cd40SWarner Losh (uint8_t *)roundup2((uintptr_t)prp_list, ctrlr->page_size); 803a965389bSScott Long } 804a965389bSScott Long 8051eab19cbSAlexander Motin tr = malloc_domainset(sizeof(*tr), M_NVME, 8061eab19cbSAlexander Motin DOMAINSET_PREF(qpair->domain), M_ZERO | M_WAITOK); 807a965389bSScott Long bus_dmamap_create(qpair->dma_tag_payload, 0, 808a965389bSScott Long &tr->payload_dma_map); 809a965389bSScott Long tr->cid = i; 810a965389bSScott Long tr->qpair = qpair; 811a965389bSScott Long tr->prp = (uint64_t *)prp_list; 812a965389bSScott Long tr->prp_bus_addr = list_phys; 813a965389bSScott Long TAILQ_INSERT_HEAD(&qpair->free_tr, tr, tailq); 814a965389bSScott Long list_phys += prpsz; 815a965389bSScott Long prp_list += prpsz; 816a965389bSScott Long } 817a965389bSScott Long 818a965389bSScott Long if (qpair->num_trackers == 0) { 819a965389bSScott Long nvme_printf(ctrlr, "failed to allocate enough trackers\n"); 820a965389bSScott Long goto out; 821a965389bSScott Long } 822a965389bSScott Long 8231eab19cbSAlexander Motin qpair->act_tr = malloc_domainset(sizeof(struct nvme_tracker *) * 8241eab19cbSAlexander Motin qpair->num_entries, M_NVME, DOMAINSET_PREF(qpair->domain), 8251eab19cbSAlexander Motin M_ZERO | M_WAITOK); 826fc9a0840SWarner Losh 827e3bdf3daSAlexander Motin if (ctrlr->msi_count > 1) { 828fc9a0840SWarner Losh /* 829fc9a0840SWarner Losh * MSI-X vector resource IDs start at 1, so we add one to 830fc9a0840SWarner Losh * the queue's vector to get the corresponding rid to use. 831fc9a0840SWarner Losh */ 832fc9a0840SWarner Losh qpair->rid = qpair->vector + 1; 833fc9a0840SWarner Losh 834fc9a0840SWarner Losh qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ, 835fc9a0840SWarner Losh &qpair->rid, RF_ACTIVE); 836e3bdf3daSAlexander Motin if (qpair->res == NULL) { 837e3bdf3daSAlexander Motin nvme_printf(ctrlr, "unable to allocate MSI\n"); 838e3bdf3daSAlexander Motin goto out; 839e3bdf3daSAlexander Motin } 840fc9a0840SWarner Losh if (bus_setup_intr(ctrlr->dev, qpair->res, 841fc9a0840SWarner Losh INTR_TYPE_MISC | INTR_MPSAFE, NULL, 842e3bdf3daSAlexander Motin nvme_qpair_msi_handler, qpair, &qpair->tag) != 0) { 843e3bdf3daSAlexander Motin nvme_printf(ctrlr, "unable to setup MSI\n"); 844fc9a0840SWarner Losh goto out; 845fc9a0840SWarner Losh } 846fc9a0840SWarner Losh if (qpair->id == 0) { 847fc9a0840SWarner Losh bus_describe_intr(ctrlr->dev, qpair->res, qpair->tag, 848fc9a0840SWarner Losh "admin"); 849fc9a0840SWarner Losh } else { 850fc9a0840SWarner Losh bus_describe_intr(ctrlr->dev, qpair->res, qpair->tag, 851fc9a0840SWarner Losh "io%d", qpair->id - 1); 852fc9a0840SWarner Losh } 853fc9a0840SWarner Losh } 854fc9a0840SWarner Losh 855a965389bSScott Long return (0); 856a965389bSScott Long 857a965389bSScott Long out: 858a965389bSScott Long nvme_qpair_destroy(qpair); 859a965389bSScott Long return (ENOMEM); 860bb0ec6b3SJim Harris } 861bb0ec6b3SJim Harris 862bb0ec6b3SJim Harris static void 863bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair) 864bb0ec6b3SJim Harris { 865bb0ec6b3SJim Harris struct nvme_tracker *tr; 866bb0ec6b3SJim Harris 867502dc84aSWarner Losh callout_drain(&qpair->timer); 868502dc84aSWarner Losh 869550d5d64SAlexander Motin if (qpair->tag) { 870bb0ec6b3SJim Harris bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag); 871550d5d64SAlexander Motin qpair->tag = NULL; 8723d7eb41cSJim Harris } 8733d7eb41cSJim Harris 874550d5d64SAlexander Motin if (qpair->act_tr) { 87596ad26eeSMark Johnston free(qpair->act_tr, M_NVME); 876550d5d64SAlexander Motin qpair->act_tr = NULL; 877550d5d64SAlexander Motin } 878bb0ec6b3SJim Harris 87965c2474eSJim Harris while (!TAILQ_EMPTY(&qpair->free_tr)) { 88065c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 88165c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 882c6c70c07SAlexander Motin bus_dmamap_destroy(qpair->dma_tag_payload, 883c6c70c07SAlexander Motin tr->payload_dma_map); 88496ad26eeSMark Johnston free(tr, M_NVME); 885bb0ec6b3SJim Harris } 886c6c70c07SAlexander Motin 887550d5d64SAlexander Motin if (qpair->cmd != NULL) { 888550d5d64SAlexander Motin bus_dmamap_unload(qpair->dma_tag, qpair->queuemem_map); 889550d5d64SAlexander Motin bus_dmamem_free(qpair->dma_tag, qpair->cmd, 890550d5d64SAlexander Motin qpair->queuemem_map); 891550d5d64SAlexander Motin qpair->cmd = NULL; 892550d5d64SAlexander Motin } 893c6c70c07SAlexander Motin 894550d5d64SAlexander Motin if (qpair->dma_tag) { 895550d5d64SAlexander Motin bus_dma_tag_destroy(qpair->dma_tag); 896550d5d64SAlexander Motin qpair->dma_tag = NULL; 897550d5d64SAlexander Motin } 898550d5d64SAlexander Motin 899550d5d64SAlexander Motin if (qpair->dma_tag_payload) { 900c6c70c07SAlexander Motin bus_dma_tag_destroy(qpair->dma_tag_payload); 901550d5d64SAlexander Motin qpair->dma_tag_payload = NULL; 902550d5d64SAlexander Motin } 903550d5d64SAlexander Motin 904550d5d64SAlexander Motin if (mtx_initialized(&qpair->lock)) 905550d5d64SAlexander Motin mtx_destroy(&qpair->lock); 906550d5d64SAlexander Motin 907550d5d64SAlexander Motin if (qpair->res) { 908550d5d64SAlexander Motin bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ, 909550d5d64SAlexander Motin rman_get_rid(qpair->res), qpair->res); 910550d5d64SAlexander Motin qpair->res = NULL; 911550d5d64SAlexander Motin } 912bb0ec6b3SJim Harris } 913bb0ec6b3SJim Harris 914b846efd7SJim Harris static void 915b846efd7SJim Harris nvme_admin_qpair_abort_aers(struct nvme_qpair *qpair) 916b846efd7SJim Harris { 917b846efd7SJim Harris struct nvme_tracker *tr; 918b846efd7SJim Harris 919b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 920b846efd7SJim Harris while (tr != NULL) { 9219544e6dcSChuck Tuffli if (tr->req->cmd.opc == NVME_OPC_ASYNC_EVENT_REQUEST) { 92243393e8bSWarner Losh nvme_qpair_manual_complete_tracker(tr, 923232e2edbSJim Harris NVME_SCT_GENERIC, NVME_SC_ABORTED_SQ_DELETION, 0, 9242ffd6fceSWarner Losh ERROR_PRINT_NONE); 925b846efd7SJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 926b846efd7SJim Harris } else { 927b846efd7SJim Harris tr = TAILQ_NEXT(tr, tailq); 928b846efd7SJim Harris } 929b846efd7SJim Harris } 930b846efd7SJim Harris } 931b846efd7SJim Harris 932bb0ec6b3SJim Harris void 933bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair) 934bb0ec6b3SJim Harris { 935bb0ec6b3SJim Harris 936b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 937bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 938bb0ec6b3SJim Harris } 939bb0ec6b3SJim Harris 940bb0ec6b3SJim Harris void 941bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair) 942bb0ec6b3SJim Harris { 943bb0ec6b3SJim Harris 944bb0ec6b3SJim Harris nvme_qpair_destroy(qpair); 945bb0ec6b3SJim Harris } 946bb0ec6b3SJim Harris 947bb0ec6b3SJim Harris static void 9482a6b7055SWarner Losh nvme_abort_complete(void *arg, const struct nvme_completion *status) 9492a6b7055SWarner Losh { 9502a6b7055SWarner Losh struct nvme_tracker *tr = arg; 9512a6b7055SWarner Losh 9522a6b7055SWarner Losh /* 9532a6b7055SWarner Losh * If cdw0 == 1, the controller was not able to abort the command 9542a6b7055SWarner Losh * we requested. We still need to check the active tracker array, 9552a6b7055SWarner Losh * to cover race where I/O timed out at same time controller was 9562a6b7055SWarner Losh * completing the I/O. 9572a6b7055SWarner Losh */ 9582a6b7055SWarner Losh if (status->cdw0 == 1 && tr->qpair->act_tr[tr->cid] != NULL) { 9592a6b7055SWarner Losh /* 9602a6b7055SWarner Losh * An I/O has timed out, and the controller was unable to 9612a6b7055SWarner Losh * abort it for some reason. Construct a fake completion 9622a6b7055SWarner Losh * status, and then complete the I/O's tracker manually. 9632a6b7055SWarner Losh */ 9642a6b7055SWarner Losh nvme_printf(tr->qpair->ctrlr, 9652a6b7055SWarner Losh "abort command failed, aborting command manually\n"); 9662a6b7055SWarner Losh nvme_qpair_manual_complete_tracker(tr, 9672a6b7055SWarner Losh NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST, 0, ERROR_PRINT_ALL); 9682a6b7055SWarner Losh } 9692a6b7055SWarner Losh } 9702a6b7055SWarner Losh 9712a6b7055SWarner Losh static void 972502dc84aSWarner Losh nvme_qpair_timeout(void *arg) 9730a0b08ccSJim Harris { 974502dc84aSWarner Losh struct nvme_qpair *qpair = arg; 97512d191ecSJim Harris struct nvme_controller *ctrlr = qpair->ctrlr; 976502dc84aSWarner Losh struct nvme_tracker *tr; 977502dc84aSWarner Losh sbintime_t now; 978502dc84aSWarner Losh bool idle; 979*d4959bfcSWarner Losh bool needs_reset; 9800d787e9bSWojciech Macek uint32_t csts; 9810d787e9bSWojciech Macek uint8_t cfs; 982448195e7SJim Harris 983*d4959bfcSWarner Losh 984502dc84aSWarner Losh mtx_lock(&qpair->lock); 985502dc84aSWarner Losh idle = TAILQ_EMPTY(&qpair->outstanding_tr); 9862a6b7055SWarner Losh 987502dc84aSWarner Losh switch (qpair->recovery_state) { 988502dc84aSWarner Losh case RECOVERY_NONE: 9892a6b7055SWarner Losh /* 990*d4959bfcSWarner Losh * Read csts to get value of cfs - controller fatal status. If 991*d4959bfcSWarner Losh * we are in the hot-plug or controller failed status proceed 992*d4959bfcSWarner Losh * directly to reset. We also bail early if the status reads all 993*d4959bfcSWarner Losh * 1's or the control fatal status bit is now 1. The latter is 994*d4959bfcSWarner Losh * always true when the former is true, but not vice versa. The 995*d4959bfcSWarner Losh * intent of the code is that if the card is gone (all 1's) or 996*d4959bfcSWarner Losh * we've failed, then try to do a reset (which someitmes 997*d4959bfcSWarner Losh * unwedges a card reading all 1's that's not gone away, but 998*d4959bfcSWarner Losh * usually doesn't). 999*d4959bfcSWarner Losh */ 1000*d4959bfcSWarner Losh csts = nvme_mmio_read_4(ctrlr, csts); 1001*d4959bfcSWarner Losh cfs = (csts >> NVME_CSTS_REG_CFS_SHIFT) & NVME_CSTS_REG_CFS_MASK; 1002*d4959bfcSWarner Losh if (csts == NVME_GONE || cfs == 1) 1003*d4959bfcSWarner Losh goto do_reset; 1004*d4959bfcSWarner Losh 1005*d4959bfcSWarner Losh /* 1006*d4959bfcSWarner Losh * Next, check to see if we have any completions. If we do, 1007*d4959bfcSWarner Losh * we've likely missed an interrupt, but the card is otherwise 1008*d4959bfcSWarner Losh * fine. This will also catch all the commands that are about 1009*d4959bfcSWarner Losh * to timeout (but there's still a tiny race). Since the timeout 1010*d4959bfcSWarner Losh * is long relative to the race between here and the check below, 1011*d4959bfcSWarner Losh * this is still a win. 1012*d4959bfcSWarner Losh */ 1013*d4959bfcSWarner Losh mtx_unlock(&qpair->lock); 1014*d4959bfcSWarner Losh nvme_qpair_process_completions(qpair); 1015*d4959bfcSWarner Losh mtx_lock(&qpair->lock); 1016*d4959bfcSWarner Losh if (qpair->recovery_state != RECOVERY_NONE) { 1017*d4959bfcSWarner Losh /* 1018*d4959bfcSWarner Losh * Somebody else adjusted recovery state while unlocked, 1019*d4959bfcSWarner Losh * we should bail. Unlock the qpair and return without 1020*d4959bfcSWarner Losh * doing anything else. 1021*d4959bfcSWarner Losh */ 1022*d4959bfcSWarner Losh mtx_unlock(&qpair->lock); 1023*d4959bfcSWarner Losh return; 1024*d4959bfcSWarner Losh } 1025*d4959bfcSWarner Losh 1026*d4959bfcSWarner Losh /* 10272a6b7055SWarner Losh * Check to see if we need to timeout any commands. If we do, then 10282a6b7055SWarner Losh * we also enter a recovery phase. 10292a6b7055SWarner Losh */ 1030502dc84aSWarner Losh now = getsbinuptime(); 1031*d4959bfcSWarner Losh needs_reset = false; 1032b3c9b606SAlexander Motin TAILQ_FOREACH(tr, &qpair->outstanding_tr, tailq) { 1033b3c9b606SAlexander Motin if (tr->deadline == SBT_MAX) 1034b3c9b606SAlexander Motin continue; 1035b3c9b606SAlexander Motin if (now > tr->deadline) { 1036*d4959bfcSWarner Losh if (tr->req->cb_fn != nvme_abort_complete && 1037*d4959bfcSWarner Losh ctrlr->enable_aborts) { 1038*d4959bfcSWarner Losh /* 1039*d4959bfcSWarner Losh * This isn't an abort command, ask 1040*d4959bfcSWarner Losh * for a hardware abort. 1041*d4959bfcSWarner Losh */ 1042*d4959bfcSWarner Losh nvme_ctrlr_cmd_abort(ctrlr, tr->cid, 1043*d4959bfcSWarner Losh qpair->id, nvme_abort_complete, tr); 1044*d4959bfcSWarner Losh } else { 1045*d4959bfcSWarner Losh /* 1046*d4959bfcSWarner Losh * Otherwise we have a live command in 1047*d4959bfcSWarner Losh * the card (either one we couldn't 1048*d4959bfcSWarner Losh * abort, or aborts weren't enabled). 1049*d4959bfcSWarner Losh * The only safe way to proceed is to do 1050*d4959bfcSWarner Losh * a reset. 1051*d4959bfcSWarner Losh */ 1052*d4959bfcSWarner Losh needs_reset = true; 1053*d4959bfcSWarner Losh } 1054*d4959bfcSWarner Losh } else { 1055*d4959bfcSWarner Losh idle = false; 10562a6b7055SWarner Losh } 10572a6b7055SWarner Losh } 1058*d4959bfcSWarner Losh if (!needs_reset) 10592a6b7055SWarner Losh break; 10602a6b7055SWarner Losh 1061502dc84aSWarner Losh /* 1062*d4959bfcSWarner Losh * We've had a command timeout that we weren't able to abort 1063*d4959bfcSWarner Losh * 1064502dc84aSWarner Losh * If we get here due to a possible surprise hot-unplug event, 1065502dc84aSWarner Losh * then we let nvme_ctrlr_reset confirm and fail the 1066502dc84aSWarner Losh * controller. 1067502dc84aSWarner Losh */ 1068*d4959bfcSWarner Losh do_reset: 1069502dc84aSWarner Losh nvme_printf(ctrlr, "Resetting controller due to a timeout%s.\n", 107018dc12bfSWarner Losh (csts == 0xffffffff) ? " and possible hot unplug" : 107118dc12bfSWarner Losh (cfs ? " and fatal error status" : "")); 1072502dc84aSWarner Losh nvme_printf(ctrlr, "RECOVERY_WAITING\n"); 1073502dc84aSWarner Losh qpair->recovery_state = RECOVERY_WAITING; 1074502dc84aSWarner Losh nvme_ctrlr_reset(ctrlr); 1075*d4959bfcSWarner Losh idle = false; /* We want to keep polling */ 1076502dc84aSWarner Losh break; 1077502dc84aSWarner Losh case RECOVERY_WAITING: 1078*d4959bfcSWarner Losh nvme_printf(ctrlr, "waiting for reset to complete\n"); 1079502dc84aSWarner Losh break; 1080d85d9648SWarner Losh } 1081bb0ec6b3SJim Harris 1082502dc84aSWarner Losh /* 1083502dc84aSWarner Losh * Rearm the timeout. 1084502dc84aSWarner Losh */ 1085502dc84aSWarner Losh if (!idle) { 1086b3c9b606SAlexander Motin callout_schedule_sbt(&qpair->timer, SBT_1S / 2, SBT_1S / 2, 0); 1087502dc84aSWarner Losh } else { 1088502dc84aSWarner Losh qpair->timer_armed = false; 1089502dc84aSWarner Losh } 1090502dc84aSWarner Losh mtx_unlock(&qpair->lock); 1091502dc84aSWarner Losh } 1092502dc84aSWarner Losh 1093502dc84aSWarner Losh /* 1094502dc84aSWarner Losh * Submit the tracker to the hardware. Must already be in the 1095502dc84aSWarner Losh * outstanding queue when called. 1096502dc84aSWarner Losh */ 1097bb0ec6b3SJim Harris void 1098b846efd7SJim Harris nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr) 1099bb0ec6b3SJim Harris { 1100ad697276SJim Harris struct nvme_request *req; 110194143332SJim Harris struct nvme_controller *ctrlr; 1102ead7e103SAlexander Motin int timeout; 1103bb0ec6b3SJim Harris 1104b846efd7SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 1105b846efd7SJim Harris 1106ad697276SJim Harris req = tr->req; 1107ad697276SJim Harris req->cmd.cid = tr->cid; 1108bb0ec6b3SJim Harris qpair->act_tr[tr->cid] = tr; 110994143332SJim Harris ctrlr = qpair->ctrlr; 1110bb0ec6b3SJim Harris 1111ead7e103SAlexander Motin if (req->timeout) { 1112ead7e103SAlexander Motin if (req->cb_fn == nvme_completion_poll_cb) 1113502dc84aSWarner Losh timeout = 1; 1114ead7e103SAlexander Motin else 1115502dc84aSWarner Losh timeout = ctrlr->timeout_period; 1116502dc84aSWarner Losh tr->deadline = getsbinuptime() + timeout * SBT_1S; 1117502dc84aSWarner Losh if (!qpair->timer_armed) { 1118502dc84aSWarner Losh qpair->timer_armed = true; 1119b3c9b606SAlexander Motin callout_reset_sbt_on(&qpair->timer, SBT_1S / 2, SBT_1S / 2, 1120b3c9b606SAlexander Motin nvme_qpair_timeout, qpair, qpair->cpu, 0); 1121ead7e103SAlexander Motin } 1122502dc84aSWarner Losh } else 1123502dc84aSWarner Losh tr->deadline = SBT_MAX; 1124bb0ec6b3SJim Harris 1125bb0ec6b3SJim Harris /* Copy the command from the tracker to the submission queue. */ 1126ad697276SJim Harris memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd)); 1127bb0ec6b3SJim Harris 1128bb0ec6b3SJim Harris if (++qpair->sq_tail == qpair->num_entries) 1129bb0ec6b3SJim Harris qpair->sq_tail = 0; 1130bb0ec6b3SJim Harris 11312e0090afSJustin Hibbits bus_dmamap_sync(qpair->dma_tag, qpair->queuemem_map, 11322e0090afSJustin Hibbits BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1133f93b7f95SWarner Losh bus_space_write_4(qpair->ctrlr->bus_tag, qpair->ctrlr->bus_handle, 1134f93b7f95SWarner Losh qpair->sq_tdbl_off, qpair->sq_tail); 1135bb0ec6b3SJim Harris qpair->num_cmds++; 1136bb0ec6b3SJim Harris } 11375ae9ed68SJim Harris 1138d6f54866SJim Harris static void 1139ca269f32SJim Harris nvme_payload_map(void *arg, bus_dma_segment_t *seg, int nseg, int error) 1140ca269f32SJim Harris { 1141ca269f32SJim Harris struct nvme_tracker *tr = arg; 1142ca269f32SJim Harris uint32_t cur_nseg; 1143ca269f32SJim Harris 1144ca269f32SJim Harris /* 1145ca269f32SJim Harris * If the mapping operation failed, return immediately. The caller 1146ca269f32SJim Harris * is responsible for detecting the error status and failing the 1147ca269f32SJim Harris * tracker manually. 1148ca269f32SJim Harris */ 1149a6e30963SJim Harris if (error != 0) { 1150a6e30963SJim Harris nvme_printf(tr->qpair->ctrlr, 1151a6e30963SJim Harris "nvme_payload_map err %d\n", error); 1152ca269f32SJim Harris return; 1153a6e30963SJim Harris } 1154ca269f32SJim Harris 1155ca269f32SJim Harris /* 11560fd4cd40SWarner Losh * Note that we specified ctrlr->page_size for alignment and max 11570fd4cd40SWarner Losh * segment size when creating the bus dma tags. So here we can safely 11580fd4cd40SWarner Losh * just transfer each segment to its associated PRP entry. 1159ca269f32SJim Harris */ 11600d787e9bSWojciech Macek tr->req->cmd.prp1 = htole64(seg[0].ds_addr); 1161ca269f32SJim Harris 1162ca269f32SJim Harris if (nseg == 2) { 11630d787e9bSWojciech Macek tr->req->cmd.prp2 = htole64(seg[1].ds_addr); 1164ca269f32SJim Harris } else if (nseg > 2) { 1165ca269f32SJim Harris cur_nseg = 1; 11660d787e9bSWojciech Macek tr->req->cmd.prp2 = htole64((uint64_t)tr->prp_bus_addr); 1167ca269f32SJim Harris while (cur_nseg < nseg) { 1168ca269f32SJim Harris tr->prp[cur_nseg-1] = 11690d787e9bSWojciech Macek htole64((uint64_t)seg[cur_nseg].ds_addr); 1170ca269f32SJim Harris cur_nseg++; 1171ca269f32SJim Harris } 1172a6e30963SJim Harris } else { 1173a6e30963SJim Harris /* 1174a6e30963SJim Harris * prp2 should not be used by the controller 1175a6e30963SJim Harris * since there is only one segment, but set 1176a6e30963SJim Harris * to 0 just to be safe. 1177a6e30963SJim Harris */ 1178a6e30963SJim Harris tr->req->cmd.prp2 = 0; 1179ca269f32SJim Harris } 1180ca269f32SJim Harris 11812e0090afSJustin Hibbits bus_dmamap_sync(tr->qpair->dma_tag_payload, tr->payload_dma_map, 11822e0090afSJustin Hibbits BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1183ca269f32SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 1184ca269f32SJim Harris } 1185ca269f32SJim Harris 1186ca269f32SJim Harris static void 1187d6f54866SJim Harris _nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 11885ae9ed68SJim Harris { 11895ae9ed68SJim Harris struct nvme_tracker *tr; 1190e2b99004SJim Harris int err = 0; 11915ae9ed68SJim Harris 1192d6f54866SJim Harris mtx_assert(&qpair->lock, MA_OWNED); 11935ae9ed68SJim Harris 119465c2474eSJim Harris tr = TAILQ_FIRST(&qpair->free_tr); 1195232e2edbSJim Harris req->qpair = qpair; 119621b6da58SJim Harris 1197502dc84aSWarner Losh if (tr == NULL || qpair->recovery_state != RECOVERY_NONE) { 11980f71ecf7SJim Harris /* 1199b846efd7SJim Harris * No tracker is available, or the qpair is disabled due to 1200232e2edbSJim Harris * an in-progress controller-level reset or controller 1201232e2edbSJim Harris * failure. 1202232e2edbSJim Harris */ 1203232e2edbSJim Harris 1204232e2edbSJim Harris if (qpair->ctrlr->is_failed) { 1205232e2edbSJim Harris /* 12064b977e6dSWarner Losh * The controller has failed, so fail the request. 1207232e2edbSJim Harris */ 12084b977e6dSWarner Losh nvme_qpair_manual_complete_request(qpair, req, 12094b977e6dSWarner Losh NVME_SCT_GENERIC, NVME_SC_ABORTED_BY_REQUEST); 1210232e2edbSJim Harris } else { 1211232e2edbSJim Harris /* 1212232e2edbSJim Harris * Put the request on the qpair's request queue to be 1213232e2edbSJim Harris * processed when a tracker frees up via a command 1214232e2edbSJim Harris * completion or when the controller reset is 1215232e2edbSJim Harris * completed. 12160f71ecf7SJim Harris */ 12170f71ecf7SJim Harris STAILQ_INSERT_TAIL(&qpair->queued_req, req, stailq); 1218232e2edbSJim Harris } 1219d6f54866SJim Harris return; 122021b6da58SJim Harris } 122121b6da58SJim Harris 122265c2474eSJim Harris TAILQ_REMOVE(&qpair->free_tr, tr, tailq); 122365c2474eSJim Harris TAILQ_INSERT_TAIL(&qpair->outstanding_tr, tr, tailq); 1224502dc84aSWarner Losh tr->deadline = SBT_MAX; 12255ae9ed68SJim Harris tr->req = req; 12265ae9ed68SJim Harris 122792103adbSJohn Baldwin if (!req->payload_valid) { 1228b846efd7SJim Harris nvme_qpair_submit_tracker(tr->qpair, tr); 122992103adbSJohn Baldwin return; 12305ae9ed68SJim Harris } 1231e2b99004SJim Harris 123292103adbSJohn Baldwin err = bus_dmamap_load_mem(tr->qpair->dma_tag_payload, 123392103adbSJohn Baldwin tr->payload_dma_map, &req->payload, nvme_payload_map, tr, 0); 1234e2b99004SJim Harris if (err != 0) { 1235e2b99004SJim Harris /* 1236e2b99004SJim Harris * The dmamap operation failed, so we manually fail the 1237e2b99004SJim Harris * tracker here with DATA_TRANSFER_ERROR status. 1238e2b99004SJim Harris * 1239e2b99004SJim Harris * nvme_qpair_manual_complete_tracker must not be called 1240e2b99004SJim Harris * with the qpair lock held. 1241e2b99004SJim Harris */ 124292103adbSJohn Baldwin nvme_printf(qpair->ctrlr, 124392103adbSJohn Baldwin "bus_dmamap_load_mem returned 0x%x!\n", err); 1244e2b99004SJim Harris mtx_unlock(&qpair->lock); 124543393e8bSWarner Losh nvme_qpair_manual_complete_tracker(tr, NVME_SCT_GENERIC, 12462ffd6fceSWarner Losh NVME_SC_DATA_TRANSFER_ERROR, DO_NOT_RETRY, ERROR_PRINT_ALL); 1247e2b99004SJim Harris mtx_lock(&qpair->lock); 1248e2b99004SJim Harris } 1249d6f54866SJim Harris } 12505ae9ed68SJim Harris 1251d6f54866SJim Harris void 1252d6f54866SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req) 1253d6f54866SJim Harris { 1254d6f54866SJim Harris 1255d6f54866SJim Harris mtx_lock(&qpair->lock); 1256d6f54866SJim Harris _nvme_qpair_submit_request(qpair, req); 12575ae9ed68SJim Harris mtx_unlock(&qpair->lock); 12585ae9ed68SJim Harris } 1259b846efd7SJim Harris 1260b846efd7SJim Harris static void 1261b846efd7SJim Harris nvme_qpair_enable(struct nvme_qpair *qpair) 1262b846efd7SJim Harris { 1263502dc84aSWarner Losh mtx_assert(&qpair->lock, MA_OWNED); 1264b846efd7SJim Harris 1265502dc84aSWarner Losh qpair->recovery_state = RECOVERY_NONE; 1266cb5b7c13SJim Harris } 1267cb5b7c13SJim Harris 1268cb5b7c13SJim Harris void 1269cb5b7c13SJim Harris nvme_qpair_reset(struct nvme_qpair *qpair) 1270cb5b7c13SJim Harris { 1271cb5b7c13SJim Harris 1272b846efd7SJim Harris qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0; 1273b846efd7SJim Harris 1274b846efd7SJim Harris /* 1275b846efd7SJim Harris * First time through the completion queue, HW will set phase 1276b846efd7SJim Harris * bit on completions to 1. So set this to 1 here, indicating 1277b846efd7SJim Harris * we're looking for a 1 to know which entries have completed. 1278b846efd7SJim Harris * we'll toggle the bit each time when the completion queue 1279b846efd7SJim Harris * rolls over. 1280b846efd7SJim Harris */ 1281b846efd7SJim Harris qpair->phase = 1; 1282b846efd7SJim Harris 1283b846efd7SJim Harris memset(qpair->cmd, 0, 1284b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_command)); 1285b846efd7SJim Harris memset(qpair->cpl, 0, 1286b846efd7SJim Harris qpair->num_entries * sizeof(struct nvme_completion)); 1287b846efd7SJim Harris } 1288b846efd7SJim Harris 1289b846efd7SJim Harris void 1290b846efd7SJim Harris nvme_admin_qpair_enable(struct nvme_qpair *qpair) 1291b846efd7SJim Harris { 129243a37256SJim Harris struct nvme_tracker *tr; 129343a37256SJim Harris struct nvme_tracker *tr_temp; 1294a510dbc8SWarner Losh bool rpt; 129543a37256SJim Harris 129643a37256SJim Harris /* 129743a37256SJim Harris * Manually abort each outstanding admin command. Do not retry 129843a37256SJim Harris * admin commands found here, since they will be left over from 129943a37256SJim Harris * a controller reset and its likely the context in which the 130043a37256SJim Harris * command was issued no longer applies. 130143a37256SJim Harris */ 1302a510dbc8SWarner Losh rpt = !TAILQ_EMPTY(&qpair->outstanding_tr); 1303a510dbc8SWarner Losh if (rpt) 1304547d523eSJim Harris nvme_printf(qpair->ctrlr, 130543a37256SJim Harris "aborting outstanding admin command\n"); 1306a510dbc8SWarner Losh TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 130743393e8bSWarner Losh nvme_qpair_manual_complete_tracker(tr, NVME_SCT_GENERIC, 13082ffd6fceSWarner Losh NVME_SC_ABORTED_BY_REQUEST, DO_NOT_RETRY, ERROR_PRINT_ALL); 130943a37256SJim Harris } 1310a510dbc8SWarner Losh if (rpt) 1311a510dbc8SWarner Losh nvme_printf(qpair->ctrlr, 1312a510dbc8SWarner Losh "done aborting outstanding admin\n"); 1313b846efd7SJim Harris 1314502dc84aSWarner Losh mtx_lock(&qpair->lock); 1315b846efd7SJim Harris nvme_qpair_enable(qpair); 1316502dc84aSWarner Losh mtx_unlock(&qpair->lock); 1317b846efd7SJim Harris } 1318b846efd7SJim Harris 1319b846efd7SJim Harris void 1320b846efd7SJim Harris nvme_io_qpair_enable(struct nvme_qpair *qpair) 1321b846efd7SJim Harris { 1322b846efd7SJim Harris STAILQ_HEAD(, nvme_request) temp; 1323b846efd7SJim Harris struct nvme_tracker *tr; 1324cb5b7c13SJim Harris struct nvme_tracker *tr_temp; 1325b846efd7SJim Harris struct nvme_request *req; 1326a510dbc8SWarner Losh bool report; 1327b846efd7SJim Harris 1328cb5b7c13SJim Harris /* 1329cb5b7c13SJim Harris * Manually abort each outstanding I/O. This normally results in a 1330cb5b7c13SJim Harris * retry, unless the retry count on the associated request has 1331cb5b7c13SJim Harris * reached its limit. 1332cb5b7c13SJim Harris */ 1333a510dbc8SWarner Losh report = !TAILQ_EMPTY(&qpair->outstanding_tr); 1334a510dbc8SWarner Losh if (report) 1335547d523eSJim Harris nvme_printf(qpair->ctrlr, "aborting outstanding i/o\n"); 1336a510dbc8SWarner Losh TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 133743393e8bSWarner Losh nvme_qpair_manual_complete_tracker(tr, NVME_SCT_GENERIC, 13382ffd6fceSWarner Losh NVME_SC_ABORTED_BY_REQUEST, 0, ERROR_PRINT_NO_RETRY); 1339cb5b7c13SJim Harris } 1340a510dbc8SWarner Losh if (report) 1341a510dbc8SWarner Losh nvme_printf(qpair->ctrlr, "done aborting outstanding i/o\n"); 1342cb5b7c13SJim Harris 1343b846efd7SJim Harris mtx_lock(&qpair->lock); 1344b846efd7SJim Harris 1345b846efd7SJim Harris nvme_qpair_enable(qpair); 1346b846efd7SJim Harris 1347b846efd7SJim Harris STAILQ_INIT(&temp); 1348b846efd7SJim Harris STAILQ_SWAP(&qpair->queued_req, &temp, nvme_request); 1349b846efd7SJim Harris 1350a510dbc8SWarner Losh report = !STAILQ_EMPTY(&temp); 1351a510dbc8SWarner Losh if (report) 1352a510dbc8SWarner Losh nvme_printf(qpair->ctrlr, "resubmitting queued i/o\n"); 1353b846efd7SJim Harris while (!STAILQ_EMPTY(&temp)) { 1354b846efd7SJim Harris req = STAILQ_FIRST(&temp); 1355b846efd7SJim Harris STAILQ_REMOVE_HEAD(&temp, stailq); 1356547d523eSJim Harris nvme_qpair_print_command(qpair, &req->cmd); 1357b846efd7SJim Harris _nvme_qpair_submit_request(qpair, req); 1358b846efd7SJim Harris } 1359a510dbc8SWarner Losh if (report) 1360a510dbc8SWarner Losh nvme_printf(qpair->ctrlr, "done resubmitting i/o\n"); 1361b846efd7SJim Harris 1362b846efd7SJim Harris mtx_unlock(&qpair->lock); 1363b846efd7SJim Harris } 1364b846efd7SJim Harris 1365b846efd7SJim Harris static void 1366b846efd7SJim Harris nvme_qpair_disable(struct nvme_qpair *qpair) 1367b846efd7SJim Harris { 1368502dc84aSWarner Losh struct nvme_tracker *tr, *tr_temp; 1369b846efd7SJim Harris 1370b846efd7SJim Harris mtx_lock(&qpair->lock); 1371502dc84aSWarner Losh qpair->recovery_state = RECOVERY_WAITING; 1372502dc84aSWarner Losh TAILQ_FOREACH_SAFE(tr, &qpair->outstanding_tr, tailq, tr_temp) { 1373502dc84aSWarner Losh tr->deadline = SBT_MAX; 1374502dc84aSWarner Losh } 1375b846efd7SJim Harris mtx_unlock(&qpair->lock); 1376b846efd7SJim Harris } 1377b846efd7SJim Harris 1378b846efd7SJim Harris void 1379b846efd7SJim Harris nvme_admin_qpair_disable(struct nvme_qpair *qpair) 1380b846efd7SJim Harris { 1381b846efd7SJim Harris 1382b846efd7SJim Harris nvme_qpair_disable(qpair); 1383b846efd7SJim Harris nvme_admin_qpair_abort_aers(qpair); 1384b846efd7SJim Harris } 1385b846efd7SJim Harris 1386b846efd7SJim Harris void 1387b846efd7SJim Harris nvme_io_qpair_disable(struct nvme_qpair *qpair) 1388b846efd7SJim Harris { 1389b846efd7SJim Harris 1390b846efd7SJim Harris nvme_qpair_disable(qpair); 1391b846efd7SJim Harris } 1392232e2edbSJim Harris 1393232e2edbSJim Harris void 1394232e2edbSJim Harris nvme_qpair_fail(struct nvme_qpair *qpair) 1395232e2edbSJim Harris { 1396232e2edbSJim Harris struct nvme_tracker *tr; 1397232e2edbSJim Harris struct nvme_request *req; 1398232e2edbSJim Harris 1399824073fbSWarner Losh if (!mtx_initialized(&qpair->lock)) 1400824073fbSWarner Losh return; 1401824073fbSWarner Losh 1402232e2edbSJim Harris mtx_lock(&qpair->lock); 1403232e2edbSJim Harris 1404232e2edbSJim Harris while (!STAILQ_EMPTY(&qpair->queued_req)) { 1405232e2edbSJim Harris req = STAILQ_FIRST(&qpair->queued_req); 1406232e2edbSJim Harris STAILQ_REMOVE_HEAD(&qpair->queued_req, stailq); 1407547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing queued i/o\n"); 1408232e2edbSJim Harris mtx_unlock(&qpair->lock); 1409232e2edbSJim Harris nvme_qpair_manual_complete_request(qpair, req, NVME_SCT_GENERIC, 14102ffd6fceSWarner Losh NVME_SC_ABORTED_BY_REQUEST); 1411232e2edbSJim Harris mtx_lock(&qpair->lock); 1412232e2edbSJim Harris } 1413232e2edbSJim Harris 1414232e2edbSJim Harris /* Manually abort each outstanding I/O. */ 1415232e2edbSJim Harris while (!TAILQ_EMPTY(&qpair->outstanding_tr)) { 1416232e2edbSJim Harris tr = TAILQ_FIRST(&qpair->outstanding_tr); 1417232e2edbSJim Harris /* 1418232e2edbSJim Harris * Do not remove the tracker. The abort_tracker path will 1419232e2edbSJim Harris * do that for us. 1420232e2edbSJim Harris */ 1421547d523eSJim Harris nvme_printf(qpair->ctrlr, "failing outstanding i/o\n"); 1422232e2edbSJim Harris mtx_unlock(&qpair->lock); 142343393e8bSWarner Losh nvme_qpair_manual_complete_tracker(tr, NVME_SCT_GENERIC, 14242ffd6fceSWarner Losh NVME_SC_ABORTED_BY_REQUEST, DO_NOT_RETRY, ERROR_PRINT_ALL); 1425232e2edbSJim Harris mtx_lock(&qpair->lock); 1426232e2edbSJim Harris } 1427232e2edbSJim Harris 1428232e2edbSJim Harris mtx_unlock(&qpair->lock); 1429232e2edbSJim Harris } 1430