diff options
author | Martin K. Petersen <martin.petersen@oracle.com> | 2018-05-23 13:29:01 -0400 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-05-23 13:29:01 -0400 |
commit | f81bcf99a7d03f73fbf4815d6bca50a85542501d (patch) | |
tree | 1faaa16b2e17e350f3e5b3d445f14093a41842ff | |
parent | a048a07d7f4535baa4cbad6bc024f175317ab938 (diff) | |
download | linux-4.19/nvme.tar.gz |
nvme: Add verbose error logging4.19/nvme
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r-- | drivers/nvme/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/nvme/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/nvme/host/core.c | 4 | ||||
-rw-r--r-- | drivers/nvme/host/errors.c | 151 | ||||
-rw-r--r-- | drivers/nvme/host/nvme.h | 6 | ||||
-rw-r--r-- | include/linux/nvme.h | 22 |
6 files changed, 190 insertions, 2 deletions
diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index 88a8b5916624ae..982ad31a63b420 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -22,6 +22,14 @@ config NVME_MULTIPATH /dev/nvmeXnY device will show up for each NVMe namespaces, even if it is accessible through multiple controllers. +config NVME_VERBOSE_ERRORS + bool "NVMe verbose error reporting" + depends on NVME_CORE + ---help--- + This option enables verbose reporting for NVMe errors. The + error translation table will grow the kernel image size by + about 4 KB. + config NVME_FABRICS tristate diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index aea459c65ae1be..73365ac31e0875 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_NVME_FC) += nvme-fc.o nvme-core-y := core.o nvme-core-$(CONFIG_TRACING) += trace.o nvme-core-$(CONFIG_NVME_MULTIPATH) += multipath.o +nvme-core-$(CONFIG_NVME_VERBOSE_ERRORS) += errors.o nvme-core-$(CONFIG_NVM) += lightnvm.o nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 99b857e5a7a9c7..8af3da38b9e409 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -238,6 +238,10 @@ void nvme_complete_rq(struct request *req) return; } } + + if (unlikely(status != BLK_STS_OK)) + nvme_error_log(req); + blk_mq_end_request(req, status); } EXPORT_SYMBOL_GPL(nvme_complete_rq); diff --git a/drivers/nvme/host/errors.c b/drivers/nvme/host/errors.c new file mode 100644 index 00000000000000..34fa4c9be1f35b --- /dev/null +++ b/drivers/nvme/host/errors.c @@ -0,0 +1,151 @@ +/* + * NVM Express device driver verbose errors + * Copyright (c) 2018, Oracle and/or its affiliates + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/blkdev.h> +#include "nvme.h" + +struct nvme_string_table { + unsigned int code; + const unsigned char *string; +}; + +static const struct nvme_string_table nvme_ops[] = { + { REQ_OP_READ , "Read" }, + { REQ_OP_WRITE , "Write" }, + { REQ_OP_FLUSH , "Flush" }, + { REQ_OP_DISCARD , "Deallocate (DSM)" }, + { REQ_OP_WRITE_ZEROES , "Write Zeroes" }, +}; +#define NVME_OPS_SIZE ARRAY_SIZE(nvme_ops) + +static const struct nvme_string_table nvme_errors[] = { + { NVME_SC_SUCCESS , "Success" }, + { NVME_SC_INVALID_OPCODE , "Invalid Command Opcode" }, + { NVME_SC_INVALID_FIELD , "Invalid Field in Command" }, + { NVME_SC_CMDID_CONFLICT , "Command ID Conflict" }, + { NVME_SC_DATA_XFER_ERROR , "Data Transfer Error" }, + { NVME_SC_POWER_LOSS , "Commands Aborted due to Power Loss Notification" }, + { NVME_SC_INTERNAL , "Internal Error" }, + { NVME_SC_ABORT_REQ , "Command Abort Requested" }, + { NVME_SC_ABORT_QUEUE , "Command Aborted due to SQ Deletion" }, + { NVME_SC_FUSED_FAIL , "Command Aborted due to Failed Fused Command" }, + { NVME_SC_FUSED_MISSING , "Command Aborted due to Missing Fused Command" }, + { NVME_SC_INVALID_NS , "Invalid Namespace or Format" }, + { NVME_SC_CMD_SEQ_ERROR , "Command Sequence Error" }, + { NVME_SC_SGL_INVALID_LAST , "Invalid SGL Segment Descriptor" }, + { NVME_SC_SGL_INVALID_COUNT , "Invalid Number of SGL Descriptors" }, + { NVME_SC_SGL_INVALID_DATA , "Data SGL Length Invalid" }, + { NVME_SC_SGL_INVALID_METADATA , "Metadata SGL Length Invalid" }, + { NVME_SC_SGL_INVALID_TYPE , "SGL Descriptor Type Invalid" }, + { NVME_SC_INVALID_CMB , "Invalid Use of Controller Memory Buffer" }, + { NVME_SC_INVALID_PRP_OFFSET , "PRP Offset Invalid" }, + { NVME_SC_ATOMIC_WRITE_EXCEEDED , "Atomic Write Unit Exceeded" }, + { NVME_SC_OPERATION_DENIED , "Operation Denied" }, + { NVME_SC_SGL_INVALID_OFFSET , "SGL Offset Invalid" }, + { NVME_SC_SGL_INVALID_SUBTYPE , "Reserved" }, + { NVME_SC_HOST_ID_INCONS_FORMAT , "Host Identifier Inconsistent Format" }, + { NVME_SC_KEEP_ALIVE_EXPIRED , "Keep Alive Timeout Expired" }, + { NVME_SC_KEEP_ALIVE_INVALID , "Keep Alive Timeout Invalid" }, + { NVME_SC_ABORTED_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_GRANULARITY_INV , "SGL Data Block Granularity Invalid" }, + { NVME_SC_CMD_UNSUP_FOR_CMB , "Command Not Supported for Queue in CMB" }, + { NVME_SC_LBA_RANGE , "LBA Out of Range" }, + { NVME_SC_CAP_EXCEEDED , "Capacity Exceeded" }, + { NVME_SC_NS_NOT_READY , "Namespace Not Ready" }, + { NVME_SC_RESERVATION_CONFLICT , "Reservation Conflict" }, + { NVME_SC_FORMAT_IN_PROGRESS , "Format In Progress" }, + { NVME_SC_CQ_INVALID , "Completion Queue Invalid" }, + { NVME_SC_QID_INVALID , "Invalid Queue Identifier" }, + { NVME_SC_QUEUE_SIZE , "Invalid Queue Size" }, + { NVME_SC_ABORT_LIMIT , "Abort Command Limit Exceeded" }, + { NVME_SC_ABORT_MISSING , "Reserved" }, + { NVME_SC_ASYNC_LIMIT , "Asynchronous Event Request Limit Exceeded" }, + { NVME_SC_FIRMWARE_SLOT , "Invalid Firmware Slot" }, + { NVME_SC_FIRMWARE_IMAGE , "Invalid Firmware Image" }, + { NVME_SC_INVALID_VECTOR , "Invalid Interrupt Vector" }, + { NVME_SC_INVALID_LOG_PAGE , "Invalid Log Page" }, + { NVME_SC_INVALID_FORMAT , "Invalid Format" }, + { NVME_SC_FW_NEEDS_CONV_RESET , "Firmware Activation Requires Conventional Reset" }, + { NVME_SC_INVALID_QUEUE , "Invalid Queue Deletion" }, + { NVME_SC_FEATURE_NOT_SAVEABLE , "Feature Identifier Not Saveable" }, + { NVME_SC_FEATURE_NOT_CHANGEABLE, "Feature Not Changeable" }, + { NVME_SC_FEATURE_NOT_PER_NS , "Feature Not Namespace Specific" }, + { NVME_SC_FW_NEEDS_SUBSYS_RESET , "Firmware Activation Requires NVM Subsystem Reset" }, + { NVME_SC_FW_NEEDS_RESET , "Firmware Activation Requires Reset" }, + { NVME_SC_FW_NEEDS_MAX_TIME , "Firmware Activation Requires Maximum Time Violation" }, + { NVME_SC_FW_ACIVATE_PROHIBITED , "Firmware Activation Prohibited" }, + { NVME_SC_OVERLAPPING_RANGE , "Overlapping Range" }, + { NVME_SC_NS_INSUFFICENT_CAP , "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 , "Namespace Not Attached" }, + { NVME_SC_THIN_PROV_NOT_SUPP , "Thin Provisioning Not Supported" }, + { NVME_SC_CTRL_LIST_INVALID , "Controller List Invalid" }, + { NVME_SC_SELF_TEST_IN_PROGRESS , "Device Self-test In Progress" }, + { NVME_SC_BOOT_PARTITION_WRITE , "Boot Partition Write Prohibited" }, + { NVME_SC_INVALID_CONTROLLER_ID , "Invalid Controller Identifier" }, + { NVME_SC_INVALID_SECONDARY_CS , "Invalid Secondary Controller State" }, + { NVME_SC_INVALID_CONTROLLER_RES, "Invalid Number of Controller Resources" }, + { NVME_SC_INVALID_RESOURCE_ID , "Invalid Resource Identifier" }, + { NVME_SC_BAD_ATTRIBUTES , "Conflicting Attributes" }, + { NVME_SC_INVALID_PI , "Invalid Protection Information" }, + { NVME_SC_READ_ONLY , "Attempted Write to Read Only Range" }, + { NVME_SC_ONCS_NOT_SUPPORTED , "ONCS Not Supported" }, + { NVME_SC_WRITE_FAULT , "Write Fault" }, + { NVME_SC_READ_ERROR , "Unrecovered Read Error" }, + { NVME_SC_GUARD_CHECK , "End-to-end Guard Check Error" }, + { NVME_SC_APPTAG_CHECK , "End-to-end Application Tag Check Error" }, + { NVME_SC_REFTAG_CHECK , "End-to-end Reference Tag Check Error" }, + { NVME_SC_COMPARE_FAILED , "Compare Failure" }, + { NVME_SC_ACCESS_DENIED , "Access Denied" }, + { NVME_SC_UNWRITTEN_BLOCK , "Deallocated or Unwritten Logical Block" }, +}; +#define NVME_ERRORS_SIZE ARRAY_SIZE(nvme_errors) + +void nvme_error_log(struct request *req) +{ + struct nvme_ns *ns = req->q->queuedata; + struct nvme_request *nr = nvme_req(req); + const struct nvme_string_table *entry; + const unsigned char *op_str = "Unknown"; + const unsigned char *err_str = "Unknown"; + unsigned int i; + + if (!ns) + return; + + for (i = 0, entry = nvme_ops ; i < NVME_OPS_SIZE ; i++) + if (entry[i].code == (req->cmd_flags & REQ_OP_MASK)) + op_str = entry[i].string; + + for (i = 0, entry = nvme_errors ; i < NVME_ERRORS_SIZE ; i++) + if (entry[i].code == (nr->status & 0x7ff)) + err_str = entry[i].string; + + pr_err("%s: %s @ LBA %llu, %llu blocks, %s (sct 0x%x / sc 0x%x) %s%s\n", + req->rq_disk ? req->rq_disk->disk_name : "?", + op_str, + (unsigned long long)nvme_block_nr(ns, blk_rq_pos(req)), + (unsigned long long)blk_rq_bytes(req) >> ns->lba_shift, + err_str, + nr->status >> 8 & 7, /* Status Code Type */ + nr->status & 0xf, /* Status Code */ + nr->status & NVME_SC_MORE ? "MORE " : "", + nr->status & NVME_SC_DNR ? "DNR " : ""); +} +EXPORT_SYMBOL(nvme_error_log); + diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 17d2f7cf3fed6f..406b3b5d67a904 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -544,4 +544,10 @@ static inline struct nvme_ns *nvme_get_ns_from_dev(struct device *dev) int __init nvme_core_init(void); void nvme_core_exit(void); +#ifdef CONFIG_NVME_VERBOSE_ERRORS +extern void nvme_error_log(struct request *req); +#else +static inline void nvme_error_log(struct request *req) {} +#endif /* CONFIG_NVME_VERBOSE_ERRORS */ + #endif /* _NVME_H */ diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 4112e2bd747f58..af5231ad25e4a3 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -1095,14 +1095,25 @@ enum { NVME_SC_SGL_INVALID_DATA = 0xf, NVME_SC_SGL_INVALID_METADATA = 0x10, NVME_SC_SGL_INVALID_TYPE = 0x11, - + NVME_SC_INVALID_CMB = 0x12, + NVME_SC_INVALID_PRP_OFFSET = 0x13, + NVME_SC_ATOMIC_WRITE_EXCEEDED = 0x14, + NVME_SC_OPERATION_DENIED = 0x15, NVME_SC_SGL_INVALID_OFFSET = 0x16, NVME_SC_SGL_INVALID_SUBTYPE = 0x17, - + NVME_SC_HOST_ID_INCONS_FORMAT = 0x18, + NVME_SC_KEEP_ALIVE_EXPIRED = 0x19, + NVME_SC_KEEP_ALIVE_INVALID = 0x1a, + NVME_SC_ABORTED_PREEMPT = 0x1b, + NVME_SC_SANITIZE_FAILED = 0x1c, + NVME_SC_SANITIZE_IN_PROGRESS = 0x1d, + NVME_SC_SGL_GRANULARITY_INV = 0x1e, + NVME_SC_CMD_UNSUP_FOR_CMB = 0x1f, NVME_SC_LBA_RANGE = 0x80, NVME_SC_CAP_EXCEEDED = 0x81, NVME_SC_NS_NOT_READY = 0x82, NVME_SC_RESERVATION_CONFLICT = 0x83, + NVME_SC_FORMAT_IN_PROGRESS = 0x84, /* * Command Specific Status: @@ -1135,6 +1146,12 @@ enum { NVME_SC_NS_NOT_ATTACHED = 0x11a, NVME_SC_THIN_PROV_NOT_SUPP = 0x11b, NVME_SC_CTRL_LIST_INVALID = 0x11c, + NVME_SC_SELF_TEST_IN_PROGRESS = 0x11d, + NVME_SC_BOOT_PARTITION_WRITE = 0x11e, + NVME_SC_INVALID_CONTROLLER_ID = 0x11f, + NVME_SC_INVALID_SECONDARY_CS = 0x120, + NVME_SC_INVALID_CONTROLLER_RES = 0x121, + NVME_SC_INVALID_RESOURCE_ID = 0x122, /* * I/O Command Set Specific - NVM commands: @@ -1168,6 +1185,7 @@ enum { NVME_SC_ACCESS_DENIED = 0x286, NVME_SC_UNWRITTEN_BLOCK = 0x287, + NVME_SC_MORE = 0x2000, NVME_SC_DNR = 0x4000, }; |