From eeea64a19ce04e12d8d85b9c11bf654e2a771009 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Fri, 15 Sep 2023 11:34:48 +0200 Subject: [PATCH] drivers/nvme: Add debug information in case of request error This will provide a detailed error status report. As for most of the original code of the driver, this is a backport of the work done by Jim Harris in FreeBSD. Signed-off-by: Tomasz Bursztyka --- drivers/disk/nvme/nvme_cmd.c | 168 ++++++++++++++++++++++++++++ drivers/disk/nvme/nvme_cmd.h | 8 +- drivers/disk/nvme/nvme_controller.c | 5 +- drivers/disk/nvme/nvme_disk.c | 3 + 4 files changed, 182 insertions(+), 2 deletions(-) diff --git a/drivers/disk/nvme/nvme_cmd.c b/drivers/disk/nvme/nvme_cmd.c index f6633b7dec1..14c662ffe4e 100644 --- a/drivers/disk/nvme/nvme_cmd.c +++ b/drivers/disk/nvme/nvme_cmd.c @@ -26,6 +26,174 @@ static void request_timeout(struct k_work *work); static K_WORK_DELAYABLE_DEFINE(request_timer, request_timeout); +#ifdef CONFIG_NVME_LOG_LEVEL_DBG +struct nvme_status_string { + uint16_t sc; + const char *str; +}; + +static struct nvme_status_string generic_status[] = { + { NVME_SC_SUCCESS, "SUCCESS" }, + { NVME_SC_INVALID_OPCODE, "INVALID OPCODE" }, + { NVME_SC_INVALID_FIELD, "INVALID_FIELD" }, + { NVME_SC_COMMAND_ID_CONFLICT, "COMMAND ID CONFLICT" }, + { NVME_SC_DATA_TRANSFER_ERROR, "DATA TRANSFER ERROR" }, + { NVME_SC_ABORTED_POWER_LOSS, "ABORTED - POWER LOSS" }, + { NVME_SC_INTERNAL_DEVICE_ERROR, "INTERNAL DEVICE ERROR" }, + { NVME_SC_ABORTED_BY_REQUEST, "ABORTED - BY REQUEST" }, + { NVME_SC_ABORTED_SQ_DELETION, "ABORTED - SQ DELETION" }, + { NVME_SC_ABORTED_FAILED_FUSED, "ABORTED - FAILED FUSED" }, + { NVME_SC_ABORTED_MISSING_FUSED, "ABORTED - MISSING FUSED" }, + { NVME_SC_INVALID_NAMESPACE_OR_FORMAT, "INVALID NAMESPACE OR FORMAT" }, + { NVME_SC_COMMAND_SEQUENCE_ERROR, "COMMAND SEQUENCE ERROR" }, + { NVME_SC_INVALID_SGL_SEGMENT_DESCR, "INVALID SGL SEGMENT DESCRIPTOR" }, + { NVME_SC_INVALID_NUMBER_OF_SGL_DESCR, "INVALID NUMBER OF SGL DESCRIPTORS" }, + { NVME_SC_DATA_SGL_LENGTH_INVALID, "DATA SGL LENGTH INVALID" }, + { NVME_SC_METADATA_SGL_LENGTH_INVALID, "METADATA SGL LENGTH INVALID" }, + { NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID, "SGL DESCRIPTOR TYPE INVALID" }, + { NVME_SC_INVALID_USE_OF_CMB, "INVALID USE OF CONTROLLER MEMORY BUFFER" }, + { NVME_SC_PRP_OFFSET_INVALID, "PRP OFFSET INVALID" }, + { NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED, "ATOMIC WRITE UNIT EXCEEDED" }, + { NVME_SC_OPERATION_DENIED, "OPERATION DENIED" }, + { NVME_SC_SGL_OFFSET_INVALID, "SGL OFFSET INVALID" }, + { NVME_SC_HOST_ID_INCONSISTENT_FORMAT, "HOST IDENTIFIER INCONSISTENT FORMAT" }, + { NVME_SC_KEEP_ALIVE_TIMEOUT_EXPIRED, "KEEP ALIVE TIMEOUT EXPIRED" }, + { NVME_SC_KEEP_ALIVE_TIMEOUT_INVALID, "KEEP ALIVE TIMEOUT INVALID" }, + { NVME_SC_ABORTED_DUE_TO_PREEMPT, "COMMAND ABORTED DUE TO PREEMPT AND ABORT" }, + { NVME_SC_SANITIZE_FAILED, "SANITIZE FAILED" }, + { NVME_SC_SANITIZE_IN_PROGRESS, "SANITIZE IN PROGRESS" }, + { NVME_SC_SGL_DATA_BLOCK_GRAN_INVALID, "SGL_DATA_BLOCK_GRANULARITY_INVALID" }, + { NVME_SC_NOT_SUPPORTED_IN_CMB, "COMMAND NOT SUPPORTED FOR QUEUE IN CMB" }, + { NVME_SC_NAMESPACE_IS_WRITE_PROTECTED, "NAMESPACE IS WRITE PROTECTED" }, + { NVME_SC_COMMAND_INTERRUPTED, "COMMAND INTERRUPTED" }, + { NVME_SC_TRANSIENT_TRANSPORT_ERROR, "TRANSIENT TRANSPORT ERROR" }, + { NVME_SC_LBA_OUT_OF_RANGE, "LBA OUT OF RANGE" }, + { NVME_SC_CAPACITY_EXCEEDED, "CAPACITY EXCEEDED" }, + { NVME_SC_NAMESPACE_NOT_READY, "NAMESPACE NOT READY" }, + { NVME_SC_RESERVATION_CONFLICT, "RESERVATION CONFLICT" }, + { NVME_SC_FORMAT_IN_PROGRESS, "FORMAT IN PROGRESS" }, + { 0xFFFF, "GENERIC" } +}; + +static struct nvme_status_string command_specific_status[] = { + { NVME_SC_COMPLETION_QUEUE_INVALID, "INVALID COMPLETION QUEUE" }, + { NVME_SC_INVALID_QUEUE_IDENTIFIER, "INVALID QUEUE IDENTIFIER" }, + { NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED, "MAX QUEUE SIZE EXCEEDED" }, + { NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED, "ABORT CMD LIMIT EXCEEDED" }, + { NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED, "ASYNC LIMIT EXCEEDED" }, + { NVME_SC_INVALID_FIRMWARE_SLOT, "INVALID FIRMWARE SLOT" }, + { NVME_SC_INVALID_FIRMWARE_IMAGE, "INVALID FIRMWARE IMAGE" }, + { NVME_SC_INVALID_INTERRUPT_VECTOR, "INVALID INTERRUPT VECTOR" }, + { NVME_SC_INVALID_LOG_PAGE, "INVALID LOG PAGE" }, + { NVME_SC_INVALID_FORMAT, "INVALID FORMAT" }, + { NVME_SC_FIRMWARE_REQUIRES_RESET, "FIRMWARE REQUIRES RESET" }, + { NVME_SC_INVALID_QUEUE_DELETION, "INVALID QUEUE DELETION" }, + { NVME_SC_FEATURE_NOT_SAVEABLE, "FEATURE IDENTIFIER NOT SAVEABLE" }, + { NVME_SC_FEATURE_NOT_CHANGEABLE, "FEATURE NOT CHANGEABLE" }, + { NVME_SC_FEATURE_NOT_NS_SPECIFIC, "FEATURE NOT NAMESPACE SPECIFIC" }, + { NVME_SC_FW_ACT_REQUIRES_NVMS_RESET, "FIRMWARE ACTIVATION REQUIRES NVM SUBSYSTEM RESET" }, + { NVME_SC_FW_ACT_REQUIRES_RESET, "FIRMWARE ACTIVATION REQUIRES RESET" }, + { NVME_SC_FW_ACT_REQUIRES_TIME, "FIRMWARE ACTIVATION REQUIRES MAXIMUM TIME VIOLATION" }, + { NVME_SC_FW_ACT_PROHIBITED, "FIRMWARE ACTIVATION PROHIBITED" }, + { NVME_SC_OVERLAPPING_RANGE, "OVERLAPPING RANGE" }, + { NVME_SC_NS_INSUFFICIENT_CAPACITY, "NAMESPACE INSUFFICIENT CAPACITY" }, + { NVME_SC_NS_ID_UNAVAILABLE, "NAMESPACE IDENTIFIER UNAVAILABLE" }, + { NVME_SC_NS_ALREADY_ATTACHED, "NAMESPACE ALREADY ATTACHED" }, + { NVME_SC_NS_IS_PRIVATE, "NAMESPACE IS PRIVATE" }, + { NVME_SC_NS_NOT_ATTACHED, "NS NOT ATTACHED" }, + { NVME_SC_THIN_PROV_NOT_SUPPORTED, "THIN PROVISIONING NOT SUPPORTED" }, + { NVME_SC_CTRLR_LIST_INVALID, "CONTROLLER LIST INVALID" }, + { NVME_SC_SELF_TEST_IN_PROGRESS, "DEVICE SELF-TEST IN PROGRESS" }, + { NVME_SC_BOOT_PART_WRITE_PROHIB, "BOOT PARTITION WRITE PROHIBITED" }, + { NVME_SC_INVALID_CTRLR_ID, "INVALID CONTROLLER IDENTIFIER" }, + { NVME_SC_INVALID_SEC_CTRLR_STATE, "INVALID SECONDARY CONTROLLER STATE" }, + { NVME_SC_INVALID_NUM_OF_CTRLR_RESRC, "INVALID NUMBER OF CONTROLLER RESOURCES" }, + { NVME_SC_INVALID_RESOURCE_ID, "INVALID RESOURCE IDENTIFIER" }, + { NVME_SC_SANITIZE_PROHIBITED_WPMRE, + "SANITIZE PROHIBITED WRITE PERSISTENT MEMORY REGION ENABLED" }, + { NVME_SC_ANA_GROUP_ID_INVALID, "ANA GROUP IDENTIFIED INVALID" }, + { NVME_SC_ANA_ATTACH_FAILED, "ANA ATTACH FAILED" }, + { NVME_SC_CONFLICTING_ATTRIBUTES, "CONFLICTING ATTRIBUTES" }, + { NVME_SC_INVALID_PROTECTION_INFO, "INVALID PROTECTION INFO" }, + { NVME_SC_ATTEMPTED_WRITE_TO_RO_PAGE, "WRITE TO RO PAGE" }, + { 0xFFFF, "COMMAND SPECIFIC" } +}; + +static struct nvme_status_string media_error_status[] = { + { NVME_SC_WRITE_FAULTS, "WRITE FAULTS" }, + { NVME_SC_UNRECOVERED_READ_ERROR, "UNRECOVERED READ ERROR" }, + { NVME_SC_GUARD_CHECK_ERROR, "GUARD CHECK ERROR" }, + { NVME_SC_APPLICATION_TAG_CHECK_ERROR, "APPLICATION TAG CHECK ERROR" }, + { NVME_SC_REFERENCE_TAG_CHECK_ERROR, "REFERENCE TAG CHECK ERROR" }, + { NVME_SC_COMPARE_FAILURE, "COMPARE FAILURE" }, + { NVME_SC_ACCESS_DENIED, "ACCESS DENIED" }, + { NVME_SC_DEALLOCATED_OR_UNWRITTEN, "DEALLOCATED OR UNWRITTEN LOGICAL BLOCK" }, + { 0xFFFF, "MEDIA ERROR" } +}; + +static struct nvme_status_string path_related_status[] = { + { NVME_SC_INTERNAL_PATH_ERROR, "INTERNAL PATH ERROR" }, + { NVME_SC_ASYMMETRIC_ACCESS_PERSISTENT_LOSS, "ASYMMETRIC ACCESS PERSISTENT LOSS" }, + { NVME_SC_ASYMMETRIC_ACCESS_INACCESSIBLE, "ASYMMETRIC ACCESS INACCESSIBLE" }, + { NVME_SC_ASYMMETRIC_ACCESS_TRANSITION, "ASYMMETRIC ACCESS TRANSITION" }, + { NVME_SC_CONTROLLER_PATHING_ERROR, "CONTROLLER PATHING ERROR" }, + { NVME_SC_HOST_PATHING_ERROR, "HOST PATHING ERROR" }, + { NVME_SC_COMMAND_ABORTED_BY_HOST, "COMMAND ABORTED BY HOST" }, + { 0xFFFF, "PATH RELATED" }, +}; + +static const char *get_status_string(uint16_t sct, uint16_t sc) +{ + struct nvme_status_string *entry; + + switch (sct) { + case NVME_SCT_GENERIC: + entry = generic_status; + break; + case NVME_SCT_COMMAND_SPECIFIC: + entry = command_specific_status; + break; + case NVME_SCT_MEDIA_ERROR: + entry = media_error_status; + break; + case NVME_SCT_PATH_RELATED: + entry = path_related_status; + break; + case NVME_SCT_VENDOR_SPECIFIC: + return "VENDOR SPECIFIC"; + default: + return "RESERVED"; + } + + while (entry->sc != 0xFFFF) { + if (entry->sc == sc) { + return entry->str; + } + + entry++; + } + return entry->str; +} + +void nvme_completion_print(const struct nvme_completion *cpl) +{ + uint8_t sct, sc, crd, m, dnr, p; + + sct = NVME_STATUS_GET_SCT(cpl->status); + sc = NVME_STATUS_GET_SC(cpl->status); + crd = NVME_STATUS_GET_CRD(cpl->status); + m = NVME_STATUS_GET_M(cpl->status); + dnr = NVME_STATUS_GET_DNR(cpl->status); + p = NVME_STATUS_GET_P(cpl->status); + + LOG_DBG("%s (%02x/%02x) crd:%x m:%x dnr:%x p:%d " + "sqid:%d cid:%d cdw0:%x\n", + get_status_string(sct, sc), sct, sc, crd, m, dnr, p, + cpl->sqid, cpl->cid, cpl->cdw0); +} + +#endif /* CONFIG_NVME_LOG_LEVEL_DBG */ + void nvme_cmd_init(void) { int idx; diff --git a/drivers/disk/nvme/nvme_cmd.h b/drivers/disk/nvme/nvme_cmd.h index 5dc17b8e8c4..c0e4e512ab4 100644 --- a/drivers/disk/nvme/nvme_cmd.h +++ b/drivers/disk/nvme/nvme_cmd.h @@ -197,7 +197,7 @@ enum nvme_path_related_status_code { NVME_SC_ASYMMETRIC_ACCESS_TRANSITION = 0x03, NVME_SC_CONTROLLER_PATHING_ERROR = 0x60, NVME_SC_HOST_PATHING_ERROR = 0x70, - NVME_SC_COMMAND_ABOTHED_BY_HOST = 0x71, + NVME_SC_COMMAND_ABORTED_BY_HOST = 0x71, }; /* admin opcodes */ @@ -377,6 +377,12 @@ void nvme_cmd_init(void); void nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl); +#ifdef CONFIG_NVME_LOG_LEVEL_DBG +void nvme_completion_print(const struct nvme_completion *cpl); +#else +#define nvme_completion_print(...) +#endif /* CONFIG_NVME_LOG_LEVEL_DBG */ + void nvme_cmd_request_free(struct nvme_request *request); struct nvme_request *nvme_cmd_request_alloc(void); diff --git a/drivers/disk/nvme/nvme_controller.c b/drivers/disk/nvme/nvme_controller.c index c387da71d20..699df325add 100644 --- a/drivers/disk/nvme/nvme_controller.c +++ b/drivers/disk/nvme/nvme_controller.c @@ -183,6 +183,7 @@ static int nvme_controller_setup_io_queues(const struct device *dev) if (nvme_cpl_status_is_error(&status)) { LOG_ERR("Could not set IO num queues to %u", nvme_ctrlr->num_io_queues); + nvme_completion_print(&status.cpl); return -EIO; } @@ -225,6 +226,7 @@ static int nvme_controller_setup_io_queues(const struct device *dev) nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_ERR("IO CQ creation failed"); + nvme_completion_print(&status.cpl); return -EIO; } @@ -240,6 +242,7 @@ static int nvme_controller_setup_io_queues(const struct device *dev) nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_ERR("IO CQ creation failed"); + nvme_completion_print(&status.cpl); return -EIO; } } @@ -372,9 +375,9 @@ static int nvme_controller_identify(struct nvme_controller *nvme_ctrlr) nvme_ctrlr_cmd_identify_controller(nvme_ctrlr, nvme_completion_poll_cb, &status); nvme_completion_poll(&status); - if (nvme_cpl_status_is_error(&status)) { LOG_ERR("Could not identify the controller"); + nvme_completion_print(&status.cpl); return -EIO; } diff --git a/drivers/disk/nvme/nvme_disk.c b/drivers/disk/nvme/nvme_disk.c index ff22e3bad41..3308e04db5b 100644 --- a/drivers/disk/nvme/nvme_disk.c +++ b/drivers/disk/nvme/nvme_disk.c @@ -57,6 +57,7 @@ static int nvme_disk_read(struct disk_info *disk, if (nvme_cpl_status_is_error(&status)) { LOG_WRN("Reading at sector %u (count %d) on disk %s failed", start_sector, num_sector, ns->name); + nvme_completion_print(&status.cpl); ret = -EIO; } out: @@ -100,6 +101,7 @@ static int nvme_disk_write(struct disk_info *disk, if (nvme_cpl_status_is_error(&status)) { LOG_WRN("Writing at sector %u (count %d) on disk %s failed", start_sector, num_sector, ns->name); + nvme_completion_print(&status.cpl); ret = -EIO; } out: @@ -128,6 +130,7 @@ static int nvme_disk_flush(struct nvme_namespace *ns) nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_ERR("Flushing disk %s failed", ns->name); + nvme_completion_print(&status.cpl); return -EIO; }