summaryrefslogtreecommitdiffstats
path: root/src/nvme/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvme/util.c')
-rw-r--r--src/nvme/util.c777
1 files changed, 777 insertions, 0 deletions
diff --git a/src/nvme/util.c b/src/nvme/util.c
new file mode 100644
index 0000000..799c0cf
--- /dev/null
+++ b/src/nvme/util.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <ccan/endian/endian.h>
+
+#include "private.h"
+#include "util.h"
+#include "log.h"
+
+static inline __u8 nvme_generic_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_INVALID_OPCODE:
+ case NVME_SC_INVALID_FIELD:
+ case NVME_SC_INVALID_NS:
+ case NVME_SC_SGL_INVALID_LAST:
+ case NVME_SC_SGL_INVALID_COUNT:
+ case NVME_SC_SGL_INVALID_DATA:
+ case NVME_SC_SGL_INVALID_METADATA:
+ case NVME_SC_SGL_INVALID_TYPE:
+ case NVME_SC_SGL_INVALID_OFFSET:
+ case NVME_SC_PRP_INVALID_OFFSET:
+ case NVME_SC_CMB_INVALID_USE:
+ case NVME_SC_KAT_INVALID:
+ return EINVAL;
+ case NVME_SC_CMDID_CONFLICT:
+ return EADDRINUSE;
+ case NVME_SC_DATA_XFER_ERROR:
+ case NVME_SC_INTERNAL:
+ case NVME_SC_SANITIZE_FAILED:
+ return EIO;
+ case NVME_SC_POWER_LOSS:
+ case NVME_SC_ABORT_REQ:
+ case NVME_SC_ABORT_QUEUE:
+ case NVME_SC_FUSED_FAIL:
+ case NVME_SC_FUSED_MISSING:
+ return EWOULDBLOCK;
+ case NVME_SC_CMD_SEQ_ERROR:
+ return EILSEQ;
+ case NVME_SC_SANITIZE_IN_PROGRESS:
+ case NVME_SC_FORMAT_IN_PROGRESS:
+ return EINPROGRESS;
+ case NVME_SC_NS_WRITE_PROTECTED:
+ case NVME_SC_NS_NOT_READY:
+ case NVME_SC_RESERVATION_CONFLICT:
+ case NVME_SC_OP_DENIED:
+ case NVME_SC_ADMIN_CMD_MEDIA_NOT_READY:
+ return EACCES;
+ case NVME_SC_LBA_RANGE:
+ return EREMOTEIO;
+ case NVME_SC_CAP_EXCEEDED:
+ case NVME_SC_AWU_EXCEEDED:
+ return ENOSPC;
+ }
+ return EIO;
+}
+
+static inline __u8 nvme_cmd_specific_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_CQ_INVALID:
+ case NVME_SC_QID_INVALID:
+ case NVME_SC_QUEUE_SIZE:
+ case NVME_SC_FIRMWARE_SLOT:
+ case NVME_SC_FIRMWARE_IMAGE:
+ case NVME_SC_INVALID_VECTOR:
+ case NVME_SC_INVALID_LOG_PAGE:
+ case NVME_SC_INVALID_FORMAT:
+ case NVME_SC_INVALID_QUEUE:
+ case NVME_SC_NS_INSUFFICIENT_CAP:
+ case NVME_SC_NS_ID_UNAVAILABLE:
+ case NVME_SC_CTRL_LIST_INVALID:
+ case NVME_SC_BAD_ATTRIBUTES:
+ case NVME_SC_INVALID_PI:
+ case NVME_SC_INVALID_CTRL_ID:
+ case NVME_SC_INVALID_SEC_CTRL_STATE:
+ case NVME_SC_INVALID_CTRL_RESOURCES:
+ case NVME_SC_INVALID_RESOURCE_ID:
+ case NVME_SC_ANA_GROUP_ID_INVALID:
+ case NVME_SC_INSUFFICIENT_CAP:
+ case NVME_SC_INVALID_IOCS:
+ case NVME_SC_ID_UNAVAILABLE:
+ return EINVAL;
+ case NVME_SC_ABORT_LIMIT:
+ case NVME_SC_ASYNC_LIMIT:
+ case NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED:
+ return EDQUOT;
+ case NVME_SC_FW_NEEDS_CONV_RESET:
+ case NVME_SC_FW_NEEDS_SUBSYS_RESET:
+ case NVME_SC_FW_NEEDS_MAX_TIME:
+ case NVME_SC_FW_NEEDS_RESET:
+ return ERESTART;
+ case NVME_SC_FEATURE_NOT_SAVEABLE:
+ case NVME_SC_FEATURE_NOT_CHANGEABLE:
+ case NVME_SC_FEATURE_NOT_PER_NS:
+ case NVME_SC_FW_ACTIVATE_PROHIBITED:
+ case NVME_SC_NS_IS_PRIVATE:
+ case NVME_SC_BP_WRITE_PROHIBITED:
+ case NVME_SC_READ_ONLY:
+ case NVME_SC_PMR_SAN_PROHIBITED:
+ return EPERM;
+ case NVME_SC_OVERLAPPING_RANGE:
+ case NVME_SC_NS_NOT_ATTACHED:
+ return ENOSPC;
+ case NVME_SC_NS_ALREADY_ATTACHED:
+ return EALREADY;
+ case NVME_SC_THIN_PROV_NOT_SUPP:
+ case NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED:
+ return EOPNOTSUPP;
+ case NVME_SC_ABORT_MISSING:
+ return EWOULDBLOCK;
+ case NVME_SC_SELF_TEST_IN_PROGRESS:
+ return EINPROGRESS;
+ }
+
+ return EIO;
+}
+
+static inline __u8 nvme_fabrics_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_CONNECT_FORMAT:
+ case NVME_SC_CONNECT_INVALID_PARAM:
+ case NVME_SC_DISCONNECT_INVALID_QTYPE:
+ return EINVAL;
+ case NVME_SC_CONNECT_CTRL_BUSY:
+ return EBUSY;
+ case NVME_SC_CONNECT_RESTART_DISC:
+ return ERESTART;
+ case NVME_SC_CONNECT_INVALID_HOST:
+ return ECONNREFUSED;
+ case NVME_SC_DISCOVERY_RESTART:
+ return EAGAIN;
+ case NVME_SC_AUTH_REQUIRED:
+ return EPERM;
+ }
+
+ return EIO;
+}
+
+__u8 nvme_status_to_errno(int status, bool fabrics)
+{
+ __u16 sc;
+
+ if (!status)
+ return 0;
+ if (status < 0)
+ return errno;
+
+ sc = nvme_status_code(status);
+ switch (nvme_status_code_type(status)) {
+ case NVME_SCT_GENERIC:
+ return nvme_generic_status_to_errno(sc);
+ case NVME_SCT_CMD_SPECIFIC:
+ if (fabrics)
+ return nvme_fabrics_status_to_errno(sc);
+ return nvme_cmd_specific_status_to_errno(sc);
+ default:
+ return EIO;
+ }
+}
+
+static const char * const generic_status[] = {
+ [NVME_SC_SUCCESS] = "Successful Completion: The command completed without error",
+ [NVME_SC_INVALID_OPCODE] = "Invalid Command Opcode: A reserved coded value or an unsupported value in the command opcode field",
+ [NVME_SC_INVALID_FIELD] = "Invalid Field in Command: A reserved coded value or an unsupported value in a defined field",
+ [NVME_SC_CMDID_CONFLICT] = "Command ID Conflict: The command identifier is already in use",
+ [NVME_SC_DATA_XFER_ERROR] = "Data Transfer Error: Transferring the data or metadata associated with a command experienced an error",
+ [NVME_SC_POWER_LOSS] = "Commands Aborted due to Power Loss Notification: Indicates that the command was aborted due to a power loss notification",
+ [NVME_SC_INTERNAL] = "Internal Error: The command was not completed successfully due to an internal error",
+ [NVME_SC_ABORT_REQ] = "Command Abort Requested: The command was aborted due to an Abort command",
+ [NVME_SC_ABORT_QUEUE] = "Command Aborted due to SQ Deletion: The command was aborted due to a Delete I/O Submission Queue",
+ [NVME_SC_FUSED_FAIL] = "Command Aborted due to Failed Fused Command: The command was aborted due to the other command in a fused operation failing",
+ [NVME_SC_FUSED_MISSING] = "Command Aborted due to Missing Fused Command: The fused command was aborted due to the adjacent submission queue entry not containing a fused command",
+ [NVME_SC_INVALID_NS] = "Invalid Namespace or Format: The namespace or the format of that namespace is invalid",
+ [NVME_SC_CMD_SEQ_ERROR] = "Command Sequence Error: The command was aborted due to a protocol violation in a multi- command sequence",
+ [NVME_SC_SGL_INVALID_LAST] = "Invalid SGL Segment Descriptor: The command includes an invalid SGL Last Segment or SGL Segment descriptor",
+ [NVME_SC_SGL_INVALID_COUNT] = "Invalid Number of SGL Descriptors: There is an SGL Last Segment descriptor or an SGL Segment descriptor in a location other than the last descriptor of a segment based on the length indicated",
+ [NVME_SC_SGL_INVALID_DATA] = "Data SGL Length Invalid: The length of a Data SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
+ [NVME_SC_SGL_INVALID_METADATA] = "Metadata SGL Length Invalid: The length of a Metadata SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
+ [NVME_SC_SGL_INVALID_TYPE] = "SGL Descriptor Type Invalid: The type of an SGL Descriptor is a type that is not supported by the controller",
+ [NVME_SC_CMB_INVALID_USE] = "Invalid Use of Controller Memory Buffer: The attempted use of the Controller Memory Buffer is not supported by the controller",
+ [NVME_SC_PRP_INVALID_OFFSET] = "PRP Offset Invalid: The Offset field for a PRP entry is invalid",
+ [NVME_SC_AWU_EXCEEDED] = "Atomic Write Unit Exceeded: The length specified exceeds the atomic write unit size",
+ [NVME_SC_OP_DENIED] = "Operation Denied: The command was denied due to lack of access rights",
+ [NVME_SC_SGL_INVALID_OFFSET] = "SGL Offset Invalid: The offset specified in a descriptor is invalid",
+ [NVME_SC_HOSTID_FORMAT] = "Host Identifier Inconsistent Format: The NVM subsystem detected the simultaneous use of 64- bit and 128-bit Host Identifier values on different controllers",
+ [NVME_SC_KAT_EXPIRED] = "Keep Alive Timer Expired: The Keep Alive Timer expired",
+ [NVME_SC_KAT_INVALID] = "Keep Alive Timeout Invalid: The Keep Alive Timeout value specified is invalid",
+ [NVME_SC_CMD_ABORTED_PREMEPT] = "Command Aborted due to Preempt and Abort: The command was aborted due to a Reservation Acquire command",
+ [NVME_SC_SANITIZE_FAILED] = "Sanitize Failed: The most recent sanitize operation failed and no recovery action has been successfully completed",
+ [NVME_SC_SANITIZE_IN_PROGRESS] = "Sanitize In Progress: The requested function is prohibited while a sanitize operation is in progress",
+ [NVME_SC_SGL_INVALID_GRANULARITY] = "SGL Data Block Granularity Invalid: The Address alignment or Length granularity for an SGL Data Block descriptor is invalid",
+ [NVME_SC_CMD_IN_CMBQ_NOT_SUPP] = "Command Not Supported for Queue in CMB: The controller does not support Submission Queue in the Controller Memory Buffer or Completion Queue in the Controller Memory Buffer",
+ [NVME_SC_NS_WRITE_PROTECTED] = "Namespace is Write Protected: The command is prohibited while the namespace is write protected",
+ [NVME_SC_CMD_INTERRUPTED] = "Command Interrupted: Command processing was interrupted and the controller is unable to successfully complete the command",
+ [NVME_SC_TRAN_TPORT_ERROR] = "Transient Transport Error: A transient transport error was detected",
+ [NVME_SC_PROHIBITED_BY_CMD_AND_FEAT] = "Command Prohibited by Command and Feature Lockdown: The command was aborted due to command execution being prohibited by the Command and Feature Lockdown",
+ [NVME_SC_ADMIN_CMD_MEDIA_NOT_READY] = "Admin Command Media Not Ready: The Admin command requires access to media and the media is not ready",
+ [NVME_SC_LBA_RANGE] = "LBA Out of Range: The command references an LBA that exceeds the size of the namespace",
+ [NVME_SC_CAP_EXCEEDED] = "Capacity Exceeded: Execution of the command has caused the capacity of the namespace to be exceeded",
+ [NVME_SC_NS_NOT_READY] = "Namespace Not Ready: The namespace is not ready to be accessed",
+ [NVME_SC_RESERVATION_CONFLICT] = "Reservation Conflict: The command was aborted due to a conflict with a reservation held on the accessed namespace",
+ [NVME_SC_FORMAT_IN_PROGRESS] = "Format In Progress: A Format NVM command is in progress on the namespace",
+};
+
+static const char * const cmd_spec_status[] = {
+ [NVME_SC_CQ_INVALID] = "Completion Queue Invalid: The Completion Queue identifier specified in the command does not exist",
+ [NVME_SC_QID_INVALID] = "Invalid Queue Identifier: The creation of the I/O Completion Queue failed due to an invalid queue identifier specified as part of the command",
+ [NVME_SC_QUEUE_SIZE] = "Invalid Queue Size: The host attempted to create an I/O Completion Queue with an invalid number of entries",
+ [NVME_SC_ABORT_LIMIT] = "Abort Command Limit Exceeded: The number of concurrently outstanding Abort commands has exceeded the limit indicated in the Identify Controller data structure",
+ [NVME_SC_ABORT_MISSING] = "Abort Command Is Missing: The abort command is missing",
+ [NVME_SC_ASYNC_LIMIT] = "Asynchronous Event Request Limit Exceeded: The number of concurrently outstanding Asynchronous Event Request commands has been exceeded",
+ [NVME_SC_FIRMWARE_SLOT] = "Invalid Firmware Slot: The firmware slot indicated is invalid or read only",
+ [NVME_SC_FIRMWARE_IMAGE] = "Invalid Firmware Image: The firmware image specified for activation is invalid and not loaded by the controller",
+ [NVME_SC_INVALID_VECTOR] = "Invalid Interrupt Vector: The creation of the I/O Completion Queue failed due to an invalid interrupt vector specified as part of the command",
+ [NVME_SC_INVALID_LOG_PAGE] = "Invalid Log Page: The log page indicated is invalid",
+ [NVME_SC_INVALID_FORMAT] = "Invalid Format: The LBA Format specified is not supported",
+ [NVME_SC_FW_NEEDS_CONV_RESET] = "Firmware Activation Requires Conventional Reset: The firmware commit was successful, however, activation of the firmware image requires a conventional reset",
+ [NVME_SC_INVALID_QUEUE] = "Invalid Queue Deletion: Invalid I/O Completion Queue specified to delete",
+ [NVME_SC_FEATURE_NOT_SAVEABLE] = "Feature Identifier Not Saveable: The Feature Identifier specified does not support a saveable value",
+ [NVME_SC_FEATURE_NOT_CHANGEABLE] = "Feature Not Changeable: The Feature Identifier is not able to be changed",
+ [NVME_SC_FEATURE_NOT_PER_NS] = "Feature Not Namespace Specific: The Feature Identifier specified is not namespace specific",
+ [NVME_SC_FW_NEEDS_SUBSYS_RESET] = "Firmware Activation Requires NVM Subsystem Reset: The firmware commit was successful, however, activation of the firmware image requires an NVM Subsystem",
+ [NVME_SC_FW_NEEDS_RESET] = "Firmware Activation Requires Controller Level Reset: The firmware commit was successful; however, the image specified does not support being activated without a reset",
+ [NVME_SC_FW_NEEDS_MAX_TIME] = "Firmware Activation Requires Maximum Time Violation: The image specified if activated immediately would exceed the Maximum Time for Firmware Activation (MTFA) value reported in Identify Controller",
+ [NVME_SC_FW_ACTIVATE_PROHIBITED] = "Firmware Activation Prohibited: The image specified is being prohibited from activation by the controller for vendor specific reasons",
+ [NVME_SC_OVERLAPPING_RANGE] = "Overlapping Range: The downloaded firmware image has overlapping ranges",
+ [NVME_SC_NS_INSUFFICIENT_CAP] = "Namespace Insufficient Capacity: Creating the namespace requires more free space than is currently available",
+ [NVME_SC_NS_ID_UNAVAILABLE] = "Namespace Identifier Unavailable: The number of namespaces supported has been exceeded",
+ [NVME_SC_NS_ALREADY_ATTACHED] = "Namespace Already Attached: The controller is already attached to the namespace specified",
+ [NVME_SC_NS_IS_PRIVATE] = "Namespace Is Private: The namespace is private and is already attached to one controller",
+ [NVME_SC_NS_NOT_ATTACHED] = "Namespace Not Attached: The request to detach the controller could not be completed because the controller is not attached to the namespace",
+ [NVME_SC_THIN_PROV_NOT_SUPP] = "Thin Provisioning Not Supported: Thin provisioning is not supported by the controller",
+ [NVME_SC_CTRL_LIST_INVALID] = "Controller List Invalid: The controller list provided contains invalid controller ids",
+ [NVME_SC_SELF_TEST_IN_PROGRESS] = "Device Self-test In Progress: The controller or NVM subsystem already has a device self-test operation in process",
+ [NVME_SC_BP_WRITE_PROHIBITED] = "Boot Partition Write Prohibited: The command tried to modify a locked Boot Partition",
+ [NVME_SC_INVALID_CTRL_ID] = "Invalid Controller Identifier: An invalid controller id was specified",
+ [NVME_SC_INVALID_SEC_CTRL_STATE] = "Invalid Secondary Controller State: The requested secondary controller action is invalid based on the secondary and primary controllers current states",
+ [NVME_SC_INVALID_CTRL_RESOURCES] = "Invalid Number of Controller Resources: The specified number of Flexible Resources is invalid",
+ [NVME_SC_INVALID_RESOURCE_ID] = "Invalid Resource Identifier: At least one of the specified resource identifiers was invalid",
+ [NVME_SC_PMR_SAN_PROHIBITED] = "Sanitize Prohibited While Persistent Memory Region is Enabled",
+ [NVME_SC_ANA_GROUP_ID_INVALID] = "ANA Group Identifier Invalid: The specified ANA Group Identifier (ANAGRPID) is not supported in the submitted command",
+ [NVME_SC_ANA_ATTACH_FAILED] = "ANA Attach Failed: The controller is not attached to the namespace as a result of an ANA condition",
+ [NVME_SC_INSUFFICIENT_CAP] = "Insufficient Capacity: Requested operation requires more free space than is currently available",
+ [NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED] = "Namespace Attachment Limit Exceeded: Attaching the ns to a controller causes max number of ns attachments allowed to be exceeded",
+ [NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED] = "Prohibition of Command Execution Not Supported",
+ [NVME_SC_IOCS_NOT_SUPPORTED] = "The I/O command set is not supported",
+ [NVME_SC_IOCS_NOT_ENABLED] = "The I/O command set is not enabled",
+ [NVME_SC_IOCS_COMBINATION_REJECTED] = "The I/O command set combination is rejected",
+ [NVME_SC_INVALID_IOCS] = "The I/O command set is invalid",
+ [NVME_SC_ID_UNAVAILABLE] = "Identifier Unavailable: The number of Endurance Groups or NVM Sets supported has been exceeded",
+ [NVME_SC_INVALID_DISCOVERY_INFO] = "Discovery Info Entry not applicable to selected entity",
+ [NVME_SC_ZONING_DATA_STRUCT_LOCKED] = "The requested Zoning data structure is locked on the CDC",
+ [NVME_SC_ZONING_DATA_STRUCT_NOTFND] = "The requested Zoning data structure does not exist on the CDC",
+ [NVME_SC_INSUFFICIENT_DISC_RES] = "Discovery Info entries exceed Discovery Controller's capacity",
+ [NVME_SC_REQSTD_FUNCTION_DISABLED] = "Fabric Zoning is not enabled on the CDC",
+ [NVME_SC_ZONEGRP_ORIGINATOR_INVLD] = "The NQN contained in the ZoneGroup Originator field does not match the Host NQN used by the DDC to connect to the CDC",
+};
+
+static const char * const nvm_status[] = {
+ [NVME_SC_BAD_ATTRIBUTES] = "Conflicting Attributes: The attributes specified in the command are conflicting",
+ [NVME_SC_INVALID_PI] = "Invalid Protection Information: The command's Protection Information Field settings are invalid for the namespace's Protection Information format",
+ [NVME_SC_READ_ONLY] = "Attempted Write to Read Only Range: The LBA range specified contains read-only blocks",
+ [NVME_SC_CMD_SIZE_LIMIT_EXCEEDED] = "Command Size Limit Exceeded",
+ [NVME_SC_ZNS_INVALID_OP_REQUEST] = "Invalid Zone Operation Request: The operation requested is invalid",
+ [NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE] = "ZRWA Resources Unavailable: No ZRWAs are available",
+ [NVME_SC_ZNS_BOUNDARY_ERROR] = "Zoned Boundary Error: Invalid Zone Boundary crossing",
+ [NVME_SC_ZNS_FULL] = "Zone Is Full: The accessed zone is in ZSF:Full state",
+ [NVME_SC_ZNS_READ_ONLY] = "Zone Is Read Only: The accessed zone is in ZSRO:Read Only state",
+ [NVME_SC_ZNS_OFFLINE] = "Zone Is Offline: The access zone is in ZSO:Offline state",
+ [NVME_SC_ZNS_INVALID_WRITE] = "Zone Invalid Write: The write to zone was not at the write pointer offset",
+ [NVME_SC_ZNS_TOO_MANY_ACTIVE] = "Too Many Active Zones: The controller does not allow additional active zones",
+ [NVME_SC_ZNS_TOO_MANY_OPENS] = "Too Many Open Zones: The controller does not allow additional open zones",
+ [NVME_SC_ZNS_INVAL_TRANSITION] = "Invalid Zone State Transition: The request is not a valid zone state transition",
+};
+
+static const char * const nvmf_status[] = {
+ [NVME_SC_CONNECT_FORMAT] = "Incompatible Format: The NVM subsystem does not support the record format specified by the host",
+ [NVME_SC_CONNECT_CTRL_BUSY] = "Controller Busy: The controller is already associated with a host",
+ [NVME_SC_CONNECT_INVALID_PARAM] = "Connect Invalid Parameters: One or more of the command parameters",
+ [NVME_SC_CONNECT_RESTART_DISC] = "Connect Restart Discovery: The NVM subsystem requested is not available",
+ [NVME_SC_CONNECT_INVALID_HOST] = "Connect Invalid Host: The host is not allowed to establish an association to either any controller in the NVM subsystem or the specified controller",
+ [NVME_SC_DISCONNECT_INVALID_QTYPE] = "Invalid Queue Type: The command was sent on the wrong queue type",
+ [NVME_SC_DISCOVERY_RESTART] = "Discover Restart: The snapshot of the records is now invalid or out of date",
+ [NVME_SC_AUTH_REQUIRED] = "Authentication Required: NVMe in-band authentication is required and the queue has not yet been authenticated",
+};
+
+static const char * const media_status[] = {
+ [NVME_SC_WRITE_FAULT] = "Write Fault: The write data could not be committed to the media",
+ [NVME_SC_READ_ERROR] = "Unrecovered Read Error: The read data could not be recovered from the media",
+ [NVME_SC_GUARD_CHECK] = "End-to-end Guard Check Error: The command was aborted due to an end-to-end guard check failure",
+ [NVME_SC_APPTAG_CHECK] = "End-to-end Application Tag Check Error: The command was aborted due to an end-to-end application tag check failure",
+ [NVME_SC_REFTAG_CHECK] = "End-to-end Reference Tag Check Error: The command was aborted due to an end-to-end reference tag check failure",
+ [NVME_SC_COMPARE_FAILED] = "Compare Failure: The command failed due to a miscompare during a Compare command",
+ [NVME_SC_ACCESS_DENIED] = "Access Denied: Access to the namespace and/or LBA range is denied due to lack of access rights",
+ [NVME_SC_UNWRITTEN_BLOCK] = "Deallocated or Unwritten Logical Block: The command failed due to an attempt to read from or verify an LBA range containing a deallocated or unwritten logical block",
+ [NVME_SC_STORAGE_TAG_CHECK] = "End-to-End Storage Tag Check Error: The command was aborted due to an end-to-end storage tag check failure",
+};
+
+static const char * const path_status[] = {
+ [NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the commmand prevented completion",
+ [NVME_SC_ANA_PERSISTENT_LOSS] = "Asymmetric Access Persistent Loss: The controller is in a persistent loss state with the requested namespace",
+ [NVME_SC_ANA_INACCESSIBLE] = "Asymmetric Access Inaccessible: The controller is in an inaccessible state with the requested namespace",
+ [NVME_SC_ANA_TRANSITION] = "Asymmetric Access Transition: The controller is currently transitioning states with the requested namespace",
+ [NVME_SC_CTRL_PATH_ERROR] = "Controller Pathing Error: A pathing error was detected by the controller",
+ [NVME_SC_HOST_PATH_ERROR] = "Host Pathing Error: A pathing error was detected by the host",
+ [NVME_SC_CMD_ABORTED_BY_HOST] = "Command Aborted By Host: The command was aborted as a result of host action",
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i)
+
+static const char *arg_str(const char * const *strings,
+ size_t array_size, size_t idx)
+{
+ if (idx < array_size && strings[idx])
+ return strings[idx];
+ return "unrecognized";
+}
+
+const char *nvme_status_to_string(int status, bool fabrics)
+{
+ const char *s = "Unknown status";
+ __u16 sc, sct;
+
+ if (status < 0)
+ return strerror(errno);
+
+ sc = nvme_status_code(status);
+ sct = nvme_status_code_type(status);
+
+ switch (sct) {
+ case NVME_SCT_GENERIC:
+ s = ARGSTR(generic_status, sc);
+ break;
+ case NVME_SCT_CMD_SPECIFIC:
+ if (sc < ARRAY_SIZE(cmd_spec_status))
+ s = ARGSTR(cmd_spec_status, sc);
+ else if (fabrics)
+ s = ARGSTR(nvmf_status, sc);
+ else
+ s = ARGSTR(nvm_status, sc);
+ break;
+ case NVME_SCT_MEDIA:
+ s = ARGSTR(media_status, sc);
+ break;
+ case NVME_SCT_PATH:
+ s = ARGSTR(path_status, sc);
+ break;
+ case NVME_SCT_VS:
+ s = "Vendor Specific Status";
+ break;
+ default:
+ break;
+ }
+
+ return s;
+}
+
+void nvme_init_copy_range(struct nvme_copy_range *copy, __u16 *nlbs,
+ __u64 *slbas, __u32 *eilbrts, __u32 *elbatms,
+ __u32 *elbats, __u16 nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ copy[i].nlb = cpu_to_le16(nlbs[i]);
+ copy[i].slba = cpu_to_le64(slbas[i]);
+ copy[i].eilbrt = cpu_to_le32(eilbrts[i]);
+ copy[i].elbatm = cpu_to_le16(elbatms[i]);
+ copy[i].elbat = cpu_to_le16(elbats[i]);
+ }
+}
+
+void nvme_init_dsm_range(struct nvme_dsm_range *dsm, __u32 *ctx_attrs,
+ __u32 *llbas, __u64 *slbas, __u16 nr_ranges)
+{
+ int i;
+
+ for (i = 0; i < nr_ranges; i++) {
+ dsm[i].cattr = cpu_to_le32(ctx_attrs[i]);
+ dsm[i].nlb = cpu_to_le32(llbas[i]);
+ dsm[i].slba = cpu_to_le64(slbas[i]);
+ }
+}
+
+void nvme_init_ctrl_list(struct nvme_ctrl_list *cntlist, __u16 num_ctrls,
+ __u16 *ctrlist)
+{
+ int i;
+
+ cntlist->num = cpu_to_le16(num_ctrls);
+ for (i = 0; i < num_ctrls; i++)
+ cntlist->identifier[i] = cpu_to_le16(ctrlist[i]);
+}
+
+int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len)
+{
+ switch (fid) {
+ case NVME_FEAT_FID_LBA_RANGE:
+ *len = sizeof(struct nvme_lba_range_type);
+ break;
+ case NVME_FEAT_FID_AUTO_PST:
+ *len = sizeof(struct nvme_feat_auto_pst);
+ break;
+ case NVME_FEAT_FID_PLM_CONFIG:
+ *len = sizeof(struct nvme_plm_config);
+ break;
+ case NVME_FEAT_FID_TIMESTAMP:
+ *len = sizeof(struct nvme_timestamp);
+ break;
+ case NVME_FEAT_FID_HOST_BEHAVIOR:
+ *len = sizeof(struct nvme_feat_host_behavior);
+ break;
+ case NVME_FEAT_FID_HOST_ID:
+ *len = (cdw11 & 0x1) ? 16 : 8;
+ break;
+ case NVME_FEAT_FID_ARBITRATION:
+ case NVME_FEAT_FID_POWER_MGMT:
+ case NVME_FEAT_FID_TEMP_THRESH:
+ case NVME_FEAT_FID_ERR_RECOVERY:
+ case NVME_FEAT_FID_VOLATILE_WC:
+ case NVME_FEAT_FID_NUM_QUEUES:
+ case NVME_FEAT_FID_IRQ_COALESCE:
+ case NVME_FEAT_FID_IRQ_CONFIG:
+ case NVME_FEAT_FID_WRITE_ATOMIC:
+ case NVME_FEAT_FID_ASYNC_EVENT:
+ case NVME_FEAT_FID_HOST_MEM_BUF:
+ case NVME_FEAT_FID_KATO:
+ case NVME_FEAT_FID_HCTM:
+ case NVME_FEAT_FID_NOPSC:
+ case NVME_FEAT_FID_RRL:
+ case NVME_FEAT_FID_PLM_WINDOW:
+ case NVME_FEAT_FID_LBA_STS_INTERVAL:
+ case NVME_FEAT_FID_SANITIZE:
+ case NVME_FEAT_FID_ENDURANCE_EVT_CFG:
+ case NVME_FEAT_FID_SW_PROGRESS:
+ case NVME_FEAT_FID_RESV_MASK:
+ case NVME_FEAT_FID_RESV_PERSIST:
+ case NVME_FEAT_FID_WRITE_PROTECT:
+ *len = 0;
+ break;
+ case NVME_FEAT_FID_ENH_CTRL_METADATA:
+ case NVME_FEAT_FID_CTRL_METADATA:
+ case NVME_FEAT_FID_NS_METADATA:
+ *len = sizeof(struct nvme_host_metadata);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int nvme_get_directive_receive_length(enum nvme_directive_dtype dtype,
+ enum nvme_directive_receive_doper doper, __u32 *len)
+{
+ switch (dtype) {
+ case NVME_DIRECTIVE_DTYPE_IDENTIFY:
+ switch (doper) {
+ case NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM:
+ *len = sizeof(struct nvme_id_directives);
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ case NVME_DIRECTIVE_DTYPE_STREAMS:
+ switch (doper) {
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM:
+ *len = sizeof(struct nvme_streams_directive_params);
+ return 0;
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS:
+ *len = (128 * 1024) * sizeof(__le16);
+ return 0;
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE:
+ *len = 0;
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static const char * const libnvme_status[] = {
+ [ENVME_CONNECT_RESOLVE] = "failed to resolve host",
+ [ENVME_CONNECT_ADDRFAM] = "unrecognized address family",
+ [ENVME_CONNECT_TRADDR] = "failed to get transport address",
+ [ENVME_CONNECT_TARG] = "no transport specified",
+ [ENVME_CONNECT_AARG] = "no transport address specified",
+ [ENVME_CONNECT_OPEN] = "failed to open nvme-fabrics device",
+ [ENVME_CONNECT_WRITE] = "failed to write to nvme-fabrics device",
+ [ENVME_CONNECT_READ] = "failed to read from nvme-fabrics device",
+ [ENVME_CONNECT_PARSE] = "failed to parse ctrl info",
+ [ENVME_CONNECT_INVAL_TR] = "invalid transport type",
+ [ENVME_CONNECT_LOOKUP_SUBSYS_NAME] = "failed to lookup subsystem name",
+ [ENVME_CONNECT_LOOKUP_SUBSYS] = "failed to lookup subsystem",
+};
+
+const char *nvme_errno_to_string(int status)
+{
+ const char *s = ARGSTR(libnvme_status, status);
+
+ return s;
+}
+
+char *hostname2traddr(struct nvme_root *r, const char *traddr)
+{
+ struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC};
+ char addrstr[NVMF_TRADDR_SIZE];
+ const char *p;
+ char *ret_traddr = NULL;
+ int ret;
+
+ ret = getaddrinfo(traddr, NULL, &hints, &host_info);
+ if (ret) {
+ nvme_msg(r, LOG_ERR, "failed to resolve host %s info\n",
+ traddr);
+ return NULL;
+ }
+
+ switch (host_info->ai_family) {
+ case AF_INET:
+ p = inet_ntop(host_info->ai_family,
+ &(((struct sockaddr_in *)host_info->ai_addr)->sin_addr),
+ addrstr, NVMF_TRADDR_SIZE);
+ break;
+ case AF_INET6:
+ p = inet_ntop(host_info->ai_family,
+ &(((struct sockaddr_in6 *)host_info->ai_addr)->sin6_addr),
+ addrstr, NVMF_TRADDR_SIZE);
+ break;
+ default:
+ nvme_msg(r, LOG_ERR, "unrecognized address family (%d) %s\n",
+ host_info->ai_family, traddr);
+ goto free_addrinfo;
+ }
+
+ if (!p) {
+ nvme_msg(r, LOG_ERR, "failed to get traddr for %s\n",
+ traddr);
+ goto free_addrinfo;
+ }
+ ret_traddr = strdup(addrstr);
+
+free_addrinfo:
+ freeaddrinfo(host_info);
+ return ret_traddr;
+}
+
+char *startswith(const char *s, const char *prefix)
+{
+ size_t l;
+
+ l = strlen(prefix);
+ if (!strncmp(s, prefix, l))
+ return (char *)s + l;
+
+ return NULL;
+}
+
+char *kv_strip(char *kv)
+{
+ char *s;
+
+ kv[strcspn(kv, "\n\r")] = '\0';
+
+ /* Remove leading newline and spaces */
+ kv += strspn(kv, " \t\n\r");
+
+ /* Skip comments and empty lines */
+ if (*kv == '#' || *kv == '\0') {
+ *kv = '\0';
+ return kv;
+ }
+
+ /* Remove trailing newline chars */
+ kv[strcspn(kv, "\n\r")] = '\0';
+
+ /* Delete trailing comments (including spaces/tabs that precede the #)*/
+ s = &kv[strcspn(kv, "#")];
+ *s-- = '\0';
+ while ((s >= kv) && ((*s == ' ') || (*s == '\t'))) {
+ *s-- = '\0';
+ }
+
+ return kv;
+}
+
+char *kv_keymatch(const char *kv, const char *key)
+{
+ char *value;
+
+ value = startswith(kv, key);
+ if (value) {
+ /* Make sure key is a whole word. I.e. it should be
+ * followed by spaces, tabs, or a equal sign. Skip
+ * leading spaces, tabs, and equal sign (=) */
+ switch (*value) {
+ case ' ':
+ case '\t':
+ case '=':
+ value += strspn(value, " \t=");
+ return value;
+ default: ;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * read_file - read contents of file into @buffer.
+ * @fname: File name
+ * @buffer: Where to save file's contents
+ * @bufsz: Size of @buffer. On success, @bufsz gets decremented by the
+ * number of characters that were writtent to @buffer.
+ *
+ * Return: The number of characters read. If the file cannot be opened or
+ * nothing is read from the file, then this function returns 0.
+ */
+static size_t read_file(const char * fname, char *buffer, size_t *bufsz)
+{
+ char *p;
+ FILE *file;
+ size_t len;
+
+ file = fopen(fname, "re");
+ if (!file)
+ return 0;
+
+ p = fgets(buffer, *bufsz, file);
+ fclose(file);
+
+ if (!p)
+ return 0;
+
+ /* Strip unwanted trailing chars */
+ len = strcspn(buffer, " \t\n\r");
+ *bufsz -= len;
+
+ return len;
+}
+
+static size_t copy_value(char *buf, size_t buflen, const char *value)
+{
+ size_t val_len;
+
+ memset(buf, 0, buflen);
+
+ /* Remove leading " */
+ if (value[0] == '"')
+ value++;
+
+ /* Remove trailing " */
+ val_len = strcspn(value, "\"");
+
+ memcpy(buf, value, MIN(val_len, buflen-1));
+
+ return val_len;
+}
+
+size_t get_entity_name(char *buffer, size_t bufsz)
+{
+ size_t len = !gethostname(buffer, bufsz) ? strlen(buffer) : 0;
+
+ /* Fill the rest of buffer with zeros */
+ memset(&buffer[len], '\0', bufsz-len);
+
+ return len;
+}
+
+size_t get_entity_version(char *buffer, size_t bufsz)
+{
+ FILE *file;
+ size_t num_bytes = 0;
+
+ /* /proc/sys/kernel/ostype typically contains the string "Linux" */
+ num_bytes += read_file("/proc/sys/kernel/ostype",
+ &buffer[num_bytes], &bufsz);
+
+ /* /proc/sys/kernel/osrelease contains the Linux
+ * version (e.g. 5.8.0-63-generic)
+ */
+ buffer[num_bytes++] = ' '; /* Append a space */
+ num_bytes += read_file("/proc/sys/kernel/osrelease",
+ &buffer[num_bytes], &bufsz);
+
+ /* /etc/os-release contains Key-Value pairs. We only care about the key
+ * PRETTY_NAME, which contains the Distro's version. For example:
+ * "SUSE Linux Enterprise Server 15 SP4", "Ubuntu 20.04.3 LTS", or
+ * "Fedora Linux 35 (Server Edition)"
+ */
+ file = fopen("/etc/os-release", "re");
+ if (file) {
+ char name[64] = {0};
+ size_t name_len = 0;
+ char ver_id[64] = {0};
+ size_t ver_id_len = 0;
+ char line[LINE_MAX];
+ char *p;
+ char *s;
+
+ /* Read key-value pairs one line at a time */
+ while ((!name_len || !ver_id_len) &&
+ (p = fgets(line, sizeof(line), file)) != NULL) {
+ /* Clean up string by removing leading/trailing blanks
+ * and new line characters. Also eliminate trailing
+ * comments, if any.
+ */
+ p = kv_strip(p);
+
+ /* Empty string? */
+ if (*p == '\0')
+ continue;
+
+ s = kv_keymatch(p, "NAME");
+ if (s)
+ name_len = copy_value(name, sizeof(name), s);
+
+ s = kv_keymatch(p, "VERSION_ID");
+ if (s)
+ ver_id_len = copy_value(ver_id, sizeof(ver_id), s);
+ }
+ fclose(file);
+
+ if (name_len) {
+ /* Append a space */
+ buffer[num_bytes++] = ' ';
+ name_len = MIN(name_len, bufsz);
+ memcpy(&buffer[num_bytes], name, name_len);
+ bufsz -= name_len;
+ num_bytes += name_len;
+ }
+
+ if (ver_id_len) {
+ /* Append a space */
+ buffer[num_bytes++] = ' ';
+ ver_id_len = MIN(ver_id_len, bufsz);
+ memcpy(&buffer[num_bytes], ver_id, ver_id_len);
+ bufsz -= ver_id_len;
+ num_bytes += ver_id_len;
+ }
+ }
+
+ /* Fill the rest of buffer with zeros */
+ memset(&buffer[num_bytes], '\0', bufsz);
+
+ return num_bytes;
+}
+
+struct nvmf_ext_attr *nvmf_exat_ptr_next(struct nvmf_ext_attr *p)
+{
+ return (struct nvmf_ext_attr *)
+ ((uintptr_t)p + (ptrdiff_t)nvmf_exat_size(le16_to_cpu(p->exatlen)));
+}