diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 19:22:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 19:22:29 +0000 |
commit | 068a45420f2c98887e220b45e946cc7074da550e (patch) | |
tree | c5b54e8b4b235232b057a9c534d9a16d2208463d /src/nvme | |
parent | Initial commit. (diff) | |
download | libnvme-068a45420f2c98887e220b45e946cc7074da550e.tar.xz libnvme-068a45420f2c98887e220b45e946cc7074da550e.zip |
Adding upstream version 1.8.upstream/1.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nvme')
-rw-r--r-- | src/nvme/api-types.h | 963 | ||||
-rw-r--r-- | src/nvme/base64.c | 94 | ||||
-rw-r--r-- | src/nvme/base64.h | 8 | ||||
-rw-r--r-- | src/nvme/cleanup.h | 41 | ||||
-rw-r--r-- | src/nvme/fabrics.c | 1736 | ||||
-rw-r--r-- | src/nvme/fabrics.h | 327 | ||||
-rw-r--r-- | src/nvme/filters.c | 163 | ||||
-rw-r--r-- | src/nvme/filters.h | 96 | ||||
-rw-r--r-- | src/nvme/ioctl.c | 2258 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 4062 | ||||
-rw-r--r-- | src/nvme/json.c | 589 | ||||
-rw-r--r-- | src/nvme/linux.c | 1297 | ||||
-rw-r--r-- | src/nvme/linux.h | 338 | ||||
-rw-r--r-- | src/nvme/log.c | 105 | ||||
-rw-r--r-- | src/nvme/log.h | 51 | ||||
-rw-r--r-- | src/nvme/mi-mctp.c | 824 | ||||
-rw-r--r-- | src/nvme/mi.c | 1651 | ||||
-rw-r--r-- | src/nvme/mi.h | 2677 | ||||
-rw-r--r-- | src/nvme/nbft.c | 754 | ||||
-rw-r--r-- | src/nvme/nbft.h | 1238 | ||||
-rw-r--r-- | src/nvme/no-json.c | 26 | ||||
-rw-r--r-- | src/nvme/private.h | 289 | ||||
-rw-r--r-- | src/nvme/tree.c | 2713 | ||||
-rw-r--r-- | src/nvme/tree.h | 1454 | ||||
-rw-r--r-- | src/nvme/types.h | 7996 | ||||
-rw-r--r-- | src/nvme/util.c | 1137 | ||||
-rw-r--r-- | src/nvme/util.h | 714 |
27 files changed, 33601 insertions, 0 deletions
diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h new file mode 100644 index 0000000..296a7b0 --- /dev/null +++ b/src/nvme/api-types.h @@ -0,0 +1,963 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Types used as part of the libnvme/libnvme-mi API, rather than specified + * by the NVM Express specification. + * + * These are shared across both libnvme and libnvme-mi interfaces. + * + * This file is part of libnvme. + * Copyright (c) 2022 Code Construct + * + * Authors: Jeremy Kerr <jk@codeconstruct.com.au> + */ +#ifndef _LIBNVME_API_TYPES_H +#define _LIBNVME_API_TYPES_H + +#include <stdbool.h> +#include "types.h" + +/* + * _args struct definitions. These are used by both the ioctl-based and + * MI-based interfaces, as the call interface for (admin/io/etc) NVMe commands, + * passed to the nvme_*() and nvme_mi_*() functions. + * + * On MI-based interfaces, the fd and timeout members are unused, and should + * be set to zero. + */ + +/** + * struct nvme_identify_args - Arguments for the NVMe Identify command + * @result: The command completion result from CQE dword0 + * @data: User space destination address to transfer the data + * @args_size: Size of &struct nvme_identify_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms (0 for default timeout) + * @cns: The Controller or Namespace structure, see @enum nvme_identify_cns + * @csi: Command Set Identifier + * @nsid: Namespace identifier, if applicable + * @cntid: The Controller Identifier, if applicable + * @cns_specific_id: Identifier that is required for a particular CNS value + * @uuidx: UUID Index if controller supports this id selection method + */ +struct nvme_identify_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + enum nvme_identify_cns cns; + enum nvme_csi csi; + __u32 nsid; + __u16 cntid; + __u16 cns_specific_id; + __u8 uuidx; +}; + +/** + * struct nvme_get_log_args - Arguments for the NVMe Admin Get Log command + * @lpo: Log page offset for partial log transfers + * @result: The command completion result from CQE dword0 + * @log: User space destination address to transfer the data + * @args_size: Length of the structure + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @lid: Log page identifier, see &enum nvme_cmd_get_log_lid for known + * values + * @len: Length of provided user buffer to hold the log data in bytes + * @nsid: Namespace identifier, if applicable + * @csi: Command set identifier, see &enum nvme_csi for known values + * @lsi: Log Specific Identifier + * @lsp: Log specific field + * @uuidx: UUID selection, if supported + * @rae: Retain asynchronous events + * @ot: Offset Type; if set @lpo specifies the index into the list + * of data structures, otherwise @lpo specifies the byte offset + * into the log page. + */ +struct nvme_get_log_args { + __u64 lpo; + __u32 *result; + void *log; + int args_size; + int fd; + __u32 timeout; + enum nvme_cmd_get_log_lid lid; + __u32 len; + __u32 nsid; + enum nvme_csi csi; + __u16 lsi; + __u8 lsp; + __u8 uuidx; + bool rae; + bool ot; +}; + +/** + * struct nvme_set_features_args - Arguments for the NVMe Admin Set Feature command + * @result: The command completion result from CQE dword0 + * @data: User address of feature data, if applicable + * @args_size: Size of &struct nvme_set_features_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID, if applicable + * @cdw11: Value to set the feature to + * @cdw12: Feature specific command dword12 field + * @cdw13: Feature specific command dword13 field + * @cdw15: Feature specific command dword15 field + * @data_len: Length of feature data, if applicable, in bytes + * @save: Save value across power states + * @uuidx: UUID Index for differentiating vendor specific encoding + * @fid: Feature identifier + */ +struct nvme_set_features_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw15; + __u32 data_len; + bool save; + __u8 uuidx; + __u8 fid; +}; + +/** + * struct nvme_get_features_args - Arguments for the NVMe Admin Get Feature command + * @args_size: Size of &struct nvme_get_features_args + * @fd: File descriptor of nvme device + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms + * @nsid: Namespace ID, if applicable + * @sel: Select which type of attribute to return, + * see &enum nvme_get_features_sel + * @cdw11: Feature specific command dword11 field + * @data_len: Length of feature data, if applicable, in bytes + * @data: User address of feature data, if applicable + * @fid: Feature identifier, see &enum nvme_features_id + * @uuidx: UUID Index for differentiating vendor specific encoding + */ +struct nvme_get_features_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_get_features_sel sel; + __u32 cdw11; + __u32 data_len; + __u8 fid; + __u8 uuidx; +}; + +/** + * struct nvme_format_nvm_args - Arguments for the Format Nvme Namespace command + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_format_nvm_args + * @fd: File descriptor of nvme device + * @timeout: Set to override default timeout to this value in milliseconds; + * useful for long running formats. 0 will use system default. + * @nsid: Namespace ID to format + * @mset: Metadata settings (extended or separated), true if extended + * @pi: Protection information type + * @pil: Protection information location (beginning or end), true if end + * @ses: Secure erase settings + * @lbaf: Logical block address format least significant 4 bits + * @rsvd1: Reserved + * @lbafu: Logical block address format most significant 2 bits + */ +struct nvme_format_nvm_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_cmd_format_mset mset; + enum nvme_cmd_format_pi pi; + enum nvme_cmd_format_pil pil; + enum nvme_cmd_format_ses ses; + __u8 lbaf; + __u8 rsvd1[7]; + __u8 lbafu; +}; + +/** + * struct nvme_ns_mgmt_args - Arguments for NVMe Namespace Management command + * @result: NVMe command result + * @ns: Namespace identification descriptors + * @args_size: Size of &struct nvme_ns_mgmt_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @sel: Type of management operation to perform + * @csi: Command Set Identifier + * @rsvd1: Reserved + * @rsvd2: Reserved + * @data: Host Software Specified Fields + */ +struct nvme_ns_mgmt_args { + __u32 *result; + struct nvme_id_ns *ns; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_ns_mgmt_sel sel; + __u8 csi; + __u8 rsvd1[3]; + void *rsvd2; + struct nvme_ns_mgmt_host_sw_specified *data; +}; + +/** + * struct nvme_ns_attach_args - Arguments for Nvme Namespace Management command + * @result: NVMe command result + * @ctrlist: Controller list to modify attachment state of nsid + * @args_size: Size of &struct nvme_ns_attach_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID to execute attach selection + * @sel: Attachment selection, see &enum nvme_ns_attach_sel + */ +struct nvme_ns_attach_args { + __u32 *result; + struct nvme_ctrl_list *ctrlist; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_ns_attach_sel sel; +}; + +/** + * struct nvme_fw_download_args - Arguments for the NVMe Firmware Download command + * @args_size: Size of &struct nvme_fw_download_args + * @fd: File descriptor of nvme device + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms + * @offset: Offset in the firmware data + * @data: Userspace address of the firmware data + * @data_len: Length of data in this command in bytes + */ +struct nvme_fw_download_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 offset; + __u32 data_len; +}; + +/** + * struct nvme_fw_commit_args - Arguments for the NVMe Firmware Commit command + * @args_size: Size of &struct nvme_fw_commit_args + * @fd: File descriptor of nvme device + * @action: Action to use for the firmware image, see &enum nvme_fw_commit_ca + * @timeout: Timeout in ms + * @result: The command completion result from CQE dword0 + * @slot: Firmware slot to commit the downloaded image + * @bpid: Set to true to select the boot partition id + */ +struct nvme_fw_commit_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + enum nvme_fw_commit_ca action; + __u8 slot; + bool bpid; +}; + +/** + * struct nvme_security_send_args - Arguments for the NVMe Security Send command + * @result: The command completion result from CQE dword0 + * @data: Security data payload to send + * @args_size: Size of &struct nvme_security_send_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID to issue security command on + * @tl: Protocol specific transfer length + * @data_len: Data length of the payload in bytes + * @nssf: NVMe Security Specific field + * @spsp0: Security Protocol Specific field + * @spsp1: Security Protocol Specific field + * @secp: Security Protocol + */ +struct nvme_security_send_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 tl; + __u32 data_len; + __u8 nssf; + __u8 spsp0; + __u8 spsp1; + __u8 secp; +}; + +/** + * struct nvme_security_receive_args - Arguments for the NVMe Security Receive command + * @result: The command completion result from CQE dword0 + * @data: Security data payload to send + * @args_size: Size of &struct nvme_security_receive_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID to issue security command on + * @al: Protocol specific allocation length + * @data_len: Data length of the payload in bytes + * @nssf: NVMe Security Specific field + * @spsp0: Security Protocol Specific field + * @spsp1: Security Protocol Specific field + * @secp: Security Protocol + */ +struct nvme_security_receive_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 al; + __u32 data_len; + __u8 nssf; + __u8 spsp0; + __u8 spsp1; + __u8 secp; +}; + +/** + * struct nvme_get_lba_status_args - Arguments for the NVMe Get LBA Status command + * @lbas: Data payload to return status descriptors + * @result: The command completion result from CQE dword0 + * @slba: Starting logical block address to check statuses + * @args_size: Size of &struct nvme_get_lba_status_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID to retrieve LBA status + * @mndw: Maximum number of dwords to return + * @atype: Action type mechanism to determine LBA status descriptors to + * return, see &enum nvme_lba_status_atype + * @rl: Range length from slba to perform the action + */ +struct nvme_get_lba_status_args { + __u64 slba; + __u32 *result; + struct nvme_lba_status *lbas; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 mndw; + enum nvme_lba_status_atype atype; + __u16 rl; +}; + +/** + * struct nvme_directive_send_args - Arguments for the NVMe Directive Send command + * @result: If successful, the CQE dword0 value + * @data: Data payload to be send + * @args_size: Size of &struct nvme_directive_send_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID, if applicable + * @doper: Directive send operation, see &enum nvme_directive_send_doper + * @dtype: Directive type, see &enum nvme_directive_dtype + * @cdw12: Directive specific command dword12 + * @data_len: Length of data payload in bytes + * @dspec: Directive specific field + */ +struct nvme_directive_send_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_directive_send_doper doper; + enum nvme_directive_dtype dtype; + __u32 cdw12; + __u32 data_len; + __u16 dspec; +}; + +/** + * struct nvme_directive_recv_args - Arguments for the NVMe Directive Receive command + * @result: If successful, the CQE dword0 value + * @data: Userspace address of data payload + * @args_size: Size of &struct nvme_directive_recv_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID, if applicable + * @doper: Directive send operation, see &enum nvme_directive_send_doper + * @dtype: Directive type, see &enum nvme_directive_dtype + * @cdw12: Directive specific command dword12 + * @data_len: Length of data payload in bytes + * @dspec: Directive specific field + */ +struct nvme_directive_recv_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_directive_receive_doper doper; + enum nvme_directive_dtype dtype; + __u32 cdw12; + __u32 data_len; + __u16 dspec; +}; + +/** + * struct nvme_capacity_mgmt_args - Arguments for the NVMe Capacity Management command + * @result: If successful, the CQE dword0 value + * @args_size: Size of &struct nvme_capacity_mgmt_args + * @fd: File descriptor of nvme device + * @cdw11: Least significant 32 bits of the capacity in bytes of the + * Endurance Group or NVM Set to be created + * @cdw12: Most significant 32 bits of the capacity in bytes of the + * Endurance Group or NVM Set to be created + * @timeout: Timeout in ms + * @element_id: Value specific to the value of the Operation field + * @op: Operation to be performed by the controller + */ +struct nvme_capacity_mgmt_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 cdw11; + __u32 cdw12; + __u16 element_id; + __u8 op; +}; + +/** + * struct nvme_lockdown_args - Arguments for the NVME Lockdown command + * @args_size: Size of &struct nvme_lockdown_args + * @fd: File descriptor of nvme device + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms (0 for default timeout) + * @scp: Scope of the command + * @prhbt: Prohibit or allow the command opcode or Set Features command + * @ifc: Affected interface + * @ofi: Opcode or Feature Identifier + * @uuidx: UUID Index if controller supports this id selection method + */ +struct nvme_lockdown_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u8 scp; + __u8 prhbt; + __u8 ifc; + __u8 ofi; + __u8 uuidx; +}; + +/** + * struct nvme_set_property_args - Arguments for NVMe Set Property command + * @args_size: Size of &struct nvme_set_property_args + * @fd: File descriptor of nvme device + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms + * @offset: Property offset from the base to set + * @value: The value to set the property + */ +struct nvme_set_property_args { + __u64 value; + __u32 *result; + int args_size; + int fd; + __u32 timeout; + int offset; +}; + +/** + * struct nvme_get_property_args - Arguments for NVMe Get Property command + * @value: Where the property's value will be stored on success + * @args_size: Size of &struct nvme_get_property_args + * @fd: File descriptor of nvme device + * @offset: Property offset from the base to retrieve + * @timeout: Timeout in ms + */ +struct nvme_get_property_args { + __u64 *value; + int args_size; + int fd; + __u32 timeout; + int offset; +}; + +/** + * struct nvme_sanitize_nvm_args - Arguments for the NVMe Sanitize NVM command + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_sanitize_nvm_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @ovrpat: Overwrite pattern + * @sanact: Sanitize action, see &enum nvme_sanitize_sanact + * @ause: Set to allow unrestricted sanitize exit + * @owpass: Overwrite pass count + * @oipbp: Set to overwrite invert pattern between passes + * @nodas: Set to not deallocate blocks after sanitizing + */ +struct nvme_sanitize_nvm_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + enum nvme_sanitize_sanact sanact; + __u32 ovrpat; + bool ause; + __u8 owpass; + bool oipbp; + bool nodas; +}; + +/** + * struct nvme_dev_self_test_args - Arguments for the NVMe Device Self Test command + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_dev_self_test_args + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to test + * @stc: Self test code, see &enum nvme_dst_stc + * @timeout: Timeout in ms + */ +struct nvme_dev_self_test_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_dst_stc stc; +}; + +/** + * struct nvme_virtual_mgmt_args - Arguments for the NVMe Virtualization + * resource management command + * @args_size: Size of &struct nvme_virtual_mgmt_args + * @fd: File descriptor of nvme device + * @result: If successful, the CQE dword0 + * @timeout: Timeout in ms + * @act: Virtual resource action, see &enum nvme_virt_mgmt_act + * @rt: Resource type to modify, see &enum nvme_virt_mgmt_rt + * @cntlid: Controller id for which resources are bing modified + * @nr: Number of resources being allocated or assigned + */ +struct nvme_virtual_mgmt_args { + __u32 *result; + int args_size; + int fd; + __u32 timeout; + enum nvme_virt_mgmt_act act; + enum nvme_virt_mgmt_rt rt; + __u16 cntlid; + __u16 nr; +}; + +/** + * struct nvme_io_args - Arguments for NVMe I/O commands + * @slba: Starting logical block + * @storage_tag: This filed specifies Variable Sized Expected Logical Block + * Storage Tag (ELBST) or Logical Block Storage Tag (LBST) + * @result: The command completion result from CQE dword0 + * @data: Pointer to user address of the data buffer + * @metadata: Pointer to user address of the metadata buffer + * @args_size: Size of &struct nvme_io_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID + * @data_len: Length of user buffer, @data, in bytes + * @metadata_len:Length of user buffer, @metadata, in bytes + * @nlb: Number of logical blocks to send (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @apptag: This field specifies the Application Tag Mask expected value. + * Used only if the namespace is formatted to use end-to-end + * protection information. + * @appmask: This field specifies the Application Tag expected value. Used + * only if the namespace is formatted to use end-to-end protection + * information. + * @reftag: This field specifies the variable sized Expected Initial + * Logical Block Reference Tag (EILBRT) or Initial Logical Block + * Reference Tag (ILBRT). Used only if the namespace is formatted + * to use end-to-end protection information. + * @dspec: Directive specific value + * @dsm: Data set management attributes, see &enum nvme_io_dsm_flags + * @rsvd1: Reserved + * @reftag_u64: This field specifies the variable sized Expected Initial + * Logical Block Reference Tag (EILBRT) or Initial Logical Block + * Reference Tag (ILBRT). It is the 8 byte version required for + * enhanced protection information. Used only if the namespace is + * formatted to use end-to-end protection information. + * @sts: Storage tag size in bits, set by namespace Extended LBA Format + * @pif: Protection information format, determines how variable sized + * storage_tag and reftag are put into dwords 2, 3, and 14. Set by + * namespace Extended LBA Format. + */ +struct nvme_io_args { + __u64 slba; + __u64 storage_tag; + __u32 *result; + void *data; + void *metadata; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 reftag; + __u32 data_len; + __u32 metadata_len; + __u16 nlb; + __u16 control; + __u16 apptag; + __u16 appmask; + __u16 dspec; + __u8 dsm; + __u8 rsvd1[1]; + __u64 reftag_u64; + __u8 sts; + __u8 pif; +}; + +/** + * struct nvme_dsm_args - Arguments for the NVMe Dataset Management command + * @result: The command completion result from CQE dword0 + * @dsm: The data set management attributes + * @args_size: Size of &struct nvme_dsm_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @attrs: DSM attributes, see &enum nvme_dsm_attributes + * @nr_ranges: Number of block ranges in the data set management attributes + */ +struct nvme_dsm_args { + __u32 *result; + struct nvme_dsm_range *dsm; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 attrs; + __u16 nr_ranges; +}; + +/** + * struct nvme_copy_args - Arguments for the NVMe Copy command + * @sdlba: Start destination LBA + * @result: The command completion result from CQE dword0 + * @copy: Range description + * @args_size: Size of &struct nvme_copy_args + * @fd: File descriptor of the nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @ilbrt: Initial logical block reference tag + * @lr: Limited retry + * @fua: Force unit access + * @nr: Number of ranges + * @dspec: Directive specific value + * @lbatm: Logical block application tag mask + * @lbat: Logical block application tag + * @prinfor: Protection information field for read + * @prinfow: Protection information field for write + * @dtype: Directive type + * @format: Descriptor format + * @ilbrt_u64: Initial logical block reference tag - 8 byte + * version required for enhanced protection info + */ +struct nvme_copy_args { + __u64 sdlba; + __u32 *result; + struct nvme_copy_range *copy; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 ilbrt; + int lr; + int fua; + __u16 nr; + __u16 dspec; + __u16 lbatm; + __u16 lbat; + __u8 prinfor; + __u8 prinfow; + __u8 dtype; + __u8 format; + __u64 ilbrt_u64; +}; + +/** + * struct nvme_resv_acquire_args - Arguments for the NVMe Reservation Acquire Command + * @nrkey: The reservation key to be unregistered from the namespace if + * the action is preempt + * @iekey: Set to ignore the existing key + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_resv_acquire_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @rtype: The type of reservation to be create, see &enum nvme_resv_rtype + * @racqa: The action that is performed by the command, see &enum nvme_resv_racqa + * @crkey: The current reservation key associated with the host + */ +struct nvme_resv_acquire_args { + __u64 crkey; + __u64 nrkey; + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_resv_rtype rtype; + enum nvme_resv_racqa racqa; + bool iekey; +}; + +/** + * struct nvme_resv_register_args - Arguments for the NVMe Reservation Register command + * @crkey: The current reservation key associated with the host + * @nrkey: The new reservation key to be register if action is register or + * replace + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_resv_register_args + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @rrega: The registration action, see &enum nvme_resv_rrega + * @cptpl: Change persist through power loss, see &enum nvme_resv_cptpl + * @iekey: Set to ignore the existing key + * @timeout: Timeout in ms + */ +struct nvme_resv_register_args { + __u64 crkey; + __u64 nrkey; + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_resv_rrega rrega; + enum nvme_resv_cptpl cptpl; + bool iekey; +}; + +/** + * struct nvme_resv_release_args - Arguments for the NVMe Reservation Release Command + * @crkey: The current reservation key to release + * @result: The command completion result from CQE dword0 + * @args_size: Size of &struct nvme_resv_release_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @rtype: The type of reservation to be create, see &enum nvme_resv_rtype + * @rrela: Reservation release action, see &enum nvme_resv_rrela + * @iekey: Set to ignore the existing key + */ +struct nvme_resv_release_args { + __u64 crkey; + __u32 *result; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_resv_rtype rtype; + enum nvme_resv_rrela rrela; + bool iekey; +}; + +/** + * struct nvme_resv_report_args - Arguments for the NVMe Reservation Report command + * @result: The command completion result from CQE dword0 + * @report: The user space destination address to store the reservation + * report + * @args_size: Size of &struct nvme_resv_report_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace identifier + * @len: Number of bytes to request transferred with this command + * @eds: Request extended Data Structure + */ +struct nvme_resv_report_args { + __u32 *result; + struct nvme_resv_status *report; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 len; + bool eds; +}; + +/** + * struct nvme_io_mgmt_recv_args - Arguments for the NVMe I/O Management Receive command + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_io_mgmt_recv_args + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of @data + * @timeout: Timeout in ms + * @mos: Management Operation Specific + * @mo: Management Operation + */ +struct nvme_io_mgmt_recv_args { + void *data; + int args_size; + int fd; + __u32 nsid; + __u32 data_len; + __u32 timeout; + __u16 mos; + __u8 mo; +}; + +/** + * struct nvme_io_mgmt_send_args - Arguments for the NVMe I/O Management Send command + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_io_mgmt_send_args + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of @data + * @timeout: Timeout in ms + * @mos: Management Operation Specific + * @mo: Management Operation + */ +struct nvme_io_mgmt_send_args { + void *data; + int args_size; + int fd; + __u32 nsid; + __u32 data_len; + __u32 timeout; + __u16 mos; + __u8 mo; +}; + +/** + * struct nvme_zns_mgmt_send_args - Arguments for the NVMe ZNS Management Send command + * @slba: Starting logical block address + * @result: The command completion result from CQE dword0 + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_zns_mgmt_send_args + * @fd: File descriptor of nvme device + * @timeout: timeout in ms + * @nsid: Namespace ID + * @zsa: Zone send action + * @data_len: Length of @data + * @select_all: Select all flag + * @zsaso: Zone Send Action Specific Option + */ +struct nvme_zns_mgmt_send_args { + __u64 slba; + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_zns_send_action zsa; + __u32 data_len; + bool select_all; + __u8 zsaso; +}; + +/** + * struct nvme_zns_mgmt_recv_args - Arguments for the NVMe ZNS Management Receive command + * @slba: Starting logical block address + * @result: The command completion result from CQE dword0 + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_zns_mgmt_recv_args + * @fd: File descriptor of nvme device + * @timeout: timeout in ms + * @nsid: Namespace ID + * @zra: zone receive action + * @data_len: Length of @data + * @zrasf: Zone receive action specific field + * @zras_feat: Zone receive action specific features + */ +struct nvme_zns_mgmt_recv_args { + __u64 slba; + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + enum nvme_zns_recv_action zra; + __u32 data_len; + __u16 zrasf; + bool zras_feat; +}; + +/** + * struct nvme_zns_append_args - Arguments for the NVMe ZNS Append command + * @zslba: Zone start logical block address + * @result: The command completion result from CQE dword0 + * @data: Userspace address of the data + * @metadata: Userspace address of the metadata + * @args_size: Size of &struct nvme_zns_append_args + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @nsid: Namespace ID + * @ilbrt: Initial logical block reference tag + * @data_len: Length of @data + * @metadata_len: Length of @metadata + * @nlb: Number of logical blocks + * @control: + * @lbat: Logical block application tag + * @lbatm: Logical block application tag mask + * @rsvd1: Reserved + * @ilbrt_u64: Initial logical block reference tag - 8 byte + * version required for enhanced protection info + * + */ +struct nvme_zns_append_args { + __u64 zslba; + __u64 *result; + void *data; + void *metadata; + int args_size; + int fd; + __u32 timeout; + __u32 nsid; + __u32 ilbrt; + __u32 data_len; + __u32 metadata_len; + __u16 nlb; + __u16 control; + __u16 lbat; + __u16 lbatm; + __u8 rsvd1[4]; + __u64 ilbrt_u64; +}; + +/** + * struct nvme_dim_args - Arguments for the Discovery Information Management (DIM) command + * @result: Set on completion to the command's CQE DWORD 0 controller response. + * @data: Pointer to the DIM data + * @args_size: Length of the structure + * @fd: File descriptor of nvme device + * @timeout: Timeout in ms + * @data_len: Length of @data + * @tas: Task field of the Command Dword 10 (cdw10) + */ +struct nvme_dim_args { + __u32 *result; + void *data; + int args_size; + int fd; + __u32 timeout; + __u32 data_len; + __u8 tas; +}; + +#endif /* _LIBNVME_API_TYPES_H */ diff --git a/src/nvme/base64.c b/src/nvme/base64.c new file mode 100644 index 0000000..5fae829 --- /dev/null +++ b/src/nvme/base64.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * base64.c - RFC4648-compliant base64 encoding + * + * Copyright (c) 2020 SUSE LLC + * + * Author: Hannes Reinecke <hare@suse.de> + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> + +static const char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode() - base64-encode some bytes + * @src: the bytes to encode + * @srclen: number of bytes to encode + * @dst: (output) the base64-encoded string. Not NUL-terminated. + * + * Encodes the input string using characters from the set [A-Za-z0-9+,]. + * The encoded string is roughly 4/3 times the size of the input string. + * + * Return: length of the encoded string + */ +int base64_encode(const unsigned char *src, int srclen, char *dst) +{ + int i, bits = 0; + u_int32_t ac = 0; + char *cp = dst; + + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; + bits += 8; + do { + bits -= 6; + *cp++ = base64_table[(ac >> bits) & 0x3f]; + } while (bits >= 6); + } + if (bits) { + *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; + bits -= 6; + } + while (bits < 0) { + *cp++ = '='; + bits += 2; + } + + return cp - dst; +} + +/** + * base64_decode() - base64-decode some bytes + * @src: the base64-encoded string to decode + * @len: number of bytes to decode + * @dst: (output) the decoded bytes. + * + * Decodes the base64-encoded bytes @src according to RFC 4648. + * + * Return: number of decoded bytes + */ +int base64_decode(const char *src, int srclen, unsigned char *dst) +{ + u_int32_t ac = 0; + int i, bits = 0; + unsigned char *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64_table, src[i]); + + if (src[i] == '=') { + ac = (ac << 6); + bits += 6; + if (bits >= 8) + bits -= 8; + continue; + } + if (!p || !src[i]) + return -EINVAL; + ac = (ac << 6) | (p - base64_table); + bits += 6; + if (bits >= 8) { + bits -= 8; + *bp++ = (unsigned char)(ac >> bits); + } + } + if (ac && ((1 << bits) - 1)) + return -EAGAIN; + + return bp - dst; +} diff --git a/src/nvme/base64.h b/src/nvme/base64.h new file mode 100644 index 0000000..c0f62e2 --- /dev/null +++ b/src/nvme/base64.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _BASE64_H +#define _BASE64_H + +int base64_encode(const unsigned char *src, int len, char *dst); +int base64_decode(const char *src, int len, unsigned char *dst); + +#endif /* _BASE64_H */ diff --git a/src/nvme/cleanup.h b/src/nvme/cleanup.h new file mode 100644 index 0000000..4327600 --- /dev/null +++ b/src/nvme/cleanup.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +#ifndef __CLEANUP_H +#define __CLEANUP_H + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define __cleanup__(fn) __attribute__((cleanup(fn))) + +#define DECLARE_CLEANUP_FUNC(name, type) \ + void name(type *__p) + +#define DEFINE_CLEANUP_FUNC(name, type, free_fn)\ +DECLARE_CLEANUP_FUNC(name, type) \ +{ \ + if (*__p) \ + free_fn(*__p); \ +} + +static inline void freep(void *p) +{ + free(*(void **)p); +} +#define _cleanup_free_ __cleanup__(freep) + +static inline DEFINE_CLEANUP_FUNC(cleanup_file, FILE *, fclose) +#define _cleanup_file_ __cleanup__(cleanup_file) + +static inline DEFINE_CLEANUP_FUNC(cleanup_dir, DIR *, closedir) +#define _cleanup_dir_ __cleanup__(cleanup_dir) + +static inline void cleanup_fd(int *fd) +{ + if (*fd >= 0) + close(*fd); +} +#define _cleanup_fd_ __cleanup__(cleanup_fd) + +#endif diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c new file mode 100644 index 0000000..1f50229 --- /dev/null +++ b/src/nvme/fabrics.c @@ -0,0 +1,1736 @@ +// 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 <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <inttypes.h> + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <net/if.h> + +#include <ccan/endian/endian.h> +#include <ccan/list/list.h> +#include <ccan/array_size/array_size.h> +#include <ccan/str/str.h> + +#include "cleanup.h" +#include "fabrics.h" +#include "linux.h" +#include "ioctl.h" +#include "util.h" +#include "log.h" +#include "private.h" + +#define NVMF_HOSTID_SIZE 37 + +#define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn" +#define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid" + +const char *nvmf_dev = "/dev/nvme-fabrics"; + +/** + * strchomp() - Strip trailing spaces + * @str: String to strip + * @max: Maximum length of string + */ +static void strchomp(char *str, int max) +{ + int i; + + for (i = max - 1; i >= 0 && str[i] == ' '; i--) { + str[i] = '\0'; + } +} + +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 * const trtypes[] = { + [NVMF_TRTYPE_RDMA] = "rdma", + [NVMF_TRTYPE_FC] = "fc", + [NVMF_TRTYPE_TCP] = "tcp", + [NVMF_TRTYPE_LOOP] = "loop", +}; + +const char *nvmf_trtype_str(__u8 trtype) +{ + return arg_str(trtypes, ARRAY_SIZE(trtypes), trtype); +} + +static const char * const adrfams[] = { + [NVMF_ADDR_FAMILY_PCI] = "pci", + [NVMF_ADDR_FAMILY_IP4] = "ipv4", + [NVMF_ADDR_FAMILY_IP6] = "ipv6", + [NVMF_ADDR_FAMILY_IB] = "infiniband", + [NVMF_ADDR_FAMILY_FC] = "fibre-channel", +}; + +const char *nvmf_adrfam_str(__u8 adrfam) +{ + return arg_str(adrfams, ARRAY_SIZE(adrfams), adrfam); +} + +static const char * const subtypes[] = { + [NVME_NQN_DISC] = "discovery subsystem referral", + [NVME_NQN_NVME] = "nvme subsystem", + [NVME_NQN_CURR] = "current discovery subsystem", +}; + +const char *nvmf_subtype_str(__u8 subtype) +{ + return arg_str(subtypes, ARRAY_SIZE(subtypes), subtype); +} + +static const char * const treqs[] = { + [NVMF_TREQ_NOT_SPECIFIED] = "not specified", + [NVMF_TREQ_REQUIRED] = "required", + [NVMF_TREQ_NOT_REQUIRED] = "not required", + [NVMF_TREQ_NOT_SPECIFIED | + NVMF_TREQ_DISABLE_SQFLOW] = "not specified, " + "sq flow control disable supported", + [NVMF_TREQ_REQUIRED | + NVMF_TREQ_DISABLE_SQFLOW] = "required, " + "sq flow control disable supported", + [NVMF_TREQ_NOT_REQUIRED | + NVMF_TREQ_DISABLE_SQFLOW] = "not required, " + "sq flow control disable supported", +}; + +const char *nvmf_treq_str(__u8 treq) +{ + return arg_str(treqs, ARRAY_SIZE(treqs), treq); +} + +static const char * const eflags_strings[] = { + [NVMF_DISC_EFLAGS_NONE] = "none", + [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections", + [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information", + [NVMF_DISC_EFLAGS_EPCSD | + NVMF_DISC_EFLAGS_DUPRETINFO] = "explicit discovery connections, " + "duplicate discovery information", + [NVMF_DISC_EFLAGS_NCC] = "no cdc connectivity", + [NVMF_DISC_EFLAGS_EPCSD | + NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, " + "no cdc connectivity", + [NVMF_DISC_EFLAGS_DUPRETINFO | + NVMF_DISC_EFLAGS_NCC] = "duplicate discovery information, " + "no cdc connectivity", + [NVMF_DISC_EFLAGS_EPCSD | + NVMF_DISC_EFLAGS_DUPRETINFO | + NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, " + "duplicate discovery information, " + "no cdc connectivity", +}; + +const char *nvmf_eflags_str(__u16 eflags) +{ + return arg_str(eflags_strings, ARRAY_SIZE(eflags_strings), eflags); +} + +static const char * const sectypes[] = { + [NVMF_TCP_SECTYPE_NONE] = "none", + [NVMF_TCP_SECTYPE_TLS] = "tls", + [NVMF_TCP_SECTYPE_TLS13] = "tls13", +}; + +const char *nvmf_sectype_str(__u8 sectype) +{ + return arg_str(sectypes, ARRAY_SIZE(sectypes), sectype); +} + +static const char * const prtypes[] = { + [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified", + [NVMF_RDMA_PRTYPE_IB] = "infiniband", + [NVMF_RDMA_PRTYPE_ROCE] = "roce", + [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2", + [NVMF_RDMA_PRTYPE_IWARP] = "iwarp", +}; + +const char *nvmf_prtype_str(__u8 prtype) +{ + return arg_str(prtypes, ARRAY_SIZE(prtypes), prtype); +} + +static const char * const qptypes[] = { + [NVMF_RDMA_QPTYPE_CONNECTED] = "connected", + [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram", +}; + +const char *nvmf_qptype_str(__u8 qptype) +{ + return arg_str(qptypes, ARRAY_SIZE(qptypes), qptype); +} + +static const char * const cms[] = { + [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm", +}; + +const char *nvmf_cms_str(__u8 cm) +{ + return arg_str(cms, ARRAY_SIZE(cms), cm); +} + +/* + * Derived from Linux's supported options (the opt_tokens table) + * when the mechanism to report supported options was added (f18ee3d988157). + * Not all of these options may actually be supported, + * but we retain the old behavior of passing all that might be. + */ +static const struct nvme_fabric_options default_supported_options = { + .ctrl_loss_tmo = true, + .data_digest = true, + .disable_sqflow = true, + .discovery = true, + .duplicate_connect = true, + .fast_io_fail_tmo = true, + .hdr_digest = true, + .host_iface = true, + .host_traddr = true, + .hostid = true, + .hostnqn = true, + .keep_alive_tmo = true, + .nqn = true, + .nr_io_queues = true, + .nr_poll_queues = true, + .nr_write_queues = true, + .queue_size = true, + .reconnect_delay = true, + .tos = true, + .traddr = true, + .transport = true, + .trsvcid = true, +}; + +void nvmf_default_config(struct nvme_fabrics_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + cfg->tos = -1; + cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; +} + +#define MERGE_CFG_OPTION(c, n, o, d) \ + if ((c)->o == d) (c)->o = (n)->o +#define MERGE_CFG_OPTION_STR(c, n, o, d) \ + if ((c)->o == d && (n)->o) (c)->o = strdup((n)->o) +static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c, + const struct nvme_fabrics_config *cfg) +{ + struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c); + + MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_traddr, NULL); + MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_iface, NULL); + MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, queue_size, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, keep_alive_tmo, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, reconnect_delay, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, ctrl_loss_tmo, + NVMF_DEF_CTRL_LOSS_TMO); + MERGE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, tos, -1); + MERGE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false); + MERGE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false); + MERGE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false); + MERGE_CFG_OPTION(ctrl_cfg, cfg, data_digest, false); + MERGE_CFG_OPTION(ctrl_cfg, cfg, tls, false); + MERGE_CFG_OPTION(ctrl_cfg, cfg, concat, false); + + return ctrl_cfg; +} + +#define UPDATE_CFG_OPTION(c, n, o, d) \ + if ((n)->o != d) (c)->o = (n)->o +void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) +{ + struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c); + + UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_traddr, NULL); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_iface, NULL); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, queue_size, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, keep_alive_tmo, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, reconnect_delay, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, ctrl_loss_tmo, + NVMF_DEF_CTRL_LOSS_TMO); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, tos, -1); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, data_digest, false); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls, false); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, concat, false); +} + +static int __add_bool_argument(char **argstr, char *tok, bool arg) +{ + char *nstr; + + if (!arg) + return 0; + if (asprintf(&nstr, "%s,%s", *argstr, tok) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + + return 0; +} + +static int __add_int_argument(char **argstr, char *tok, int arg, bool allow_zero) +{ + char *nstr; + + if (arg < 0 || (!arg && !allow_zero)) + return 0; + if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + + return 0; +} + +static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg) +{ + char *nstr; + + if (arg < -1) + return 0; + if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + + return 0; +} + +static int __add_argument(char **argstr, const char *tok, const char *arg) +{ + char *nstr; + + if (!arg || arg[0] == '\0' || !strcmp(arg, "none")) + return 0; + if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + + return 0; +} + +static int __nvmf_supported_options(nvme_root_t r); +#define nvmf_check_option(r, tok) \ +({ \ + !__nvmf_supported_options(r) && (r)->options->tok; \ +}) + +#define add_bool_argument(o, argstr, tok, arg) \ +({ \ + int ret; \ + if (nvmf_check_option(r, tok)) { \ + ret = __add_bool_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_int_argument(o, argstr, tok, arg, allow_zero) \ +({ \ + int ret; \ + if (nvmf_check_option(r, tok)) { \ + ret = __add_int_argument(argstr, \ + stringify(tok), \ + arg, \ + allow_zero); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_int_or_minus_one_argument(o, argstr, tok, arg) \ +({ \ + int ret; \ + if (nvmf_check_option(r, tok)) { \ + ret = __add_int_or_minus_one_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_DEBUG, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +#define add_argument(r, argstr, tok, arg) \ +({ \ + int ret; \ + if (nvmf_check_option(r, tok)) { \ + ret = __add_argument(argstr, \ + stringify(tok), \ + arg); \ + } else { \ + nvme_msg(r, LOG_NOTICE, \ + "option \"%s\" ignored\n", \ + stringify(tok)); \ + ret = 0; \ + } \ + ret; \ +}) + +static int inet4_pton(const char *src, uint16_t port, + struct sockaddr_storage *addr) +{ + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + + if (strlen(src) > INET_ADDRSTRLEN) + return -EINVAL; + + if (inet_pton(AF_INET, src, &addr4->sin_addr.s_addr) <= 0) + return -EINVAL; + + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + + return 0; +} + +static int inet6_pton(nvme_root_t r, const char *src, uint16_t port, + struct sockaddr_storage *addr) +{ + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + const char *scope = NULL; + char *p; + + if (strlen(src) > INET6_ADDRSTRLEN) + return -EINVAL; + + _cleanup_free_ char *tmp = strdup(src); + if (!tmp) { + nvme_msg(r, LOG_ERR, "cannot copy: %s\n", src); + return -ENOMEM; + } + + p = strchr(tmp, '%'); + if (p) { + *p = '\0'; + scope = src + (p - tmp) + 1; + } + + if (inet_pton(AF_INET6, tmp, &addr6->sin6_addr) != 1) + return -EINVAL; + + if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr) && scope) { + addr6->sin6_scope_id = if_nametoindex(scope); + if (addr6->sin6_scope_id == 0) { + nvme_msg(r, LOG_ERR, + "can't find iface index for: %s (%m)\n", scope); + return -EINVAL; + } + } + + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(port); + return 0; +} + +/** + * inet_pton_with_scope - convert an IPv4/IPv6 to socket address + * @r: nvme_root_t object + * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either + * @src: the start of the address string + * @trsvcid: transport service identifier + * @addr: output socket address + * + * Return 0 on success, errno otherwise. + */ +static int inet_pton_with_scope(nvme_root_t r, int af, + const char *src, const char * trsvcid, + struct sockaddr_storage *addr) +{ + int ret = -EINVAL; + uint16_t port = 0; + + if (trsvcid) { + unsigned long long tmp = strtoull(trsvcid, NULL, 0); + port = (uint16_t)tmp; + if (tmp != port) { + nvme_msg(r, LOG_ERR, "trsvcid out of range: %s\n", + trsvcid); + return -ERANGE; + } + } else { + port = 0; + } + + switch (af) { + case AF_INET: + ret = inet4_pton(src, port, addr); + break; + case AF_INET6: + ret = inet6_pton(r, src, port, addr); + break; + case AF_UNSPEC: + ret = inet4_pton(src, port, addr); + if (ret) + ret = inet6_pton(r, src, port, addr); + break; + default: + nvme_msg(r, LOG_ERR, "unexpected address family %d\n", af); + } + + return ret; +} + +static bool traddr_is_hostname(nvme_root_t r, nvme_ctrl_t c) +{ + struct sockaddr_storage addr; + + if (!c->traddr) + return false; + if (strcmp(c->transport, "tcp") && strcmp(c->transport, "rdma")) + return false; + if (inet_pton_with_scope(r, AF_UNSPEC, c->traddr, c->trsvcid, &addr) == 0) + return false; + return true; +} + +static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) +{ + struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + const char *transport = nvme_ctrl_get_transport(c); + const char *hostnqn, *hostid, *hostkey, *ctrlkey; + bool discover = false, discovery_nqn = false; + nvme_root_t r = h->r; + + if (!transport) { + nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n"); + errno = ENVME_CONNECT_TARG; + return -1; + } + + if (strncmp(transport, "loop", 4)) { + if (!nvme_ctrl_get_traddr(c)) { + nvme_msg(h->r, LOG_ERR, "need a address (-a) argument\n"); + errno = ENVME_CONNECT_AARG; + return -1; + } + } + + /* always specify nqn as first arg - this will init the string */ + if (asprintf(argstr, "nqn=%s", + nvme_ctrl_get_subsysnqn(c)) < 0) { + errno = ENOMEM; + return -1; + } + if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) { + nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, false); + discovery_nqn = true; + } + if (nvme_ctrl_is_discovery_ctrl(c)) + discover = true; + hostnqn = nvme_host_get_hostnqn(h); + hostid = nvme_host_get_hostid(h); + hostkey = nvme_host_get_dhchap_key(h); + if (!hostkey) + hostkey = nvme_ctrl_get_dhchap_host_key(c); + ctrlkey = nvme_ctrl_get_dhchap_key(c); + if (add_argument(r, argstr, transport, transport) || + add_argument(r, argstr, traddr, + nvme_ctrl_get_traddr(c)) || + add_argument(r, argstr, host_traddr, + cfg->host_traddr) || + add_argument(r, argstr, host_iface, + cfg->host_iface) || + add_argument(r, argstr, trsvcid, + nvme_ctrl_get_trsvcid(c)) || + (hostnqn && add_argument(r, argstr, hostnqn, hostnqn)) || + (hostid && add_argument(r, argstr, hostid, hostid)) || + (discover && !discovery_nqn && + add_bool_argument(r, argstr, discovery, true)) || + (!discover && hostkey && + add_argument(r, argstr, dhchap_secret, hostkey)) || + (!discover && ctrlkey && + add_argument(r, argstr, dhchap_ctrl_secret, ctrlkey)) || + (!discover && + add_int_argument(r, argstr, nr_io_queues, + cfg->nr_io_queues, false)) || + (!discover && + add_int_argument(r, argstr, nr_write_queues, + cfg->nr_write_queues, false)) || + (!discover && + add_int_argument(r, argstr, nr_poll_queues, + cfg->nr_poll_queues, false)) || + (!discover && + add_int_argument(r, argstr, queue_size, + cfg->queue_size, false)) || + add_int_argument(r, argstr, keep_alive_tmo, + cfg->keep_alive_tmo, false) || + add_int_argument(r, argstr, reconnect_delay, + cfg->reconnect_delay, false) || + (strcmp(transport, "loop") && + add_int_or_minus_one_argument(r, argstr, ctrl_loss_tmo, + cfg->ctrl_loss_tmo)) || + (strcmp(transport, "loop") && + add_int_argument(r, argstr, fast_io_fail_tmo, + cfg->fast_io_fail_tmo, false)) || + (strcmp(transport, "loop") && + add_int_argument(r, argstr, tos, cfg->tos, true)) || + add_int_argument(r, argstr, keyring, cfg->keyring, false) || + (!strcmp(transport, "tcp") && + add_int_argument(r, argstr, tls_key, cfg->tls_key, false)) || + add_bool_argument(r, argstr, duplicate_connect, + cfg->duplicate_connect) || + add_bool_argument(r, argstr, disable_sqflow, + cfg->disable_sqflow) || + (!strcmp(transport, "tcp") && + add_bool_argument(r, argstr, hdr_digest, cfg->hdr_digest)) || + (!strcmp(transport, "tcp") && + add_bool_argument(r, argstr, data_digest, cfg->data_digest)) || + (!strcmp(transport, "tcp") && + add_bool_argument(r, argstr, tls, cfg->tls)) || + (!strcmp(transport, "tcp") && + add_bool_argument(r, argstr, concat, cfg->concat))) { + free(*argstr); + return -1; + } + + return 0; +} + +#define parse_option(r, v, name) \ + if (!strcmp(v, stringify(name))) { \ + r->options->name = true; \ + continue; \ + } + +static int __nvmf_supported_options(nvme_root_t r) +{ + char buf[0x1000], *options, *p, *v; + _cleanup_fd_ int fd = -1; + ssize_t len; + + if (r->options) + return 0; + + r->options = calloc(1, sizeof(*r->options)); + if (!r->options) + return -ENOMEM; + + fd = open(nvmf_dev, O_RDONLY); + if (fd < 0) { + nvme_msg(r, LOG_ERR, "Failed to open %s: %s\n", + nvmf_dev, strerror(errno)); + return -ENVME_CONNECT_OPEN; + } + + memset(buf, 0x0, sizeof(buf)); + len = read(fd, buf, sizeof(buf) - 1); + if (len < 0) { + if (errno == EINVAL) { + /* + * Older Linux kernels don't allow reading from nvmf_dev + * to get supported options, so use a default set + */ + nvme_msg(r, LOG_DEBUG, + "Cannot read %s, using default options\n", + nvmf_dev); + *r->options = default_supported_options; + return 0; + } + + nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n", + nvmf_dev, strerror(errno)); + return -ENVME_CONNECT_READ; + } + + buf[len] = '\0'; + options = buf; + + nvme_msg(r, LOG_DEBUG, "kernel supports: "); + + while ((p = strsep(&options, ",\n")) != NULL) { + if (!*p) + continue; + v = strsep(&p, "= "); + if (!v) + continue; + nvme_msg(r, LOG_DEBUG, "%s ", v); + + parse_option(r, v, cntlid); + parse_option(r, v, concat); + parse_option(r, v, ctrl_loss_tmo); + parse_option(r, v, data_digest); + parse_option(r, v, dhchap_ctrl_secret); + parse_option(r, v, dhchap_secret); + parse_option(r, v, disable_sqflow); + parse_option(r, v, discovery); + parse_option(r, v, duplicate_connect); + parse_option(r, v, fast_io_fail_tmo); + parse_option(r, v, hdr_digest); + parse_option(r, v, host_iface); + parse_option(r, v, host_traddr); + parse_option(r, v, hostid); + parse_option(r, v, hostnqn); + parse_option(r, v, instance); + parse_option(r, v, keep_alive_tmo); + parse_option(r, v, keyring); + parse_option(r, v, nqn); + parse_option(r, v, nr_io_queues); + parse_option(r, v, nr_poll_queues); + parse_option(r, v, nr_write_queues); + parse_option(r, v, queue_size); + parse_option(r, v, reconnect_delay); + parse_option(r, v, tls); + parse_option(r, v, tls_key); + parse_option(r, v, tos); + parse_option(r, v, traddr); + parse_option(r, v, transport); + parse_option(r, v, trsvcid); + } + nvme_msg(r, LOG_DEBUG, "\n"); + return 0; +} + +static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) +{ + _cleanup_fd_ int fd = -1; + int ret, len = strlen(argstr); + char buf[0x1000], *options, *p; + + fd = open(nvmf_dev, O_RDWR); + if (fd < 0) { + nvme_msg(r, LOG_ERR, "Failed to open %s: %s\n", + nvmf_dev, strerror(errno)); + return -ENVME_CONNECT_OPEN; + } + + nvme_msg(r, LOG_DEBUG, "connect ctrl, '%.*s'\n", + (int)strcspn(argstr,"\n"), argstr); + ret = write(fd, argstr, len); + if (ret != len) { + nvme_msg(r, LOG_NOTICE, "Failed to write to %s: %s\n", + nvmf_dev, strerror(errno)); + switch (errno) { + case EALREADY: + return -ENVME_CONNECT_ALREADY; + case EINVAL: + return -ENVME_CONNECT_INVAL; + case EADDRINUSE: + return -ENVME_CONNECT_ADDRINUSE; + case ENODEV: + return -ENVME_CONNECT_NODEV; + case EOPNOTSUPP: + return -ENVME_CONNECT_OPNOTSUPP; + case ECONNREFUSED: + return -ENVME_CONNECT_CONNREFUSED; + case EADDRNOTAVAIL: + return -ENVME_CONNECT_ADDRNOTAVAIL; + default: + return -ENVME_CONNECT_WRITE; + } + } + + memset(buf, 0x0, sizeof(buf)); + len = read(fd, buf, sizeof(buf) - 1); + if (len < 0) { + nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n", + nvmf_dev, strerror(errno)); + return -ENVME_CONNECT_READ; + } + nvme_msg(r, LOG_DEBUG, "connect ctrl, response '%.*s'\n", + (int)strcspn(buf, "\n"), buf); + buf[len] = '\0'; + options = buf; + while ((p = strsep(&options, ",\n")) != NULL) { + if (!*p) + continue; + if (sscanf(p, "instance=%d", &ret) == 1) + return ret; + } + + nvme_msg(r, LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr); + return -ENVME_CONNECT_PARSE; +} + +static const char *lookup_context(nvme_root_t r, nvme_ctrl_t c) +{ + + nvme_host_t h; + nvme_subsystem_t s; + + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, s) { + if (__nvme_lookup_ctrl(s, nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + NULL, + NULL, + nvme_ctrl_get_trsvcid(c), + NULL, + NULL)) + return nvme_subsystem_get_application(s); + } + } + + return NULL; +} + +int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, + const struct nvme_fabrics_config *cfg) +{ + nvme_subsystem_t s; + const char *root_app, *app; + _cleanup_free_ char *argstr = NULL; + int ret; + + /* highest prio have configs from command line */ + cfg = merge_config(c, cfg); + + /* apply configuration from config file (JSON) */ + s = nvme_lookup_subsystem(h, NULL, nvme_ctrl_get_subsysnqn(c)); + if (s) { + nvme_ctrl_t fc; + + fc = __nvme_lookup_ctrl(s, nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + nvme_ctrl_get_host_traddr(c), + nvme_ctrl_get_host_iface(c), + nvme_ctrl_get_trsvcid(c), + NULL, + NULL); + if (fc) { + const char *key; + + cfg = merge_config(c, nvme_ctrl_get_config(fc)); + /* + * An authentication key might already been set + * in @cfg, so ensure to update @c with the correct + * controller key. + */ + key = nvme_ctrl_get_dhchap_host_key(fc); + if (key) + nvme_ctrl_set_dhchap_host_key(c, key); + key = nvme_ctrl_get_dhchap_key(fc); + if (key) + nvme_ctrl_set_dhchap_key(c, key); + } + + } + + root_app = nvme_root_get_application(h->r); + if (root_app) { + app = nvme_subsystem_get_application(s); + if (!app && nvme_ctrl_is_discovery_ctrl(c)) + app = lookup_context(h->r, c); + + /* + * configuration is managed by an application, + * refuse to act on subsystems which either have + * no application set or which habe a different + * application string. + */ + if (app && strcmp(app, root_app)) { + nvme_msg(h->r, LOG_INFO, "skip %s, not managed by %s\n", + nvme_subsystem_get_nqn(s), root_app); + errno = ENVME_CONNECT_IGNORED; + return -1; + } + } + + nvme_ctrl_set_discovered(c, true); + if (traddr_is_hostname(h->r, c)) { + char *traddr = c->traddr; + + c->traddr = hostname2traddr(h->r, traddr); + if (!c->traddr) { + c->traddr = traddr; + errno = ENVME_CONNECT_TRADDR; + return -1; + } + free(traddr); + } + + ret = build_options(h, c, &argstr); + if (ret) + return ret; + + ret = __nvmf_add_ctrl(h->r, argstr); + if (ret < 0) { + errno = -ret; + return -1; + } + + nvme_msg(h->r, LOG_INFO, "nvme%d: %s connected\n", ret, + nvme_ctrl_get_subsysnqn(c)); + return nvme_init_ctrl(h, c, ret); +} + +nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, + struct nvmf_disc_log_entry *e, + const struct nvme_fabrics_config *cfg, + bool *discover) +{ + const char *transport; + char *traddr = NULL, *trsvcid = NULL; + nvme_ctrl_t c; + int ret; + + switch (e->trtype) { + case NVMF_TRTYPE_RDMA: + case NVMF_TRTYPE_TCP: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_IP4: + case NVMF_ADDR_FAMILY_IP6: + traddr = e->traddr; + trsvcid = e->trsvcid; + break; + default: + nvme_msg(h->r, LOG_ERR, + "skipping unsupported adrfam %d\n", + e->adrfam); + errno = EINVAL; + return NULL; + } + break; + case NVMF_TRTYPE_FC: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_FC: + traddr = e->traddr; + break; + default: + nvme_msg(h->r, LOG_ERR, + "skipping unsupported adrfam %d\n", + e->adrfam); + errno = EINVAL; + return NULL; + } + break; + case NVMF_TRTYPE_LOOP: + traddr = strlen(e->traddr) ? e->traddr : NULL; + break; + default: + nvme_msg(h->r, LOG_ERR, "skipping unsupported transport %d\n", + e->trtype); + errno = EINVAL; + return NULL; + } + + transport = nvmf_trtype_str(e->trtype); + + nvme_msg(h->r, LOG_DEBUG, "lookup ctrl " + "(transport: %s, traddr: %s, trsvcid %s)\n", + transport, traddr, trsvcid); + c = nvme_create_ctrl(h->r, e->subnqn, transport, traddr, + cfg->host_traddr, cfg->host_iface, trsvcid); + if (!c) { + nvme_msg(h->r, LOG_DEBUG, "skipping discovery entry, " + "failed to allocate %s controller with traddr %s\n", + transport, traddr); + errno = ENOMEM; + return NULL; + } + + switch (e->subtype) { + case NVME_NQN_CURR: + nvme_ctrl_set_discovered(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)); + break; + case NVME_NQN_DISC: + if (discover) + *discover = true; + nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)); + break; + default: + nvme_msg(h->r, LOG_ERR, "unsupported subtype %d\n", + e->subtype); + fallthrough; + case NVME_NQN_NVME: + nvme_ctrl_set_discovery_ctrl(c, false); + nvme_ctrl_set_unique_discovery_ctrl(c, false); + break; + } + + if (nvme_ctrl_is_discovered(c)) { + nvme_free_ctrl(c); + errno = EAGAIN; + return NULL; + } + + if (e->treq & NVMF_TREQ_DISABLE_SQFLOW && + nvmf_check_option(h->r, disable_sqflow)) + c->cfg.disable_sqflow = true; + + if (e->trtype == NVMF_TRTYPE_TCP && + e->tsas.tcp.sectype != NVMF_TCP_SECTYPE_NONE) + c->cfg.tls = true; + + ret = nvmf_add_ctrl(h, c, cfg); + if (!ret) + return c; + + if (errno == EINVAL && c->cfg.disable_sqflow) { + errno = 0; + /* disable_sqflow is unrecognized option on older kernels */ + nvme_msg(h->r, LOG_INFO, "failed to connect controller, " + "retry with disabling SQ flow control\n"); + c->cfg.disable_sqflow = false; + ret = nvmf_add_ctrl(h, c, cfg); + if (!ret) + return c; + } + nvme_free_ctrl(c); + return NULL; +} + +/* + * Most of nvmf_discovery_log is reserved, so only fetch the initial bytes. + * 8 bytes for GENCTR, 8 for NUMREC, and 2 for RECFMT. + * Since only multiples of 4 bytes are allowed, round 18 up to 20. + */ +#define DISCOVERY_HEADER_LEN 20 + +static struct nvmf_discovery_log *nvme_discovery_log( + const struct nvme_get_discovery_args *args) +{ + nvme_root_t r = root_from_ctrl(args->c); + struct nvmf_discovery_log *log; + int retries = 0; + const char *name = nvme_ctrl_get_name(args->c); + uint64_t genctr, numrec; + int fd = nvme_ctrl_get_fd(args->c); + struct nvme_get_log_args log_args = { + .result = args->result, + .args_size = sizeof(log_args), + .timeout = args->timeout, + .lid = NVME_LOG_LID_DISCOVER, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = args->lsp, + .uuidx = NVME_UUID_NONE, + }; + + log = __nvme_alloc(sizeof(*log)); + if (!log) { + nvme_msg(r, LOG_ERR, + "could not allocate memory for discovery log header\n"); + errno = ENOMEM; + return NULL; + } + + nvme_msg(r, LOG_DEBUG, "%s: get header (try %d/%d)\n", + name, retries, args->max_retries); + log_args.log = log; + log_args.len = DISCOVERY_HEADER_LEN; + if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) { + nvme_msg(r, LOG_INFO, + "%s: discover try %d/%d failed, error %d\n", + name, retries, args->max_retries, errno); + goto out_free_log; + } + + do { + size_t entries_size; + + numrec = le64_to_cpu(log->numrec); + genctr = le64_to_cpu(log->genctr); + + if (numrec == 0) + break; + + free(log); + entries_size = sizeof(*log->entries) * numrec; + log = __nvme_alloc(sizeof(*log) + entries_size); + if (!log) { + nvme_msg(r, LOG_ERR, + "could not alloc memory for discovery log page\n"); + errno = ENOMEM; + return NULL; + } + + nvme_msg(r, LOG_DEBUG, + "%s: get %" PRIu64 " records (genctr %" PRIu64 ")\n", + name, numrec, genctr); + + log_args.lpo = sizeof(*log); + log_args.log = log->entries; + log_args.len = entries_size; + if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) { + nvme_msg(r, LOG_INFO, + "%s: discover try %d/%d failed, error %d\n", + name, retries, args->max_retries, errno); + goto out_free_log; + } + + /* + * If the log page was read with multiple Get Log Page commands, + * genctr must be checked afterwards to ensure atomicity + */ + nvme_msg(r, LOG_DEBUG, "%s: get header again\n", name); + + log_args.lpo = 0; + log_args.log = log; + log_args.len = DISCOVERY_HEADER_LEN; + if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) { + nvme_msg(r, LOG_INFO, + "%s: discover try %d/%d failed, error %d\n", + name, retries, args->max_retries, errno); + goto out_free_log; + } + } while (genctr != le64_to_cpu(log->genctr) && + ++retries < args->max_retries); + + if (genctr != le64_to_cpu(log->genctr)) { + nvme_msg(r, LOG_INFO, "%s: discover genctr mismatch\n", name); + errno = EAGAIN; + } else if (numrec != le64_to_cpu(log->numrec)) { + nvme_msg(r, LOG_INFO, + "%s: numrec changed unexpectedly " + "from %" PRIu64 " to %" PRIu64 "\n", + name, numrec, le64_to_cpu(log->numrec)); + errno = EBADSLT; + } else { + return log; + } + +out_free_log: + free(log); + return NULL; +} + +static void sanitize_discovery_log_entry(struct nvmf_disc_log_entry *e) +{ + strchomp(e->trsvcid, sizeof(e->trsvcid)); + strchomp(e->traddr, sizeof(e->traddr)); +} + +int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, + int max_retries) +{ + struct nvme_get_discovery_args args = { + .c = c, + .max_retries = max_retries, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lsp = NVMF_LOG_DISC_LSP_NONE, + }; + + *logp = nvmf_get_discovery_wargs(&args); + return *logp ? 0 : -1; +} + +struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args) +{ + struct nvmf_discovery_log *log; + + log = nvme_discovery_log(args); + if (!log) + return NULL; + + for (int i = 0; i < le64_to_cpu(log->numrec); i++) + sanitize_discovery_log_entry(&log->entries[i]); + + return log; +} + +#define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" + +static char *uuid_ibm_filename(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_UUID_IBM); + + if (!asprintf(&str, "%s" PATH_UUID_IBM, basepath)) + return NULL; + + return str; +} + +static int uuid_from_device_tree(char *system_uuid) +{ + _cleanup_free_ char *filename = uuid_ibm_filename(); + _cleanup_fd_ int f = -1; + ssize_t len; + + f = open(filename, O_RDONLY); + if (f < 0) + return -ENXIO; + + memset(system_uuid, 0, NVME_UUID_LEN_STRING); + len = read(f, system_uuid, NVME_UUID_LEN_STRING - 1); + if (len < 0) + return -ENXIO; + + return strlen(system_uuid) ? 0 : -ENXIO; +} + +#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" + +static char *dmi_entries_dir(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_DMI_ENTRIES); + + if (!asprintf(&str, "%s" PATH_DMI_ENTRIES, basepath)) + return NULL; + + return str; +} + +/* + * See System Management BIOS (SMBIOS) Reference Specification + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf + */ +#define DMI_SYSTEM_INFORMATION 1 + +static bool is_dmi_uuid_valid(const char *buf, size_t len) +{ + int i; + + /* UUID bytes are from byte 8 to 23 */ + if (len < 24) + return false; + + /* Test it's a invalid UUID with all zeros */ + for (i = 8; i < 24; i++) { + if (buf[i]) + break; + } + if (i == 24) + return false; + + return true; +} + +static int uuid_from_dmi_entries(char *system_uuid) +{ + _cleanup_dir_ DIR *d = NULL; + _cleanup_free_ char *entries_dir = dmi_entries_dir(); + int f; + struct dirent *de; + char buf[512] = {0}; + + system_uuid[0] = '\0'; + d = opendir(entries_dir); + if (!d) + return -ENXIO; + while ((de = readdir(d))) { + char filename[PATH_MAX]; + int len, type; + + if (de->d_name[0] == '.') + continue; + sprintf(filename, "%s/%s/type", entries_dir, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + if (len <= 0) + continue; + if (sscanf(buf, "%d", &type) != 1) + continue; + if (type != DMI_SYSTEM_INFORMATION) + continue; + sprintf(filename, "%s/%s/raw", entries_dir, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + + if (!is_dmi_uuid_valid(buf, len)) + continue; + + /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ + /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ + sprintf(system_uuid, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2], + (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0], + (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4], + (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6], + (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9], + (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11], + (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13], + (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]); + break; + } + return strlen(system_uuid) ? 0 : -ENXIO; +} + +#define PATH_DMI_PROD_UUID "/sys/class/dmi/id/product_uuid" + +/** + * uuid_from_product_uuid() - Get system UUID from product_uuid + * @system_uuid: Where to save the system UUID. + * + * Return: 0 on success, -ENXIO otherwise. + */ +static int uuid_from_product_uuid(char *system_uuid) +{ + _cleanup_file_ FILE *stream = NULL; + ssize_t nread; + _cleanup_free_ char *line = NULL; + size_t len = 0; + + stream = fopen(PATH_DMI_PROD_UUID, "re"); + if (!stream) + return -ENXIO; + system_uuid[0] = '\0'; + + nread = getline(&line, &len, stream); + if (nread != NVME_UUID_LEN_STRING) + return -ENXIO; + + /* The kernel is handling the byte swapping according DMTF + * SMBIOS 3.0 Section 7.2.1 System UUID */ + + memcpy(system_uuid, line, NVME_UUID_LEN_STRING - 1); + system_uuid[NVME_UUID_LEN_STRING - 1] = '\0'; + + return 0; +} + +/** + * uuid_from_dmi() - read system UUID + * @system_uuid: buffer for the UUID + * + * The system UUID can be read from two different locations: + * + * 1) /sys/class/dmi/id/product_uuid + * 2) /sys/firmware/dmi/entries + * + * Note that the second location is not present on Debian-based systems. + * + * Return: 0 on success, negative errno otherwise. + */ +static int uuid_from_dmi(char *system_uuid) +{ + int ret = uuid_from_product_uuid(system_uuid); + if (ret != 0) + ret = uuid_from_dmi_entries(system_uuid); + return ret; +} + +char *nvmf_hostnqn_generate() +{ + char *hostnqn; + int ret; + char uuid_str[NVME_UUID_LEN_STRING]; + unsigned char uuid[NVME_UUID_LEN]; + + ret = uuid_from_dmi(uuid_str); + if (ret < 0) { + ret = uuid_from_device_tree(uuid_str); + } + if (ret < 0) { + if (nvme_uuid_random(uuid) < 0) + memset(uuid, 0, NVME_UUID_LEN); + nvme_uuid_to_string(uuid, uuid_str); + } + + if (asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid_str) < 0) + return NULL; + + return hostnqn; +} + +static char *nvmf_read_file(const char *f, int len) +{ + char buf[len]; + _cleanup_fd_ int fd = -1; + int ret; + + fd = open(f, O_RDONLY); + if (fd < 0) + return NULL; + + memset(buf, 0, len); + ret = read(fd, buf, len - 1); + + if (ret < 0 || !strlen(buf)) + return NULL; + return strndup(buf, strcspn(buf, "\n")); +} + +char *nvmf_hostnqn_from_file() +{ + char *hostnqn = getenv("LIBNVME_HOSTNQN"); + + if (hostnqn) + return strdup(hostnqn); + + return nvmf_read_file(NVMF_HOSTNQN_FILE, NVMF_NQN_SIZE); +} + +char *nvmf_hostid_from_file() +{ + char *hostid = getenv("LIBNVME_HOSTID"); + + if (hostid) + return strdup(hostid); + + return nvmf_read_file(NVMF_HOSTID_FILE, NVMF_HOSTID_SIZE); +} + +/** + * nvmf_get_tel() - Calculate the amount of memory needed for a DIE. + * @hostsymname: Symbolic name (may be NULL) + * + * Each Discovery Information Entry (DIE) must contain at a minimum an + * Extended Attribute for the HostID. The Entry may optionally contain an + * Extended Attribute for the Symbolic Name. + * + * Return: Total Entry Length + */ +static __u32 nvmf_get_tel(const char *hostsymname) +{ + __u32 tel = sizeof(struct nvmf_ext_die); + __u16 len; + + /* Host ID is mandatory */ + tel += nvmf_exat_size(NVME_UUID_LEN); + + /* Symbolic name is optional */ + len = hostsymname ? strlen(hostsymname) : 0; + if (len) + tel += nvmf_exat_size(len); + + return tel; +} + +/** + * nvmf_fill_die() - Fill a Discovery Information Entry. + * @die: Pointer to Discovery Information Entry to be filled + * @h: Pointer to the host data structure + * @tel: Length of the DIE + * @trtype: Transport type + * @adrfam: Address family + * @reg_addr: Address to register. Setting this to an empty string tells + * the DC to infer address from the source address of the socket. + * @tsas: Transport Specific Address Subtype for the address being + * registered. + */ +static void nvmf_fill_die(struct nvmf_ext_die *die, struct nvme_host *h, + __u32 tel, __u8 trtype, __u8 adrfam, + const char *reg_addr, union nvmf_tsas *tsas) +{ + __u16 numexat = 0; + size_t symname_len; + struct nvmf_ext_attr *exat; + + die->tel = cpu_to_le32(tel); + die->trtype = trtype; + die->adrfam = adrfam; + + memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn))); + memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr))); + + if (tsas) + memcpy(&die->tsas, tsas, sizeof(die->tsas)); + + /* Extended Attribute for the HostID (mandatory) */ + numexat++; + exat = die->exat; + exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID); + exat->exatlen = cpu_to_le16(nvmf_exat_len(NVME_UUID_LEN)); + nvme_uuid_from_string(h->hostid, exat->exatval); + + /* Extended Attribute for the Symbolic Name (optional) */ + symname_len = h->hostsymname ? strlen(h->hostsymname) : 0; + if (symname_len) { + __u16 exatlen = nvmf_exat_len(symname_len); + + numexat++; + exat = nvmf_exat_ptr_next(exat); + exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME); + exat->exatlen = cpu_to_le16(exatlen); + memcpy(exat->exatval, h->hostsymname, symname_len); + /* Per Base specs, ASCII strings must be padded with spaces */ + memset(&exat->exatval[symname_len], ' ', exatlen - symname_len); + } + + die->numexat = cpu_to_le16(numexat); +} + +/** + * nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM + * @c: Host NVMe controller instance maintaining the admin queue used to + * submit the DIM command to the DC. + * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to + * perform a Registration, Deregistration, or Registration-update. + * @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP) + * @adrfam: Address family (&enum nvmf_addr_family) + * @reg_addr: Address to register. Setting this to an empty string tells + * the DC to infer address from the source address of the socket. + * @tsas: Transport Specific Address Subtype for the address being + * registered. + * @result: Location where to save the command-specific result returned by + * the discovery controller. + * + * Perform explicit registration, deregistration, or + * registration-update (specified by @tas) by sending a Discovery + * Information Management (DIM) command to the Discovery Controller + * (DC). + * + * Return: 0 on success; on failure -1 is returned and errno is set + */ +static int nvmf_dim(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u8 trtype, + __u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas, + __u32 *result) +{ + nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL; + _cleanup_free_ struct nvmf_dim_data *dim = NULL; + struct nvmf_ext_die *die; + __u32 tdl; + __u32 tel; + int ret; + + struct nvme_dim_args args = { + .args_size = sizeof(args), + .fd = nvme_ctrl_get_fd(c), + .result = result, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .tas = tas + }; + + if (!c->s) { + nvme_msg(r, LOG_ERR, + "%s: failed to perform DIM. subsystem undefined.\n", + c->name); + errno = EINVAL; + return -1; + } + + if (!c->s->h) { + nvme_msg(r, LOG_ERR, + "%s: failed to perform DIM. host undefined.\n", + c->name); + errno = EINVAL; + return -1; + } + + if (!c->s->h->hostid) { + nvme_msg(r, LOG_ERR, + "%s: failed to perform DIM. hostid undefined.\n", + c->name); + errno = EINVAL; + return -1; + } + + if (!c->s->h->hostnqn) { + nvme_msg(r, LOG_ERR, + "%s: failed to perform DIM. hostnqn undefined.\n", + c->name); + errno = EINVAL; + return -1; + } + + if (strcmp(c->transport, "tcp")) { + nvme_msg(r, LOG_ERR, + "%s: DIM only supported for TCP connections.\n", + c->name); + errno = EINVAL; + return -1; + } + + /* Register one Discovery Information Entry (DIE) of size TEL */ + tel = nvmf_get_tel(c->s->h->hostsymname); + tdl = sizeof(struct nvmf_dim_data) + tel; + + dim = (struct nvmf_dim_data *)calloc(1, tdl); + if (!dim) { + errno = ENOMEM; + return -1; + } + + dim->tdl = cpu_to_le32(tdl); + dim->nument = cpu_to_le64(1); /* only one DIE to register */ + dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED); + dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST); + dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */ + + memcpy(dim->eid, c->s->h->hostnqn, + MIN(sizeof(dim->eid), strlen(c->s->h->hostnqn))); + + ret = get_entity_name(dim->ename, sizeof(dim->ename)); + if (ret <= 0) + nvme_msg(r, LOG_INFO, "%s: Failed to retrieve ENAME. %s.\n", + c->name, strerror(errno)); + + ret = get_entity_version(dim->ever, sizeof(dim->ever)); + if (ret <= 0) + nvme_msg(r, LOG_INFO, "%s: Failed to retrieve EVER.\n", c->name); + + die = &dim->die->extended; + nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas); + + args.data_len = tdl; + args.data = dim; + return nvme_dim_send(&args); +} + +/** + * nvme_get_adrfam() - Get address family for the address we're registering + * with the DC. + * + * We retrieve this info from the socket itself. If we can't get the source + * address from the socket, then we'll infer the address family from the + * address of the DC since the DC address has the same address family. + * + * @ctrl: Host NVMe controller instance maintaining the admin queue used to + * submit the DIM command to the DC. + * + * Return: The address family of the source address associated with the + * socket connected to the DC. + */ +static __u8 nvme_get_adrfam(nvme_ctrl_t c) +{ + struct sockaddr_storage addr; + __u8 adrfam = NVMF_ADDR_FAMILY_IP4; + nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL; + + if (!inet_pton_with_scope(r, AF_UNSPEC, c->traddr, c->trsvcid, &addr)) { + if (addr.ss_family == AF_INET6) + adrfam = NVMF_ADDR_FAMILY_IP6; + } + + return adrfam; +} + +/* These string definitions must match with the kernel */ +static const char *cntrltype_str[] = { + [NVME_CTRL_CNTRLTYPE_IO] = "io", + [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery", + [NVME_CTRL_CNTRLTYPE_ADMIN] = "admin", +}; + +static const char *dctype_str[] = { + [NVME_CTRL_DCTYPE_NOT_REPORTED] = "none", + [NVME_CTRL_DCTYPE_DDC] = "ddc", + [NVME_CTRL_DCTYPE_CDC] = "cdc", +}; + +/** + * nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command + * @c: Controller instance + * + * On legacy kernels the cntrltype and dctype are not exposed through the + * sysfs. We must get them directly from the controller by performing an + * identify command. + */ +static int nvme_fetch_cntrltype_dctype_from_id(nvme_ctrl_t c) +{ + _cleanup_free_ struct nvme_id_ctrl *id = NULL; + int ret; + + id = __nvme_alloc(sizeof(*id)); + if (!id) { + errno = ENOMEM; + return -1; + } + + ret = nvme_ctrl_identify(c, id); + if (ret) + return ret; + + if (!c->cntrltype) { + if (id->cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id->cntrltype]) + c->cntrltype = strdup("reserved"); + else + c->cntrltype = strdup(cntrltype_str[id->cntrltype]); + } + + if (!c->dctype) { + if (id->dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id->dctype]) + c->dctype = strdup("reserved"); + else + c->dctype = strdup(dctype_str[id->dctype]); + } + return 0; +} + +bool nvmf_is_registration_supported(nvme_ctrl_t c) +{ + if (!c->cntrltype || !c->dctype) + if (nvme_fetch_cntrltype_dctype_from_id(c)) + return false; + + return !strcmp(c->dctype, "ddc") || !strcmp(c->dctype, "cdc"); +} + +int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result) +{ + if (!nvmf_is_registration_supported(c)) { + errno = ENOTSUP; + return -1; + } + + /* We're registering our source address with the DC. To do + * that, we can simply send an empty string. This tells the DC + * to retrieve the source address from the socket and use that + * as the registration address. + */ + return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL, result); +} diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h new file mode 100644 index 0000000..a2504de --- /dev/null +++ b/src/nvme/fabrics.h @@ -0,0 +1,327 @@ +// 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> + */ +#ifndef _LIBNVME_FABRICS_H +#define _LIBNVME_FABRICS_H + +#include <stdbool.h> +#include <stdint.h> +#include "tree.h" + +/** + * DOC: fabrics.h + * + * Fabrics-specific definitions. + */ + +/* default to 600 seconds of reconnect attempts before giving up */ +#define NVMF_DEF_CTRL_LOSS_TMO 600 + +/** + * struct nvme_fabrics_config - Defines all linux nvme fabrics initiator options + * @host_traddr: Host transport address + * @host_iface: Host interface name + * @queue_size: Number of IO queue entries + * @nr_io_queues: Number of controller IO queues to establish + * @reconnect_delay: Time between two consecutive reconnect attempts. + * @ctrl_loss_tmo: Override the default controller reconnect attempt timeout in seconds + * @fast_io_fail_tmo: Set the fast I/O fail timeout in seconds. + * @keep_alive_tmo: Override the default keep-alive-timeout to this value in seconds + * @nr_write_queues: Number of queues to use for exclusively for writing + * @nr_poll_queues: Number of queues to reserve for polling completions + * @tos: Type of service + * @keyring: Keyring to store and lookup keys + * @tls_key: TLS PSK for the connection + * @duplicate_connect: Allow multiple connections to the same target + * @disable_sqflow: Disable controller sq flow control + * @hdr_digest: Generate/verify header digest (TCP) + * @data_digest: Generate/verify data digest (TCP) + * @tls: Start TLS on the connection (TCP) + * @concat: Enable secure concatenation (TCP) + */ +struct nvme_fabrics_config { + char *host_traddr; + char *host_iface; + int queue_size; + int nr_io_queues; + int reconnect_delay; + int ctrl_loss_tmo; + int fast_io_fail_tmo; + int keep_alive_tmo; + int nr_write_queues; + int nr_poll_queues; + int tos; + int keyring; + int tls_key; + + bool duplicate_connect; + bool disable_sqflow; + bool hdr_digest; + bool data_digest; + bool tls; + bool concat; +}; + +/** + * nvmf_trtype_str() - Decode TRTYPE field + * @trtype: value to be decoded + * + * Decode the transport type field in the discovery + * log page entry. + * + * Return: decoded string + */ +const char *nvmf_trtype_str(__u8 trtype); + +/** + * nvmf_adrfam_str() - Decode ADRFAM field + * @adrfam: value to be decoded + * + * Decode the address family field in the discovery + * log page entry. + * + * Return: decoded string + */ +const char *nvmf_adrfam_str(__u8 adrfam); + +/** + * nvmf_subtype_str() - Decode SUBTYPE field + * @subtype: value to be decoded + * + * Decode the subsystem type field in the discovery + * log page entry. + * + * Return: decoded string + */ +const char *nvmf_subtype_str(__u8 subtype); + +/** + * nvmf_treq_str() - Decode TREQ field + * @treq: value to be decoded + * + * Decode the transport requirements field in the + * discovery log page entry. + * + * Return: decoded string + */ +const char *nvmf_treq_str(__u8 treq); + +/** + * nvmf_eflags_str() - Decode EFLAGS field + * @eflags: value to be decoded + * + * Decode the EFLAGS field in the discovery log page + * entry. + * + * Return: decoded string + */ +const char *nvmf_eflags_str(__u16 eflags); + +/** + * nvmf_sectype_str() - Decode SECTYPE field + * @sectype: value to be decoded + * + * Decode the SECTYPE field in the discovery log page + * entry. + * + * Return: decoded string + */ +const char *nvmf_sectype_str(__u8 sectype); + +/** + * nvmf_prtype_str() - Decode RDMA Provider type field + * @prtype: value to be decoded + * + * Decode the RDMA Provider type field in the discovery + * log page entry. + * + * Return: decoded string + */ +const char *nvmf_prtype_str(__u8 prtype); + +/** + * nvmf_qptype_str() - Decode RDMA QP Service type field + * @qptype: value to be decoded + * + * Decode the RDMA QP Service type field in the discovery log page + * entry. + * + * Return: decoded string + */ +const char *nvmf_qptype_str(__u8 qptype); + +/** + * nvmf_cms_str() - Decode RDMA connection management service field + * @cms: value to be decoded + * + * Decode the RDMA connection management service field in the discovery + * log page entry. + * + * Return: decoded string + */ +const char *nvmf_cms_str(__u8 cms); + +/** + * nvmf_default_config() - Default values for fabrics configuration + * @cfg: config values to set + * + * Initializes @cfg with default values. + */ +void nvmf_default_config(struct nvme_fabrics_config *cfg); + +/** + * nvmf_update_config() - Update fabrics configuration values + * @c: Controller to be modified + * @cfg: Updated configuration values + * + * Updates the values from @c with the configuration values from @cfg; + * all non-default values from @cfg will overwrite the values in @c. + */ +void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg); + +/** + * nvmf_add_ctrl() - Connect a controller and update topology + * @h: Host to which the controller should be attached + * @c: Controller to be connected + * @cfg: Default configuration for the controller + * + * Issues a 'connect' command to the NVMe-oF controller and inserts @c + * into the topology using @h as parent. + * @c must be initialized and not connected to the topology. + * + * Return: 0 on success; on failure errno is set and -1 is returned. + */ +int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, + const struct nvme_fabrics_config *cfg); + +/** + * nvmf_get_discovery_log() - Return the discovery log page + * @c: Discovery controller to use + * @logp: Pointer to the log page to be returned + * @max_retries: Number of retries in case of failure + * + * The memory allocated for the log page and returned in @logp + * must be freed by the caller using free(). + * + * Note: Consider using nvmf_get_discovery_wargs() instead. + * + * Return: 0 on success; on failure -1 is returned and errno is set + */ +int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, + int max_retries); + +/** + * struct nvme_get_discovery_args - Arguments for nvmf_get_discovery_wargs() + * @c: Discovery controller + * @args_size: Length of the structure + * @max_retries: Number of retries in case of failure + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms (default: NVME_DEFAULT_IOCTL_TIMEOUT) + * @lsp: Log specific field (See enum nvmf_log_discovery_lsp) + */ +struct nvme_get_discovery_args { + nvme_ctrl_t c; + int args_size; + int max_retries; + __u32 *result; + __u32 timeout; + __u8 lsp; +}; + +/** + * nvmf_get_discovery_wargs() - Get the discovery log page with args + * @args: Argument structure + * + * This function is similar to nvmf_get_discovery_log(), but + * takes an extensible @args parameter. @args provides more + * options than nvmf_get_discovery_log(). + * + * This function performs a get discovery log page (DLP) command + * and returns the DLP. The memory allocated for the returned + * DLP must be freed by the caller using free(). + * + * Return: Pointer to the discovery log page (to be freed). NULL + * on failure and errno is set. + */ +struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args); + +/** + * nvmf_hostnqn_generate() - Generate a machine specific host nqn + * Returns: An nvm namespace qualified name string based on the machine + * identifier, or NULL if not successful. + */ +char *nvmf_hostnqn_generate(); + +/** + * nvmf_hostnqn_from_file() - Reads the host nvm qualified name from the config + * default location + * + * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme. + * $SYSCONFDIR is usually /etc. + * + * Return: The host nqn, or NULL if unsuccessful. If found, the caller + * is responsible to free the string. + */ +char *nvmf_hostnqn_from_file(); + +/** + * nvmf_hostid_from_file() - Reads the host identifier from the config default + * location + * + * Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/. + * $SYSCONFDIR is usually /etc. + * + * Return: The host identifier, or NULL if unsuccessful. If found, the caller + * is responsible to free the string. + */ +char *nvmf_hostid_from_file(); + +/** + * nvmf_connect_disc_entry() - Connect controller based on the discovery log page entry + * @h: Host to which the controller should be connected + * @e: Discovery log page entry + * @defcfg: Default configuration to be used for the new controller + * @discover: Set to 'true' if the new controller is a discovery controller + * + * Return: Pointer to the new controller + */ +nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, + struct nvmf_disc_log_entry *e, + const struct nvme_fabrics_config *defcfg, bool *discover); + +/** + * nvmf_is_registration_supported - check whether registration can be performed. + * @c: Controller instance + * + * Only discovery controllers (DC) that comply with TP8010 support + * explicit registration with the DIM PDU. These can be identified by + * looking at the value of a dctype in the Identify command + * response. A value of 1 (DDC) or 2 (CDC) indicates that the DC + * supports explicit registration. + * + * Return: true if controller supports explicit registration. false + * otherwise. + */ +bool nvmf_is_registration_supported(nvme_ctrl_t c); + +/** + * nvmf_register_ctrl() - Perform registration task with a DC + * @c: Controller instance + * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to + * perform a Registration, Deregistration, or Registration-update. + * @result: The command-specific result returned by the DC upon command + * completion. + * + * Perform registration task with a Discovery Controller (DC). Three + * tasks are supported: register, deregister, and registration update. + * + * Return: 0 on success; on failure -1 is returned and errno is set + */ +int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result); + +#endif /* _LIBNVME_FABRICS_H */ diff --git a/src/nvme/filters.c b/src/nvme/filters.c new file mode 100644 index 0000000..312b8f6 --- /dev/null +++ b/src/nvme/filters.c @@ -0,0 +1,163 @@ +// 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> +#include <libgen.h> + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + +#include "filters.h" +#include "types.h" +#include "util.h" +#include "cleanup.h" + +#define PATH_SYSFS_NVME "/sys/class/nvme" +#define PATH_SYSFS_NVME_SUBSYSTEM "/sys/class/nvme-subsystem" +#define PATH_SYSFS_BLOCK "/sys/block" + +char *nvme_ctrl_sysfs_dir(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_SYSFS_NVME); + + if (!asprintf(&str, "%s" PATH_SYSFS_NVME, basepath)) + return NULL; + + return str; +} + +char *nvme_ns_sysfs_dir(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_SYSFS_BLOCK); + + if (!asprintf(&str, "%s" PATH_SYSFS_BLOCK, basepath)) + return NULL; + + return str; +} + +char *nvme_subsys_sysfs_dir(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_SYSFS_NVME_SUBSYSTEM); + + if (!asprintf(&str, "%s" PATH_SYSFS_NVME_SUBSYSTEM, basepath)) + return NULL; + + return str; +} + +int nvme_namespace_filter(const struct dirent *d) +{ + int i, n; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) + if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2) + return 1; + + return 0; +} + +int nvme_paths_filter(const struct dirent *d) +{ + int i, c, n; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) + if (sscanf(d->d_name, "nvme%dc%dn%d", &i, &c, &n) == 3) + return 1; + + return 0; +} + +int nvme_ctrls_filter(const struct dirent *d) +{ + int i, c, n; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) { + if (sscanf(d->d_name, "nvme%dc%dn%d", &i, &c, &n) == 3) + return 0; + if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2) + return 0; + if (sscanf(d->d_name, "nvme%d", &i) == 1) + return 1; + } + + return 0; +} + +int nvme_subsys_filter(const struct dirent *d) +{ + int i; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme-subsys")) + if (sscanf(d->d_name, "nvme-subsys%d", &i) == 1) + return 1; + + return 0; +} + +int nvme_scan_subsystems(struct dirent ***subsys) +{ + _cleanup_free_ char *dir = nvme_subsys_sysfs_dir(); + + return scandir(dir, subsys, nvme_subsys_filter, alphasort); +} + +int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***ns) +{ + return scandir(nvme_subsystem_get_sysfs_dir(s), ns, + nvme_namespace_filter, alphasort); +} + +int nvme_scan_ctrls(struct dirent ***ctrls) +{ + _cleanup_free_ char *dir = nvme_ctrl_sysfs_dir(); + + return scandir(dir, ctrls, nvme_ctrls_filter, alphasort); +} + +int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***paths) +{ + return scandir(nvme_ctrl_get_sysfs_dir(c), paths, + nvme_paths_filter, alphasort); +} + +int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***ns) +{ + return scandir(nvme_ctrl_get_sysfs_dir(c), ns, + nvme_namespace_filter, alphasort); +} diff --git a/src/nvme/filters.h b/src/nvme/filters.h new file mode 100644 index 0000000..49bbeea --- /dev/null +++ b/src/nvme/filters.h @@ -0,0 +1,96 @@ +// 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> + */ + +#ifndef _LIBNVME_FILTERS_H +#define _LIBNVME_FILTERS_H + +#include <dirent.h> +#include "tree.h" + +/** + * DOC: filters.h + * + * libnvme directory filter + */ + +/** + * nvme_namespace_filter() - Filter for namespaces + * @d: dirent to check + * + * Return: 1 if @d matches, 0 otherwise + */ +int nvme_namespace_filter(const struct dirent *d); + +/** + * nvme_paths_filter() - Filter for paths + * @d: dirent to check + * + * Return: 1 if @d matches, 0 otherwise + */ +int nvme_paths_filter(const struct dirent *d); + +/** + * nvme_ctrls_filter() - Filter for controllers + * @d: dirent to check + * + * Return: 1 if @d matches, 0 otherwise + */ +int nvme_ctrls_filter(const struct dirent *d); + +/** + * nvme_subsys_filter() - Filter for subsystems + * @d: dirent to check + * + * Return: 1 if @d matches, 0 otherwise + */ +int nvme_subsys_filter(const struct dirent *d); + +/** + * nvme_scan_subsystems() - Scan for subsystems + * @subsys: Pointer to array of dirents + * + * Return: number of entries in @subsys + */ +int nvme_scan_subsystems(struct dirent ***subsys); + +/** + * nvme_scan_subsystem_namespaces() - Scan for namespaces in a subsystem + * @s: Subsystem to scan + * @ns: Pointer to array of dirents + * + * Return: number of entries in @ns + */ +int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***ns); + +/** + * nvme_scan_ctrls() - Scan for controllers + * @ctrls: Pointer to array of dirents + * + * Return: number of entries in @ctrls + */ +int nvme_scan_ctrls(struct dirent ***ctrls); + +/** + * nvme_scan_ctrl_namespace_paths() - Scan for namespace paths in a controller + * @c: Controller to scan + * @paths: Pointer to array of dirents + * + * Return: number of entries in @paths + */ +int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***paths); + +/** + * nvme_scan_ctrl_namespaces() - Scan for namespaces in a controller + * @c: Controller to scan + * @ns: Pointer to array of dirents + * + * Return: number of entries in @ns + */ +int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***ns); + +#endif /* _LIBNVME_FILTERS_H */ diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c new file mode 100644 index 0000000..9090b7e --- /dev/null +++ b/src/nvme/ioctl.c @@ -0,0 +1,2258 @@ +// 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 <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <ccan/build_assert/build_assert.h> +#include <ccan/endian/endian.h> + +#include "ioctl.h" +#include "util.h" + +static bool nvme_debug; + +static int nvme_verify_chr(int fd) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) + return errno; + + if (!S_ISCHR(nvme_stat.st_mode)) { + errno = ENOTBLK; + return -1; + } + return 0; +} + +int nvme_subsystem_reset(int fd) +{ + int ret; + + ret = nvme_verify_chr(fd); + if (ret) + return ret; + return ioctl(fd, NVME_IOCTL_SUBSYS_RESET); +} + +int nvme_ctrl_reset(int fd) +{ + int ret; + + ret = nvme_verify_chr(fd); + if (ret) + return ret; + return ioctl(fd, NVME_IOCTL_RESET); +} + +int nvme_ns_rescan(int fd) +{ + int ret; + + ret = nvme_verify_chr(fd); + if (ret) + return ret; + return ioctl(fd, NVME_IOCTL_RESCAN); +} + +int nvme_get_nsid(int fd, __u32 *nsid) +{ + errno = 0; + *nsid = ioctl(fd, NVME_IOCTL_ID); + return -1 * (errno != 0); +} + +static int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd64 *cmd, + __u64 *result) +{ + int err = ioctl(fd, ioctl_cmd, cmd); + + if (err >= 0 && result) + *result = cmd->result; + return err; +} + +static void nvme_show_command(struct nvme_passthru_cmd *cmd, int err, struct timeval start, + struct timeval end) +{ + printf("opcode : %02x\n", cmd->opcode); + printf("flags : %02x\n", cmd->flags); + printf("rsvd1 : %04x\n", cmd->rsvd1); + printf("nsid : %08x\n", cmd->nsid); + printf("cdw2 : %08x\n", cmd->cdw2); + printf("cdw3 : %08x\n", cmd->cdw3); + printf("data_len : %08x\n", cmd->data_len); + printf("metadata_len : %08x\n", cmd->metadata_len); + printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->addr); + printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->metadata); + printf("cdw10 : %08x\n", cmd->cdw10); + printf("cdw11 : %08x\n", cmd->cdw11); + printf("cdw12 : %08x\n", cmd->cdw12); + printf("cdw13 : %08x\n", cmd->cdw13); + printf("cdw14 : %08x\n", cmd->cdw14); + printf("cdw15 : %08x\n", cmd->cdw15); + printf("timeout_ms : %08x\n", cmd->timeout_ms); + printf("result : %08x\n", cmd->result); + printf("err : %d\n", err); + printf("latency : %lu us\n", + (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec)); +} + +void nvme_set_debug(bool debug) +{ + nvme_debug = debug; +} + +bool nvme_get_debug(void) +{ + return nvme_debug; +} + +static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd *cmd, __u32 *result) +{ + struct timeval start; + struct timeval end; + int err; + + if (nvme_get_debug()) + gettimeofday(&start, NULL); + + err = ioctl(fd, ioctl_cmd, cmd); + + if (nvme_get_debug()) { + gettimeofday(&end, NULL); + nvme_show_command(cmd, err, start, end); + } + + if (err >= 0 && result) + *result = cmd->result; + + return err; +} + +static int nvme_passthru64(int fd, unsigned long ioctl_cmd, __u8 opcode, + __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, + __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u64 *result) +{ + struct nvme_passthru_cmd64 cmd = { + .opcode = opcode, + .flags = flags, + .rsvd1 = rsvd, + .nsid = nsid, + .cdw2 = cdw2, + .cdw3 = cdw3, + .metadata = (__u64)(uintptr_t)metadata, + .addr = (__u64)(uintptr_t)data, + .metadata_len = metadata_len, + .data_len = data_len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .cdw15 = cdw15, + .timeout_ms = timeout_ms, + }; + + return nvme_submit_passthru64(fd, ioctl_cmd, &cmd, result); +} + +static int nvme_passthru(int fd, unsigned long ioctl_cmd, __u8 opcode, + __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, + __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, + void *data, __u32 metadata_len, void *metadata, + __u32 timeout_ms, __u32 *result) +{ + struct nvme_passthru_cmd cmd = { + .opcode = opcode, + .flags = flags, + .rsvd1 = rsvd, + .nsid = nsid, + .cdw2 = cdw2, + .cdw3 = cdw3, + .metadata = (__u64)(uintptr_t)metadata, + .addr = (__u64)(uintptr_t)data, + .metadata_len = metadata_len, + .data_len = data_len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .cdw15 = cdw15, + .timeout_ms = timeout_ms, + }; + + return nvme_submit_passthru(fd, ioctl_cmd, &cmd, result); +} + +int nvme_submit_admin_passthru64(int fd, struct nvme_passthru_cmd64 *cmd, + __u64 *result) +{ + return nvme_submit_passthru64(fd, NVME_IOCTL_ADMIN64_CMD, cmd, result); +} + +int nvme_admin_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, + __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, + __u32 cdw15, __u32 data_len, void *data, + __u32 metadata_len, void *metadata, __u32 timeout_ms, + __u64 *result) +{ + return nvme_passthru64(fd, NVME_IOCTL_ADMIN64_CMD, opcode, flags, rsvd, + nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, + cdw14, cdw15, data_len, data, metadata_len, + metadata, timeout_ms, result); +} + +int nvme_submit_admin_passthru(int fd, struct nvme_passthru_cmd *cmd, __u32 *result) +{ + return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, cmd, result); +} + +int nvme_admin_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, + __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, + __u32 cdw15, __u32 data_len, void *data, + __u32 metadata_len, void *metadata, __u32 timeout_ms, + __u32 *result) +{ + return nvme_passthru(fd, NVME_IOCTL_ADMIN_CMD, opcode, flags, rsvd, + nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, + cdw14, cdw15, data_len, data, metadata_len, + metadata, timeout_ms, result); +} + +enum nvme_cmd_dword_fields { + NVME_DEVICE_SELF_TEST_CDW10_STC_SHIFT = 0, + NVME_DEVICE_SELF_TEST_CDW10_STC_MASK = 0xf, + NVME_DIRECTIVE_CDW11_DOPER_SHIFT = 0, + NVME_DIRECTIVE_CDW11_DTYPE_SHIFT = 8, + NVME_DIRECTIVE_CDW11_DPSEC_SHIFT = 16, + NVME_DIRECTIVE_CDW11_DOPER_MASK = 0xff, + NVME_DIRECTIVE_CDW11_DTYPE_MASK = 0xff, + NVME_DIRECTIVE_CDW11_DPSEC_MASK = 0xffff, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_SHIFT = 0, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_SHIFT = 1, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_MASK = 0x1, + NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_MASK = 0x1, + NVME_FW_COMMIT_CDW10_FS_SHIFT = 0, + NVME_FW_COMMIT_CDW10_CA_SHIFT = 3, + NVME_FW_COMMIT_CDW10_BPID_SHIFT = 31, + NVME_FW_COMMIT_CDW10_FS_MASK = 0x7, + NVME_FW_COMMIT_CDW10_CA_MASK = 0x7, + NVME_FW_COMMIT_CDW10_BPID_MASK = 0x1, + NVME_GET_FEATURES_CDW10_SEL_SHIFT = 8, + NVME_GET_FEATURES_CDW10_SEL_MASK = 0x7, + NVME_SET_FEATURES_CDW10_SAVE_SHIFT = 31, + NVME_SET_FEATURES_CDW10_SAVE_MASK = 0x1, + NVME_FEATURES_CDW10_FID_SHIFT = 0, + NVME_FEATURES_CDW14_UUID_SHIFT = 0, + NVME_FEATURES_CDW10_FID_MASK = 0xff, + NVME_FEATURES_CDW14_UUID_MASK = 0x7f, + NVME_LOG_CDW10_LID_SHIFT = 0, + NVME_LOG_CDW10_LSP_SHIFT = 8, + NVME_LOG_CDW10_RAE_SHIFT = 15, + NVME_LOG_CDW10_NUMDL_SHIFT = 16, + NVME_LOG_CDW11_NUMDU_SHIFT = 0, + NVME_LOG_CDW11_LSI_SHIFT = 16, + NVME_LOG_CDW14_UUID_SHIFT = 0, + NVME_LOG_CDW14_CSI_SHIFT = 24, + NVME_LOG_CDW14_OT_SHIFT = 23, + NVME_LOG_CDW10_LID_MASK = 0xff, + NVME_LOG_CDW10_LSP_MASK = 0x7f, + NVME_LOG_CDW10_RAE_MASK = 0x1, + NVME_LOG_CDW10_NUMDL_MASK = 0xffff, + NVME_LOG_CDW11_NUMDU_MASK = 0xffff, + NVME_LOG_CDW11_LSI_MASK = 0xffff, + NVME_LOG_CDW14_UUID_MASK = 0x7f, + NVME_LOG_CDW14_CSI_MASK = 0xff, + NVME_LOG_CDW14_OT_MASK = 0x1, + NVME_IDENTIFY_CDW10_CNS_SHIFT = 0, + NVME_IDENTIFY_CDW10_CNTID_SHIFT = 16, + NVME_IDENTIFY_CDW11_CNSSPECID_SHIFT = 0, + NVME_IDENTIFY_CDW14_UUID_SHIFT = 0, + NVME_IDENTIFY_CDW11_CSI_SHIFT = 24, + NVME_IDENTIFY_CDW10_CNS_MASK = 0xff, + NVME_IDENTIFY_CDW10_CNTID_MASK = 0xffff, + NVME_IDENTIFY_CDW11_CNSSPECID_MASK = 0xffff, + NVME_IDENTIFY_CDW14_UUID_MASK = 0x7f, + NVME_IDENTIFY_CDW11_CSI_MASK = 0xff, + NVME_NAMESPACE_ATTACH_CDW10_SEL_SHIFT = 0, + NVME_NAMESPACE_ATTACH_CDW10_SEL_MASK = 0xf, + NVME_NAMESPACE_MGMT_CDW10_SEL_SHIFT = 0, + NVME_NAMESPACE_MGMT_CDW10_SEL_MASK = 0xf, + NVME_NAMESPACE_MGMT_CDW11_CSI_SHIFT = 24, + NVME_NAMESPACE_MGMT_CDW11_CSI_MASK = 0xff, + NVME_VIRT_MGMT_CDW10_ACT_SHIFT = 0, + NVME_VIRT_MGMT_CDW10_RT_SHIFT = 8, + NVME_VIRT_MGMT_CDW10_CNTLID_SHIFT = 16, + NVME_VIRT_MGMT_CDW11_NR_SHIFT = 0, + NVME_VIRT_MGMT_CDW10_ACT_MASK = 0xf, + NVME_VIRT_MGMT_CDW10_RT_MASK = 0x7, + NVME_VIRT_MGMT_CDW10_CNTLID_MASK = 0xffff, + NVME_VIRT_MGMT_CDW11_NR_MASK = 0xffff, + NVME_FORMAT_CDW10_LBAF_SHIFT = 0, + NVME_FORMAT_CDW10_MSET_SHIFT = 4, + NVME_FORMAT_CDW10_PI_SHIFT = 5, + NVME_FORMAT_CDW10_PIL_SHIFT = 8, + NVME_FORMAT_CDW10_SES_SHIFT = 9, + NVME_FORMAT_CDW10_LBAFU_SHIFT = 12, + NVME_FORMAT_CDW10_LBAF_MASK = 0xf, + NVME_FORMAT_CDW10_MSET_MASK = 0x1, + NVME_FORMAT_CDW10_PI_MASK = 0x7, + NVME_FORMAT_CDW10_PIL_MASK = 0x1, + NVME_FORMAT_CDW10_SES_MASK = 0x7, + NVME_FORMAT_CDW10_LBAFU_MASK = 0x3, + NVME_SANITIZE_CDW10_SANACT_SHIFT = 0, + NVME_SANITIZE_CDW10_AUSE_SHIFT = 3, + NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4, + NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8, + NVME_SANITIZE_CDW10_NODAS_SHIFT = 9, + NVME_SANITIZE_CDW10_SANACT_MASK = 0x7, + NVME_SANITIZE_CDW10_AUSE_MASK = 0x1, + NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf, + NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1, + NVME_SANITIZE_CDW10_NODAS_MASK = 0x1, + NVME_SECURITY_NSSF_SHIFT = 0, + NVME_SECURITY_SPSP0_SHIFT = 8, + NVME_SECURITY_SPSP1_SHIFT = 16, + NVME_SECURITY_SECP_SHIFT = 24, + NVME_SECURITY_NSSF_MASK = 0xff, + NVME_SECURITY_SPSP0_MASK = 0xff, + NVME_SECURITY_SPSP1_MASK = 0xff, + NVME_SECURITY_SECP_MASK = 0xffff, + NVME_GET_LBA_STATUS_CDW13_RL_SHIFT = 0, + NVME_GET_LBA_STATUS_CDW13_ATYPE_SHIFT = 24, + NVME_GET_LBA_STATUS_CDW13_RL_MASK = 0xffff, + NVME_GET_LBA_STATUS_CDW13_ATYPE_MASK = 0xff, + NVME_ZNS_MGMT_SEND_ZSASO_SHIFT = 9, + NVME_ZNS_MGMT_SEND_ZSASO_MASK = 0x1, + NVME_ZNS_MGMT_SEND_SEL_SHIFT = 8, + NVME_ZNS_MGMT_SEND_SEL_MASK = 0x1, + NVME_ZNS_MGMT_SEND_ZSA_SHIFT = 0, + NVME_ZNS_MGMT_SEND_ZSA_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRA_SHIFT = 0, + NVME_ZNS_MGMT_RECV_ZRA_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRASF_SHIFT = 8, + NVME_ZNS_MGMT_RECV_ZRASF_MASK = 0xff, + NVME_ZNS_MGMT_RECV_ZRAS_FEAT_SHIFT = 16, + NVME_ZNS_MGMT_RECV_ZRAS_FEAT_MASK = 0x1, + NVME_DIM_TAS_SHIFT = 0, + NVME_DIM_TAS_MASK = 0xF, +}; + +enum features { + NVME_FEATURES_ARBITRATION_BURST_SHIFT = 0, + NVME_FEATURES_ARBITRATION_LPW_SHIFT = 8, + NVME_FEATURES_ARBITRATION_MPW_SHIFT = 16, + NVME_FEATURES_ARBITRATION_HPW_SHIFT = 24, + NVME_FEATURES_ARBITRATION_BURST_MASK = 0x7, + NVME_FEATURES_ARBITRATION_LPW_MASK = 0xff, + NVME_FEATURES_ARBITRATION_MPW_MASK = 0xff, + NVME_FEATURES_ARBITRATION_HPW_MASK = 0xff, + NVME_FEATURES_PWRMGMT_PS_SHIFT = 0, + NVME_FEATURES_PWRMGMT_WH_SHIFT = 5, + NVME_FEATURES_PWRMGMT_PS_MASK = 0x1f, + NVME_FEATURES_PWRMGMT_WH_MASK = 0x7, + NVME_FEATURES_TMPTH_SHIFT = 0, + NVME_FEATURES_TMPSEL_SHIFT = 16, + NVME_FEATURES_THSEL_SHIFT = 20, + NVME_FEATURES_TMPTH_MASK = 0xff, + NVME_FEATURES_TMPSEL_MASK = 0xf, + NVME_FEATURES_THSEL_MASK = 0x3, + NVME_FEATURES_ERROR_RECOVERY_TLER_SHIFT = 0, + NVME_FEATURES_ERROR_RECOVERY_DULBE_SHIFT = 16, + NVME_FEATURES_ERROR_RECOVERY_TLER_MASK = 0xff, + NVME_FEATURES_ERROR_RECOVERY_DULBE_MASK = 0x1, + NVME_FEATURES_VWC_WCE_SHIFT = 0, + NVME_FEATURES_VWC_WCE_MASK = 0x1, + NVME_FEATURES_IRQC_THR_SHIFT = 0, + NVME_FEATURES_IRQC_TIME_SHIFT = 8, + NVME_FEATURES_IRQC_THR_MASK = 0xff, + NVME_FEATURES_IRQC_TIME_MASK = 0xff, + NVME_FEATURES_IVC_IV_SHIFT = 0, + NVME_FEATURES_IVC_CD_SHIFT = 16, + NVME_FEATURES_IVC_IV_MASK = 0xffff, + NVME_FEATURES_IVC_CD_MASK = 0x1, + NVME_FEATURES_WAN_DN_SHIFT = 0, + NVME_FEATURES_WAN_DN_MASK = 0x1, + NVME_FEATURES_APST_APSTE_SHIFT = 0, + NVME_FEATURES_APST_APSTE_MASK = 0x1, + NVME_FEATURES_HCTM_TMT2_SHIFT = 0, + NVME_FEATURES_HCTM_TMT1_SHIFT = 16, + NVME_FEATURES_HCTM_TMT2_MASK = 0xffff, + NVME_FEATURES_HCTM_TMT1_MASK = 0xffff, + NVME_FEATURES_NOPS_NOPPME_SHIFT = 0, + NVME_FEATURES_NOPS_NOPPME_MASK = 0x1, + NVME_FEATURES_PLM_PLE_SHIFT = 0, + NVME_FEATURES_PLM_PLE_MASK = 0x1, + NVME_FEATURES_PLM_WINDOW_SELECT_SHIFT = 0, + NVME_FEATURES_PLM_WINDOW_SELECT_MASK = 0xf, + NVME_FEATURES_LBAS_LSIRI_SHIFT = 0, + NVME_FEATURES_LBAS_LSIPI_SHIFT = 16, + NVME_FEATURES_LBAS_LSIRI_MASK = 0xffff, + NVME_FEATURES_LBAS_LSIPI_MASK = 0xffff, + NVME_FEATURES_IOCSP_IOCSCI_SHIFT = 0, + NVME_FEATURES_IOCSP_IOCSCI_MASK = 0xff, +}; + +int nvme_identify(struct nvme_identify_args *args) +{ + __u32 cdw10 = NVME_SET(args->cntid, IDENTIFY_CDW10_CNTID) | + NVME_SET(args->cns, IDENTIFY_CDW10_CNS); + __u32 cdw11 = NVME_SET(args->cns_specific_id, IDENTIFY_CDW11_CNSSPECID) | + NVME_SET(args->csi, IDENTIFY_CDW11_CSI); + __u32 cdw14 = NVME_SET(args->uuidx, IDENTIFY_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_identify, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->data, + .data_len = NVME_IDENTIFY_DATA_SIZE, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw14 = cdw14, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_get_log(struct nvme_get_log_args *args) +{ + __u32 numd = (args->len >> 2) - 1; + __u16 numdu = numd >> 16, numdl = numd & 0xffff; + + __u32 cdw10 = NVME_SET(args->lid, LOG_CDW10_LID) | + NVME_SET(args->lsp, LOG_CDW10_LSP) | + NVME_SET(!!args->rae, LOG_CDW10_RAE) | + NVME_SET(numdl, LOG_CDW10_NUMDL); + __u32 cdw11 = NVME_SET(numdu, LOG_CDW11_NUMDU) | + NVME_SET(args->lsi, LOG_CDW11_LSI); + __u32 cdw12 = args->lpo & 0xffffffff; + __u32 cdw13 = args->lpo >> 32; + __u32 cdw14 = NVME_SET(args->uuidx, LOG_CDW14_UUID) | + NVME_SET(!!args->ot, LOG_CDW14_OT) | + NVME_SET(args->csi, LOG_CDW14_CSI); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_log_page, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->log, + .data_len = args->len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(struct nvme_get_log_args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) +{ + __u64 offset = 0, xfer, data_len = args->len; + __u64 start = args->lpo; + bool retain = args->rae; + void *ptr = args->log; + int ret; + + args->fd = fd; + + /* + * 4k is the smallest possible transfer unit, so restricting to 4k + * avoids having to check the MDTS value of the controller. + */ + do { + xfer = data_len - offset; + if (xfer > xfer_len) + xfer = xfer_len; + + /* + * Always retain regardless of the RAE parameter until the very + * last portion of this log page so the data remains latched + * during the fetch sequence. + */ + args->lpo = start + offset; + args->len = xfer; + args->log = ptr; + args->rae = offset + xfer < data_len || retain; + ret = nvme_get_log(args); + if (ret) + return ret; + + offset += xfer; + ptr += xfer; + } while (offset < data_len); + + return 0; +} + +int nvme_set_features(struct nvme_set_features_args *args) +{ + __u32 cdw10 = NVME_SET(args->fid, FEATURES_CDW10_FID) | + NVME_SET(!!args->save, SET_FEATURES_CDW10_SAVE); + __u32 cdw14 = NVME_SET(args->uuidx, FEATURES_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_set_features, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .cdw10 = cdw10, + .cdw11 = args->cdw11, + .cdw12 = args->cdw12, + .cdw13 = args->cdw13, + .cdw14 = cdw14, + .cdw15 = args->cdw15, + .timeout_ms = args->timeout, + }; + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +static int __nvme_set_features(int fd, __u8 fid, __u32 cdw11, bool save, + __u32 *result) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = NVME_NSID_NONE, + .cdw11 = cdw11, + .cdw12 = 0, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + return nvme_set_features(&args); +} + +int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw, + __u8 hpw, bool save, __u32 *result) +{ + __u32 value = NVME_SET(ab, FEAT_ARBITRATION_BURST) | + NVME_SET(lpw, FEAT_ARBITRATION_LPW) | + NVME_SET(mpw, FEAT_ARBITRATION_MPW) | + NVME_SET(hpw, FEAT_ARBITRATION_HPW); + + return __nvme_set_features(fd, NVME_FEAT_FID_ARBITRATION, value, save, + result); +} + +int nvme_set_features_power_mgmt(int fd, __u8 ps, __u8 wh, bool save, + __u32 *result) +{ + __u32 value = NVME_SET(ps, FEAT_PWRMGMT_PS) | + NVME_SET(wh, FEAT_PWRMGMT_WH); + + return __nvme_set_features(fd, NVME_FEAT_FID_POWER_MGMT, value, save, + result); +} + +int nvme_set_features_lba_range(int fd, __u32 nsid, __u8 nr_ranges, bool save, + struct nvme_lba_range_type *data, __u32 *result) +{ + return nvme_set_features_data( + fd, NVME_FEAT_FID_LBA_RANGE, nsid, nr_ranges - 1, save, + sizeof(*data), data, result); +} + +int nvme_set_features_temp_thresh(int fd, __u16 tmpth, __u8 tmpsel, + enum nvme_feat_tmpthresh_thsel thsel, + bool save, __u32 *result) +{ + __u32 value = NVME_SET(tmpth, FEAT_TT_TMPTH) | + NVME_SET(tmpsel, FEAT_TT_TMPSEL) | + NVME_SET(thsel, FEAT_TT_THSEL); + + return __nvme_set_features(fd, NVME_FEAT_FID_TEMP_THRESH, value, save, + result); +} + +int nvme_set_features_err_recovery(int fd, __u32 nsid, __u16 tler, bool dulbe, + bool save, __u32 *result) +{ + __u32 value = NVME_SET(tler, FEAT_ERROR_RECOVERY_TLER) | + NVME_SET(!!dulbe, FEAT_ERROR_RECOVERY_DULBE); + + return nvme_set_features_simple( + fd, NVME_FEAT_FID_ERR_RECOVERY, nsid, value, save, result); +} + +int nvme_set_features_volatile_wc(int fd, bool wce, bool save, __u32 *result) +{ + __u32 value = NVME_SET(!!wce, FEAT_VWC_WCE); + + return __nvme_set_features(fd, NVME_FEAT_FID_VOLATILE_WC, value, save, + result); +} + +int nvme_set_features_irq_coalesce(int fd, __u8 thr, __u8 time, bool save, + __u32 *result) +{ + __u32 value = NVME_SET(thr, FEAT_IRQC_THR) | + NVME_SET(time, FEAT_IRQC_TIME); + + return __nvme_set_features(fd, NVME_FEAT_FID_IRQ_COALESCE, value, save, + result); +} + +int nvme_set_features_irq_config(int fd, __u16 iv, bool cd, bool save, + __u32 *result) +{ + __u32 value = NVME_SET(iv, FEAT_ICFG_IV) | + NVME_SET(!!cd, FEAT_ICFG_CD); + + return __nvme_set_features(fd, NVME_FEAT_FID_IRQ_CONFIG, value, save, + result); +} + +int nvme_set_features_write_atomic(int fd, bool dn, bool save, __u32 *result) +{ + __u32 value = NVME_SET(!!dn, FEAT_WA_DN); + + return __nvme_set_features(fd, NVME_FEAT_FID_WRITE_ATOMIC, value, save, + result); +} + +int nvme_set_features_async_event(int fd, __u32 events, + bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_ASYNC_EVENT, events, save, + result); +} + +int nvme_set_features_auto_pst(int fd, bool apste, bool save, + struct nvme_feat_auto_pst *apst, __u32 *result) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_AUTO_PST, + .nsid = NVME_NSID_NONE, + .cdw11 = NVME_SET(!!apste, FEAT_APST_APSTE), + .save = save, + .uuidx = NVME_UUID_NONE, + .data = apst, + .data_len = sizeof(*apst), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp) +{ + __le64 t = cpu_to_le64(timestamp); + struct nvme_timestamp ts = {}; + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_TIMESTAMP, + .nsid = NVME_NSID_NONE, + .cdw11 = 0, + .cdw12 = 0, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = sizeof(ts), + .data = &ts, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + memcpy(ts.timestamp, &t, sizeof(ts.timestamp)); + return nvme_set_features(&args); +} + +int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, + bool save, __u32 *result) +{ + __u32 value = NVME_SET(tmt2, FEAT_HCTM_TMT2) | + NVME_SET(tmt1, FEAT_HCTM_TMT1); + + return __nvme_set_features(fd, NVME_FEAT_FID_HCTM, value, save, + result); +} + +int nvme_set_features_nopsc(int fd, bool noppme, bool save, __u32 *result) +{ + __u32 value = NVME_SET(noppme, FEAT_NOPS_NOPPME); + + return __nvme_set_features(fd, NVME_FEAT_FID_NOPSC, value, save, + result); +} + +int nvme_set_features_rrl(int fd, __u8 rrl, __u16 nvmsetid, + bool save, __u32 *result) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_RRL, + .nsid = NVME_NSID_NONE, + .cdw11 = nvmsetid, + .cdw12 = rrl, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_plm_config(int fd, bool plm, __u16 nvmsetid, bool save, + struct nvme_plm_config *data, __u32 *result) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_PLM_CONFIG, + .nsid = NVME_NSID_NONE, + .cdw11 = nvmsetid, + .cdw12 = !!plm, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = sizeof(*data), + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_plm_window(int fd, enum nvme_feat_plm_window_select sel, + __u16 nvmsetid, bool save, __u32 *result) +{ + __u32 cdw12 = NVME_SET(sel, FEAT_PLMW_WS); + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_PLM_WINDOW, + .nsid = NVME_NSID_NONE, + .cdw11 = nvmsetid, + .cdw12 = cdw12, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi, + bool save, __u32 *result) +{ + __u32 value = NVME_SET(lsiri, FEAT_LBAS_LSIRI) | + NVME_SET(lsipi, FEAT_LBAS_LSIPI); + + return __nvme_set_features(fd, NVME_FEAT_FID_LBA_STS_INTERVAL, value, + save, result); +} + +int nvme_set_features_host_behavior(int fd, bool save, + struct nvme_feat_host_behavior *data) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_HOST_BEHAVIOR, + .nsid = NVME_NSID_NONE, + .cdw11 = 0, + .cdw12 = 0, + .save = false, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = sizeof(*data), + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_SANITIZE, !!nodrm, save, + result); +} + +int nvme_set_features_endurance_evt_cfg(int fd, __u16 endgid, __u8 egwarn, + bool save, __u32 *result) +{ + __u32 value = endgid | egwarn << 16; + + return __nvme_set_features(fd, NVME_FEAT_FID_ENDURANCE_EVT_CFG, value, + save, result); +} + +int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save, + __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_SW_PROGRESS, pbslc, save, + result); +} + +int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid) +{ + __u32 len = exhid ? 16 : 8; + __u32 value = !!exhid; + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_HOST_ID, + .nsid = NVME_NSID_NONE, + .cdw11 = value, + .cdw12 = 0, + .save = save, + .uuidx = NVME_UUID_NONE, + .cdw15 = 0, + .data_len = len, + .data = hostid, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + return nvme_set_features(&args); +} + +int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_RESV_MASK, mask, save, + result); +} + +int nvme_set_features_resv_mask2(int fd, __u32 nsid, __u32 mask, bool save, + __u32 *result) +{ + return nvme_set_features_simple( + fd, NVME_FEAT_FID_RESV_MASK, nsid, mask, save, result); +} + +int nvme_set_features_resv_persist(int fd, bool ptpl, bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_RESV_PERSIST, !!ptpl, save, + result); +} + +int nvme_set_features_resv_persist2(int fd, __u32 nsid, bool ptpl, bool save, + __u32 *result) +{ + return nvme_set_features_simple( + fd, NVME_FEAT_FID_RESV_PERSIST, nsid, !!ptpl, save, result); +} + +int nvme_set_features_write_protect(int fd, enum nvme_feat_nswpcfg_state state, + bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_FID_WRITE_PROTECT, state, + false, result); +} + +int nvme_set_features_write_protect2(int fd, __u32 nsid, + enum nvme_feat_nswpcfg_state state, + bool save, __u32 *result) +{ + return nvme_set_features_simple( + fd, NVME_FEAT_FID_WRITE_PROTECT, nsid, state, false, result); +} + +int nvme_set_features_iocs_profile(int fd, __u16 iocsi, bool save) +{ + __u32 value = NVME_SET(iocsi, FEAT_IOCSP_IOCSCI); + + return __nvme_set_features(fd, NVME_FEAT_FID_IOCS_PROFILE, value, + save, NULL); +} + +int nvme_get_features(struct nvme_get_features_args *args) +{ + __u32 cdw10 = NVME_SET(args->fid, FEATURES_CDW10_FID) | + NVME_SET(args->sel, GET_FEATURES_CDW10_SEL); + __u32 cdw14 = NVME_SET(args->uuidx, FEATURES_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_features, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .cdw10 = cdw10, + .cdw11 = args->cdw11, + .cdw14 = cdw14, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +static int __nvme_get_features(int fd, enum nvme_features_id fid, + enum nvme_get_features_sel sel, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_arbitration(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_ARBITRATION, sel, result); +} + +int nvme_get_features_power_mgmt(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_POWER_MGMT, sel, result); +} + +int nvme_get_features_lba_range(int fd, enum nvme_get_features_sel sel, + struct nvme_lba_range_type *data, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_LBA_RANGE, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = sizeof(*data), + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_lba_range2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, struct nvme_lba_range_type *data, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_LBA_RANGE, + .nsid = nsid, + .sel = sel, + .uuidx = NVME_UUID_NONE, + .data = data, + .data_len = sizeof(*data), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_temp_thresh(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_TEMP_THRESH, sel, result); +} + +int nvme_get_features_err_recovery(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_ERR_RECOVERY, sel, + result); +} + +int nvme_get_features_err_recovery2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result) +{ + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_ERR_RECOVERY, + .nsid = nsid, + .sel = sel, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_volatile_wc(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_VOLATILE_WC, sel, result); +} + +int nvme_get_features_num_queues(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_NUM_QUEUES, sel, result); +} + +int nvme_get_features_irq_coalesce(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_IRQ_COALESCE, sel, + result); +} + +int nvme_get_features_irq_config(int fd, enum nvme_get_features_sel sel, + __u16 iv, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_IRQ_CONFIG, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = iv, + .uuidx = NVME_UUID_NONE, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_write_atomic(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_WRITE_ATOMIC, sel, + result); +} + +int nvme_get_features_async_event(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_ASYNC_EVENT, sel, result); +} + +int nvme_get_features_auto_pst(int fd, enum nvme_get_features_sel sel, + struct nvme_feat_auto_pst *apst, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_AUTO_PST, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = sizeof(*apst), + .data = apst, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_host_mem_buf(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_HOST_MEM_BUF, sel, result); +} + +int nvme_get_features_host_mem_buf2(int fd, enum nvme_get_features_sel sel, + struct nvme_host_mem_buf_attrs *attrs, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_HOST_MEM_BUF, + .nsid = NVME_NSID_NONE, + .sel = sel, + .uuidx = NVME_UUID_NONE, + .data = attrs, + .data_len = sizeof(*attrs), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_timestamp(int fd, enum nvme_get_features_sel sel, + struct nvme_timestamp *ts) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_TIMESTAMP, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = sizeof(*ts), + .data = ts, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_kato(int fd, enum nvme_get_features_sel sel, __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_KATO, sel, result); +} + +int nvme_get_features_hctm(int fd, enum nvme_get_features_sel sel, __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_HCTM, sel, result); +} + +int nvme_get_features_nopsc(int fd, enum nvme_get_features_sel sel, __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_NOPSC, sel, result); +} + +int nvme_get_features_rrl(int fd, enum nvme_get_features_sel sel, __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_RRL, sel, result); +} + +int nvme_get_features_plm_config(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, struct nvme_plm_config *data, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_PLM_CONFIG, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = nvmsetid, + .uuidx = NVME_UUID_NONE, + .data_len = sizeof(*data), + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_PLM_WINDOW, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = nvmsetid, + .uuidx = NVME_UUID_NONE, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_lba_sts_interval(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_LBA_STS_INTERVAL, sel, + result); +} + +int nvme_get_features_host_behavior(int fd, enum nvme_get_features_sel sel, + struct nvme_feat_host_behavior *data, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_HOST_BEHAVIOR, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = sizeof(*data), + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_sanitize(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_SANITIZE, sel, result); +} + +int nvme_get_features_endurance_event_cfg(int fd, enum nvme_get_features_sel sel, + __u16 endgid, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_ENDURANCE_EVT_CFG, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = endgid, + .uuidx = NVME_UUID_NONE, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_sw_progress(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_SW_PROGRESS, sel, result); +} + +int nvme_get_features_host_id(int fd, enum nvme_get_features_sel sel, + bool exhid, __u32 len, __u8 *hostid) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_HOST_ID, + .nsid = NVME_NSID_NONE, + .sel = sel, + .cdw11 = !!exhid, + .uuidx = NVME_UUID_NONE, + .data_len = len, + .data = hostid, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_resv_mask(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_RESV_MASK, sel, result); +} + +int nvme_get_features_resv_mask2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_RESV_MASK, + .nsid = nsid, + .sel = sel, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_resv_persist(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_RESV_PERSIST, sel, result); +} + +int nvme_get_features_resv_persist2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_RESV_PERSIST, + .nsid = nsid, + .sel = sel, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_write_protect(int fd, __u32 nsid, + enum nvme_get_features_sel sel, + __u32 *result) +{ + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = NVME_FEAT_FID_WRITE_PROTECT, + .nsid = nsid, + .sel = sel, + .cdw11 = 0, + .uuidx = NVME_UUID_NONE, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + + return nvme_get_features(&args); +} + +int nvme_get_features_iocs_profile(int fd, enum nvme_get_features_sel sel, + __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_FID_IOCS_PROFILE, sel, result); +} + +int nvme_format_nvm(struct nvme_format_nvm_args *args) +{ + const size_t size_v1 = sizeof_args(struct nvme_format_nvm_args, lbaf, __u64); + const size_t size_v2 = sizeof_args(struct nvme_format_nvm_args, lbafu, __u64); + __u32 cdw10; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + cdw10 = NVME_SET(args->lbaf, FORMAT_CDW10_LBAF) | + NVME_SET(args->mset, FORMAT_CDW10_MSET) | + NVME_SET(args->pi, FORMAT_CDW10_PI) | + NVME_SET(args->pil, FORMAT_CDW10_PIL) | + NVME_SET(args->ses, FORMAT_CDW10_SES); + + if (args->args_size == size_v2) { + /* set lbafu extension */ + cdw10 |= NVME_SET(args->lbafu, FORMAT_CDW10_LBAFU); + } + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_format_nvm, + .nsid = args->nsid, + .cdw10 = cdw10, + .timeout_ms = args->timeout, + }; + + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args) +{ + const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64); + const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64); + __u32 cdw10 = NVME_SET(args->sel, NAMESPACE_MGMT_CDW10_SEL); + __u32 cdw11 = NVME_SET(args->csi, NAMESPACE_MGMT_CDW11_CSI); + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + struct nvme_passthru_cmd cmd = { + .nsid = args->nsid, + .opcode = nvme_admin_ns_mgmt, + .cdw10 = cdw10, + .cdw11 = cdw11, + .timeout_ms = args->timeout, + }; + + if (args->args_size == size_v2) { + if (args->data) { + cmd.data_len = sizeof(*args->data); + cmd.addr = (__u64)(uintptr_t)args->data; + } + } + else { + if (args->ns) { + cmd.data_len = sizeof(*args->ns); + cmd.addr = (__u64)(uintptr_t)args->ns; + } + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_ns_attach(struct nvme_ns_attach_args *args) +{ + __u32 cdw10 = NVME_SET(args->sel, NAMESPACE_ATTACH_CDW10_SEL); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_ns_attach, + .nsid = args->nsid, + .cdw10 = cdw10, + .data_len = sizeof(*args->ctrlist), + .addr = (__u64)(uintptr_t)args->ctrlist, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_fw_download(struct nvme_fw_download_args *args) +{ + __u32 cdw10 = (args->data_len >> 2) - 1; + __u32 cdw11 = args->offset >> 2; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_download, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_fw_commit(struct nvme_fw_commit_args *args) +{ + __u32 cdw10 = NVME_SET(args->slot, FW_COMMIT_CDW10_FS) | + NVME_SET(args->action, FW_COMMIT_CDW10_CA) | + NVME_SET(args->bpid, FW_COMMIT_CDW10_BPID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_commit, + .cdw10 = cdw10, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_security_send(struct nvme_security_send_args *args) +{ + __u32 cdw10 = NVME_SET(args->secp, SECURITY_SECP) | + NVME_SET(args->spsp0, SECURITY_SPSP0) | + NVME_SET(args->spsp1, SECURITY_SPSP1) | + NVME_SET(args->nssf, SECURITY_NSSF); + __u32 cdw11 = args->tl; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_security_send, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_security_receive(struct nvme_security_receive_args *args) +{ + __u32 cdw10 = NVME_SET(args->secp, SECURITY_SECP) | + NVME_SET(args->spsp0, SECURITY_SPSP0) | + NVME_SET(args->spsp1, SECURITY_SPSP1) | + NVME_SET(args->nssf, SECURITY_NSSF); + __u32 cdw11 = args->al; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_security_recv, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_get_lba_status(struct nvme_get_lba_status_args *args) +{ + __u32 cdw10 = args->slba & 0xffffffff; + __u32 cdw11 = args->slba >> 32; + __u32 cdw12 = args->mndw; + __u32 cdw13 = NVME_SET(args->rl, GET_LBA_STATUS_CDW13_RL) | + NVME_SET(args->atype, GET_LBA_STATUS_CDW13_ATYPE); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_lba_status, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->lbas, + .data_len = (args->mndw + 1) << 2, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_directive_send(struct nvme_directive_send_args *args) +{ + __u32 cdw10 = args->data_len ? (args->data_len >> 2) - 1 : 0; + __u32 cdw11 = NVME_SET(args->doper, DIRECTIVE_CDW11_DOPER) | + NVME_SET(args->dtype, DIRECTIVE_CDW11_DTYPE) | + NVME_SET(args->dspec, DIRECTIVE_CDW11_DPSEC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_directive_send, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = args->cdw12, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_directive_send_id_endir(int fd, __u32 nsid, bool endir, + enum nvme_directive_dtype dtype, + struct nvme_id_directives *id) +{ + __u32 cdw12 = NVME_SET(dtype, DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE) | + NVME_SET(endir, DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR); + struct nvme_directive_send_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .dspec = 0, + .dtype = NVME_DIRECTIVE_DTYPE_IDENTIFY, + .doper = NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR, + .cdw12 = cdw12, + .data_len = sizeof(*id), + .data = id, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + return nvme_directive_send(&args); +} + +int nvme_directive_recv(struct nvme_directive_recv_args *args) +{ + __u32 cdw10 = args->data_len ? (args->data_len >> 2) - 1 : 0; + __u32 cdw11 = NVME_SET(args->doper, DIRECTIVE_CDW11_DOPER) | + NVME_SET(args->dtype, DIRECTIVE_CDW11_DTYPE) | + NVME_SET(args->dspec, DIRECTIVE_CDW11_DPSEC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_directive_recv, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = args->cdw12, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_capacity_mgmt(struct nvme_capacity_mgmt_args *args) +{ + __u32 cdw10 = args->op | args->element_id << 16; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_capacity_mgmt, + .cdw10 = cdw10, + .cdw11 = args->cdw11, + .cdw12 = args->cdw12, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_lockdown(struct nvme_lockdown_args *args) +{ + __u32 cdw10 = args->ofi << 8 | + (args->ifc & 0x3) << 5 | + (args->prhbt & 0x1) << 4 | + (args->scp & 0xF); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_lockdown, + .cdw10 = cdw10, + .cdw14 = args->uuidx & 0x3F, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_set_property(struct nvme_set_property_args *args) +{ + __u32 cdw10 = nvme_is_64bit_reg(args->offset); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_set, + .cdw10 = cdw10, + .cdw11 = args->offset, + .cdw12 = args->value & 0xffffffff, + .cdw13 = args->value >> 32, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_get_property(struct nvme_get_property_args *args) +{ + __u32 cdw10 = nvme_is_64bit_reg(args->offset); + + struct nvme_passthru_cmd64 cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_get, + .cdw10 = cdw10, + .cdw11 = args->offset, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru64(args->fd, &cmd, args->value); +} + +int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args) +{ + __u32 cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) | + NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) | + NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) | + NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) | + NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS); + __u32 cdw11 = args->ovrpat; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_sanitize_nvm, + .cdw10 = cdw10, + .cdw11 = cdw11, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_dev_self_test(struct nvme_dev_self_test_args *args) +{ + __u32 cdw10 = NVME_SET(args->stc, DEVICE_SELF_TEST_CDW10_STC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_dev_self_test, + .nsid = args->nsid, + .cdw10 = cdw10, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_virtual_mgmt(struct nvme_virtual_mgmt_args *args) +{ + __u32 cdw10 = NVME_SET(args->act, VIRT_MGMT_CDW10_ACT) | + NVME_SET(args->rt, VIRT_MGMT_CDW10_RT) | + NVME_SET(args->cntlid, VIRT_MGMT_CDW10_CNTLID); + __u32 cdw11 = NVME_SET(args->nr, VIRT_MGMT_CDW11_NR); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_virtual_mgmt, + .cdw10 = cdw10, + .cdw11 = cdw11, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} + +int nvme_submit_io_passthru64(int fd, struct nvme_passthru_cmd64 *cmd, + __u64 *result) +{ + return nvme_submit_passthru64(fd, NVME_IOCTL_IO64_CMD, cmd, result); +} + +int nvme_io_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, + __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, + __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u64 *result) +{ + return nvme_passthru64(fd, NVME_IOCTL_IO64_CMD, opcode, flags, rsvd, + nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, + cdw14, cdw15, data_len, data, metadata_len, metadata, + timeout_ms, result); +} + +int nvme_submit_io_passthru(int fd, struct nvme_passthru_cmd *cmd, __u32 *result) +{ + return nvme_submit_passthru(fd, NVME_IOCTL_IO_CMD, cmd, result); +} + +int nvme_io_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, + __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, + __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result) +{ + return nvme_passthru(fd, NVME_IOCTL_IO_CMD, opcode, flags, rsvd, nsid, + cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, + cdw15, data_len, data, metadata_len, metadata, + timeout_ms, result); +} + +static int nvme_set_var_size_tags(__u32 *cmd_dw2, __u32 *cmd_dw3, __u32 *cmd_dw14, + __u8 pif, __u8 sts, __u64 reftag, __u64 storage_tag) +{ + __u32 cdw2 = 0, cdw3 = 0, cdw14; + beint64_t be_reftag = cpu_to_be64(reftag); + beint64_t be_storage_tag = cpu_to_be64(storage_tag); + + switch (pif) { + /* 16b Protection Information */ + case 0: + cdw14 = be_reftag & 0xffffffff; + cdw14 |= ((be_storage_tag << (32 - sts)) & 0xffffffff); + break; + /* 32b Protection Information */ + case 1: + cdw14 = be_reftag & 0xffffffff; + cdw3 = be_reftag >> 32; + cdw14 |= ((be_storage_tag << (80 - sts)) & 0xffff0000); + if (sts >= 48) + cdw3 |= ((be_storage_tag >> (sts - 48)) & 0xffffffff); + else + cdw3 |= ((be_storage_tag << (48 - sts)) & 0xffffffff); + cdw2 = (be_storage_tag >> (sts - 16)) & 0xffff; + break; + /* 64b Protection Information */ + case 2: + cdw14 = be_reftag & 0xffffffff; + cdw3 = (be_reftag >> 32) & 0xffff; + cdw14 |= ((be_storage_tag << (48 - sts)) & 0xffffffff); + if (sts >= 16) + cdw3 |= ((be_storage_tag >> (sts - 16)) & 0xffff); + else + cdw3 |= ((be_storage_tag << (16 - sts)) & 0xffff); + break; + default: + perror("Unsupported Protection Information Format"); + errno = EINVAL; + return -1; + } + + *cmd_dw2 = cdw2; + *cmd_dw3 = cdw3; + *cmd_dw14 = cdw14; + return 0; +} + +int nvme_io(struct nvme_io_args *args, __u8 opcode) +{ + const size_t size_v1 = sizeof_args(struct nvme_io_args, dsm, __u64); + const size_t size_v2 = sizeof_args(struct nvme_io_args, pif, __u64); + __u32 cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, cdw15; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + cdw10 = args->slba & 0xffffffff; + cdw11 = args->slba >> 32; + cdw12 = args->nlb | (args->control << 16); + cdw13 = args->dsm | (args->dspec << 16); + cdw15 = args->apptag | (args->appmask << 16); + + if (args->args_size == size_v1) { + cdw2 = (args->storage_tag >> 32) & 0xffff; + cdw3 = args->storage_tag & 0xffffffff; + cdw14 = args->reftag; + } else { + if (nvme_set_var_size_tags(&cdw2, &cdw3, &cdw14, + args->pif, + args->sts, + args->reftag_u64, + args->storage_tag)) { + errno = EINVAL; + return -1; + } + } + + struct nvme_passthru_cmd cmd = { + .opcode = opcode, + .nsid = args->nsid, + .cdw2 = cdw2, + .cdw3 = cdw3, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .cdw15 = cdw15, + .data_len = args->data_len, + .metadata_len = args->metadata_len, + .addr = (__u64)(uintptr_t)args->data, + .metadata = (__u64)(uintptr_t)args->metadata, + .timeout_ms = args->timeout, + }; + + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_dsm(struct nvme_dsm_args *args) +{ + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_dsm, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->dsm, + .data_len = args->nr_ranges * sizeof(*args->dsm), + .cdw10 = args->nr_ranges - 1, + .cdw11 = args->attrs, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_copy(struct nvme_copy_args *args) +{ + const size_t size_v1 = sizeof_args(struct nvme_copy_args, format, __u64); + const size_t size_v2 = sizeof_args(struct nvme_copy_args, ilbrt_u64, __u64); + __u32 cdw3, cdw12, cdw14, data_len; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + cdw12 = ((args->nr - 1) & 0xff) | ((args->format & 0xf) << 8) | + ((args->prinfor & 0xf) << 12) | ((args->dtype & 0xf) << 20) | + ((args->prinfow & 0xf) << 26) | ((args->fua & 0x1) << 30) | + ((args->lr & 0x1) << 31); + + if (args->args_size == size_v1) { + cdw3 = 0; + cdw14 = args->ilbrt; + } else { + cdw3 = (args->ilbrt_u64 >> 32) & 0xffffffff; + cdw14 = args->ilbrt_u64 & 0xffffffff; + } + + if (args->format == 1) + data_len = args->nr * sizeof(struct nvme_copy_range_f1); + else if (args->format == 2) + data_len = args->nr * sizeof(struct nvme_copy_range_f2); + else if (args->format == 3) + data_len = args->nr * sizeof(struct nvme_copy_range_f3); + else + data_len = args->nr * sizeof(struct nvme_copy_range); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_copy, + .nsid = args->nsid, + .addr = (__u64)(uintptr_t)args->copy, + .data_len = data_len, + .cdw3 = cdw3, + .cdw10 = args->sdlba & 0xffffffff, + .cdw11 = args->sdlba >> 32, + .cdw12 = cdw12, + .cdw13 = (args->dspec & 0xffff) << 16, + .cdw14 = cdw14, + .cdw15 = (args->lbatm << 16) | args->lbat, + .timeout_ms = args->timeout, + }; + + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_resv_acquire(struct nvme_resv_acquire_args *args) +{ + __le64 payload[2] = { + cpu_to_le64(args->crkey), + cpu_to_le64(args->nrkey) + }; + __u32 cdw10 = (args->racqa & 0x7) | + (args->iekey ? 1 << 3 : 0) | + (args->rtype << 8); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_acquire, + .nsid = args->nsid, + .cdw10 = cdw10, + .data_len = sizeof(payload), + .addr = (__u64)(uintptr_t)(payload), + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_resv_register(struct nvme_resv_register_args *args) +{ + __le64 payload[2] = { + cpu_to_le64(args->crkey), + cpu_to_le64(args->nrkey) + }; + __u32 cdw10 = (args->rrega & 0x7) | + (args->iekey ? 1 << 3 : 0) | + (args->cptpl << 30); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_register, + .nsid = args->nsid, + .cdw10 = cdw10, + .data_len = sizeof(payload), + .addr = (__u64)(uintptr_t)(payload), + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_resv_release(struct nvme_resv_release_args *args) +{ + __le64 payload[1] = { cpu_to_le64(args->crkey) }; + __u32 cdw10 = (args->rrela & 0x7) | + (args->iekey ? 1 << 3 : 0) | + (args->rtype << 8); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_release, + .nsid = args->nsid, + .cdw10 = cdw10, + .addr = (__u64)(uintptr_t)(payload), + .data_len = sizeof(payload), + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_resv_report(struct nvme_resv_report_args *args) +{ + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_report, + .nsid = args->nsid, + .cdw10 = (args->len >> 2) - 1, + .cdw11 = args->eds ? 1 : 0, + .addr = (__u64)(uintptr_t)args->report, + .data_len = args->len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args) +{ + __u32 cdw10 = args->mo | (args->mos << 16); + __u32 cdw11 = (args->data_len >> 2) - 1; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_io_mgmt_recv, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + return nvme_submit_io_passthru(args->fd, &cmd, NULL); +} + +int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args) +{ + __u32 cdw10 = args->mo | (args->mos << 16); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_io_mgmt_send, + .nsid = args->nsid, + .cdw10 = cdw10, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + return nvme_submit_io_passthru(args->fd, &cmd, NULL); +} + +int nvme_zns_mgmt_send(struct nvme_zns_mgmt_send_args *args) +{ + __u32 cdw10 = args->slba & 0xffffffff; + __u32 cdw11 = args->slba >> 32; + __u32 cdw13 = NVME_SET(args->zsaso, ZNS_MGMT_SEND_ZSASO) | + NVME_SET(!!args->select_all, ZNS_MGMT_SEND_SEL) | + NVME_SET(args->zsa, ZNS_MGMT_SEND_ZSA); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_send, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_zns_mgmt_recv(struct nvme_zns_mgmt_recv_args *args) +{ + __u32 cdw10 = args->slba & 0xffffffff; + __u32 cdw11 = args->slba >> 32; + __u32 cdw12 = (args->data_len >> 2) - 1; + __u32 cdw13 = NVME_SET(args->zra, ZNS_MGMT_RECV_ZRA) | + NVME_SET(args->zrasf, ZNS_MGMT_RECV_ZRASF) | + NVME_SET(args->zras_feat, ZNS_MGMT_RECV_ZRAS_FEAT); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_recv, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + return nvme_submit_io_passthru(args->fd, &cmd, args->result); +} + +int nvme_zns_append(struct nvme_zns_append_args *args) +{ + const size_t size_v1 = sizeof_args(struct nvme_zns_append_args, lbatm, __u64); + const size_t size_v2 = sizeof_args(struct nvme_zns_append_args, ilbrt_u64, __u64); + __u32 cdw3, cdw10, cdw11, cdw12, cdw14, cdw15; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + cdw10 = args->zslba & 0xffffffff; + cdw11 = args->zslba >> 32; + cdw12 = args->nlb | (args->control << 16); + cdw15 = args->lbat | (args->lbatm << 16); + + if (args->args_size == size_v1) { + cdw3 = 0; + cdw14 = args->ilbrt; + } else { + cdw3 = (args->ilbrt_u64 >> 32) & 0xffffffff; + cdw14 = args->ilbrt_u64 & 0xffffffff; + } + + struct nvme_passthru_cmd64 cmd = { + .opcode = nvme_zns_cmd_append, + .nsid = args->nsid, + .cdw3 = cdw3, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw14 = cdw14, + .cdw15 = cdw15, + .data_len = args->data_len, + .addr = (__u64)(uintptr_t)args->data, + .metadata_len = args->metadata_len, + .metadata = (__u64)(uintptr_t)args->metadata, + .timeout_ms = args->timeout, + }; + + return nvme_submit_io_passthru64(args->fd, &cmd, args->result); +} + +int nvme_dim_send(struct nvme_dim_args *args) +{ + __u32 cdw10 = NVME_SET(args->tas, DIM_TAS); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_discovery_info_mgmt, + .cdw10 = cdw10, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + return nvme_submit_admin_passthru(args->fd, &cmd, args->result); +} diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h new file mode 100644 index 0000000..4a0698f --- /dev/null +++ b/src/nvme/ioctl.h @@ -0,0 +1,4062 @@ +// 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> + */ + +#ifndef _LIBNVME_IOCTL_H +#define _LIBNVME_IOCTL_H + +#include <stddef.h> +#include <sys/ioctl.h> +#include "types.h" +#include "api-types.h" + +/* + * We can not always count on the kernel UAPI being installed. Use the same + * 'ifdef' guard to avoid double definitions just in case. + */ +#ifndef _UAPI_LINUX_NVME_IOCTL_H +#define _UAPI_LINUX_NVME_IOCTL_H + +#ifndef _LINUX_NVME_IOCTL_H +#define _LINUX_NVME_IOCTL_H + +/** + * DOC: ioctl.h + * + * Linux NVMe ioctl interface functions + */ + +/* '0' is interpreted by the kernel to mean 'apply the default timeout' */ +#define NVME_DEFAULT_IOCTL_TIMEOUT 0 + +/* + * 4k is the smallest possible transfer unit, so restricting to 4k + * avoids having to check the MDTS value of the controller. + */ +#define NVME_LOG_PAGE_PDU_SIZE 4096 + +/** + * struct nvme_passthru_cmd - nvme passthrough command structure + * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes + * @flags: Not supported: intended for command flags (eg: SGL, FUSE) + * @rsvd1: Reserved for future use + * @nsid: Namespace Identifier, or Fabrics type + * @cdw2: Command Dword 2 (no spec defined use) + * @cdw3: Command Dword 3 (no spec defined use) + * @metadata: User space address to metadata buffer (NULL if not used) + * @addr: User space address to data buffer (NULL if not used) + * @metadata_len: Metadata buffer transfer length + * @data_len: Data buffer transfer length + * @cdw10: Command Dword 10 (command specific) + * @cdw11: Command Dword 11 (command specific) + * @cdw12: Command Dword 12 (command specific) + * @cdw13: Command Dword 13 (command specific) + * @cdw14: Command Dword 14 (command specific) + * @cdw15: Command Dword 15 (command specific) + * @timeout_ms: If non-zero, overrides system default timeout in milliseconds + * @result: Set on completion to the command's CQE DWORD 0 controller response + */ +struct nvme_passthru_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 result; +}; + +/** + * struct nvme_passthru_cmd64 - 64-bit nvme passthrough command structure + * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes + * @flags: Not supported: intended for command flags (eg: SGL, FUSE) + * @rsvd1: Reserved for future use + * @nsid: Namespace Identifier, or Fabrics type + * @cdw2: Command Dword 2 (no spec defined use) + * @cdw3: Command Dword 3 (no spec defined use) + * @metadata: User space address to metadata buffer (NULL if not used) + * @addr: User space address to data buffer (NULL if not used) + * @metadata_len: Metadata buffer transfer length + * @data_len: Data buffer transfer length + * @cdw10: Command Dword 10 (command specific) + * @cdw11: Command Dword 11 (command specific) + * @cdw12: Command Dword 12 (command specific) + * @cdw13: Command Dword 13 (command specific) + * @cdw14: Command Dword 14 (command specific) + * @cdw15: Command Dword 15 (command specific) + * @timeout_ms: If non-zero, overrides system default timeout in milliseconds + * @rsvd2: Reserved for future use (and fills an implicit struct pad + * @result: Set on completion to the command's CQE DWORD 0-1 controller response + */ +struct nvme_passthru_cmd64 { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 rsvd2; + __u64 result; +}; + +/** + * struct nvme_uring_cmd - nvme uring command structure + * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes + * @flags: Not supported: intended for command flags (eg: SGL, FUSE) + * @rsvd1: Reserved for future use + * @nsid: Namespace Identifier, or Fabrics type + * @cdw2: Command Dword 2 (no spec defined use) + * @cdw3: Command Dword 3 (no spec defined use) + * @metadata: User space address to metadata buffer (NULL if not used) + * @addr: User space address to data buffer (NULL if not used) + * @metadata_len: Metadata buffer transfer length + * @data_len: Data buffer transfer length + * @cdw10: Command Dword 10 (command specific) + * @cdw11: Command Dword 11 (command specific) + * @cdw12: Command Dword 12 (command specific) + * @cdw13: Command Dword 13 (command specific) + * @cdw14: Command Dword 14 (command specific) + * @cdw15: Command Dword 15 (command specific) + * @timeout_ms: If non-zero, overrides system default timeout in milliseconds + * @rsvd2: Reserved for future use (and fills an implicit struct pad + */ +struct nvme_uring_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 rsvd2; +}; + +#define NVME_IOCTL_ID _IO('N', 0x40) +#define NVME_IOCTL_RESET _IO('N', 0x44) +#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) +#define NVME_IOCTL_RESCAN _IO('N', 0x46) +#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_passthru_cmd) +#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd) +#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64) +#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64) + +/* io_uring async commands: */ +#define NVME_URING_CMD_IO _IOWR('N', 0x80, struct nvme_uring_cmd) +#define NVME_URING_CMD_IO_VEC _IOWR('N', 0x81, struct nvme_uring_cmd) + +#endif /* _UAPI_LINUX_NVME_IOCTL_H */ + +#endif /* _LINUX_NVME_IOCTL_H */ + +/** + * sizeof_args - Helper function used to determine structure sizes + * @type: Argument structure type + * @member: Member inside the type + * @align: Alignment information + */ +#define sizeof_args(type, member, align) \ +({ \ + type s; \ + size_t t = offsetof(type, member) + sizeof(s.member); \ + size_t p = (sizeof(align) - (t % sizeof(align))) % sizeof(align); \ + t + p; \ +}) + +/** + * nvme_submit_admin_passthru64() - Submit a 64-bit nvme passthrough admin + * command + * @fd: File descriptor of nvme device + * @cmd: The nvme admin command to send + * @result: Optional field to return the result from the CQE DW0-1 + * + * Uses NVME_IOCTL_ADMIN64_CMD for the ioctl request. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_submit_admin_passthru64(int fd, struct nvme_passthru_cmd64 *cmd, + __u64 *result); + +/** + * nvme_admin_passthru64() - Submit a 64-bit nvme passthrough command + * @fd: File descriptor of nvme device + * @opcode: The nvme io command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command + * @metadata: Pointer to user address of the metadata buffer + * @timeout_ms: How long the kernel waits for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Parameterized form of nvme_submit_admin_passthru64(). This sets up and + * submits a &struct nvme_passthru_cmd64. + * + * Known values for @opcode are defined in &enum nvme_admin_opcode. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_admin_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, + __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, void *metadata, + __u32 timeout_ms, __u64 *result); + +/** + * nvme_submit_admin_passthru() - Submit an nvme passthrough admin command + * @fd: File descriptor of nvme device + * @cmd: The nvme admin command to send + * @result: Optional field to return the result from the CQE DW0 + * + * Uses NVME_IOCTL_ADMIN_CMD for the ioctl request. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_submit_admin_passthru(int fd, struct nvme_passthru_cmd *cmd, + __u32 *result); + +/** + * nvme_admin_passthru() - Submit an nvme passthrough command + * @fd: File descriptor of nvme device + * @opcode: The nvme io command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command + * @metadata: Pointer to user address of the metadata buffer + * @timeout_ms: How long the kernel waits for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Parameterized form of nvme_submit_admin_passthru(). This sets up and + * submits a &struct nvme_passthru_cmd. + * + * Known values for @opcode are defined in &enum nvme_admin_opcode. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_admin_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, + __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, void *metadata, + __u32 timeout_ms, __u32 *result); + +/** + * nvme_submit_io_passthru64() - Submit a 64-bit nvme passthrough command + * @fd: File descriptor of nvme device + * @cmd: The nvme io command to send + * @result: Optional field to return the result from the CQE DW0-1 + * + * Uses NVME_IOCTL_IO64_CMD for the ioctl request. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_submit_io_passthru64(int fd, struct nvme_passthru_cmd64 *cmd, + __u64 *result); + +/** + * nvme_io_passthru64() - Submit an nvme io passthrough command + * @fd: File descriptor of nvme device + * @opcode: The nvme io command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command + * @metadata: Pointer to user address of the metadata buffer + * @timeout_ms: How long the kernel waits for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Parameterized form of nvme_submit_io_passthru64(). This sets up and submits + * a &struct nvme_passthru_cmd64. + * + * Known values for @opcode are defined in &enum nvme_io_opcode. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, + __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, void *metadata, + __u32 timeout_ms, __u64 *result); + +/** + * nvme_submit_io_passthru() - Submit an nvme passthrough command + * @fd: File descriptor of nvme device + * @cmd: The nvme io command to send + * @result: Optional field to return the result from the CQE dword 0 + * @result: Optional field to return the result from the CQE DW0 + * + * Uses NVME_IOCTL_IO_CMD for the ioctl request. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_submit_io_passthru(int fd, struct nvme_passthru_cmd *cmd, + __u32 *result); + +/** + * nvme_io_passthru() - Submit an nvme io passthrough command + * @fd: File descriptor of nvme device + * @opcode: The nvme io command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command + * @metadata: Pointer to user address of the metadata buffer + * @timeout_ms: How long the kernel waits for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Parameterized form of nvme_submit_io_passthru(). This sets up and submits + * a &struct nvme_passthru_cmd. + * + * Known values for @opcode are defined in &enum nvme_io_opcode. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd, + __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, + __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, void *metadata, + __u32 timeout_ms, __u32 *result); + +/** + * nvme_subsystem_reset() - Initiate a subsystem reset + * @fd: File descriptor of nvme device + * + * This should only be sent to controller handles, not to namespaces. + * + * Return: Zero if a subsystem reset was initiated or -1 with errno set + * otherwise. + */ +int nvme_subsystem_reset(int fd); + +/** + * nvme_ctrl_reset() - Initiate a controller reset + * @fd: File descriptor of nvme device + * + * This should only be sent to controller handles, not to namespaces. + * + * Return: 0 if a reset was initiated or -1 with errno set otherwise. + */ +int nvme_ctrl_reset(int fd); + +/** + * nvme_ns_rescan() - Initiate a controller rescan + * @fd: File descriptor of nvme device + * + * This should only be sent to controller handles, not to namespaces. + * + * Return: 0 if a rescan was initiated or -1 with errno set otherwise. + */ +int nvme_ns_rescan(int fd); + +/** + * nvme_get_nsid() - Retrieve the NSID from a namespace file descriptor + * @fd: File descriptor of nvme namespace + * @nsid: User pointer to namespace id + * + * This should only be sent to namespace handles, not to controllers. The + * kernel's interface returns the nsid as the return value. This is unfortunate + * for many architectures that are incapable of allowing distinguishing a + * namespace id > 0x80000000 from a negative error number. + * + * Return: 0 if @nsid was set successfully or -1 with errno set otherwise. + */ +int nvme_get_nsid(int fd, __u32 *nsid); + +/** + * nvme_identify() - Send the NVMe Identify command + * @args: &struct nvme_identify_args argument structure + * + * The Identify command returns a data buffer that describes information about + * the NVM subsystem, the controller or the namespace(s). + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_identify(struct nvme_identify_args *args); + +static inline int nvme_identify_cns_nsid(int fd, enum nvme_identify_cns cns, + __u32 nsid, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = cns, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_ctrl() - Retrieves nvme identify controller + * @fd: File descriptor of nvme device + * @id: User space destination address to transfer the data, + * + * Sends nvme identify with CNS value %NVME_IDENTIFY_CNS_CTRL. + * + * See &struct nvme_id_ctrl for details on the data returned. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ctrl(int fd, struct nvme_id_ctrl *id) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_CTRL, + NVME_NSID_NONE, id); +} + +/** + * nvme_identify_ns() - Retrieves nvme identify namespace + * @fd: File descriptor of nvme device + * @nsid: Namespace to identify + * @ns: User space destination address to transfer the data + * + * If the Namespace Identifier (NSID) field specifies an active NSID, then the + * Identify Namespace data structure is returned to the host for that specified + * namespace. + * + * If the controller supports the Namespace Management capability and the NSID + * field is set to %NVME_NSID_ALL, then the controller returns an Identify Namespace + * data structure that specifies capabilities that are common across namespaces + * for this controller. + * + * See &struct nvme_id_ns for details on the structure returned. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ns(int fd, __u32 nsid, struct nvme_id_ns *ns) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS, nsid, ns); +} + +/** + * nvme_identify_allocated_ns() - Same as nvme_identify_ns, but only for + * allocated namespaces + * @fd: File descriptor of nvme device + * @nsid: Namespace to identify + * @ns: User space destination address to transfer the data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_allocated_ns(int fd, __u32 nsid, + struct nvme_id_ns *ns) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS, + nsid, ns); +} + +/** + * nvme_identify_active_ns_list() - Retrieves active namespaces id list + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifier + * @list: User space destination address to transfer the data + * + * A list of 1024 namespace IDs is returned to the host containing NSIDs in + * increasing order that are greater than the value specified in the Namespace + * Identifier (nsid) field of the command. + * + * See &struct nvme_ns_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_active_ns_list(int fd, __u32 nsid, + struct nvme_ns_list *list) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, + nsid, list); +} + +/** + * nvme_identify_allocated_ns_list() - Retrieves allocated namespace id list + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifier + * @list: User space destination address to transfer the data + * + * A list of 1024 namespace IDs is returned to the host containing NSIDs in + * increasing order that are greater than the value specified in the Namespace + * Identifier (nsid) field of the command. + * + * See &struct nvme_ns_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_allocated_ns_list(int fd, __u32 nsid, + struct nvme_ns_list *list) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, + nsid, list); +} + +/** + * nvme_identify_ctrl_list() - Retrieves identify controller list + * @fd: File descriptor of nvme device + * @cntid: Starting CNTLID to return in the list + * @cntlist: User space destination address to transfer the data + * + * Up to 2047 controller identifiers is returned containing a controller + * identifier greater than or equal to the controller identifier specified in + * @cntid. + * + * See &struct nvme_ctrl_list for a definition of the structure returned. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ctrl_list(int fd, __u16 cntid, + struct nvme_ctrl_list *cntlist) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = cntlist, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_nsid_ctrl_list() - Retrieves controller list attached to an nsid + * @fd: File descriptor of nvme device + * @nsid: Return controllers that are attached to this nsid + * @cntid: Starting CNTLID to return in the list + * @cntlist: User space destination address to transfer the data + * + * Up to 2047 controller identifiers are returned containing a controller + * identifier greater than or equal to the controller identifier specified in + * @cntid attached to @nsid. + * + * See &struct nvme_ctrl_list for a definition of the structure returned. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 + */ +static inline int nvme_identify_nsid_ctrl_list(int fd, __u32 nsid, __u16 cntid, + struct nvme_ctrl_list *cntlist) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = cntlist, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_NS_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_ns_descs() - Retrieves namespace descriptor list + * @fd: File descriptor of nvme device + * @nsid: The namespace id to retrieve descriptors + * @descs: User space destination address to transfer the data + * + * A list of Namespace Identification Descriptor structures is returned to the + * host for the namespace specified in the Namespace Identifier (NSID) field if + * it is an active NSID. + * + * The data returned is in the form of an array of 'struct nvme_ns_id_desc'. + * + * See &struct nvme_ns_id_desc for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ns_descs(int fd, __u32 nsid, + struct nvme_ns_id_desc *descs) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_DESC_LIST, + nsid, descs); +} + +/** + * nvme_identify_nvmset_list() - Retrieves NVM Set List + * @fd: File descriptor of nvme device + * @nvmsetid: NVM Set Identifier + * @nvmset: User space destination address to transfer the data + * + * Retrieves an NVM Set List, &struct nvme_id_nvmset_list. The data structure + * is an ordered list by NVM Set Identifier, starting with the first NVM Set + * Identifier supported by the NVM subsystem that is equal to or greater than + * the NVM Set Identifier. + * + * See &struct nvme_id_nvmset_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_nvmset_list(int fd, __u16 nvmsetid, + struct nvme_id_nvmset_list *nvmset) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = nvmset, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_NVMSET_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = nvmsetid, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_primary_ctrl() - Retrieve NVMe Primary Controller + * identification + * @fd: File descriptor of nvme device + * @cntid: Return controllers starting at this identifier + * @cap: User space destination buffer address to transfer the data + * + * See &struct nvme_primary_ctrl_cap for the definition of the returned structure, @cap. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_primary_ctrl(int fd, __u16 cntid, + struct nvme_primary_ctrl_cap *cap) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = cap, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_secondary_ctrl_list() - Retrieves secondary controller list + * @fd: File descriptor of nvme device + * @cntid: Return controllers starting at this identifier + * @sc_list: User space destination address to transfer the data + * + * A Secondary Controller List is returned to the host for up to 127 secondary + * controllers associated with the primary controller processing this command. + * The list contains entries for controller identifiers greater than or equal + * to the value specified in the Controller Identifier (cntid). + * + * See &struct nvme_secondary_ctrls_list for a definition of the returned + * structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_secondary_ctrl_list(int fd, + __u16 cntid, struct nvme_secondary_ctrl_list *sc_list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = sc_list, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_ns_granularity() - Retrieves namespace granularity + * identification + * @fd: File descriptor of nvme device + * @gr_list: User space destination address to transfer the data + * + * If the controller supports reporting of Namespace Granularity, then a + * Namespace Granularity List is returned to the host for up to sixteen + * namespace granularity descriptors + * + * See &struct nvme_id_ns_granularity_list for the definition of the returned + * structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ns_granularity(int fd, + struct nvme_id_ns_granularity_list *gr_list) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_GRANULARITY, + NVME_NSID_NONE, gr_list); +} + +/** + * nvme_identify_uuid() - Retrieves device's UUIDs + * @fd: File descriptor of nvme device + * @uuid_list: User space destination address to transfer the data + * + * Each UUID List entry is either 0h, the NVMe Invalid UUID, or a valid UUID. + * Valid UUIDs are those which are non-zero and are not the NVMe Invalid UUID. + * + * See &struct nvme_id_uuid_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_uuid(int fd, struct nvme_id_uuid_list *uuid_list) +{ + return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_UUID_LIST, + NVME_NSID_NONE, uuid_list); +} + +/** + * nvme_identify_ns_csi() - I/O command set specific identify namespace data + * @fd: File descriptor of nvme device + * @nsid: Namespace to identify + * @uuidx: UUID Index for differentiating vendor specific encoding + * @csi: Command Set Identifier + * @data: User space destination address to transfer the data + * + * An I/O Command Set specific Identify Namespace data structure is returned + * for the namespace specified in @nsid. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ns_csi(int fd, __u32 nsid, __u8 uuidx, + enum nvme_csi csi, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CSI_NS, + .csi = csi, + .nsid = nsid, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = uuidx, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_ctrl_csi() - I/O command set specific Identify Controller data + * @fd: File descriptor of nvme device + * @csi: Command Set Identifier + * @data: User space destination address to transfer the data + * + * An I/O Command Set specific Identify Controller data structure is returned + * to the host for the controller processing the command. The specific Identify + * Controller data structure to be returned is specified by @csi. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ctrl_csi(int fd, enum nvme_csi csi, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CSI_CTRL, + .csi = csi, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_active_ns_list_csi() - Active namespace ID list associated with a specified I/O command set + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifier + * @csi: Command Set Identifier + * @ns_list: User space destination address to transfer the data + * + * A list of 1024 namespace IDs is returned to the host containing active + * NSIDs in increasing order that are greater than the value specified in + * the Namespace Identifier (nsid) field of the command and matching the + * I/O Command Set specified in the @csi argument. + * + * See &struct nvme_ns_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_active_ns_list_csi(int fd, __u32 nsid, + enum nvme_csi csi, struct nvme_ns_list *ns_list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = ns_list, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST, + .csi = csi, + .nsid = nsid, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_allocated_ns_list_csi() - Allocated namespace ID list associated with a specified I/O command set + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifier + * @csi: Command Set Identifier + * @ns_list: User space destination address to transfer the data + * + * A list of 1024 namespace IDs is returned to the host containing allocated + * NSIDs in increasing order that are greater than the value specified in + * the @nsid field of the command and matching the I/O Command Set + * specified in the @csi argument. + * + * See &struct nvme_ns_list for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_allocated_ns_list_csi(int fd, __u32 nsid, + enum nvme_csi csi, struct nvme_ns_list *ns_list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = ns_list, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST, + .csi = csi, + .nsid = nsid, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_independent_identify_ns() - I/O command set independent Identify namespace data + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifier + * @ns: I/O Command Set Independent Identify Namespace data + * structure + * + * The I/O command set independent Identify namespace data structure for + * the namespace identified with @ns is returned to the host. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_independent_identify_ns(int fd, __u32 nsid, + struct nvme_id_independent_id_ns *ns) +{ + return nvme_identify_cns_nsid( + fd, NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS, nsid, ns); +} + +/** + * nvme_identify_ns_csi_user_data_format() - Identify namespace user data format + * @fd: File descriptor of nvme device + * @user_data_format: Return namespaces capability of identifier + * @uuidx: UUID selection, if supported + * @csi: Command Set Identifier + * @data: User space destination address to transfer the data + * + * Identify Namespace data structure for the specified User Data Format + * index containing the namespace capabilities for the NVM Command Set. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_ns_csi_user_data_format(int fd, + __u16 user_data_format, __u8 uuidx, + enum nvme_csi csi, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT, + .csi = csi, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = user_data_format, + .uuidx = uuidx, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_iocs_ns_csi_user_data_format() - Identify I/O command set namespace data structure + * @fd: File descriptor of nvme device + * @user_data_format: Return namespaces capability of identifier + * @uuidx: UUID selection, if supported + * @csi: Command Set Identifier + * @data: User space destination address to transfer the data + * + * I/O Command Set specific Identify Namespace data structure for + * the specified User Data Format index containing the namespace + * capabilities for the I/O Command Set specified in the CSI field. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_iocs_ns_csi_user_data_format(int fd, + __u16 user_data_format, __u8 uuidx, + enum nvme_csi csi, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT, + .csi = csi, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = user_data_format, + .uuidx = uuidx, + }; + + return nvme_identify(&args); +} + +/** + * nvme_nvm_identify_ctrl() - Identify controller data + * @fd: File descriptor of nvme device + * @id: User space destination address to transfer the data + * + * Return an identify controller data structure to the host of + * processing controller. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_nvm_identify_ctrl(int fd, struct nvme_id_ctrl_nvm *id) +{ + return nvme_identify_ctrl_csi(fd, NVME_CSI_NVM, id); +} + +/** + * nvme_identify_domain_list() - Domain list data + * @fd: File descriptor of nvme device + * @domid: Domain ID + * @list: User space destination address to transfer data + * + * A list of 31 domain IDs is returned to the host containing domain + * attributes in increasing order that are greater than the value + * specified in the @domid field. + * + * See &struct nvme_identify_domain_attr for the definition of the + * returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_domain_list(int fd, __u16 domid, + struct nvme_id_domain_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_DOMAIN_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = domid, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_endurance_group_list() - Endurance group list data + * @fd: File descriptor of nvme device + * @endgrp_id: Endurance group identifier + * @list: Array of endurance group identifiers + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_endurance_group_list(int fd, __u16 endgrp_id, + struct nvme_id_endurance_group_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = endgrp_id, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_identify_iocs() - I/O command set data structure + * @fd: File descriptor of nvme device + * @cntlid: Controller ID + * @iocs: User space destination address to transfer the data + * + * Retrieves list of the controller's supported io command set vectors. See + * &struct nvme_id_iocs. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_identify_iocs(int fd, __u16 cntlid, + struct nvme_id_iocs *iocs) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = iocs, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .cns = NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntlid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_identify(&args); +} + +/** + * nvme_zns_identify_ns() - ZNS identify namespace data + * @fd: File descriptor of nvme device + * @nsid: Namespace to identify + * @data: User space destination address to transfer the data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_zns_identify_ns(int fd, __u32 nsid, + struct nvme_zns_id_ns *data) +{ + return nvme_identify_ns_csi( + fd, nsid, NVME_UUID_NONE, NVME_CSI_ZNS, data); +} + +/** + * nvme_zns_identify_ctrl() - ZNS identify controller data + * @fd: File descriptor of nvme device + * @id: User space destination address to transfer the data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_zns_identify_ctrl(int fd, struct nvme_zns_id_ctrl *id) +{ + return nvme_identify_ctrl_csi(fd, NVME_CSI_ZNS, id); +} + +/** + * nvme_get_log() - NVMe Admin Get Log command + * @args: &struct nvme_get_log_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_log(struct nvme_get_log_args *args); + +/** + * nvme_get_log_page() - Get log page data + * @fd: File descriptor of nvme device + * @xfer_len: Max log transfer size per request to split the total. + * @args: &struct nvme_get_log_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args); + +static inline int nvme_get_nsid_log(int fd, bool rae, + enum nvme_cmd_get_log_lid lid, + __u32 nsid, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = lid, + .len = len, + .nsid = nsid, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +static inline int nvme_get_log_simple(int fd, enum nvme_cmd_get_log_lid lid, + __u32 len, void *log) +{ + return nvme_get_nsid_log(fd, false, lid, NVME_NSID_ALL, len, log); +} + +/** + * nvme_get_log_supported_log_pages() - Retrieve nmve supported log pages + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: Array of LID supported and Effects data structures + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_supported_log_pages(int fd, bool rae, + struct nvme_supported_log_pages *log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SUPPORTED_LOG_PAGES, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_get_log_error() - Retrieve nvme error log + * @fd: File descriptor of nvme device + * @nr_entries: Number of error log entries allocated + * @rae: Retain asynchronous events + * @err_log: Array of error logs of size 'entries' + * + * This log page describes extended error information for a command that + * completed with error, or may report an error that is not specific to a + * particular command. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_error(int fd, unsigned int nr_entries, bool rae, + struct nvme_error_log_page *err_log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_ERROR, + NVME_NSID_ALL, sizeof(*err_log) * nr_entries, + err_log); +} + +/** + * nvme_get_log_smart() - Retrieve nvme smart log + * @fd: File descriptor of nvme device + * @nsid: Optional namespace identifier + * @rae: Retain asynchronous events + * @smart_log: User address to store the smart log + * + * This log page provides SMART and general health information. The information + * provided is over the life of the controller and is retained across power + * cycles. To request the controller log page, the namespace identifier + * specified is FFFFFFFFh. The controller may also support requesting the log + * page on a per namespace basis, as indicated by bit 0 of the LPA field in the + * Identify Controller data structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_smart(int fd, __u32 nsid, bool rae, + struct nvme_smart_log *smart_log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SMART, + nsid, sizeof(*smart_log), smart_log); +} + +/** + * nvme_get_log_fw_slot() - Retrieves the controller firmware log + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @fw_log: User address to store the log page + * + * This log page describes the firmware revision stored in each firmware slot + * supported. The firmware revision is indicated as an ASCII string. The log + * page also indicates the active slot number. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_fw_slot(int fd, bool rae, + struct nvme_firmware_slot *fw_log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_FW_SLOT, + NVME_NSID_ALL, sizeof(*fw_log), fw_log); +} + +/** + * nvme_get_log_changed_ns_list() - Retrieve namespace changed list + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @ns_log: User address to store the log page + * + * This log page describes namespaces attached to this controller that have + * changed since the last time the namespace was identified, been added, or + * deleted. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_changed_ns_list(int fd, bool rae, + struct nvme_ns_list *ns_log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_CHANGED_NS, + NVME_NSID_ALL, sizeof(*ns_log), ns_log); +} + +/** + * nvme_get_log_cmd_effects() - Retrieve nvme command effects log + * @fd: File descriptor of nvme device + * @csi: Command Set Identifier + * @effects_log:User address to store the effects log + * + * This log page describes the commands that the controller supports and the + * effects of those commands on the state of the NVM subsystem. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_cmd_effects(int fd, enum nvme_csi csi, + struct nvme_cmd_effects_log *effects_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = effects_log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_CMD_EFFECTS, + .len = sizeof(*effects_log), + .nsid = NVME_NSID_ALL, + .csi = csi, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_device_self_test() - Retrieve the device self test log + * @fd: File descriptor of nvme device + * @log: Userspace address of the log payload + * + * The log page indicates the status of an in progress self test and the + * percent complete of that operation, and the results of the previous 20 + * self-test operations. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_device_self_test(int fd, + struct nvme_self_test_log *log) +{ + return nvme_get_nsid_log(fd, false, NVME_LOG_LID_DEVICE_SELF_TEST, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_get_log_create_telemetry_host() - Create host telemetry log + * @fd: File descriptor of nvme device + * @log: Userspace address of the log payload + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_create_telemetry_host(int fd, + struct nvme_telemetry_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_telemetry_host() - Get Telemetry Host-Initiated log page + * @fd: File descriptor of nvme device + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Host-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_telemetry_host(int fd, __u64 offset, + __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_RETAIN, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_telemetry_ctrl() - Get Telemetry Controller-Initiated log page + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Controller-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_telemetry_ctrl(int fd, bool rae, + __u64 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_TELEMETRY_CTRL, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_endurance_group() - Get Endurance Group log + * @fd: File descriptor of nvme device + * @endgid: Starting group identifier to return in the list + * @log: User address to store the endurance log + * + * This log page indicates if an Endurance Group Event has occurred for a + * particular Endurance Group. If an Endurance Group Event has occurred, the + * details of the particular event are included in the Endurance Group + * Information log page for that Endurance Group. An asynchronous event is + * generated when an entry for an Endurance Group is newly added to this log + * page. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_endurance_group(int fd, __u16 endgid, + struct nvme_endurance_group_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_ENDURANCE_GROUP, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = endgid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_predictable_lat_nvmset() - Predictable Latency Per NVM Set + * @fd: File descriptor of nvme device + * @nvmsetid: NVM set id + * @log: User address to store the predictable latency log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_predictable_lat_nvmset(int fd, __u16 nvmsetid, + struct nvme_nvmset_predictable_lat_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_PREDICTABLE_LAT_NVMSET, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = nvmsetid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_predictable_lat_event() - Retrieve Predictable Latency Event Aggregate Log Page + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @offset: Offset into the predictable latency event + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_predictable_lat_event(int fd, bool rae, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_PREDICTABLE_LAT_AGG, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_fdp_configurations() - Get list of Flexible Data Placement configurations + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_configurations(int fd, __u16 egid, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_CONFIGS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_reclaim_unit_handle_usage() - Get reclaim unit handle usage + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_reclaim_unit_handle_usage(int fd, __u16 egid, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_RUH_USAGE, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_fdp_stats() - Get Flexible Data Placement statistics + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_stats(int fd, __u16 egid, __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_STATS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_fdp_events() - Get Flexible Data Placement events + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @host_events: Whether to report host or controller events + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_events(int fd, __u16 egid, bool host_events, __u32 offset, + __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_EVENTS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = (__u8)(host_events ? 0x1 : 0x0), + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_ana() - Retrieve Asymmetric Namespace Access log page + * @fd: File descriptor of nvme device + * @lsp: Log specific, see &enum nvme_get_log_ana_lsp + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the ana log + * + * This log consists of a header describing the log and descriptors containing + * the asymmetric namespace access information for ANA Groups that contain + * namespaces that are attached to the controller processing the command. + * + * See &struct nvme_ana_rsp_hdr for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_ana(int fd, enum nvme_log_ana_lsp lsp, bool rae, + __u64 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_ANA, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)lsp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_ana_groups() - Retrieve Asymmetric Namespace Access groups only log page + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @len: The allocated length of the log page + * @log: User address to store the ana group log + * + * See &struct nvme_ana_group_desc for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_ana_groups(int fd, bool rae, __u32 len, + struct nvme_ana_group_desc *log) +{ + return nvme_get_log_ana(fd, NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY, rae, 0, + len, log); +} + +/** + * nvme_get_log_lba_status() - Retrieve LBA Status + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_lba_status(int fd, bool rae, + __u64 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_LBA_STATUS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_endurance_grp_evt() - Retrieve Rotational Media Information + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_endurance_grp_evt(int fd, bool rae, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_ENDURANCE_GRP_EVT, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_fid_supported_effects() - Retrieve Feature Identifiers Supported and Effects + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: FID Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_fid_supported_effects(int fd, bool rae, + struct nvme_fid_supported_effects_log *log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_FID_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_get_log_mi_cmd_supported_effects() - displays the MI Commands Supported by the controller + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: MI Command Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_mi_cmd_supported_effects(int fd, bool rae, + struct nvme_mi_cmd_supported_effects_log *log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_get_log_boot_partition() - Retrieve Boot Partition + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @lsp: The log specified field of LID + * @len: The allocated size, minimum + * struct nvme_boot_partition + * @part: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_boot_partition(int fd, bool rae, + __u8 lsp, __u32 len, struct nvme_boot_partition *part) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = part, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_BOOT_PARTITION, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = lsp, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_phy_rx_eom() - Retrieve Physical Interface Receiver Eye Opening Measurement Log + * @fd: File descriptor of nvme device + * @lsp: Log specific, controls action and measurement quality + * @controller: Target controller ID + * @len: The allocated size, minimum + * struct nvme_phy_rx_eom_log + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_phy_rx_eom(int fd, __u8 lsp, __u16 controller, + __u32 len, struct nvme_phy_rx_eom_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_PHY_RX_EOM, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = controller, + .lsp = lsp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_discovery() - Retrieve Discovery log page + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @offset: Offset of this log to retrieve + * @len: The allocated size for this portion of the log + * @log: User address to store the discovery log + * + * Supported only by fabrics discovery controllers, returning discovery + * records. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_discovery(int fd, bool rae, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_DISCOVER, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_media_unit_stat() - Retrieve Media Unit Status + * @fd: File descriptor of nvme device + * @domid: Domain Identifier selection, if supported + * @mus: User address to store the Media Unit statistics log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_media_unit_stat(int fd, __u16 domid, + struct nvme_media_unit_stat_log *mus) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = mus, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_MEDIA_UNIT_STATUS, + .len = sizeof(*mus), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_support_cap_config_list() - Retrieve Supported Capacity Configuration List + * @fd: File descriptor of nvme device + * @domid: Domain Identifier selection, if supported + * @cap: User address to store supported capabilities config list + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_support_cap_config_list(int fd, __u16 domid, + struct nvme_supported_cap_config_list_log *cap) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = cap, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST, + .len = sizeof(*cap), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_reservation() - Retrieve Reservation Notification + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: User address to store the reservation log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_get_log_reservation(int fd, bool rae, + struct nvme_resv_notification_log *log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_RESERVATION, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_get_log_sanitize() - Retrieve Sanitize Status + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: User address to store the sanitize log + * + * The Sanitize Status log page reports sanitize operation time estimates and + * information about the most recent sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_sanitize(int fd, bool rae, + struct nvme_sanitize_log_page *log) +{ + return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SANITIZE, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_get_log_zns_changed_zones() - Retrieve list of zones that have changed + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @rae: Retain asynchronous events + * @log: User address to store the changed zone log + * + * The list of zones that have changed state due to an exceptional event. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_zns_changed_zones(int fd, __u32 nsid, bool rae, + struct nvme_zns_changed_zone_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_ZNS_CHANGED_ZONES, + .len = sizeof(*log), + .nsid = nsid, + .csi = NVME_CSI_ZNS, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_persistent_event() - Retrieve Persistent Event Log + * @fd: File descriptor of nvme device + * @action: Action the controller should take during processing this command + * @size: Size of @pevent_log + * @pevent_log: User address to store the persistent event log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_log_persistent_event(int fd, + enum nvme_pevent_log_action action, + __u32 size, void *pevent_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = pevent_log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_PERSISTENT_EVENT, + .len = size, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)action, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_set_features() - Set a feature attribute + * @args: &struct nvme_set_features_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features(struct nvme_set_features_args *args); + +/** + * nvme_set_features_data() - Helper function for @nvme_set_features() + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable + * @cdw11: Value to set the feature to + * @save: Save value across power states + * @data_len: Length of feature data, if applicable, in bytes + * @data: User address of feature data, if applicable + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_set_features_data(int fd, __u8 fid, __u32 nsid, + __u32 cdw11, bool save, __u32 data_len, void *data, + __u32 *result) +{ + struct nvme_set_features_args args = { + .result = result, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .cdw11 = cdw11, + .cdw12 = 0, + .cdw13 = 0, + .cdw15 = 0, + .data_len = data_len, + .save = save, + .uuidx = 0, + .fid = fid, + }; + return nvme_set_features(&args); +} + +/** + * nvme_set_features_simple() - Helper function for @nvme_set_features() + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable + * @cdw11: Value to set the feature to + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_set_features_simple(int fd, __u8 fid, __u32 nsid, + __u32 cdw11, bool save, __u32 *result) +{ + return nvme_set_features_data(fd, fid, nsid, cdw11, save, 0, NULL, + result); +} + +/** + * nvme_set_features_arbitration() - Set arbitration features + * @fd: File descriptor of nvme device + * @ab: Arbitration Burst + * @lpw: Low Priority Weight + * @mpw: Medium Priority Weight + * @hpw: High Priority Weight + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw, + __u8 hpw, bool save, __u32 *result); + +/** + * nvme_set_features_power_mgmt() - Set power management feature + * @fd: File descriptor of nvme device + * @ps: Power State + * @wh: Workload Hint + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_power_mgmt(int fd, __u8 ps, __u8 wh, bool save, + __u32 *result); + +/** + * nvme_set_features_lba_range() - Set LBA range feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @nr_ranges: Number of ranges in @data + * @save: Save value across power states + * @data: User address of feature data + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_lba_range(int fd, __u32 nsid, __u8 nr_ranges, bool save, + struct nvme_lba_range_type *data, __u32 *result); + +/** + * nvme_set_features_temp_thresh() - Set temperature threshold feature + * @fd: File descriptor of nvme device + * @tmpth: Temperature Threshold + * @tmpsel: Threshold Temperature Select + * @thsel: Threshold Type Select + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_temp_thresh(int fd, __u16 tmpth, __u8 tmpsel, + enum nvme_feat_tmpthresh_thsel thsel, + bool save, __u32 *result); + +/** + * nvme_set_features_err_recovery() - Set error recovery feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @tler: Time-limited error recovery value + * @dulbe: Deallocated or Unwritten Logical Block Error Enable + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_err_recovery(int fd, __u32 nsid, __u16 tler, + bool dulbe, bool save, __u32 *result); + +/** + * nvme_set_features_volatile_wc() - Set volatile write cache feature + * @fd: File descriptor of nvme device + * @wce: Write cache enable + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_volatile_wc(int fd, bool wce, bool save, + __u32 *result); + +/** + * nvme_set_features_irq_coalesce() - Set IRQ coalesce feature + * @fd: File descriptor of nvme device + * @thr: Aggregation Threshold + * @time: Aggregation Time + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_irq_coalesce(int fd, __u8 thr, __u8 time, + bool save, __u32 *result); + +/** + * nvme_set_features_irq_config() - Set IRQ config feature + * @fd: File descriptor of nvme device + * @iv: Interrupt Vector + * @cd: Coalescing Disable + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_irq_config(int fd, __u16 iv, bool cd, bool save, + __u32 *result); + +/** + * nvme_set_features_write_atomic() - Set write atomic feature + * @fd: File descriptor of nvme device + * @dn: Disable Normal + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_write_atomic(int fd, bool dn, bool save, + __u32 *result); + +/** + * nvme_set_features_async_event() - Set asynchronous event feature + * @fd: File descriptor of nvme device + * @events: Events to enable + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_async_event(int fd, __u32 events, bool save, + __u32 *result); + +/** + * nvme_set_features_auto_pst() - Set autonomous power state feature + * @fd: File descriptor of nvme device + * @apste: Autonomous Power State Transition Enable + * @apst: Autonomous Power State Transition + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_auto_pst(int fd, bool apste, bool save, + struct nvme_feat_auto_pst *apst, + __u32 *result); + +/** + * nvme_set_features_timestamp() - Set timestamp feature + * @fd: File descriptor of nvme device + * @save: Save value across power states + * @timestamp: The current timestamp value to assign to this feature + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp); + +/** + * nvme_set_features_hctm() - Set thermal management feature + * @fd: File descriptor of nvme device + * @tmt2: Thermal Management Temperature 2 + * @tmt1: Thermal Management Temperature 1 + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, bool save, + __u32 *result); + +/** + * nvme_set_features_nopsc() - Set non-operational power state feature + * @fd: File descriptor of nvme device + * @noppme: Non-Operational Power State Permissive Mode Enable + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_nopsc(int fd, bool noppme, bool save, __u32 *result); + +/** + * nvme_set_features_rrl() - Set read recovery level feature + * @fd: File descriptor of nvme device + * @rrl: Read recovery level setting + * @nvmsetid: NVM set id + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_rrl(int fd, __u8 rrl, __u16 nvmsetid, bool save, + __u32 *result); + +/** + * nvme_set_features_plm_config() - Set predictable latency feature + * @fd: File descriptor of nvme device + * @enable: Predictable Latency Enable + * @nvmsetid: NVM Set Identifier + * @save: Save value across power states + * @data: Pointer to structure nvme_plm_config + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_plm_config(int fd, bool enable, __u16 nvmsetid, + bool save, struct nvme_plm_config *data, + __u32 *result); + +/** + * nvme_set_features_plm_window() - Set window select feature + * @fd: File descriptor of nvme device + * @sel: Window Select + * @nvmsetid: NVM Set Identifier + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_plm_window(int fd, enum nvme_feat_plm_window_select sel, + __u16 nvmsetid, bool save, __u32 *result); + +/** + * nvme_set_features_lba_sts_interval() - Set LBA status information feature + * @fd: File descriptor of nvme device + * @save: Save value across power states + * @lsiri: LBA Status Information Report Interval + * @lsipi: LBA Status Information Poll Interval + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi, + bool save, __u32 *result); + +/** + * nvme_set_features_host_behavior() - Set host behavior feature + * @fd: File descriptor of nvme device + * @save: Save value across power states + * @data: Pointer to structure nvme_feat_host_behavior + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_host_behavior(int fd, bool save, + struct nvme_feat_host_behavior *data); + +/** + * nvme_set_features_sanitize() - Set sanitize feature + * @fd: File descriptor of nvme device + * @nodrm: No-Deallocate Response Mode + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result); + +/** + * nvme_set_features_endurance_evt_cfg() - Set endurance event config feature + * @fd: File descriptor of nvme device + * @endgid: Endurance Group Identifier + * @egwarn: Flags to enable warning, see &enum nvme_eg_critical_warning_flags + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_endurance_evt_cfg(int fd, __u16 endgid, __u8 egwarn, + bool save, __u32 *result); + +/** + * nvme_set_features_sw_progress() - Set pre-boot software load count feature + * @fd: File descriptor of nvme device + * @pbslc: Pre-boot Software Load Count + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save, + __u32 *result); + +/** + * nvme_set_features_host_id() - Set enable extended host identifiers feature + * @fd: File descriptor of nvme device + * @exhid: Enable Extended Host Identifier + * @save: Save value across power states + * @hostid: Host ID to set + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid); + +/** + * nvme_set_features_resv_mask() - Set reservation notification mask feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_set_features_resv_mask2() instead. + * + * @fd: File descriptor of nvme device + * @mask: Reservation Notification Mask Field + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result) + __attribute__((deprecated)); + +/** + * nvme_set_features_resv_mask2() - Set reservation notification mask feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @mask: Reservation Notification Mask Field + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_resv_mask2(int fd, __u32 nsid, __u32 mask, bool save, + __u32 *result); + +/** + * nvme_set_features_resv_persist() - Set persist through power loss feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_set_features_resv_persist2() instead. + * + * @fd: File descriptor of nvme device + * @ptpl: Persist Through Power Loss + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_resv_persist(int fd, bool ptpl, bool save, __u32 *result) + __attribute__((deprecated)); + +/** + * nvme_set_features_resv_persist2() - Set persist through power loss feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @ptpl: Persist Through Power Loss + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_resv_persist2(int fd, __u32 nsid, bool ptpl, bool save, + __u32 *result); + +/** + * nvme_set_features_write_protect() - Set write protect feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_set_features_write_protect2() instead. + * + * @fd: File descriptor of nvme device + * @state: Write Protection State + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_write_protect(int fd, enum nvme_feat_nswpcfg_state state, + bool save, __u32 *result) + __attribute__((deprecated)); + +/** + * nvme_set_features_write_protect2() - Set write protect feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @state: Write Protection State + * @save: Save value across power states + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_write_protect2(int fd, __u32 nsid, + enum nvme_feat_nswpcfg_state state, + bool save, __u32 *result); + +/** + * nvme_set_features_iocs_profile() - Set I/O command set profile feature + * @fd: File descriptor of nvme device + * @iocsi: I/O Command Set Combination Index + * @save: Save value across power states + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_features_iocs_profile(int fd, __u16 iocsi, bool save); + +/** + * nvme_get_features() - Retrieve a feature attribute + * @args: &struct nvme_get_features_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features(struct nvme_get_features_args *args); + +/** + * nvme_get_features_data() - Helper function for @nvme_get_features() + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable + * @data_len: Length of feature data, if applicable, in bytes + * @data: User address of feature data, if applicable + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_features_data(int fd, enum nvme_features_id fid, + __u32 nsid, __u32 data_len, void *data, __u32 *result) +{ + struct nvme_get_features_args args = { + .result = result, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .sel = NVME_GET_FEATURES_SEL_CURRENT, + .cdw11 = 0, + .data_len = data_len, + .fid = (__u8)fid, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_features(&args); +} + +/** + * nvme_get_features_simple() - Helper function for @nvme_get_features() + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_get_features_simple(int fd, enum nvme_features_id fid, + __u32 nsid, __u32 *result) +{ + return nvme_get_features_data(fd, fid, nsid, 0, NULL, result); +} + +/** + * nvme_get_features_arbitration() - Get arbitration feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_arbitration(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_power_mgmt() - Get power management feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_power_mgmt(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_lba_range() - Get LBA range feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_get_features_lba_range2() instead. + * + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @data: User address of feature data, if applicable + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_lba_range(int fd, enum nvme_get_features_sel sel, + struct nvme_lba_range_type *data, + __u32 *result) __attribute__((deprecated)); + +/** + * nvme_get_features_lba_range2() - Get LBA range feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nsid: Namespace ID + * @data: Buffer to receive LBA Range Type data structure + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_lba_range2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, struct nvme_lba_range_type *data, + __u32 *result); + +/** + * nvme_get_features_temp_thresh() - Get temperature threshold feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_temp_thresh(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_err_recovery() - Get error recovery feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_get_features_err_recovery2() instead. + * + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_err_recovery(int fd, enum nvme_get_features_sel sel, + __u32 *result) __attribute__((deprecated)); + +/** + * nvme_get_features_err_recovery2() - Get error recovery feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nsid: Namespace ID + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_err_recovery2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result); + +/** + * nvme_get_features_volatile_wc() - Get volatile write cache feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_volatile_wc(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_num_queues() - Get number of queues feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_num_queues(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_irq_coalesce() - Get IRQ coalesce feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_irq_coalesce(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_irq_config() - Get IRQ config feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @iv: + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_irq_config(int fd, enum nvme_get_features_sel sel, + __u16 iv, __u32 *result); + +/** + * nvme_get_features_write_atomic() - Get write atomic feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_write_atomic(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_async_event() - Get asynchronous event feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_async_event(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_auto_pst() - Get autonomous power state feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @apst: + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_auto_pst(int fd, enum nvme_get_features_sel sel, + struct nvme_feat_auto_pst *apst, __u32 *result); + +/** + * nvme_get_features_host_mem_buf() - Get host memory buffer feature + * + * Deprecated: doesn't fetch the Host Memory Buffer Attributes data structure. + * Use nvme_get_features_host_mem_buf2() instead. + * + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_host_mem_buf(int fd, enum nvme_get_features_sel sel, + __u32 *result) __attribute__((deprecated)); + +/** + * nvme_get_features_host_mem_buf2() - Get host memory buffer feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @attrs: Buffer for returned Host Memory Buffer Attributes + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_host_mem_buf2(int fd, enum nvme_get_features_sel sel, + struct nvme_host_mem_buf_attrs *attrs, + __u32 *result); + +/** + * nvme_get_features_timestamp() - Get timestamp feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @ts: Current timestamp + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_timestamp(int fd, enum nvme_get_features_sel sel, + struct nvme_timestamp *ts); + +/** + * nvme_get_features_kato() - Get keep alive timeout feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_kato(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_hctm() - Get thermal management feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_hctm(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_nopsc() - Get non-operational power state feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_nopsc(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_rrl() - Get read recovery level feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_rrl(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_plm_config() - Get predictable latency feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nvmsetid: NVM set id + * @data: + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_plm_config(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, struct nvme_plm_config *data, + __u32 *result); + +/** + * nvme_get_features_plm_window() - Get window select feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nvmsetid: NVM set id + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, __u32 *result); + +/** + * nvme_get_features_lba_sts_interval() - Get LBA status information feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_lba_sts_interval(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_host_behavior() - Get host behavior feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @data: Pointer to structure nvme_feat_host_behavior + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_host_behavior(int fd, enum nvme_get_features_sel sel, + struct nvme_feat_host_behavior *data, + __u32 *result); + +/** + * nvme_get_features_sanitize() - Get sanitize feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_sanitize(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_endurance_event_cfg() - Get endurance event config feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @endgid: Endurance Group Identifier + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_endurance_event_cfg(int fd, enum nvme_get_features_sel sel, + __u16 endgid, __u32 *result); + +/** + * nvme_get_features_sw_progress() - Get software progress feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_sw_progress(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_host_id() - Get host id feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @exhid: Enable Extended Host Identifier + * @len: Length of @hostid + * @hostid: Buffer for returned host ID + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_host_id(int fd, enum nvme_get_features_sel sel, + bool exhid, __u32 len, __u8 *hostid); + +/** + * nvme_get_features_resv_mask() - Get reservation mask feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_get_features_resv_mask2() instead. + * + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_resv_mask(int fd, enum nvme_get_features_sel sel, + __u32 *result) __attribute__((deprecated)); + +/** + * nvme_get_features_resv_mask2() - Get reservation mask feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nsid: Namespace ID + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_resv_mask2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result); + +/** + * nvme_get_features_resv_persist() - Get reservation persist feature + * + * Deprecated: doesn't support specifying a NSID. + * Use nvme_get_features_resv_persist2() instead. + * + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_resv_persist(int fd, enum nvme_get_features_sel sel, + __u32 *result) __attribute__((deprecated)); + +/** + * nvme_get_features_resv_persist2() - Get reservation persist feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @nsid: Namespace ID + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_resv_persist2(int fd, enum nvme_get_features_sel sel, + __u32 nsid, __u32 *result); + +/** + * nvme_get_features_write_protect() - Get write protect feature + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_write_protect(int fd, __u32 nsid, + enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_iocs_profile() - Get IOCS profile feature + * @fd: File descriptor of nvme device + * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_features_iocs_profile(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_format_nvm() - Format nvme namespace(s) + * @args: &struct nvme_format_nvme_args argument structure + * + * The Format NVM command low level formats the NVM media. This command is used + * by the host to change the LBA data size and/or metadata size. A low level + * format may destroy all data and metadata associated with all namespaces or + * only the specific namespace associated with the command + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_format_nvm(struct nvme_format_nvm_args *args); + +/** + * nvme_ns_mgmt() - Issue a Namespace management command + * @args: &struct nvme_ns_mgmt_args Argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args); + +/** + * nvme_ns_mgmt_create() - Create a non attached namespace + * @fd: File descriptor of nvme device + * @ns: Namespace identification that defines ns creation parameters + * @nsid: On success, set to the namespace id that was created + * @timeout: Override the default timeout to this value in milliseconds; + * set to 0 to use the system default. + * @csi: Command Set Identifier + * @data: Host Software Specified Fields that defines ns creation parameters + * + * On successful creation, the namespace exists in the subsystem, but is not + * attached to any controller. Use the nvme_ns_attach_ctrls() to assign the + * namespace to one or more controllers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, + __u32 *nsid, __u32 timeout, __u8 csi, + struct nvme_ns_mgmt_host_sw_specified *data) +{ + struct nvme_ns_mgmt_args args = { + .result = nsid, + .ns = ns, + .args_size = sizeof(args), + .fd = fd, + .timeout = timeout, + .nsid = NVME_NSID_NONE, + .sel = NVME_NS_MGMT_SEL_CREATE, + .csi = csi, + .rsvd1 = { 0, }, + .rsvd2 = NULL, + .data = data, + }; + + return nvme_ns_mgmt(&args); +} + +/** + * nvme_ns_mgmt_delete() - Delete a non attached namespace + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier to delete + * + * It is recommended that a namespace being deleted is not attached to any + * controller. Use the nvme_ns_detach_ctrls() first if the namespace is still + * attached. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid) +{ + struct nvme_ns_mgmt_args args = { + .result = NULL, + .ns = NULL, + .args_size = sizeof(args), + .fd = fd, + .timeout = 0, + .nsid = nsid, + .sel = NVME_NS_MGMT_SEL_DELETE, + .csi = 0, + .rsvd1 = { 0, }, + .rsvd2 = NULL, + .data = NULL, + }; + + return nvme_ns_mgmt(&args); +} + +/** + * nvme_ns_attach() - Attach or detach namespace to controller(s) + * @args: &struct nvme_ns_attach_args Argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_ns_attach(struct nvme_ns_attach_args *args); + +/** + * nvme_ns_attach_ctrls() - Attach namespace to controllers + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to attach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_ns_attach_ctrls(int fd, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH, + }; + + return nvme_ns_attach(&args); +} + +/** + * nvme_ns_detach_ctrls() - Detach namespace from controllers + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to detach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_ns_detach_ctrls(int fd, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + }; + + return nvme_ns_attach(&args); +} + +/** + * nvme_fw_download() - Download part or all of a firmware image to the + * controller + * @args: &struct nvme_fw_download_args argument structure + * + * The Firmware Image Download command downloads all or a portion of an image + * for a future update to the controller. The Firmware Image Download command + * downloads a new image (in whole or in part) to the controller. + * + * The image may be constructed of multiple pieces that are individually + * downloaded with separate Firmware Image Download commands. Each Firmware + * Image Download command includes a Dword Offset and Number of Dwords that + * specify a dword range. + * + * The new firmware image is not activated as part of the Firmware Image + * Download command. Use the nvme_fw_commit() to activate a newly downloaded + * image. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_fw_download(struct nvme_fw_download_args *args); + +/** + * nvme_fw_commit() - Commit firmware using the specified action + * @args: &struct nvme_fw_commit_args argument structure + * + * The Firmware Commit command modifies the firmware image or Boot Partitions. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. The command + * status response may specify additional reset actions required to complete + * the commit process. + */ +int nvme_fw_commit(struct nvme_fw_commit_args *args); + +/** + * nvme_security_send() - Security Send command + * @args: &struct nvme_security_send argument structure + * + * The Security Send command transfers security protocol data to the + * controller. The data structure transferred to the controller as part of this + * command contains security protocol specific commands to be performed by the + * controller. The data structure transferred may also contain data or + * parameters associated with the security protocol commands. + * + * The security data is protocol specific and is not defined by the NVMe + * specification. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_security_send(struct nvme_security_send_args *args); + +/** + * nvme_security_receive() - Security Receive command + * @args: &struct nvme_security_receive argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_security_receive(struct nvme_security_receive_args *args); + +/** + * nvme_get_lba_status() - Retrieve information on possibly unrecoverable LBAs + * @args: &struct nvme_get_lba_status_args argument structure + * + * The Get LBA Status command requests information about Potentially + * Unrecoverable LBAs. Refer to the specification for action type descriptions. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_lba_status(struct nvme_get_lba_status_args *args); + +/** + * nvme_directive_send() - Send directive command + * @args: &struct nvme_directive_send_args argument structure + * + * Directives is a mechanism to enable host and NVM subsystem or controller + * information exchange. The Directive Send command transfers data related to a + * specific Directive Type from the host to the controller. + * + * See the NVMe specification for more information. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_directive_send(struct nvme_directive_send_args *args); + +/** + * nvme_directive_send_id_endir() - Directive Send Enable Directive + * @fd: File descriptor of nvme device + * @nsid: Namespace Identifier + * @endir: Enable Directive + * @dtype: Directive Type + * @id: Pointer to structure nvme_id_directives + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_directive_send_id_endir(int fd, __u32 nsid, bool endir, + enum nvme_directive_dtype dtype, + struct nvme_id_directives *id); + +/** + * nvme_directive_send_stream_release_identifier() - Directive Send Stream release + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @stream_id: Stream identifier + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_send_stream_release_identifier(int fd, + __u32 nsid, __u16 stream_id) +{ + struct nvme_directive_send_args args = { + .result = NULL, + .data = NULL, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0, + .data_len = 0, + .dspec = stream_id, + }; + + return nvme_directive_send(&args); +} + +/** + * nvme_directive_send_stream_release_resource() - Directive Send Stream release resources + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_send_stream_release_resource(int fd, __u32 nsid) +{ + struct nvme_directive_send_args args = { + .result = NULL, + .data = NULL, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0, + .data_len = 0, + .dspec = 0, + }; + + return nvme_directive_send(&args); +} + +/** + * nvme_directive_recv() - Receive directive specific data + * @args: &struct nvme_directive_recv_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_directive_recv(struct nvme_directive_recv_args *args); + +/** + * nvme_directive_recv_identify_parameters() - Directive receive identifier parameters + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @id: Identify parameters buffer + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_recv_identify_parameters(int fd, __u32 nsid, + struct nvme_id_directives *id) +{ + struct nvme_directive_recv_args args = { + .result = NULL, + .data = id, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM, + .dtype = NVME_DIRECTIVE_DTYPE_IDENTIFY, + .cdw12 = 0, + .data_len = sizeof(*id), + .dspec = 0, + }; + + return nvme_directive_recv(&args); +} + +/** + * nvme_directive_recv_stream_parameters() - Directive receive stream parameters + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @parms: Streams directive parameters buffer + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_recv_stream_parameters(int fd, __u32 nsid, + struct nvme_streams_directive_params *parms) +{ + struct nvme_directive_recv_args args = { + .result = NULL, + .data = parms, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0, + .data_len = sizeof(*parms), + .dspec = 0, + }; + + return nvme_directive_recv(&args); +} + +/** + * nvme_directive_recv_stream_status() - Directive receive stream status + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @nr_entries: Number of streams to receive + * @id: Stream status buffer + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_recv_stream_status(int fd, __u32 nsid, + unsigned int nr_entries, + struct nvme_streams_directive_status *id) +{ + struct nvme_directive_recv_args args = { + .result = NULL, + .data = id, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0, + .data_len = sizeof(*id), + .dspec = 0, + }; + + return nvme_directive_recv(&args); +} + +/** + * nvme_directive_recv_stream_allocate() - Directive receive stream allocate + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @nsr: Namespace Streams Requested + * @result: If successful, the CQE dword0 value + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_directive_recv_stream_allocate(int fd, __u32 nsid, + __u16 nsr, __u32 *result) +{ + struct nvme_directive_recv_args args = { + .result = result, + .data = NULL, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .nsid = nsid, + .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = nsr, + .data_len = 0, + .dspec = 0, + }; + + return nvme_directive_recv(&args); +} + +/** + * nvme_capacity_mgmt() - Capacity management command + * @args: &struct nvme_capacity_mgmt_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_capacity_mgmt(struct nvme_capacity_mgmt_args *args); + +/** + * nvme_lockdown() - Issue lockdown command + * @args: &struct nvme_lockdown_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_lockdown(struct nvme_lockdown_args *args); + +/** + * nvme_set_property() - Set controller property + * @args: &struct nvme_set_property_args argument structure + * + * This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These + * properties align to the PCI MMIO controller registers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_set_property(struct nvme_set_property_args *args); + +/** + * nvme_get_property() - Get a controller property + * @args: &struct nvme_get_propert_args argument structure + * + * This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These + * properties align to the PCI MMIO controller registers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_property(struct nvme_get_property_args *args); + +/** + * nvme_sanitize_nvm() - Start a sanitize operation + * @args: &struct nvme_sanitize_nvm_args argument structure + * + * A sanitize operation alters all user data in the NVM subsystem such that + * recovery of any previous user data from any cache, the non-volatile media, + * or any Controller Memory Buffer is not possible. + * + * The Sanitize command starts a sanitize operation or to recover from a + * previously failed sanitize operation. The sanitize operation types that may + * be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize + * operations are processed in the background, i.e., completion of the sanitize + * command does not indicate completion of the sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args); + +/** + * nvme_dev_self_test() - Start or abort a self test + * @args: &struct nvme_dev_self_test argument structure + * + * The Device Self-test command starts a device self-test operation or abort a + * device self-test operation. A device self-test operation is a diagnostic + * testing sequence that tests the integrity and functionality of the + * controller and may include testing of the media associated with namespaces. + * The controller may return a response to this command immediately while + * running the self-test in the background. + * + * Set the 'nsid' field to 0 to not include namespaces in the test. Set to + * 0xffffffff to test all namespaces. All other values tests a specific + * namespace, if present. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_dev_self_test(struct nvme_dev_self_test_args *args); + +/** + * nvme_virtual_mgmt() - Virtualization resource management + * @args: &struct nvme_virtual_mgmt_args argument structure + * + * The Virtualization Management command is supported by primary controllers + * that support the Virtualization Enhancements capability. This command is + * used for several functions: + * + * - Modifying Flexible Resource allocation for the primary controller + * - Assigning Flexible Resources for secondary controllers + * - Setting the Online and Offline state for secondary controllers + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_virtual_mgmt(struct nvme_virtual_mgmt_args *args); + +/** + * nvme_flush() - Send an nvme flush command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * + * The Flush command requests that the contents of volatile write cache be made + * non-volatile. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_flush(int fd, __u32 nsid) +{ + struct nvme_passthru_cmd cmd = {}; + + cmd.opcode = nvme_cmd_flush; + cmd.nsid = nsid; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +/** + * nvme_io() - Submit an nvme user I/O command + * @args: &struct nvme_io_args argument structure + * @opcode: Opcode to execute + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io(struct nvme_io_args *args, __u8 opcode); + +/** + * nvme_read() - Submit an nvme user read command + * @args: &struct nvme_io_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_read(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_read); +} + +/** + * nvme_write() - Submit an nvme user write command + * @args: &struct nvme_io_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_write(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_write); +} + +/** + * nvme_compare() - Submit an nvme user compare command + * @args: &struct nvme_io_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_compare(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_compare); +} + +/** + * nvme_write_zeros() - Submit an nvme write zeroes command + * @args: &struct nvme_io_args argument structure + * + * The Write Zeroes command sets a range of logical blocks to zero. After + * successful completion of this command, the value returned by subsequent + * reads of logical blocks in this range shall be all bytes cleared to 0h until + * a write occurs to this LBA range. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_write_zeros(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_write_zeroes); +} + +/** + * nvme_write_uncorrectable() - Submit an nvme write uncorrectable command + * @args: &struct nvme_io_args argument structure + * + * The Write Uncorrectable command marks a range of logical blocks as invalid. + * When the specified logical block(s) are read after this operation, a failure + * is returned with Unrecovered Read Error status. To clear the invalid logical + * block status, a write operation on those logical blocks is required. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_write_uncorrectable(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_write_uncor); +} + +/** + * nvme_verify() - Send an nvme verify command + * @args: &struct nvme_io_args argument structure + * + * The Verify command verifies integrity of stored information by reading data + * and metadata, if applicable, for the LBAs indicated without transferring any + * data or metadata to the host. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_verify(struct nvme_io_args *args) +{ + return nvme_io(args, nvme_cmd_verify); +} + +/** + * nvme_dsm() - Send an nvme data set management command + * @args: &struct nvme_dsm_args argument structure + * + * The Dataset Management command is used by the host to indicate attributes + * for ranges of logical blocks. This includes attributes like frequency that + * data is read or written, access size, and other information that may be used + * to optimize performance and reliability, and may be used to + * deallocate/unmap/trim those logical blocks. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_dsm(struct nvme_dsm_args *args); + +/** + * nvme_copy() - Copy command + * + * @args: &struct nvme_copy_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_copy(struct nvme_copy_args *args); + +/** + * nvme_resv_acquire() - Send an nvme reservation acquire + * @args: &struct nvme_resv_acquire argument structure + * + * The Reservation Acquire command acquires a reservation on a namespace, + * preempt a reservation held on a namespace, and abort a reservation held on a + * namespace. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_resv_acquire(struct nvme_resv_acquire_args *args); + +/** + * nvme_resv_register() - Send an nvme reservation register + * @args: &struct nvme_resv_register_args argument structure + * + * The Reservation Register command registers, unregisters, or replaces a + * reservation key. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_resv_register(struct nvme_resv_register_args *args); + +/** + * nvme_resv_release() - Send an nvme reservation release + * @args: &struct nvme_resv_release_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_resv_release(struct nvme_resv_release_args *args); + +/** + * nvme_resv_report() - Send an nvme reservation report + * @args: struct nvme_resv_report_args argument structure + * + * Returns a Reservation Status data structure to memory that describes the + * registration and reservation status of a namespace. See the definition for + * the returned structure, &struct nvme_reservation_status, for more details. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_resv_report(struct nvme_resv_report_args *args); + +/** + * nvme_io_mgmt_recv() - I/O Management Receive command + * @args: &struct nvme_io_mgmt_recv_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args); + +/** + * nvme_fdp_reclaim_unit_handle_status() - Get reclaim unit handle status + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of response buffer + * @data: Response buffer + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_fdp_reclaim_unit_handle_status(int fd, __u32 nsid, + __u32 data_len, void *data) +{ + struct nvme_io_mgmt_recv_args args = { + .data = data, + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .data_len = data_len, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .mos = 0, + .mo = NVME_IO_MGMT_RECV_RUH_STATUS, + }; + + return nvme_io_mgmt_recv(&args); +} + +/** + * nvme_io_mgmt_send() - I/O Management Send command + * @args: &struct nvme_io_mgmt_send_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args); + +/** + * nvme_fdp_reclaim_unit_handle_update() - Update a list of reclaim unit handles + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @npids: Number of placement identifiers + * @pids: List of placement identifiers + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_fdp_reclaim_unit_handle_update(int fd, __u32 nsid, + unsigned int npids, __u16 *pids) +{ + struct nvme_io_mgmt_send_args args = { + .data = (void *)pids, + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .data_len = (__u32)(npids * sizeof(__u16)), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .mos = (__u16)(npids - 1), + .mo = NVME_IO_MGMT_SEND_RUH_UPDATE, + }; + + return nvme_io_mgmt_send(&args); +} + +/** + * nvme_zns_mgmt_send() - ZNS management send command + * @args: &struct nvme_zns_mgmt_send_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_zns_mgmt_send(struct nvme_zns_mgmt_send_args *args); + + +/** + * nvme_zns_mgmt_recv() - ZNS management receive command + * @args: &struct nvme_zns_mgmt_recv_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_zns_mgmt_recv(struct nvme_zns_mgmt_recv_args *args); + +/** + * nvme_zns_report_zones() - Return the list of zones + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * @slba: Starting LBA + * @opts: Reporting options + * @extended: Extended report + * @partial: Partial report requested + * @data_len: Length of the data buffer + * @data: Userspace address of the report zones data + * @timeout: timeout in ms + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba, + enum nvme_zns_report_options opts, + bool extended, bool partial, + __u32 data_len, void *data, + __u32 timeout, __u32 *result) +{ + struct nvme_zns_mgmt_recv_args args = { + .slba = slba, + .result = result, + .data = data, + .args_size = sizeof(args), + .fd = fd, + .timeout = timeout, + .nsid = nsid, + .zra = extended ? NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES : + NVME_ZNS_ZRA_REPORT_ZONES, + .data_len = data_len, + .zrasf = (__u16)opts, + .zras_feat = partial, + }; + + return nvme_zns_mgmt_recv(&args); +} + +/** + * nvme_zns_append() - Append data to a zone + * @args: &struct nvme_zns_append_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_zns_append(struct nvme_zns_append_args *args); + +/** + * nvme_dim_send - Send a Discovery Information Management (DIM) command + * @args: &struct nvme_dim_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_dim_send(struct nvme_dim_args *args); + +/** + * nvme_set_debug - Set NVMe command debugging output + * @debug: true to enable or false to disable + */ +void nvme_set_debug(bool debug); + +/** + * nvme_get_debug - Get NVMe command debugging output + * + * Return: false if disabled or true if enabled. + */ +bool nvme_get_debug(void); +#endif /* _LIBNVME_IOCTL_H */ diff --git a/src/nvme/json.c b/src/nvme/json.c new file mode 100644 index 0000000..b49498a --- /dev/null +++ b/src/nvme/json.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021 SUSE Software Solutions + * + * Authors: Hannes Reinecke <hare@suse.de> + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <json.h> + +#include "cleanup.h" +#include "fabrics.h" +#include "log.h" +#include "private.h" +#include "linux.h" + +#define JSON_UPDATE_INT_OPTION(c, k, a, o) \ + if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_int(o); +#define JSON_UPDATE_BOOL_OPTION(c, k, a, o) \ + if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_boolean(o); + +static void json_update_attributes(nvme_ctrl_t c, + struct json_object *ctrl_obj) +{ + struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + + json_object_object_foreach(ctrl_obj, key_str, val_obj) { + JSON_UPDATE_INT_OPTION(cfg, key_str, + nr_io_queues, val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + nr_write_queues, val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + nr_poll_queues, val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + queue_size, val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + keep_alive_tmo, val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + reconnect_delay, val_obj); + if (!strcmp("ctrl_loss_tmo", key_str) && + cfg->ctrl_loss_tmo != NVMF_DEF_CTRL_LOSS_TMO) + cfg->ctrl_loss_tmo = json_object_get_int(val_obj); + JSON_UPDATE_INT_OPTION(cfg, key_str, + fast_io_fail_tmo, val_obj); + if (!strcmp("tos", key_str) && cfg->tos != -1) + cfg->tos = json_object_get_int(val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + duplicate_connect, val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + disable_sqflow, val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + hdr_digest, val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + data_digest, val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + tls, val_obj); + JSON_UPDATE_BOOL_OPTION(cfg, key_str, + concat, val_obj); + if (!strcmp("persistent", key_str) && + !nvme_ctrl_is_persistent(c)) + nvme_ctrl_set_persistent(c, true); + if (!strcmp("discovery", key_str) && + !nvme_ctrl_is_discovery_ctrl(c)) + nvme_ctrl_set_discovery_ctrl(c, true); + /* + * The JSON configuration holds the keyring description + * which needs to be converted into the keyring serial number. + */ + if (!strcmp("keyring", key_str) && cfg->keyring == 0) { + long keyring; + + keyring = nvme_lookup_keyring(json_object_get_string(val_obj)); + if (keyring) { + cfg->keyring = keyring; + nvme_set_keyring(cfg->keyring); + } + } + if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) { + long key; + + key = nvme_lookup_key("psk", + json_object_get_string(val_obj)); + if (key) + cfg->tls_key = key; + } + } +} + +static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) +{ + nvme_ctrl_t c; + struct json_object *attr_obj; + const char *transport, *traddr = NULL; + const char *host_traddr = NULL, *host_iface = NULL, *trsvcid = NULL; + + attr_obj = json_object_object_get(port_obj, "transport"); + if (!attr_obj) + return; + transport = json_object_get_string(attr_obj); + attr_obj = json_object_object_get(port_obj, "traddr"); + if (attr_obj) + traddr = json_object_get_string(attr_obj); + attr_obj = json_object_object_get(port_obj, "host_traddr"); + if (attr_obj) + host_traddr = json_object_get_string(attr_obj); + attr_obj = json_object_object_get(port_obj, "host_iface"); + if (attr_obj) + host_iface = json_object_get_string(attr_obj); + attr_obj = json_object_object_get(port_obj, "trsvcid"); + if (attr_obj) + trsvcid = json_object_get_string(attr_obj); + c = nvme_lookup_ctrl(s, transport, traddr, host_traddr, + host_iface, trsvcid, NULL); + if (!c) + return; + json_update_attributes(c, port_obj); + attr_obj = json_object_object_get(port_obj, "dhchap_key"); + if (attr_obj) + nvme_ctrl_set_dhchap_host_key(c, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key"); + if (attr_obj) + nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj)); +} + +static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj) +{ + struct json_object *nqn_obj, *app_obj, *port_array; + nvme_subsystem_t s; + const char *nqn; + int p; + + nqn_obj = json_object_object_get(subsys_obj, "nqn"); + if (!nqn_obj) + return; + nqn = json_object_get_string(nqn_obj); + s = nvme_lookup_subsystem(h, NULL, nqn); + if (!s) + return; + app_obj = json_object_object_get(subsys_obj, "application"); + if (app_obj) + nvme_subsystem_set_application(s, json_object_get_string(app_obj)); + + port_array = json_object_object_get(subsys_obj, "ports"); + if (!port_array) + return; + for (p = 0; p < json_object_array_length(port_array); p++) { + struct json_object *port_obj; + + port_obj = json_object_array_get_idx(port_array, p); + if (port_obj) + json_parse_port(s, port_obj); + } +} + +static void json_parse_host(nvme_root_t r, struct json_object *host_obj) +{ + struct json_object *attr_obj, *subsys_array, *subsys_obj; + nvme_host_t h; + const char *hostnqn, *hostid = NULL; + int s; + + attr_obj = json_object_object_get(host_obj, "hostnqn"); + if (!attr_obj) + return; + hostnqn = json_object_get_string(attr_obj); + attr_obj = json_object_object_get(host_obj, "hostid"); + if (attr_obj) + hostid = json_object_get_string(attr_obj); + h = nvme_lookup_host(r, hostnqn, hostid); + attr_obj = json_object_object_get(host_obj, "dhchap_key"); + if (attr_obj) + nvme_host_set_dhchap_key(h, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(host_obj, "hostsymname"); + if (attr_obj) + nvme_host_set_hostsymname(h, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(host_obj, "persistent_discovery_ctrl"); + if (attr_obj) + nvme_host_set_pdc_enabled(h, json_object_get_boolean(attr_obj)); + subsys_array = json_object_object_get(host_obj, "subsystems"); + if (!subsys_array) + return; + for (s = 0; s < json_object_array_length(subsys_array); s++) { + subsys_obj = json_object_array_get_idx(subsys_array, s); + if (subsys_obj) + json_parse_subsys(h, subsys_obj); + } +} + +static DEFINE_CLEANUP_FUNC(cleanup_tokener, json_tokener *, json_tokener_free) +#define _cleanup_tokener_ __cleanup__(cleanup_tokener) + +static struct json_object *parse_json(nvme_root_t r, int fd) +{ + char buf[JSON_FILE_BUF_SIZE]; + struct json_object *obj; + char *str = NULL; + _cleanup_tokener_ json_tokener *tok = NULL; + int ret; + _cleanup_free_ void *ptr = NULL; + int len = 0; + + while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + str = realloc(ptr, len + ret); + if (!str) + return NULL; + memcpy(&str[len], buf, ret); + len += ret; + ptr = str; + } + + if (ret < 0 || !len) + return NULL; + + tok = json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); + if (!tok) + return NULL; + + /* Enforce correctly formatted JSON */ + tok->flags = JSON_TOKENER_STRICT; + + obj = json_tokener_parse_ex(tok, str, len); + if (!obj) + nvme_msg(r, LOG_DEBUG, "JSON parsing failed: %s\n", + json_util_get_last_err()); + + return obj; +} + +int json_read_config(nvme_root_t r, const char *config_file) +{ + struct json_object *json_root, *host_obj; + int fd, h; + + fd = open(config_file, O_RDONLY); + if (fd < 0) { + nvme_msg(r, LOG_DEBUG, "Error opening %s, %s\n", + config_file, strerror(errno)); + return fd; + } + json_root = parse_json(r, fd); + close(fd); + if (!json_root) { + errno = EPROTO; + return -1; + } + if (!json_object_is_type(json_root, json_type_array)) { + nvme_msg(r, LOG_DEBUG, "Wrong format, expected array\n"); + json_object_put(json_root); + errno = EPROTO; + return -1; + } + for (h = 0; h < json_object_array_length(json_root); h++) { + host_obj = json_object_array_get_idx(json_root, h); + if (host_obj) + json_parse_host(r, host_obj); + } + json_object_put(json_root); + return 0; +} + +#define JSON_STRING_OPTION(c, p, o) \ + if ((c)->o && strcmp((c)->o, "none")) \ + json_object_object_add((p), # o , \ + json_object_new_string((c)->o)) +#define JSON_INT_OPTION(c, p, o, d) \ + if ((c)->o != d) \ + json_object_object_add((p), # o , \ + json_object_new_int((c)->o)) +#define JSON_BOOL_OPTION(c, p, o) \ + if ((c)->o) \ + json_object_object_add((p), # o , \ + json_object_new_boolean((c)->o)) + +static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) +{ + struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + struct json_object *port_obj = json_object_new_object(); + const char *transport, *value; + + transport = nvme_ctrl_get_transport(c); + json_object_object_add(port_obj, "transport", + json_object_new_string(transport)); + value = nvme_ctrl_get_traddr(c); + if (value) + json_object_object_add(port_obj, "traddr", + json_object_new_string(value)); + value = nvme_ctrl_get_host_traddr(c); + if (value) + json_object_object_add(port_obj, "host_traddr", + json_object_new_string(value)); + value = nvme_ctrl_get_host_iface(c); + if (value) + json_object_object_add(port_obj, "host_iface", + json_object_new_string(value)); + value = nvme_ctrl_get_trsvcid(c); + if (value) + json_object_object_add(port_obj, "trsvcid", + json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_host_key(c); + if (value) + json_object_object_add(port_obj, "dhchap_key", + json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_key(c); + if (value) + json_object_object_add(port_obj, "dhchap_ctrl_key", + json_object_new_string(value)); + JSON_INT_OPTION(cfg, port_obj, nr_io_queues, 0); + JSON_INT_OPTION(cfg, port_obj, nr_write_queues, 0); + JSON_INT_OPTION(cfg, port_obj, nr_poll_queues, 0); + JSON_INT_OPTION(cfg, port_obj, queue_size, 0); + JSON_INT_OPTION(cfg, port_obj, keep_alive_tmo, 0); + JSON_INT_OPTION(cfg, port_obj, reconnect_delay, 0); + if (strcmp(transport, "loop")) { + JSON_INT_OPTION(cfg, port_obj, ctrl_loss_tmo, + NVMF_DEF_CTRL_LOSS_TMO); + JSON_INT_OPTION(cfg, port_obj, fast_io_fail_tmo, 0); + } + JSON_INT_OPTION(cfg, port_obj, tos, -1); + JSON_BOOL_OPTION(cfg, port_obj, duplicate_connect); + JSON_BOOL_OPTION(cfg, port_obj, disable_sqflow); + JSON_BOOL_OPTION(cfg, port_obj, hdr_digest); + JSON_BOOL_OPTION(cfg, port_obj, data_digest); + JSON_BOOL_OPTION(cfg, port_obj, tls); + JSON_BOOL_OPTION(cfg, port_obj, concat); + if (nvme_ctrl_is_persistent(c)) + json_object_object_add(port_obj, "persistent", + json_object_new_boolean(true)); + if (nvme_ctrl_is_discovery_ctrl(c)) + json_object_object_add(port_obj, "discovery", + json_object_new_boolean(true)); + /* + * Store the keyring description in the JSON config file. + */ + if (cfg->keyring) { + _cleanup_free_ char *desc = + nvme_describe_key_serial(cfg->keyring); + + if (desc) { + json_object_object_add(port_obj, "keyring", + json_object_new_string(desc)); + } + } + if (cfg->tls_key) { + _cleanup_free_ char *desc = + nvme_describe_key_serial(cfg->tls_key); + + if (desc) { + json_object_object_add(port_obj, "tls_key", + json_object_new_string(desc)); + } + } + + json_object_array_add(ctrl_array, port_obj); +} + +static void json_update_subsys(struct json_object *subsys_array, + nvme_subsystem_t s) +{ + nvme_ctrl_t c; + const char *subsysnqn = nvme_subsystem_get_nqn(s), *app; + struct json_object *subsys_obj = json_object_new_object(); + struct json_object *port_array; + + /* Skip discovery subsystems as the nqn is not unique */ + if (!strcmp(subsysnqn, NVME_DISC_SUBSYS_NAME)) + return; + + json_object_object_add(subsys_obj, "nqn", + json_object_new_string(subsysnqn)); + app = nvme_subsystem_get_application(s); + if (app) + json_object_object_add(subsys_obj, "application", + json_object_new_string(app)); + port_array = json_object_new_array(); + nvme_subsystem_for_each_ctrl(s, c) { + json_update_port(port_array, c); + } + if (json_object_array_length(port_array)) + json_object_object_add(subsys_obj, "ports", port_array); + else + json_object_put(port_array); + json_object_array_add(subsys_array, subsys_obj); +} + +int json_update_config(nvme_root_t r, const char *config_file) +{ + nvme_host_t h; + struct json_object *json_root, *host_obj; + struct json_object *subsys_array; + int ret = 0; + + json_root = json_object_new_array(); + nvme_for_each_host(r, h) { + nvme_subsystem_t s; + const char *hostnqn, *hostid, *dhchap_key, *hostsymname; + + host_obj = json_object_new_object(); + if (!host_obj) + continue; + hostnqn = nvme_host_get_hostnqn(h); + json_object_object_add(host_obj, "hostnqn", + json_object_new_string(hostnqn)); + hostid = nvme_host_get_hostid(h); + if (hostid) + json_object_object_add(host_obj, "hostid", + json_object_new_string(hostid)); + dhchap_key = nvme_host_get_dhchap_key(h); + if (dhchap_key) + json_object_object_add(host_obj, "dhchap_key", + json_object_new_string(dhchap_key)); + hostsymname = nvme_host_get_hostsymname(h); + if (hostsymname) + json_object_object_add(host_obj, "hostsymname", + json_object_new_string(hostsymname)); + if (h->pdc_enabled_valid) + json_object_object_add(host_obj, "persistent_discovery_ctrl", + json_object_new_boolean(h->pdc_enabled)); + subsys_array = json_object_new_array(); + nvme_for_each_subsystem(h, s) { + json_update_subsys(subsys_array, s); + } + if (json_object_array_length(subsys_array)) + json_object_object_add(host_obj, "subsystems", + subsys_array); + else + json_object_put(subsys_array); + json_object_array_add(json_root, host_obj); + } + if (!config_file) { + ret = json_object_to_fd(1, json_root, JSON_C_TO_STRING_PRETTY); + printf("\n"); + } else + ret = json_object_to_file_ext(config_file, json_root, + JSON_C_TO_STRING_PRETTY); + if (ret < 0) { + nvme_msg(r, LOG_ERR, "Failed to write to %s, %s\n", + config_file ? "stdout" : config_file, + json_util_get_last_err()); + ret = -1; + errno = EIO; + } + json_object_put(json_root); + + return ret; +} + +static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c) +{ + struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c); + struct json_object *ctrl_obj = json_object_new_object(); + const char *name, *transport, *value; + + name = nvme_ctrl_get_name(c); + if (name && strlen(name)) + json_object_object_add(ctrl_obj, "name", + json_object_new_string(name)); + transport = nvme_ctrl_get_transport(c); + json_object_object_add(ctrl_obj, "transport", + json_object_new_string(transport)); + value = nvme_ctrl_get_traddr(c); + if (value) + json_object_object_add(ctrl_obj, "traddr", + json_object_new_string(value)); + value = nvme_ctrl_get_host_traddr(c); + if (value) + json_object_object_add(ctrl_obj, "host_traddr", + json_object_new_string(value)); + value = nvme_ctrl_get_host_iface(c); + if (value) + json_object_object_add(ctrl_obj, "host_iface", + json_object_new_string(value)); + value = nvme_ctrl_get_trsvcid(c); + if (value) + json_object_object_add(ctrl_obj, "trsvcid", + json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_host_key(c); + if (value) + json_object_object_add(ctrl_obj, "dhchap_key", + json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_key(c); + if (value) + json_object_object_add(ctrl_obj, "dhchap_ctrl_key", + json_object_new_string(value)); + JSON_INT_OPTION(cfg, ctrl_obj, nr_io_queues, 0); + JSON_INT_OPTION(cfg, ctrl_obj, nr_write_queues, 0); + JSON_INT_OPTION(cfg, ctrl_obj, nr_poll_queues, 0); + JSON_INT_OPTION(cfg, ctrl_obj, queue_size, 0); + JSON_INT_OPTION(cfg, ctrl_obj, keep_alive_tmo, 0); + JSON_INT_OPTION(cfg, ctrl_obj, reconnect_delay, 0); + if (strcmp(transport, "loop")) { + JSON_INT_OPTION(cfg, ctrl_obj, ctrl_loss_tmo, + NVMF_DEF_CTRL_LOSS_TMO); + JSON_INT_OPTION(cfg, ctrl_obj, fast_io_fail_tmo, 0); + } + JSON_INT_OPTION(cfg, ctrl_obj, tos, -1); + JSON_BOOL_OPTION(cfg, ctrl_obj, duplicate_connect); + JSON_BOOL_OPTION(cfg, ctrl_obj, disable_sqflow); + JSON_BOOL_OPTION(cfg, ctrl_obj, hdr_digest); + JSON_BOOL_OPTION(cfg, ctrl_obj, data_digest); + JSON_BOOL_OPTION(cfg, ctrl_obj, tls); + JSON_BOOL_OPTION(cfg, ctrl_obj, concat); + if (nvme_ctrl_is_persistent(c)) + json_object_object_add(ctrl_obj, "persistent", + json_object_new_boolean(true)); + if (nvme_ctrl_is_discovery_ctrl(c)) + json_object_object_add(ctrl_obj, "discovery", + json_object_new_boolean(true)); + json_object_array_add(ctrl_array, ctrl_obj); +} + +static void json_dump_subsys(struct json_object *subsys_array, + nvme_subsystem_t s) +{ + nvme_ctrl_t c; + struct json_object *subsys_obj = json_object_new_object(); + struct json_object *ctrl_array; + + json_object_object_add(subsys_obj, "name", + json_object_new_string(nvme_subsystem_get_name(s))); + json_object_object_add(subsys_obj, "nqn", + json_object_new_string(nvme_subsystem_get_nqn(s))); + ctrl_array = json_object_new_array(); + nvme_subsystem_for_each_ctrl(s, c) { + json_dump_ctrl(ctrl_array, c); + } + if (json_object_array_length(ctrl_array)) + json_object_object_add(subsys_obj, "controllers", ctrl_array); + else + json_object_put(ctrl_array); + json_object_array_add(subsys_array, subsys_obj); +} + +int json_dump_tree(nvme_root_t r) +{ + nvme_host_t h; + struct json_object *json_root, *host_obj; + struct json_object *host_array, *subsys_array; + int ret = 0; + + json_root = json_object_new_object(); + host_array = json_object_new_array(); + nvme_for_each_host(r, h) { + nvme_subsystem_t s; + const char *hostid, *dhchap_key; + + host_obj = json_object_new_object(); + json_object_object_add(host_obj, "hostnqn", + json_object_new_string(nvme_host_get_hostnqn(h))); + hostid = nvme_host_get_hostid(h); + if (hostid) + json_object_object_add(host_obj, "hostid", + json_object_new_string(hostid)); + dhchap_key = nvme_host_get_dhchap_key(h); + if (dhchap_key) + json_object_object_add(host_obj, "dhchap_key", + json_object_new_string(dhchap_key)); + if (h->pdc_enabled_valid) + json_object_object_add(host_obj, "persistent_discovery_ctrl", + json_object_new_boolean(h->pdc_enabled)); + subsys_array = json_object_new_array(); + nvme_for_each_subsystem(h, s) { + json_dump_subsys(subsys_array, s); + } + if (json_object_array_length(subsys_array)) + json_object_object_add(host_obj, "subsystems", + subsys_array); + else + json_object_put(subsys_array); + json_object_array_add(host_array, host_obj); + } + json_object_object_add(json_root, "hosts", host_array); + + ret = json_object_to_fd(fileno(r->fp), json_root, JSON_C_TO_STRING_PRETTY); + if (ret < 0) { + nvme_msg(r, LOG_ERR, "Failed to write, %s\n", + json_util_get_last_err()); + ret = -1; + errno = EIO; + } + json_object_put(json_root); + + return ret; +} diff --git a/src/nvme/linux.c b/src/nvme/linux.c new file mode 100644 index 0000000..e29d9e7 --- /dev/null +++ b/src/nvme/linux.c @@ -0,0 +1,1297 @@ +// 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 <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/param.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#ifdef CONFIG_OPENSSL +#include <openssl/engine.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/kdf.h> + +#ifdef CONFIG_OPENSSL_3 +#include <openssl/core_names.h> +#include <openssl/params.h> +#endif +#endif + +#ifdef CONFIG_KEYUTILS +#include <keyutils.h> +#endif + +#include <ccan/endian/endian.h> + +#include "cleanup.h" +#include "linux.h" +#include "tree.h" +#include "log.h" +#include "private.h" +#include "base64.h" + +static int __nvme_open(const char *name) +{ + _cleanup_free_ char *path = NULL; + int ret; + + ret = asprintf(&path, "%s/%s", "/dev", name); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + return open(path, O_RDONLY); +} + +int nvme_open(const char *name) +{ + int ret, fd, id, ns; + struct stat stat; + bool c; + + ret = sscanf(name, "nvme%dn%d", &id, &ns); + if (ret != 1 && ret != 2) { + errno = EINVAL; + return -1; + } + c = ret == 1; + + fd = __nvme_open(name); + if (fd < 0) + return fd; + + ret = fstat(fd, &stat); + if (ret < 0) + goto close_fd; + + if (c) { + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; + goto close_fd; + } + } else if (!S_ISBLK(stat.st_mode)) { + errno = EINVAL; + goto close_fd; + } + + return fd; + +close_fd: + close(fd); + return -1; +} + +int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset, + void *buf) +{ + int err = 0; + struct nvme_fw_download_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = offset, + .data_len = xfer, + .data = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + while (size > 0) { + args.data_len = MIN(xfer, size); + err = nvme_fw_download(&args); + if (err) + break; + + args.data += xfer; + size -= xfer; + args.offset += xfer; + } + + return err; +} + +int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *data_tx) +{ + _cleanup_free_ struct nvme_id_ctrl *id_ctrl = NULL; + int err; + + id_ctrl = __nvme_alloc(sizeof(*id_ctrl)); + if (!id_ctrl) { + errno = ENOMEM; + return -1; + } + err = nvme_identify_ctrl(fd, id_ctrl); + if (err) + return err; + + if (data_tx) { + *data_tx = id_ctrl->mdts; + if (id_ctrl->mdts) { + /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least + * 4096 bytes + */ + *data_tx = (1 << id_ctrl->mdts) * 4096; + } + } + if (da) { + if (id_ctrl->lpa & 0x8) + *da = NVME_TELEMETRY_DA_3; + if (id_ctrl->lpa & 0x40) + *da = NVME_TELEMETRY_DA_4; + + } + return err; +} + +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, + enum nvme_telemetry_da da, struct nvme_telemetry_log **buf, + size_t *size) +{ + static const __u32 xfer = NVME_LOG_TELEM_BLOCK_SIZE; + + struct nvme_telemetry_log *telem; + enum nvme_cmd_get_log_lid lid; + _cleanup_free_ void *log; + void *tmp; + int err; + size_t dalb; + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = NVME_NSID_NONE, + .lsp = NVME_LOG_LSP_NONE, + .lsi = NVME_LOG_LSI_NONE, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + .csi = NVME_CSI_NVM, + .rae = rae, + .ot = false, + }; + + *size = 0; + + log = malloc(xfer); + if (!log) { + errno = ENOMEM; + return -1; + } + + if (ctrl) { + err = nvme_get_log_telemetry_ctrl(fd, true, 0, xfer, log); + lid = NVME_LOG_LID_TELEMETRY_CTRL; + } else { + lid = NVME_LOG_LID_TELEMETRY_HOST; + if (create) + err = nvme_get_log_create_telemetry_host(fd, log); + else + err = nvme_get_log_telemetry_host(fd, 0, xfer, log); + } + + if (err) + return err; + + telem = log; + if (ctrl && !telem->ctrlavail) { + *buf = log; + log = NULL; + *size = xfer; + return 0; + } + + switch (da) { + case NVME_TELEMETRY_DA_1: + dalb = le16_to_cpu(telem->dalb1); + break; + case NVME_TELEMETRY_DA_2: + dalb = le16_to_cpu(telem->dalb2); + break; + case NVME_TELEMETRY_DA_3: + /* dalb3 >= dalb2 >= dalb1 */ + dalb = le16_to_cpu(telem->dalb3); + break; + case NVME_TELEMETRY_DA_4: + dalb = le32_to_cpu(telem->dalb4); + break; + default: + errno = EINVAL; + return -1; + } + + if (dalb == 0) { + errno = ENOENT; + return -1; + } + + *size = (dalb + 1) * xfer; + tmp = realloc(log, *size); + if (!tmp) { + errno = ENOMEM; + return -1; + } + log = tmp; + + args.lid = lid; + args.log = log; + args.len = *size; + err = nvme_get_log_page(fd, max_data_tx, &args); + if (err) + return err; + + *buf = log; + log = NULL; + return 0; +} + + +static int nvme_check_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, + struct nvme_telemetry_log **log, enum nvme_telemetry_da da, + size_t *size) +{ + enum nvme_telemetry_da max_da = 0; + int err = nvme_get_telemetry_max(fd, &max_da, NULL); + + if (err) + return err; + if (da > max_da) { + errno = ENOENT; + return -1; + } + return nvme_get_telemetry_log(fd, create, ctrl, rae, 4096, da, log, size); +} + + +int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size) +{ + return nvme_check_get_telemetry_log(fd, false, true, rae, log, da, size); +} + +int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size) +{ + return nvme_check_get_telemetry_log(fd, false, false, false, log, da, size); +} + +int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size) +{ + return nvme_check_get_telemetry_log(fd, true, false, false, log, da, size); +} + +int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log) +{ + __u32 size; + _cleanup_free_ struct nvme_lba_status_log *buf; + void *tmp; + int err; + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = NVME_NSID_NONE, + .lsp = NVME_LOG_LSP_NONE, + .lsi = NVME_LOG_LSI_NONE, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + .csi = NVME_CSI_NVM, + .rae = rae, + .ot = false, + }; + + buf = malloc(sizeof(*buf)); + if (!buf) + return -1; + + err = nvme_get_log_lba_status(fd, true, 0, sizeof(*buf), buf); + if (err) { + *log = NULL; + return err; + } + + size = le32_to_cpu(buf->lslplen); + if (!size) { + *log = buf; + buf = NULL; + return 0; + } + + tmp = realloc(buf, size); + if (!tmp) { + *log = NULL; + return -1; + } + buf = tmp; + + args.lid = NVME_LOG_LID_LBA_STATUS; + args.log = buf; + args.len = size; + err = nvme_get_log_page(fd, 4096, &args); + if (err) { + *log = NULL; + return err; + } + + *log = buf; + buf = NULL; + return 0; +} + +static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, + __u16 *ctrlist, bool attach, __u32 timeout) +{ + struct nvme_ctrl_list cntlist = { 0 }; + struct nvme_ns_attach_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + .ctrlist = &cntlist, + .timeout = timeout, + }; + + if (attach) + args.sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH; + + nvme_init_ctrl_list(args.ctrlist, num_ctrls, ctrlist); + return nvme_ns_attach(&args); +} + +int nvme_namespace_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, + __u16 *ctrlist) +{ + return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, true, + NVME_DEFAULT_IOCTL_TIMEOUT); +} + +int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, + __u16 *ctrlist) +{ + return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, false, + NVME_DEFAULT_IOCTL_TIMEOUT); +} + +int nvme_get_ana_log_len(int fd, size_t *analen) +{ + _cleanup_free_ struct nvme_id_ctrl *ctrl = NULL; + int ret; + + ctrl = __nvme_alloc(sizeof(*ctrl)); + if (!ctrl) { + errno = ENOMEM; + return -1; + } + ret = nvme_identify_ctrl(fd, ctrl); + if (ret) + return ret; + + *analen = sizeof(struct nvme_ana_log) + + le32_to_cpu(ctrl->nanagrpid) * sizeof(struct nvme_ana_group_desc) + + le32_to_cpu(ctrl->mnan) * sizeof(__le32); + return 0; +} + +int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize) +{ + _cleanup_free_ struct nvme_id_ns *ns = NULL; + __u8 flbas; + int ret; + + ns = __nvme_alloc(sizeof(*ns)); + if (!ns) { + errno = ENOMEM; + return -1; + } + ret = nvme_identify_ns(fd, nsid, ns); + if (ret) + return ret; + + nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &flbas); + *blksize = 1 << ns->lbaf[flbas].ds; + + return 0; +} + +static int __nvme_set_attr(const char *path, const char *value) +{ + _cleanup_fd_ int fd = -1; + + fd = open(path, O_WRONLY); + if (fd < 0) { +#if 0 + nvme_msg(LOG_DEBUG, "Failed to open %s: %s\n", path, + strerror(errno)); +#endif + return -1; + } + return write(fd, value, strlen(value)); +} + +int nvme_set_attr(const char *dir, const char *attr, const char *value) +{ + _cleanup_free_ char *path = NULL; + int ret; + + ret = asprintf(&path, "%s/%s", dir, attr); + if (ret < 0) + return -1; + + return __nvme_set_attr(path, value); +} + +static char *__nvme_get_attr(const char *path) +{ + char value[4096] = { 0 }; + int ret, fd; + int saved_errno; + + fd = open(path, O_RDONLY); + if (fd < 0) + return NULL; + + ret = read(fd, value, sizeof(value) - 1); + saved_errno = errno; + close(fd); + if (ret < 0) { + errno = saved_errno; + return NULL; + } + errno = 0; + if (!strlen(value)) + return NULL; + + if (value[strlen(value) - 1] == '\n') + value[strlen(value) - 1] = '\0'; + while (strlen(value) > 0 && value[strlen(value) - 1] == ' ') + value[strlen(value) - 1] = '\0'; + + return strlen(value) ? strdup(value) : NULL; +} + +char *nvme_get_attr(const char *dir, const char *attr) +{ + _cleanup_free_ char *path = NULL; + int ret; + + ret = asprintf(&path, "%s/%s", dir, attr); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + + return __nvme_get_attr(path); +} + +char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr) +{ + return nvme_get_attr(nvme_subsystem_get_sysfs_dir(s), attr); +} + +char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr) +{ + return nvme_get_attr(nvme_ctrl_get_sysfs_dir(c), attr); +} + +char *nvme_get_ns_attr(nvme_ns_t n, const char *attr) +{ + return nvme_get_attr(nvme_ns_get_sysfs_dir(n), attr); +} + +char *nvme_get_path_attr(nvme_path_t p, const char *attr) +{ + return nvme_get_attr(nvme_path_get_sysfs_dir(p), attr); +} + +#ifndef CONFIG_OPENSSL +int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, + unsigned int key_len, unsigned char *secret, + unsigned char *key) +{ + if (hmac != NVME_HMAC_ALG_NONE) { + nvme_msg(NULL, LOG_ERR, "HMAC transformation not supported; " \ + "recompile with OpenSSL support.\n"); + errno = -EINVAL; + return -1; + } + + memcpy(key, secret, key_len); + return 0; +} + +static int derive_retained_key(int hmac, const char *hostnqn, + unsigned char *generated, + unsigned char *retained, + size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; +} + +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) +{ + if (version != 0) { + nvme_msg(NULL, LOG_ERR, "NVMe TLS 2.0 is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; + } + sprintf(identity, "NVMe0R%02d %s %s", + hmac, hostnqn, subsysnqn); + return strlen(identity); +} + +static int derive_tls_key(int hmac, const char *identity, + unsigned char *retained, + unsigned char *psk, size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; +} +#else /* CONFIG_OPENSSL */ +static const EVP_MD *select_hmac(int hmac, size_t *key_len) +{ + const EVP_MD *md = NULL; + + switch (hmac) { + case NVME_HMAC_ALG_SHA2_256: + md = EVP_sha256(); + *key_len = 32; + break; + case NVME_HMAC_ALG_SHA2_384: + md = EVP_sha384(); + *key_len = 48; + break; + default: + break; + } + return md; +} + +static DEFINE_CLEANUP_FUNC( + cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free) +#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx) + +static int derive_retained_key(int hmac, const char *hostnqn, + unsigned char *generated, + unsigned char *retained, + size_t key_len) +{ + const EVP_MD *md; + _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; + uint16_t length = key_len & 0xFFFF; + size_t hmac_len; + + md = select_hmac(hmac, &hmac_len); + if (!md || hmac_len > key_len) { + errno = EINVAL; + return -1; + } + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"tls13 ", 6) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"HostNQN", 7) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) { + errno = ENOKEY; + return -1; + } + + if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) { + errno = ENOKEY; + return -1; + } + + return key_len; +} + +static int derive_tls_key(int hmac, const char *identity, + unsigned char *retained, + unsigned char *psk, size_t key_len) +{ + const EVP_MD *md; + _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; + size_t hmac_len; + uint16_t length = key_len & 0xFFFF; + + md = select_hmac(hmac, &hmac_len); + if (!md || hmac_len > key_len) { + errno = EINVAL; + return -1; + } + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"tls13 ", 6) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"nvme-tls-psk", 12) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)identity, + strlen(identity)) <= 0) { + errno = ENOKEY; + return -1; + } + + if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) { + errno = ENOKEY; + return -1; + } + + return key_len; +} +#endif /* CONFIG_OPENSSL */ + +#ifdef CONFIG_OPENSSL_1 +static DEFINE_CLEANUP_FUNC(cleanup_hmac_ctx, HMAC_CTX *, HMAC_CTX_free) +#define _cleanup_hmac_ctx_ __cleanup__(cleanup_hmac_ctx) + +int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, + unsigned int key_len, unsigned char *secret, + unsigned char *key) +{ + const char hmac_seed[] = "NVMe-over-Fabrics"; + _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL; + const EVP_MD *md; + + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + + hmac_ctx = HMAC_CTX_new(); + if (!hmac_ctx) { + errno = ENOMEM; + return -1; + } + + switch (hmac) { + case NVME_HMAC_ALG_NONE: + memcpy(key, secret, key_len); + return 0; + case NVME_HMAC_ALG_SHA2_256: + md = EVP_sha256(); + break; + case NVME_HMAC_ALG_SHA2_384: + md = EVP_sha384(); + break; + case NVME_HMAC_ALG_SHA2_512: + md = EVP_sha512(); + break; + default: + errno = EINVAL; + return -1; + } + + if (!md) { + errno = EINVAL; + return -1; + } + + if (!HMAC_Init_ex(hmac_ctx, secret, key_len, md, NULL)) { + errno = ENOMEM; + return -1; + } + + if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + + if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + + if (!HMAC_Final(hmac_ctx, key, &key_len)) { + errno = ENOKEY; + return -1; + } + + return 0; +} + +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) +{ + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + const EVP_MD *md = select_hmac(hmac, &hmac_len); + _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL; + _cleanup_free_ unsigned char *psk_ctx = NULL; + _cleanup_free_ char *enc_ctx = NULL; + size_t len; + + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + hmac_ctx = HMAC_CTX_new(); + if (!hmac_ctx) { + errno = ENOMEM; + return -1; + } + if (!md) { + errno = EINVAL; + return -1; + } + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + return -1; + } + if (!HMAC_Init_ex(hmac_ctx, retained, key_len, md, NULL)) { + errno = ENOMEM; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Final(hmac_ctx, psk_ctx, (unsigned int *)&key_len)) { + errno = ENOKEY; + return -1; + } + enc_ctx = malloc(key_len * 2); + memset(enc_ctx, 0, key_len * 2); + len = base64_encode(psk_ctx, key_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + return len; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + return strlen(identity); +} +#endif /* !CONFIG_OPENSSL_1 */ + +#ifdef CONFIG_OPENSSL_3 +static DEFINE_CLEANUP_FUNC( + cleanup_ossl_lib_ctx, OSSL_LIB_CTX *, OSSL_LIB_CTX_free) +#define _cleanup_ossl_lib_ctx_ __cleanup__(cleanup_ossl_lib_ctx) +static DEFINE_CLEANUP_FUNC(cleanup_evp_mac_ctx, EVP_MAC_CTX *, EVP_MAC_CTX_free) +#define _cleanup_evp_mac_ctx_ __cleanup__(cleanup_evp_mac_ctx) +static DEFINE_CLEANUP_FUNC(cleanup_evp_mac, EVP_MAC *, EVP_MAC_free) +#define _cleanup_evp_mac_ __cleanup__(cleanup_evp_mac) + +int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, + unsigned int key_len, unsigned char *secret, + unsigned char *key) +{ + const char hmac_seed[] = "NVMe-over-Fabrics"; + OSSL_PARAM params[2], *p = params; + _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL; + _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; + _cleanup_evp_mac_ EVP_MAC *mac = NULL; + char *progq = NULL; + char *digest; + size_t len; + + lib_ctx = OSSL_LIB_CTX_new(); + if (!lib_ctx) { + errno = ENOMEM; + return -1; + } + + mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq); + if (!mac) { + errno = ENOMEM; + return -1; + } + + mac_ctx = EVP_MAC_CTX_new(mac); + if (!mac_ctx) { + errno = ENOMEM; + return -1; + } + + switch (hmac) { + case NVME_HMAC_ALG_NONE: + memcpy(key, secret, key_len); + return 0; + case NVME_HMAC_ALG_SHA2_256: + digest = OSSL_DIGEST_NAME_SHA2_256; + break; + case NVME_HMAC_ALG_SHA2_384: + digest = OSSL_DIGEST_NAME_SHA2_384; + break; + case NVME_HMAC_ALG_SHA2_512: + digest = OSSL_DIGEST_NAME_SHA2_512; + break; + default: + errno = EINVAL; + return -1; + } + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + digest, + 0); + *p = OSSL_PARAM_construct_end(); + + if (!EVP_MAC_init(mac_ctx, secret, key_len, params)) { + errno = ENOKEY; + return -1; + } + + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + + if (!EVP_MAC_final(mac_ctx, key, &len, key_len)) { + errno = ENOKEY; + return -1; + } + + if (len != key_len) { + errno = EMSGSIZE; + return -1; + } + + return 0; +} + +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) +{ + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + OSSL_PARAM params[2], *p = params; + _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL; + _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; + _cleanup_evp_mac_ EVP_MAC *mac = NULL; + char *progq = NULL; + char *digest = NULL; + _cleanup_free_ unsigned char *psk_ctx = NULL; + _cleanup_free_ char *enc_ctx = NULL; + size_t len; + + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + lib_ctx = OSSL_LIB_CTX_new(); + if (!lib_ctx) { + errno = ENOMEM; + return -1; + } + mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq); + if (!mac) { + errno = ENOMEM; + return -1; + } + + mac_ctx = EVP_MAC_CTX_new(mac); + if (!mac_ctx) { + errno = ENOMEM; + return -1; + } + switch (hmac) { + case NVME_HMAC_ALG_SHA2_256: + digest = OSSL_DIGEST_NAME_SHA2_256; + break; + case NVME_HMAC_ALG_SHA2_384: + digest = OSSL_DIGEST_NAME_SHA2_384; + break; + default: + errno = EINVAL; + break; + } + if (!digest) + return -1; + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + digest, 0); + *p = OSSL_PARAM_construct_end(); + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + return -1; + } + + if (!EVP_MAC_init(mac_ctx, retained, key_len, params)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_final(mac_ctx, psk_ctx, &hmac_len, key_len)) { + errno = ENOKEY; + return -1; + } + if (hmac_len > key_len) { + errno = EMSGSIZE; + return -1; + } + enc_ctx = malloc(hmac_len * 2); + memset(enc_ctx, 0, hmac_len * 2); + len = base64_encode(psk_ctx, hmac_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + return len; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + return strlen(identity); +} +#endif /* !CONFIG_OPENSSL_3 */ + +static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, + char *identity, int version, + int hmac, unsigned char *configured, + unsigned char *psk, int key_len) +{ + _cleanup_free_ unsigned char *retained = NULL; + int ret = -1; + + if (!hostnqn || !subsysnqn || !identity || !psk) { + errno = EINVAL; + return -1; + } + + retained = malloc(key_len); + if (!retained) { + errno = ENOMEM; + return -1; + } + ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len); + if (ret < 0) + return ret; + ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac, + identity, retained, key_len); + if (ret < 0) + return ret; + return derive_tls_key(hmac, identity, retained, psk, key_len); +} + +static size_t nvme_identity_len(int hmac, int version, const char *hostnqn, + const char *subsysnqn) +{ + size_t len; + + if (!hostnqn || !subsysnqn) { + errno = EINVAL; + return -1; + } + + len = strlen(hostnqn) + strlen(subsysnqn) + 12; + if (version == 1) { + len += 66; + if (hmac == NVME_HMAC_ALG_SHA2_384) + len += 32; + } else if (version > 1) { + errno = EINVAL; + return -1; + } + return len; +} + +char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + char *identity; + size_t identity_len; + _cleanup_free_ unsigned char *psk = NULL; + int ret = -1; + + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) + return NULL; + + identity = malloc(identity_len); + if (!identity) + return NULL; + + psk = malloc(key_len); + if (!psk) + goto out_free_identity; + + memset(psk, 0, key_len); + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, + configured_key, psk, key_len); +out_free_identity: + if (ret < 0) { + free(identity); + identity = NULL; + } + return identity; +} + +#ifdef CONFIG_KEYUTILS +long nvme_lookup_keyring(const char *keyring) +{ + key_serial_t keyring_id; + + keyring_id = find_key_by_type_and_desc("keyring", keyring, 0); + if (keyring_id < 0) + return 0; + return keyring_id; +} + +char *nvme_describe_key_serial(long key_id) +{ + char *desc; + + if (keyctl_describe_alloc(key_id, &desc) < 0) + desc = NULL; + return desc; +} + +long nvme_lookup_key(const char *type, const char *identity) +{ + key_serial_t key; + + key = keyctl_search(KEY_SPEC_SESSION_KEYRING, type, identity, 0); + if (key < 0) + return 0; + return key; +} + +int nvme_set_keyring(long key_id) +{ + long err; + + err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING); + if (err < 0) + return -1; + return 0; +} + +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + key_serial_t keyring_id, key; + _cleanup_free_ char *identity = NULL; + size_t identity_len; + _cleanup_free_ unsigned char *psk = NULL; + int ret = -1; + + keyring_id = nvme_lookup_keyring(keyring); + if (keyring_id == 0) + return -1; + + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) + return -1; + + identity = malloc(identity_len); + if (!identity) { + errno = ENOMEM; + return -1; + } + + psk = malloc(key_len); + if (!psk) { + errno = ENOMEM; + return 0; + } + memset(psk, 0, key_len); + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, + configured_key, psk, key_len); + if (ret != key_len) + return 0; + + key = keyctl_search(keyring_id, key_type, identity, 0); + if (key > 0) { + if (keyctl_update(key, psk, key_len) < 0) + key = 0; + } else { + key = add_key(key_type, identity, + psk, key_len, keyring_id); + if (key < 0) + key = 0; + } + return key; +} + +#else +long nvme_lookup_keyring(const char *keyring) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return 0; +} + +char *nvme_describe_key_serial(long key_id) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return NULL; +} + +long nvme_lookup_key(const char *type, const char *identity) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return 0; +} + +int nvme_set_keyring(long key_id) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return -1; +} + +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; " + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return -1; +} +#endif + +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len) +{ + return nvme_insert_tls_key_versioned(keyring, key_type, + hostnqn, subsysnqn, 0, hmac, + configured_key, key_len); +} diff --git a/src/nvme/linux.h b/src/nvme/linux.h new file mode 100644 index 0000000..11ee76e --- /dev/null +++ b/src/nvme/linux.h @@ -0,0 +1,338 @@ +// 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> + */ +#ifndef _LIBNVME_LINUX_H +#define _LIBNVME_LINUX_H + +#include <stddef.h> + +#include "ioctl.h" +#include "types.h" + +/** + * DOC: linux.h + * + * linux-specific utility functions + */ + +/** + * nvme_fw_download_seq() - Firmware download sequence + * @fd: File descriptor of nvme device + * @size: Total size of the firmware image to transfer + * @xfer: Maximum size to send with each partial transfer + * @offset: Starting offset to send with this firmware download + * @buf: Address of buffer containing all or part of the firmware image. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset, + void *buf); + +/** + * enum nvme_telemetry_da - Telemetry Log Data Area + * @NVME_TELEMETRY_DA_1: Data Area 1 + * @NVME_TELEMETRY_DA_2: Data Area 2 + * @NVME_TELEMETRY_DA_3: Data Area 3 + * @NVME_TELEMETRY_DA_4: Data Area 4 + */ +enum nvme_telemetry_da { + NVME_TELEMETRY_DA_1 = 1, + NVME_TELEMETRY_DA_2 = 2, + NVME_TELEMETRY_DA_3 = 3, + NVME_TELEMETRY_DA_4 = 4, +}; + +/** + * nvme_get_telemetry_max() - Get telemetry limits + * @fd: File descriptor of nvme device + * @da: On success return max supported data area + * @max_data_tx: On success set to max transfer chunk supported by the controller + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *max_data_tx); + +/** + * nvme_get_telemetry_log() - Get specified telemetry log + * @fd: File descriptor of nvme device + * @create: Generate new host initated telemetry capture + * @ctrl: Get controller Initiated log + * @rae: Retain asynchronous events + * @max_data_tx: Set the max data transfer size to be used retrieving telemetry. + * @da: Log page data area, valid values: &enum nvme_telemetry_da. + * @log: On success, set to the value of the allocated and retrieved log. + * @size: Ptr to the telemetry log size, so it can be returned + * + * The total size allocated can be calculated as: + * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, + enum nvme_telemetry_da da, struct nvme_telemetry_log **log, + size_t *size); +/** + * nvme_get_ctrl_telemetry() - Get controller telemetry log + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: On success, set to the value of the allocated and retrieved log. + * @da: Log page data area, valid values: &enum nvme_telemetry_da + * @size: Ptr to the telemetry log size, so it can be returned + * + * The total size allocated can be calculated as: + * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size); + +/** + * nvme_get_host_telemetry() - Get host telemetry log + * @fd: File descriptor of nvme device + * @log: On success, set to the value of the allocated and retrieved log. + * @da: Log page data area, valid values: &enum nvme_telemetry_da + * @size: Ptr to the telemetry log size, so it can be returned + * + * The total size allocated can be calculated as: + * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size); + +/** + * nvme_get_new_host_telemetry() - Get new host telemetry log + * @fd: File descriptor of nvme device + * @log: On success, set to the value of the allocated and retrieved log. + * @da: Log page data area, valid values: &enum nvme_telemetry_da + * @size: Ptr to the telemetry log size, so it can be returned + * + * The total size allocated can be calculated as: + * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log, + enum nvme_telemetry_da da, size_t *size); + +/** + * nvme_get_ana_log_len() - Retrieve size of the current ANA log + * @fd: File descriptor of nvme device + * @analen: Pointer to where the length will be set on success + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_ana_log_len(int fd, size_t *analen); + +/** + * nvme_get_logical_block_size() - Retrieve block size + * @fd: File descriptor of nvme device + * @nsid: Namespace id + * @blksize: Pointer to where the block size will be set on success + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize); + +/** + * nvme_get_lba_status_log() - Retrieve the LBA Status log page + * @fd: File descriptor of the nvme device + * @rae: Retain asynchronous events + * @log: On success, set to the value of the allocated and retrieved log. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log); + +/** + * nvme_namespace_attach_ctrls() - Attach namespace to controller(s) + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to attach + * @num_ctrls: Number of controllers in ctrlist + * @ctrlist: List of controller IDs to perform the attach action + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_namespace_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist); + +/** + * nvme_namespace_detach_ctrls() - Detach namespace from controller(s) + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to detach + * @num_ctrls: Number of controllers in ctrlist + * @ctrlist: List of controller IDs to perform the detach action + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist); + +/** + * nvme_open() - Open an nvme controller or namespace device + * @name: The basename of the device to open + * + * This will look for the handle in /dev/ and validate the name and filetype + * match linux conventions. + * + * Return: A file descriptor for the device on a successful open, or -1 with + * errno set otherwise. + */ +int nvme_open(const char *name); + +/** + * enum nvme_hmac_alg - HMAC algorithm + * @NVME_HMAC_ALG_NONE: No HMAC algorithm + * @NVME_HMAC_ALG_SHA2_256: SHA2-256 + * @NVME_HMAC_ALG_SHA2_384: SHA2-384 + * @NVME_HMAC_ALG_SHA2_512: SHA2-512 + */ +enum nvme_hmac_alg { + NVME_HMAC_ALG_NONE = 0, + NVME_HMAC_ALG_SHA2_256 = 1, + NVME_HMAC_ALG_SHA2_384 = 2, + NVME_HMAC_ALG_SHA2_512 = 3, +}; + +/** + * nvme_gen_dhchap_key() - DH-HMAC-CHAP key generation + * @hostnqn: Host NVMe Qualified Name + * @hmac: HMAC algorithm + * @key_len: Output key length + * @secret: Secret to used for digest + * @key: Generated DH-HMAC-CHAP key + * + * Return: If key generation was successful the function returns 0 or + * -1 with errno set otherwise. + */ +int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, + unsigned int key_len, unsigned char *secret, + unsigned char *key); + +/** + * nvme_lookup_keyring() - Lookup keyring serial number + * @keyring: Keyring name + * + * Looks up the serial number of the keyring @keyring. + * + * Return: The key serial number of the keyring + * or 0 with errno set otherwise. + */ +long nvme_lookup_keyring(const char *keyring); + +/** + * nvme_describe_key_serial() - Return key description + * @key_id: Key serial number + * + * Fetches the description of the key or keyring identified + * by the serial number @key_id. + * + * Return: The description of @key_id or NULL on failure. + * The returned string needs to be freed by the caller. + */ +char *nvme_describe_key_serial(long key_id); + +/** + * nvme_lookup_key() - Lookup key serial number + * @type: Key type + * @identity: Key description + * + * Looks up the serial number of the key @identity + * with type %type in the current session keyring. + * + * Return: The key serial number of the key + * or 0 with errno set otherwise. + */ +long nvme_lookup_key(const char *type, const char *identity); + +/** + * nvme_set_keyring() - Link keyring for lookup + * @keyring_id: Keyring id + * + * Links @keyring_id into the session keyring such that + * its keys are available for further key lookups. + * + * Return: 0 on success, a negative number on error + * with errno set. + */ +int nvme_set_keyring(long keyring_id); + +/** + * nvme_insert_tls_key() - Derive and insert TLS key + * @keyring: Keyring to use + * @key_type: Type of the resulting key + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and + * stores it as type @key_type in the keyring specified by @keyring. + * + * Return: The key serial number if the key could be inserted into + * the keyring or 0 with errno otherwise. + */ +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len); + +/** + * nvme_insert_tls_key_versioned() - Derive and insert TLS key + * @keyring: Keyring to use + * @key_type: Type of the resulting key + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @version: Key version to use + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a (if + * @version s set to '0') or NVMe TP8028 (if @version is set to '1) and + * stores it as type @key_type in the keyring specified by @keyring. + * + * Return: The key serial number if the key could be inserted into + * the keyring or 0 with errno otherwise. + */ +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len); + +/** + * nvme_generate_tls_key_identity() - Generate the TLS key identity + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @version: Key version to use + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP and + * generate the corresponding TLs identity. + * + * Return: The string containing the TLS identity. It is the responsibility + * of the caller to free the returned string. + */ +char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len); + +#endif /* _LIBNVME_LINUX_H */ diff --git a/src/nvme/log.c b/src/nvme/log.c new file mode 100644 index 0000000..2ffca3e --- /dev/null +++ b/src/nvme/log.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (C) 2021 SUSE LLC + * + * Authors: Hannes Reinecke <hare@suse.de> + * + * This file implements basic logging functionality. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <syslog.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#define LOG_FUNCNAME 1 +#include "private.h" +#include "log.h" +#include "cleanup.h" + +#ifndef LOG_CLOCK +#define LOG_CLOCK CLOCK_MONOTONIC +#endif + +static nvme_root_t root; + +void __attribute__((format(printf, 4, 5))) +__nvme_msg(nvme_root_t r, int lvl, + const char *func, const char *format, ...) +{ + FILE *fp = stderr; + va_list ap; + char pidbuf[16]; + char timebuf[32]; + static const char *const formats[] = { + "%s%s%s", + "%s%s%s: ", + "%s<%s>%s ", + "%s<%s> %s: ", + "[%s] %s%s ", + "[%s]%s %s: ", + "[%s] <%s>%s ", + "[%s] <%s> %s: ", + }; + _cleanup_free_ char *header = NULL; + _cleanup_free_ char *message = NULL; + int idx = 0; + + if (!r) + r = root; + + if (r) + fp = r->fp; + + if (r && lvl > r->log_level) + return; + + if (r && r->log_timestamp) { + struct timespec now; + + clock_gettime(LOG_CLOCK, &now); + snprintf(timebuf, sizeof(timebuf), "%6ld.%06ld", + (long)now.tv_sec, now.tv_nsec / 1000); + idx |= 1 << 2; + } else + *timebuf = '\0'; + + if (r && r->log_pid) { + snprintf(pidbuf, sizeof(pidbuf), "%ld", (long)getpid()); + idx |= 1 << 1; + } else + *pidbuf = '\0'; + + if (func) + idx |= 1 << 0; + + if (asprintf(&header, formats[idx], + timebuf, pidbuf, func ? func : "") == -1) + header = NULL; + + va_start(ap, format); + if (vasprintf(&message, format, ap) == -1) + message = NULL; + va_end(ap); + + fprintf(fp, "%s%s", + header ? header : "<error>", + message ? message : "<error>"); +} + +void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp) +{ + r->log_level = lvl; + r->log_pid = log_pid; + r->log_timestamp = log_tstamp; +} + +void nvme_set_root(nvme_root_t r) +{ + root = r; +} diff --git a/src/nvme/log.h b/src/nvme/log.h new file mode 100644 index 0000000..7c345f6 --- /dev/null +++ b/src/nvme/log.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (c) 2021 Martin Wilck, SUSE LLC + */ +#ifndef _LOG_H +#define _LOG_H + +#include <stdbool.h> +#include <syslog.h> + +/* for nvme_root_t */ +#include "tree.h" + +#ifndef MAX_LOGLEVEL +# define MAX_LOGLEVEL LOG_DEBUG +#endif +#ifndef DEFAULT_LOGLEVEL +# define DEFAULT_LOGLEVEL LOG_NOTICE +#endif + +/** + * DOC: log.h + * + * logging functions + */ + +/** + * nvme_init_logging() - Initialize logging + * @r: nvme_root_t context + * @lvl: Logging level to set + * @log_pid: Boolean to enable logging of the PID + * @log_tstamp: Boolean to enable logging of the timestamp + * + * Sets the default logging variables for the library. + */ +void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp); + +/** + * nvme_set_root() - Set nvme_root_t context + * @r: nvme_root_t context + * + * In order to be able to log from code paths where no root object is passed in + * via the arguments use the the default one which can be set via this call. + * When creating a new root object with @nvme_create_root the global root object + * will be set as well. This means the global root object is always pointing to + * the latest created root object. Note the first @nvme_free_tree call will reset + * the global root object. + */ +void nvme_set_root(nvme_root_t r); + +#endif /* _LOG_H */ diff --git a/src/nvme/mi-mctp.c b/src/nvme/mi-mctp.c new file mode 100644 index 0000000..86c4c29 --- /dev/null +++ b/src/nvme/mi-mctp.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021 Code Construct Pty Ltd + * + * Authors: Jeremy Kerr <jk@codeconstruct.com.au> + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> + +#if HAVE_LINUX_MCTP_H +#include <linux/mctp.h> +#endif + +#include <ccan/endian/endian.h> + +#ifdef CONFIG_DBUS +#include <dbus/dbus.h> + +#define MCTP_DBUS_PATH "/xyz/openbmc_project/mctp" +#define MCTP_DBUS_IFACE "xyz.openbmc_project.MCTP" +#define MCTP_DBUS_IFACE_ENDPOINT "xyz.openbmc_project.MCTP.Endpoint" +#endif + +#include "private.h" +#include "log.h" +#include "mi.h" + + +#if !defined(AF_MCTP) +#define AF_MCTP 45 +#endif + +#if !HAVE_LINUX_MCTP_H +/* As of kernel v5.15, these AF_MCTP-related definitions are provided by + * linux/mctp.h. However, we provide a set here while that header percolates + * through to standard includes. + * + * These were all introduced in the same version as AF_MCTP was defined, + * so we can key off the presence of that. + */ + +typedef __u8 mctp_eid_t; + +struct mctp_addr { + mctp_eid_t s_addr; +}; + +struct sockaddr_mctp { + unsigned short int smctp_family; + __u16 __smctp_pad0; + unsigned int smctp_network; + struct mctp_addr smctp_addr; + __u8 smctp_type; + __u8 smctp_tag; + __u8 __smctp_pad1; +}; + +#define MCTP_NET_ANY 0x0 + +#define MCTP_ADDR_NULL 0x00 +#define MCTP_ADDR_ANY 0xff + +#define MCTP_TAG_MASK 0x07 +#define MCTP_TAG_OWNER 0x08 + +#endif /* !AF_MCTP */ + +#define MCTP_TYPE_NVME 0x04 +#define MCTP_TYPE_MIC 0x80 + +struct nvme_mi_transport_mctp { + int net; + __u8 eid; + int sd; + void *resp_buf; + size_t resp_buf_size; +}; + +static int ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl) +{ + return ioctl(sd, req, ctl); +} + +static struct __mi_mctp_socket_ops ops = { + socket, + sendmsg, + recvmsg, + poll, + ioctl_tag, +}; + +void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops) +{ + ops = *newops; +} +static const struct nvme_mi_transport nvme_mi_transport_mctp; + +#ifdef SIOCMCTPALLOCTAG +static __u8 nvme_mi_mctp_tag_alloc(struct nvme_mi_ep *ep) +{ + struct nvme_mi_transport_mctp *mctp; + struct mctp_ioc_tag_ctl ctl = { 0 }; + static bool logged; + int rc; + + mctp = ep->transport_data; + + ctl.peer_addr = mctp->eid; + + errno = 0; + rc = ops.ioctl_tag(mctp->sd, SIOCMCTPALLOCTAG, &ctl); + if (rc) { + if (!logged) { + /* not necessarily fatal, just means we can't handle + * "more processing required" messages */ + nvme_msg(ep->root, LOG_INFO, + "System does not support explicit tag allocation\n"); + logged = true; + } + return MCTP_TAG_OWNER; + } + + return ctl.tag; +} + +static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag) +{ + struct nvme_mi_transport_mctp *mctp; + struct mctp_ioc_tag_ctl ctl = { 0 }; + + mctp = ep->transport_data; + + if (!(tag & MCTP_TAG_PREALLOC)) + return; + + ctl.peer_addr = mctp->eid; + ctl.tag = tag; + + ops.ioctl_tag(mctp->sd, SIOCMCTPDROPTAG, &ctl); +} + +#else /* !defined SIOMCTPTAGALLOC */ + +static __u8 nvme_mi_mctp_tag_alloc(struct nvme_mi_ep *ep) +{ + static bool logged; + if (!logged) { + nvme_msg(ep->root, LOG_INFO, + "Build does not support explicit tag allocation\n"); + logged = true; + } + return MCTP_TAG_OWNER; +} + +static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag) +{ +} + +#endif /* !defined SIOMCTPTAGALLOC */ + +struct nvme_mi_msg_resp_mpr { + struct nvme_mi_msg_hdr hdr; + __u8 status; + __u8 rsvd0; + __u16 mprt; +}; + +/* Check if this response was a More Processing Required response; if so, + * populate the worst-case expected processing time, given in milliseconds. + * + * buf is the incoming message data, including type byte, but excluding + * the MIC which has been extracted into the mic argument already. + */ +static bool nvme_mi_mctp_resp_is_mpr(void *buf, size_t len, + __le32 mic, unsigned int *mpr_time) +{ + struct nvme_mi_msg_resp_mpr *msg; + __u32 crc; + + /* We need at least the minimal header */ + if (len < sizeof(*msg)) + return false; + + msg = (struct nvme_mi_msg_resp_mpr *)buf; + + if (msg->status != NVME_MI_RESP_MPR) + return false; + + /* Devices may send a MPR response as a full-sized Admin response, + * rather than the minimal MI-only header. Allow this, but only if the + * type indicates admin, and the allocated response header is the + * correct size for an Admin response. + */ + if (!(len == sizeof(*msg) || + ((msg->hdr.nmp >> 3 & 0x0f) == NVME_MI_MT_ADMIN && + len == sizeof(struct nvme_mi_admin_resp_hdr)))) + return false; + + /* Verify the MIC from the response. We're dealing with linear + * header data here, and need to preserve the resp pointer & size + * values, so can't use verify_resp_mic here. + */ + crc = ~nvme_mi_crc32_update(0xffffffff, buf, len); + if (le32_to_cpu(mic) != crc) + return false; + + if (mpr_time) + *mpr_time = cpu_to_le16(msg->mprt) * 100; + + return true; +} + +static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep, + struct nvme_mi_req *req, + struct nvme_mi_resp *resp) +{ + ssize_t len, resp_len, resp_hdr_len, resp_data_len; + struct nvme_mi_transport_mctp *mctp; + struct iovec req_iov[3], resp_iov[1]; + struct msghdr req_msg, resp_msg; + int i, rc, errno_save, timeout; + struct sockaddr_mctp addr; + struct pollfd pollfds[1]; + unsigned int mpr_time; + __le32 mic; + __u8 tag; + + if (ep->transport != &nvme_mi_transport_mctp) { + errno = EINVAL; + return -1; + } + + /* we need enough space for at least a generic (/error) response */ + if (resp->hdr_len < sizeof(struct nvme_mi_msg_resp)) { + errno = EINVAL; + return -1; + } + + mctp = ep->transport_data; + tag = nvme_mi_mctp_tag_alloc(ep); + + memset(&addr, 0, sizeof(addr)); + addr.smctp_family = AF_MCTP; + addr.smctp_network = mctp->net; + addr.smctp_addr.s_addr = mctp->eid; + addr.smctp_type = MCTP_TYPE_NVME | MCTP_TYPE_MIC; + addr.smctp_tag = tag; + + i = 0; + req_iov[i].iov_base = ((__u8 *)req->hdr) + 1; + req_iov[i].iov_len = req->hdr_len - 1; + i++; + + if (req->data_len) { + req_iov[i].iov_base = req->data; + req_iov[i].iov_len = req->data_len; + i++; + } + + mic = cpu_to_le32(req->mic); + req_iov[i].iov_base = &mic; + req_iov[i].iov_len = sizeof(mic); + i++; + + memset(&req_msg, 0, sizeof(req_msg)); + req_msg.msg_name = &addr; + req_msg.msg_namelen = sizeof(addr); + req_msg.msg_iov = req_iov; + req_msg.msg_iovlen = i; + + len = ops.sendmsg(mctp->sd, &req_msg, 0); + if (len < 0) { + errno_save = errno; + nvme_msg(ep->root, LOG_ERR, + "Failure sending MCTP message: %m\n"); + errno = errno_save; + rc = -1; + goto out; + } + + resp_len = resp->hdr_len + resp->data_len + sizeof(mic); + if (resp_len > mctp->resp_buf_size) { + void *tmp = realloc(mctp->resp_buf, resp_len); + if (!tmp) { + errno_save = errno; + nvme_msg(ep->root, LOG_ERR, + "Failure allocating response buffer: %m\n"); + errno = errno_save; + rc = -1; + goto out; + } + mctp->resp_buf = tmp; + mctp->resp_buf_size = resp_len; + } + + /* offset by one: the MCTP message type is excluded from the buffer */ + resp_iov[0].iov_base = mctp->resp_buf + 1; + resp_iov[0].iov_len = resp_len - 1; + + memset(&resp_msg, 0, sizeof(resp_msg)); + resp_msg.msg_name = &addr; + resp_msg.msg_namelen = sizeof(addr); + resp_msg.msg_iov = resp_iov; + resp_msg.msg_iovlen = 1; + + pollfds[0].fd = mctp->sd; + pollfds[0].events = POLLIN; + timeout = ep->timeout ?: -1; +retry: + rc = ops.poll(pollfds, 1, timeout); + if (rc < 0) { + if (errno == EINTR) + goto retry; + errno_save = errno; + nvme_msg(ep->root, LOG_ERR, + "Failed polling on MCTP socket: %m"); + errno = errno_save; + goto out; + } + + if (rc == 0) { + nvme_msg(ep->root, LOG_DEBUG, "Timeout on MCTP socket"); + errno = ETIMEDOUT; + rc = -1; + goto out; + } + + rc = -1; + len = ops.recvmsg(mctp->sd, &resp_msg, MSG_DONTWAIT); + + if (len < 0) { + errno_save = errno; + nvme_msg(ep->root, LOG_ERR, + "Failure receiving MCTP message: %m\n"); + errno = errno_save; + goto out; + } + + + if (len == 0) { + nvme_msg(ep->root, LOG_WARNING, "No data from MCTP endpoint\n"); + errno = EIO; + goto out; + } + + /* Re-add the type byte, so we can work on aligned lengths from here */ + ((uint8_t *)mctp->resp_buf)[0] = MCTP_TYPE_NVME | MCTP_TYPE_MIC; + len += 1; + + /* The smallest response data is 8 bytes: generic 4-byte message header + * plus four bytes of error data (excluding MIC). Ensure we have enough. + */ + if (len < 8 + sizeof(mic)) { + nvme_msg(ep->root, LOG_ERR, + "Invalid MCTP response: too short (%zd bytes, needed %zd)\n", + len, 8 + sizeof(mic)); + errno = EPROTO; + goto out; + } + + /* Start unpacking the linear resp buffer into the split header + data + * + MIC. We check for a MPR response before fully unpacking, as we'll + * need to preserve the resp layout if we need to retry the receive. + */ + + /* MIC is always at the tail */ + memcpy(&mic, mctp->resp_buf + len - sizeof(mic), sizeof(mic)); + len -= 4; + + /* Check for a More Processing Required response. This is a slight + * layering violation, as we're pre-checking the MIC and inspecting + * header fields. However, we need to do this in the transport in order + * to keep the tag allocated and retry the recvmsg + */ + if (nvme_mi_mctp_resp_is_mpr(mctp->resp_buf, len, mic, &mpr_time)) { + nvme_msg(ep->root, LOG_DEBUG, + "Received More Processing Required, waiting for response\n"); + + /* if the controller hasn't set MPRT, fall back to our command/ + * response timeout, or the largest possible MPRT if none set */ + if (!mpr_time) + mpr_time = ep->timeout ?: 0xffff; + + /* clamp to the endpoint max */ + if (ep->mprt_max && mpr_time > ep->mprt_max) + mpr_time = ep->mprt_max; + + timeout = mpr_time; + goto retry; + } + + /* we expect resp->hdr_len bytes, but we may have less */ + resp_hdr_len = resp->hdr_len; + if (resp_hdr_len > len) + resp_hdr_len = len; + memcpy(resp->hdr, mctp->resp_buf, resp_hdr_len); + resp->hdr_len = resp_hdr_len; + len -= resp_hdr_len; + + /* any remaining bytes are the data payload */ + resp_data_len = resp->data_len; + if (resp_data_len > len) + resp_data_len = len; + memcpy(resp->data, mctp->resp_buf + resp_hdr_len, resp_data_len); + resp->data_len = resp_data_len; + + resp->mic = le32_to_cpu(mic); + + rc = 0; + +out: + nvme_mi_mctp_tag_drop(ep, tag); + + return rc; +} + +static void nvme_mi_mctp_close(struct nvme_mi_ep *ep) +{ + struct nvme_mi_transport_mctp *mctp; + + if (ep->transport != &nvme_mi_transport_mctp) + return; + + mctp = ep->transport_data; + close(mctp->sd); + free(mctp->resp_buf); + free(ep->transport_data); +} + +static int nvme_mi_mctp_desc_ep(struct nvme_mi_ep *ep, char *buf, size_t len) +{ + struct nvme_mi_transport_mctp *mctp; + + if (ep->transport != &nvme_mi_transport_mctp) { + errno = EINVAL; + return -1; + } + + mctp = ep->transport_data; + + snprintf(buf, len, "net %d eid %d", mctp->net, mctp->eid); + + return 0; +} + +static const struct nvme_mi_transport nvme_mi_transport_mctp = { + .name = "mctp", + .mic_enabled = true, + .submit = nvme_mi_mctp_submit, + .close = nvme_mi_mctp_close, + .desc_ep = nvme_mi_mctp_desc_ep, +}; + +nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid) +{ + struct nvme_mi_transport_mctp *mctp; + struct nvme_mi_ep *ep; + int errno_save; + + ep = nvme_mi_init_ep(root); + if (!ep) + return NULL; + + mctp = malloc(sizeof(*mctp)); + if (!mctp) { + errno_save = errno; + goto err_close_ep; + } + + memset(mctp, 0, sizeof(*mctp)); + mctp->sd = -1; + + mctp->resp_buf_size = 4096; + mctp->resp_buf = malloc(mctp->resp_buf_size); + if (!mctp->resp_buf) { + errno_save = errno; + goto err_free_mctp; + } + + mctp->net = netid; + mctp->eid = eid; + + mctp->sd = ops.socket(AF_MCTP, SOCK_DGRAM, 0); + if (mctp->sd < 0) { + errno_save = errno; + goto err_free_rspbuf; + } + + ep->transport = &nvme_mi_transport_mctp; + ep->transport_data = mctp; + + /* Assuming an i2c transport at 100kHz, smallest MTU (64+4). Given + * a worst-case clock stretch, and largest-sized packets, we can + * expect up to 1.6s per command/response pair. Allowing for a + * retry or two (handled by lower layers), 5s is a reasonable timeout. + */ + ep->timeout = 5000; + + nvme_mi_ep_probe(ep); + + return ep; + +err_free_rspbuf: + free(mctp->resp_buf); +err_free_mctp: + free(mctp); +err_close_ep: + /* the ep->transport is not set yet, so this will not call back + * into nvme_mi_mctp_close() */ + nvme_mi_close(ep); + errno = errno_save; + return NULL; +} + +#ifdef CONFIG_DBUS + +static int nvme_mi_mctp_add(nvme_root_t root, unsigned int netid, __u8 eid) +{ + nvme_mi_ep_t ep = NULL; + + /* ensure we don't already have an endpoint with the same net/eid. if + * we do, just skip, no need to re-add. */ + list_for_each(&root->endpoints, ep, root_entry) { + if (ep->transport != &nvme_mi_transport_mctp) { + continue; + } + const struct nvme_mi_transport_mctp *t = ep->transport_data; + if (t->eid == eid && t->net == netid) + return 0; + } + + ep = nvme_mi_open_mctp(root, netid, eid); + if (!ep) + return -1; + + return 0; +} + +static bool dbus_object_is_type(DBusMessageIter *obj, int type) +{ + return dbus_message_iter_get_arg_type(obj) == type; +} + +static bool dbus_object_is_dict(DBusMessageIter *obj) +{ + return dbus_object_is_type(obj, DBUS_TYPE_ARRAY) && + dbus_message_iter_get_element_type(obj) == DBUS_TYPE_DICT_ENTRY; +} + +static int read_variant_basic(DBusMessageIter *var, int type, void *val) +{ + if (!dbus_object_is_type(var, type)) + return -1; + + dbus_message_iter_get_basic(var, val); + + return 0; +} + +static bool has_message_type(DBusMessageIter *prop, uint8_t type) +{ + DBusMessageIter inner; + uint8_t *types; + int i, n; + + if (!dbus_object_is_type(prop, DBUS_TYPE_ARRAY) || + dbus_message_iter_get_element_type(prop) != DBUS_TYPE_BYTE) + return false; + + dbus_message_iter_recurse(prop, &inner); + + dbus_message_iter_get_fixed_array(&inner, &types, &n); + + for (i = 0; i < n; i++) { + if (types[i] == type) + return true; + } + + return false; +} + +static int handle_mctp_endpoint(nvme_root_t root, const char* objpath, + DBusMessageIter *props) +{ + bool have_eid = false, have_net = false, have_nvmemi = false; + mctp_eid_t eid; + int net; + int rc; + + /* for each property */ + for (;;) { + DBusMessageIter prop, val; + const char *propname; + + dbus_message_iter_recurse(props, &prop); + + if (!dbus_object_is_type(&prop, DBUS_TYPE_STRING)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (propname)\n"); + return -1; + } + + dbus_message_iter_get_basic(&prop, &propname); + + dbus_message_iter_next(&prop); + + if (!dbus_object_is_type(&prop, DBUS_TYPE_VARIANT)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (propval)\n"); + return -1; + } + + dbus_message_iter_recurse(&prop, &val); + + if (!strcmp(propname, "EID")) { + rc = read_variant_basic(&val, DBUS_TYPE_BYTE, &eid); + have_eid = true; + + } else if (!strcmp(propname, "NetworkId")) { + rc = read_variant_basic(&val, DBUS_TYPE_INT32, &net); + have_net = true; + + } else if (!strcmp(propname, "SupportedMessageTypes")) { + have_nvmemi = has_message_type(&val, MCTP_TYPE_NVME); + } + + if (rc) + return rc; + + if (!dbus_message_iter_next(props)) + break; + } + + if (have_nvmemi) { + if (!(have_eid && have_net)) { + nvme_msg(root, LOG_ERR, + "Missing property for %s\n", objpath); + errno = ENOENT; + return -1; + } + rc = nvme_mi_mctp_add(root, net, eid); + if (rc < 0) { + int errno_save = errno; + nvme_msg(root, LOG_ERR, + "Error adding net %d eid %d: %m\n", net, eid); + errno = errno_save; + } + } else { + /* Ignore other endpoints */ + rc = 0; + } + return rc; +} + +/* obj is an array of (object path, interfaces) dict entries - ie., dbus type + * a{oa{sa{sv}}} + */ +static int handle_mctp_obj(nvme_root_t root, DBusMessageIter *obj) +{ + const char *objpath = NULL; + DBusMessageIter intfs; + + if (!dbus_object_is_type(obj, DBUS_TYPE_OBJECT_PATH)) { + nvme_msg(root, LOG_ERR, "error unmashalling object (path)\n"); + return -1; + } + + dbus_message_iter_get_basic(obj, &objpath); + + dbus_message_iter_next(obj); + + if (!dbus_object_is_dict(obj)) { + nvme_msg(root, LOG_ERR, "error unmashalling object (intfs)\n"); + return -1; + } + + dbus_message_iter_recurse(obj, &intfs); + + /* for each interface */ + for (;;) { + DBusMessageIter props, intf; + const char *intfname; + + dbus_message_iter_recurse(&intfs, &intf); + + if (!dbus_object_is_type(&intf, DBUS_TYPE_STRING)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (intf)\n"); + return -1; + } + + dbus_message_iter_get_basic(&intf, &intfname); + + if (strcmp(intfname, MCTP_DBUS_IFACE_ENDPOINT)) { + if (!dbus_message_iter_next(&intfs)) + break; + continue; + } + + dbus_message_iter_next(&intf); + + if (!dbus_object_is_dict(&intf)) { + nvme_msg(root, LOG_ERR, + "error unmarshalling object (props)\n"); + return -1; + } + + dbus_message_iter_recurse(&intf, &props); + return handle_mctp_endpoint(root, objpath, &props); + } + + return 0; +} + +nvme_root_t nvme_mi_scan_mctp(void) +{ + DBusMessage *msg, *resp = NULL; + DBusConnection *bus = NULL; + DBusMessageIter args, objs; + int errno_save, rc = -1; + nvme_root_t root; + dbus_bool_t drc; + DBusError berr; + + root = nvme_mi_create_root(NULL, DEFAULT_LOGLEVEL); + if (!root) { + errno = ENOMEM; + return NULL; + } + + dbus_error_init(&berr); + + bus = dbus_bus_get(DBUS_BUS_SYSTEM, &berr); + if (!bus) { + nvme_msg(root, LOG_ERR, "Failed connecting to D-Bus: %s (%s)\n", + berr.message, berr.name); + goto out; + } + + msg = dbus_message_new_method_call(MCTP_DBUS_IFACE, + MCTP_DBUS_PATH, + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); + if (!msg) { + nvme_msg(root, LOG_ERR, "Failed creating call message\n"); + goto out; + } + + resp = dbus_connection_send_with_reply_and_block(bus, msg, + DBUS_TIMEOUT_USE_DEFAULT, + &berr); + dbus_message_unref(msg); + if (!resp) { + nvme_msg(root, LOG_ERR, "Failed querying MCTP D-Bus: %s (%s)\n", + berr.message, berr.name); + goto out; + } + + /* argument container */ + drc = dbus_message_iter_init(resp, &args); + if (!drc) { + nvme_msg(root, LOG_ERR, "can't read dbus reply args\n"); + goto out; + } + + if (!dbus_object_is_dict(&args)) { + nvme_msg(root, LOG_ERR, "error unmashalling args\n"); + goto out; + } + + /* objects container */ + dbus_message_iter_recurse(&args, &objs); + + rc = 0; + + for (;;) { + DBusMessageIter ent; + + dbus_message_iter_recurse(&objs, &ent); + + rc = handle_mctp_obj(root, &ent); + if (rc) + break; + + if (!dbus_message_iter_next(&objs)) + break; + } + +out: + errno_save = errno; + if (resp) + dbus_message_unref(resp); + if (bus) + dbus_connection_unref(bus); + dbus_error_free(&berr); + + if (rc < 0) { + if (root) { + nvme_mi_free_root(root); + } + errno = errno_save; + root = NULL; + } + return root; +} + +#else /* CONFIG_DBUS */ + +nvme_root_t nvme_mi_scan_mctp(void) +{ + return NULL; +} + +#endif /* CONFIG_DBUS */ diff --git a/src/nvme/mi.c b/src/nvme/mi.c new file mode 100644 index 0000000..84d51b0 --- /dev/null +++ b/src/nvme/mi.c @@ -0,0 +1,1651 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021 Code Construct Pty Ltd + * + * Authors: Jeremy Kerr <jk@codeconstruct.com.au> + */ + +#include <errno.h> +#include <stdlib.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <ccan/array_size/array_size.h> +#include <ccan/endian/endian.h> + +#include "log.h" +#include "mi.h" +#include "private.h" + +static const int default_timeout = 1000; /* milliseconds; endpoints may + override */ + +static bool nvme_mi_probe_enabled_default(void) +{ + char *val; + + val = getenv("LIBNVME_MI_PROBE_ENABLED"); + if (!val) + return true; + + return strcmp(val, "0") && + strcasecmp(val, "false") && + strncasecmp(val, "disable", 7); + +} + +/* MI-equivalent of nvme_create_root, but avoids clashing symbol names + * when linking against both libnvme and libnvme-mi. + */ +nvme_root_t nvme_mi_create_root(FILE *fp, int log_level) +{ + struct nvme_root *r = calloc(1, sizeof(*r)); + + if (!r) { + return NULL; + } + r->log_level = log_level; + r->fp = stderr; + r->mi_probe_enabled = nvme_mi_probe_enabled_default(); + if (fp) + r->fp = fp; + list_head_init(&r->hosts); + list_head_init(&r->endpoints); + return r; +} + +void nvme_mi_free_root(nvme_root_t root) +{ + nvme_mi_ep_t ep, tmp; + + nvme_mi_for_each_endpoint_safe(root, ep, tmp) + nvme_mi_close(ep); + + free(root); +} + +void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled) +{ + root->mi_probe_enabled = enabled; +} + +static void nvme_mi_record_resp_time(struct nvme_mi_ep *ep) +{ + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ep->last_resp_time); + ep->last_resp_time_valid = !rc; +} + +static bool nvme_mi_compare_vid_mn(struct nvme_mi_ep *ep, + struct nvme_id_ctrl *id, + __u16 vid, const char *mn) + +{ + int len; + + len = strlen(mn); + if (len >= sizeof(id->mn)) { + nvme_msg(ep->root, LOG_ERR, + "Internal error: invalid model number for %s\n", + __func__); + return false; + } + + return le16_to_cpu(id->vid) == vid && !strncmp(id->mn, mn, len); +} + +static void __nvme_mi_format_mn(struct nvme_id_ctrl *id, + char *mn, size_t mn_len) +{ + const size_t id_mn_size = sizeof(id->mn); + int i; + + /* A BUILD_ASSERT() would be nice here, but we're not const enough for + * that + */ + if (mn_len <= id_mn_size) + abort(); + + memcpy(mn, id->mn, id_mn_size); + mn[id_mn_size] = '\0'; + + for (i = id_mn_size - 1; i >= 0; i--) { + if (mn[i] != '\0' && mn[i] != ' ') + break; + mn[i] = '\0'; + } +} + +#define nvme_mi_format_mn(id, m) __nvme_mi_format_mn(id, m, sizeof(m)) + +void nvme_mi_ep_probe(struct nvme_mi_ep *ep) +{ + struct nvme_identify_args id_args = { 0 }; + struct nvme_id_ctrl id = { 0 }; + struct nvme_mi_ctrl *ctrl; + int rc; + + if (!ep->root->mi_probe_enabled) + return; + + /* start with no quirks, detect as we go */ + ep->quirks = 0; + + ctrl = nvme_mi_init_ctrl(ep, 0); + if (!ctrl) + return; + + /* Do enough of an identify (assuming controller 0) to retrieve + * device and firmware identification information. This gives us the + * following fields in id: + * + * - vid (PCI vendor ID) + * - ssvid (PCI subsystem vendor ID) + * - sn (Serial number) + * - mn (Model number) + * - fr (Firmware revision) + * + * all other fields - rab and onwards - will be zero! + */ + id_args.args_size = sizeof(id_args); + id_args.data = &id; + id_args.cns = NVME_IDENTIFY_CNS_CTRL; + id_args.nsid = NVME_NSID_NONE; + id_args.cntid = 0; + id_args.csi = NVME_CSI_NVM; + + rc = nvme_mi_admin_identify_partial(ctrl, &id_args, 0, + offsetof(struct nvme_id_ctrl, rab)); + if (rc) { + nvme_msg(ep->root, LOG_WARNING, + "Identify Controller failed, no quirks applied\n"); + goto out_close; + } + + /* Samsung MZUL2512: cannot receive commands sent within ~1ms of + * the previous response. Set an inter-command delay of 1.2ms for + * a little extra tolerance. + */ + if (nvme_mi_compare_vid_mn(ep, &id, 0x144d, "MZUL2512HCJQ")) { + ep->quirks |= NVME_QUIRK_MIN_INTER_COMMAND_TIME; + ep->inter_command_us = 1200; + } + + /* If we're quirking for the inter-command time, record the last + * command time now, so we don't conflict with the just-sent identify. + */ + if (ep->quirks & NVME_QUIRK_MIN_INTER_COMMAND_TIME) + nvme_mi_record_resp_time(ep); + + if (ep->quirks) { + char tmp[sizeof(id.mn) + 1]; + + nvme_mi_format_mn(&id, tmp); + nvme_msg(ep->root, LOG_DEBUG, + "device %02x:%s: applying quirks 0x%08lx\n", + id.vid, tmp, ep->quirks); + } + +out_close: + nvme_mi_close_ctrl(ctrl); +} + +static const int nsec_per_sec = 1000 * 1000 * 1000; +/* timercmp and timersub, but for struct timespec */ +#define timespec_cmp(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) \ + ? ((a)->tv_nsec CMP (b)->tv_nsec) \ + : ((a)->tv_sec CMP (b)->tv_sec)) + +#define timespec_sub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += nsec_per_sec; \ + } \ + } while (0) + +static void nvme_mi_insert_delay(struct nvme_mi_ep *ep) +{ + struct timespec now, next, delay; + int rc; + + if (!ep->last_resp_time_valid) + return; + + /* calculate earliest next command time */ + next.tv_nsec = ep->last_resp_time.tv_nsec + ep->inter_command_us * 1000; + next.tv_sec = ep->last_resp_time.tv_sec; + if (next.tv_nsec > nsec_per_sec) { + next.tv_nsec -= nsec_per_sec; + next.tv_sec += 1; + } + + rc = clock_gettime(CLOCK_MONOTONIC, &now); + if (rc) { + /* not much we can do; continue immediately */ + return; + } + + if (timespec_cmp(&now, &next, >=)) + return; + + timespec_sub(&next, &now, &delay); + + nanosleep(&delay, NULL); +} + +struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root) +{ + struct nvme_mi_ep *ep; + + ep = calloc(1, sizeof(*ep)); + if (!ep) + return NULL; + + list_node_init(&ep->root_entry); + ep->root = root; + ep->controllers_scanned = false; + ep->timeout = default_timeout; + ep->mprt_max = 0; + list_head_init(&ep->controllers); + + list_add(&root->endpoints, &ep->root_entry); + + return ep; +} + +int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms) +{ + if (ep->transport->check_timeout) { + int rc; + rc = ep->transport->check_timeout(ep, timeout_ms); + if (rc) + return rc; + } + + ep->timeout = timeout_ms; + return 0; +} + +void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms) +{ + ep->mprt_max = mprt_max_ms; +} + +unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep) +{ + return ep->timeout; +} + +static bool nvme_mi_ep_has_quirk(nvme_mi_ep_t ep, unsigned long quirk) +{ + return ep->quirks & quirk; +} + +struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id) +{ + struct nvme_mi_ctrl *ctrl; + + ctrl = malloc(sizeof(*ctrl)); + if (!ctrl) + return NULL; + + ctrl->ep = ep; + ctrl->id = ctrl_id; + + list_add_tail(&ep->controllers, &ctrl->ep_entry); + + return ctrl; +} + +__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl) +{ + return ctrl->id; +} + +int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan) +{ + struct nvme_ctrl_list list; + unsigned int i, n_ctrl; + int rc; + + if (ep->controllers_scanned) { + if (force_rescan) { + struct nvme_mi_ctrl *ctrl, *tmp; + nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp) + nvme_mi_close_ctrl(ctrl); + } else { + return 0; + } + } + + rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &list); + if (rc) + return -1; + + n_ctrl = le16_to_cpu(list.num); + if (n_ctrl > NVME_ID_CTRL_LIST_MAX) { + errno = EPROTO; + return -1; + } + + for (i = 0; i < n_ctrl; i++) { + struct nvme_mi_ctrl *ctrl; + __u16 id; + + id = le16_to_cpu(list.identifier[i]); + + ctrl = nvme_mi_init_ctrl(ep, id); + if (!ctrl) + break; + } + + ep->controllers_scanned = true; + return 0; +} + +__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len) +{ + int i; + + while (len--) { + crc ^= *(unsigned char *)(data++); + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78 : 0); + } + return crc; +} + +static void nvme_mi_calc_req_mic(struct nvme_mi_req *req) +{ + __u32 crc = 0xffffffff; + + crc = nvme_mi_crc32_update(crc, req->hdr, req->hdr_len); + crc = nvme_mi_crc32_update(crc, req->data, req->data_len); + + req->mic = ~crc; +} + +/* returns zero on correct MIC */ +static int nvme_mi_verify_resp_mic(struct nvme_mi_resp *resp) +{ + __u32 crc = 0xffffffff; + + crc = nvme_mi_crc32_update(crc, resp->hdr, resp->hdr_len); + crc = nvme_mi_crc32_update(crc, resp->data, resp->data_len); + + return resp->mic != ~crc; +} + +int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req, + struct nvme_mi_resp *resp) +{ + int rc; + + if (req->hdr_len < sizeof(struct nvme_mi_msg_hdr)) { + errno = EINVAL; + return -1; + } + + if (req->hdr_len & 0x3) { + errno = EINVAL; + return -1; + } + + if (req->data_len & 0x3) { + errno = EINVAL; + return -1; + } + + if (resp->hdr_len < sizeof(struct nvme_mi_msg_hdr)) { + errno = EINVAL; + return -1; + } + + if (resp->hdr_len & 0x3) { + errno = EINVAL; + return -1; + } + + if (ep->transport->mic_enabled) + nvme_mi_calc_req_mic(req); + + if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME)) + nvme_mi_insert_delay(ep); + + rc = ep->transport->submit(ep, req, resp); + + if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME)) + nvme_mi_record_resp_time(ep); + + if (rc) { + nvme_msg(ep->root, LOG_INFO, "transport failure\n"); + return rc; + } + + if (ep->transport->mic_enabled) { + rc = nvme_mi_verify_resp_mic(resp); + if (rc) { + nvme_msg(ep->root, LOG_WARNING, "crc mismatch\n"); + errno = EBADMSG; + return -1; + } + } + + /* basic response checks */ + if (resp->hdr_len < sizeof(struct nvme_mi_msg_hdr)) { + nvme_msg(ep->root, LOG_DEBUG, + "Bad response header len: %zd\n", resp->hdr_len); + errno = EPROTO; + return -1; + } + + if (resp->hdr->type != NVME_MI_MSGTYPE_NVME) { + nvme_msg(ep->root, LOG_DEBUG, + "Invalid message type 0x%02x\n", resp->hdr->type); + errno = EPROTO; + return -1; + } + + if (!(resp->hdr->nmp & (NVME_MI_ROR_RSP << 7))) { + nvme_msg(ep->root, LOG_DEBUG, + "ROR value in response indicates a request\n"); + errno = EIO; + return -1; + } + + if ((resp->hdr->nmp & 0x1) != (req->hdr->nmp & 0x1)) { + nvme_msg(ep->root, LOG_WARNING, + "Command slot mismatch: req %d, resp %d\n", + req->hdr->nmp & 0x1, + resp->hdr->nmp & 0x1); + errno = EIO; + return -1; + } + + return 0; +} + +static void nvme_mi_admin_init_req(struct nvme_mi_req *req, + struct nvme_mi_admin_req_hdr *hdr, + __u16 ctrl_id, __u8 opcode) +{ + memset(req, 0, sizeof(*req)); + memset(hdr, 0, sizeof(*hdr)); + + hdr->hdr.type = NVME_MI_MSGTYPE_NVME; + hdr->hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_ADMIN << 3); /* we always use command slot 0 */ + hdr->opcode = opcode; + hdr->ctrl_id = cpu_to_le16(ctrl_id); + + req->hdr = &hdr->hdr; + req->hdr_len = sizeof(*hdr); +} + +static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp, + struct nvme_mi_admin_resp_hdr *hdr) +{ + memset(resp, 0, sizeof(*resp)); + resp->hdr = &hdr->hdr; + resp->hdr_len = sizeof(*hdr); +} + +static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result) +{ + struct nvme_mi_admin_resp_hdr *admin_hdr; + struct nvme_mi_msg_resp *resp_hdr; + __u32 nvme_status; + __u32 nvme_result; + + /* we have a few different sources of "result" here: the status header + * in the MI response, the cdw3 status field, and (command specific) + * return values in cdw0. The latter is returned in the result pointer, + * the former two generate return values here + */ + + if (resp->hdr_len < sizeof(*resp_hdr)) { + errno = -EPROTO; + return -1; + } + resp_hdr = (struct nvme_mi_msg_resp *)resp->hdr; + + /* If we have a MI error, we can't be sure there's an admin header + * following; return just the MI status, with the status type + * indicator of MI. + */ + if (resp_hdr->status) + return resp_hdr->status | + (NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT); + + /* We shouldn't hit this, as we'd have an error reported earlier. + * However, for pointer safety, ensure we have a full admin header + */ + if (resp->hdr_len < sizeof(*admin_hdr)) { + errno = EPROTO; + return -1; + } + + admin_hdr = (struct nvme_mi_admin_resp_hdr *)resp->hdr; + nvme_result = le32_to_cpu(admin_hdr->cdw0); + + /* Shift down 17 here: the SC starts at bit 17, and the NVME_SC_* + * definitions align to this bit (and up). The CRD, MORE and DNR + * bits are defined accordingly (eg., DNR is 0x4000). + */ + nvme_status = le32_to_cpu(admin_hdr->cdw3) >> 17; + + /* the result pointer, optionally stored if the caller needs it */ + if (result) + *result = nvme_result; + + return nvme_status; +} + +int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, + struct nvme_mi_admin_req_hdr *admin_req, + size_t req_data_size, + struct nvme_mi_admin_resp_hdr *admin_resp, + off_t resp_data_offset, + size_t *resp_data_size) +{ + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + /* length/offset checks. The common _submit() API will do further + * checking on the message lengths too, so these are kept specific + * to the requirements of the Admin command set + */ + + /* NVMe-MI v1.2 imposes a limit of 4096 bytes on the dlen field */ + if (*resp_data_size > 4096) { + errno = EINVAL; + return -1; + } + + /* we only have 32 bits of offset */ + if (resp_data_offset > 0xffffffff) { + errno = EINVAL; + return -1; + } + + /* request and response lengths & offset must be aligned */ + if ((req_data_size & 0x3) || + (*resp_data_size & 0x3) || + (resp_data_offset & 0x3)) { + errno = EINVAL; + return -1; + } + + /* bidirectional not permitted (see DLEN definition) */ + if (req_data_size && *resp_data_size) { + errno = EINVAL; + return -1; + } + + if (!*resp_data_size && resp_data_offset) { + errno = EINVAL; + return -1; + } + + admin_req->hdr.type = NVME_MI_MSGTYPE_NVME; + admin_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_ADMIN << 3); + admin_req->ctrl_id = cpu_to_le16(ctrl->id); + memset(&req, 0, sizeof(req)); + req.hdr = &admin_req->hdr; + req.hdr_len = sizeof(*admin_req); + req.data = admin_req + 1; + req.data_len = req_data_size; + + nvme_mi_calc_req_mic(&req); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &admin_resp->hdr; + resp.hdr_len = sizeof(*admin_resp); + resp.data = admin_resp + 1; + resp.data_len = *resp_data_size; + + /* limit the response size, specify offset */ + admin_req->flags = 0x3; + admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff); + admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + *resp_data_size = resp.data_len; + + return 0; +} + +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result) +{ + /* Input parameters flags, rsvd, metadata, metadata_len are not used */ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + int direction = opcode & 0x3; + bool has_write_data = false; + bool has_read_data = false; + + if (direction == NVME_DATA_TFR_BIDIRECTIONAL) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support bidirectional commands\n"); + errno = EINVAL; + return -1; + } + + if (data_len > 4096) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support data_len over 4096 bytes.\n"); + errno = EINVAL; + return -1; + } + + if (data != NULL && data_len != 0) { + if (direction == NVME_DATA_TFR_HOST_TO_CTRL) + has_write_data = true; + if (direction == NVME_DATA_TFR_CTRL_TO_HOST) + has_read_data = true; + } + + if (timeout_ms > nvme_mi_ep_get_timeout(ctrl->ep)) { + /* Set timeout if user needs a bigger timeout */ + nvme_mi_ep_set_timeout(ctrl->ep, timeout_ms); + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, opcode); + req_hdr.cdw1 = cpu_to_le32(nsid); + req_hdr.cdw2 = cpu_to_le32(cdw2); + req_hdr.cdw3 = cpu_to_le32(cdw3); + req_hdr.cdw10 = cpu_to_le32(cdw10); + req_hdr.cdw11 = cpu_to_le32(cdw11); + req_hdr.cdw12 = cpu_to_le32(cdw12); + req_hdr.cdw13 = cpu_to_le32(cdw13); + req_hdr.cdw14 = cpu_to_le32(cdw14); + req_hdr.cdw15 = cpu_to_le32(cdw15); + req_hdr.doff = 0; + if (data_len != 0) { + req_hdr.dlen = cpu_to_le32(data_len); + /* Bit 0 set to 1 means DLEN contains a value */ + req_hdr.flags = 0x1; + } + + if (has_write_data) { + req.data = data; + req.data_len = data_len; + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + if (has_read_data) { + resp.data = data; + resp.data_len = data_len; + } + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, result); + if (rc) + return rc; + + if (has_read_data && (resp.data_len != data_len)) { + errno = EPROTO; + return -1; + } + + return 0; +} + +int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, + struct nvme_identify_args *args, + off_t offset, size_t size) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + if (!size || size > 0xffffffff) { + errno = EINVAL; + return -1; + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_identify); + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(args->cntid << 16 | args->cns); + req_hdr.cdw11 = cpu_to_le32((args->csi & 0xff) << 24 | + args->cns_specific_id); + req_hdr.cdw14 = cpu_to_le32(args->uuidx); + req_hdr.dlen = cpu_to_le32(size & 0xffffffff); + req_hdr.flags = 0x1; + if (offset) { + req_hdr.flags |= 0x2; + req_hdr.doff = cpu_to_le32(offset); + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + resp.data = args->data; + resp.data_len = size; + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + /* callers will expect a full response; if the data buffer isn't + * fully valid, return an error */ + if (resp.data_len != size) { + errno = EPROTO; + return -1; + } + + return 0; +} + +/* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are + * specified within the args->data area. The `offset` parameter is a relative + * offset to the args->lpo ! + * + * What's more, we change the LPO of original command to chunk the request + * message into proper size which is allowed by MI interface. One reason is that + * this option seems to be supported better by devices. For more information + * about this option, please check https://github.com/linux-nvme/libnvme/pull/539 + * */ +static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, + const struct nvme_get_log_args *args, + off_t offset, size_t *lenp, bool final) +{ + __u64 log_page_offset = args->lpo + offset; + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + size_t len; + __u32 ndw; + int rc; + + /* MI spec requires that the data length field is less than or equal + * to 4096 */ + len = *lenp; + if (!len || len > 4096 || len < 4) { + errno = EINVAL; + return -1; + } + + if (offset < 0 || offset >= args->len || offset + len > args->len) { + errno = EINVAL; + return -1; + } + + ndw = (len >> 2) - 1; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_get_log_page); + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32((ndw & 0xffff) << 16 | + ((!final || args->rae) ? 1 : 0) << 15 | + args->lsp << 8 | + (args->lid & 0xff)); + req_hdr.cdw11 = cpu_to_le32(args->lsi << 16 | + ndw >> 16); + req_hdr.cdw12 = cpu_to_le32(log_page_offset & 0xffffffff); + req_hdr.cdw13 = cpu_to_le32(log_page_offset >> 32); + req_hdr.cdw14 = cpu_to_le32(args->csi << 24 | + (args->ot ? 1 : 0) << 23 | + args->uuidx); + req_hdr.flags = 0x1; + req_hdr.dlen = cpu_to_le32(len & 0xffffffff); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + resp.data = args->log + offset; + resp.data_len = len; + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (!rc) + *lenp = resp.data_len; + + return rc; +} + +int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_size, + struct nvme_get_log_args *args) +{ + const size_t max_xfer_size = xfer_size; + off_t xfer_offset; + int rc = 0; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + if (args->ot && (args->len > max_xfer_size)) { + errno = EINVAL; + return -1; + } + + for (xfer_offset = 0; xfer_offset < args->len;) { + size_t xfered_size, cur_xfer_size = max_xfer_size; + bool final; + + if (xfer_offset + cur_xfer_size > args->len) + cur_xfer_size = args->len - xfer_offset; + + xfered_size = cur_xfer_size; + + final = xfer_offset + cur_xfer_size >= args->len; + + /* xfered_size is used as both input and output parameter */ + rc = __nvme_mi_admin_get_log(ctrl, args, xfer_offset, + &xfered_size, final); + if (rc) + break; + + xfer_offset += xfered_size; + /* if we returned less data than expected, consider that + * the end of the log page */ + if (xfered_size != cur_xfer_size) + break; + } + + if (!rc) + args->len = xfer_offset; + + return rc; +} + +int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) +{ + return nvme_mi_admin_get_log_page(ctrl, 4096, args); +} + +int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, + struct nvme_security_send_args *args) +{ + + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + if (args->data_len > 4096) { + errno = EINVAL; + return -1; + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_security_send); + + req_hdr.cdw10 = cpu_to_le32(args->secp << 24 | + args->spsp1 << 16 | + args->spsp0 << 8 | + args->nssf); + + req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff); + + req_hdr.flags = 0x1; + req_hdr.dlen = cpu_to_le32(args->data_len & 0xffffffff); + req.data = args->data; + req.data_len = args->data_len; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, + struct nvme_security_receive_args *args) +{ + + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + if (args->data_len > 4096) { + errno = EINVAL; + return -1; + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_security_recv); + + req_hdr.cdw10 = cpu_to_le32(args->secp << 24 | + args->spsp1 << 16 | + args->spsp0 << 8 | + args->nssf); + + req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff); + + req_hdr.flags = 0x1; + req_hdr.dlen = cpu_to_le32(args->data_len & 0xffffffff); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + resp.data = args->data; + resp.data_len = args->data_len; + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + args->data_len = resp.data_len; + + return 0; +} + +int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl, + struct nvme_get_features_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_get_features); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32((args->sel & 0x7) << 8 | args->fid); + req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f); + req_hdr.cdw11 = cpu_to_le32(args->cdw11); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + resp.data = args->data; + resp.data_len = args->data_len; + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + args->data_len = resp.data_len; + + return 0; +} + +int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl, + struct nvme_set_features_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_set_features); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32((__u32)!!args->save << 31 | + (args->fid & 0xff)); + req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f); + req_hdr.cdw11 = cpu_to_le32(args->cdw11); + req_hdr.cdw12 = cpu_to_le32(args->cdw12); + req_hdr.cdw13 = cpu_to_le32(args->cdw13); + req_hdr.cdw15 = cpu_to_le32(args->cdw15); + + req.data_len = args->data_len; + req.data = args->data; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + args->data_len = resp.data_len; + + return 0; +} + +int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, + struct nvme_ns_mgmt_args *args) +{ + const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64); + const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64); + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + size_t data_len; + + if (args->args_size < size_v1 || args->args_size > size_v2) { + errno = EINVAL; + return -1; + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_ns_mgmt); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf); + req_hdr.cdw11 = cpu_to_le32(args->csi << 24); + + if (args->args_size == size_v2) { + if (args->data) { + req.data = args->data; + data_len = sizeof(*args->data); + } + } + else { + if (args->ns) { + req.data = args->ns; + data_len = sizeof(*args->ns); + } + } + + if (req.data) { + req.data_len = data_len; + req_hdr.dlen = cpu_to_le32(data_len); + req_hdr.flags = 0x1; + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl, + struct nvme_ns_attach_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_ns_attach); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf); + req.data = args->ctrlist; + req.data_len = sizeof(*args->ctrlist); + req_hdr.dlen = cpu_to_le32(sizeof(*args->ctrlist)); + req_hdr.flags = 0x1; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl, + struct nvme_fw_download_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + if (args->data_len & 0x3) + return -EINVAL; + + if (args->offset & 0x3) + return -EINVAL; + + if (!args->data_len) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_fw_download); + + req_hdr.cdw10 = cpu_to_le32((args->data_len >> 2) - 1); + req_hdr.cdw11 = cpu_to_le32(args->offset >> 2); + req.data = args->data; + req.data_len = args->data_len; + req_hdr.dlen = cpu_to_le32(args->data_len); + req_hdr.flags = 0x1; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, NULL); +} + +int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl, + struct nvme_fw_commit_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_fw_commit); + + req_hdr.cdw10 = cpu_to_le32(((__u32)(args->bpid & 0x1) << 31) | + ((args->action & 0x7) << 3) | + ((args->slot & 0x7) << 0)); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, NULL); +} + +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_format_nvm); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(((args->lbafu & 0x3) << 12) + | ((args->ses & 0x7) << 9) + | ((args->pil & 0x1) << 8) + | ((args->pi & 0x7) << 5) + | ((args->mset & 0x1) << 4) + | ((args->lbaf & 0xf) << 0)); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_sanitize_nvm_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_sanitize_nvm); + + req_hdr.cdw10 = cpu_to_le32(((args->nodas ? 1 : 0) << 9) + | ((args->oipbp ? 1 : 0) << 8) + | ((args->owpass & 0xf) << 4) + | ((args->ause ? 1 : 0) << 3) + | ((args->sanact & 0x7) << 0)); + req_hdr.cdw11 = cpu_to_le32(args->ovrpat); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0, + void *data, size_t *data_len) +{ + struct nvme_mi_mi_resp_hdr resp_hdr; + struct nvme_mi_mi_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + memset(&req_hdr, 0, sizeof(req_hdr)); + req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME; + req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_MI << 3); /* we always use command slot 0 */ + req_hdr.opcode = nvme_mi_mi_opcode_mi_data_read; + req_hdr.cdw0 = cpu_to_le32(cdw0); + + memset(&req, 0, sizeof(req)); + req.hdr = &req_hdr.hdr; + req.hdr_len = sizeof(req_hdr); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &resp_hdr.hdr; + resp.hdr_len = sizeof(resp_hdr); + resp.data = data; + resp.data_len = *data_len; + + rc = nvme_mi_submit(ep, &req, &resp); + if (rc) + return rc; + + if (resp_hdr.status) + return resp_hdr.status; + + *data_len = resp.data_len; + + return 0; +} + +int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep, + struct nvme_mi_read_nvm_ss_info *s) +{ + size_t len; + __u32 cdw0; + int rc; + + cdw0 = (__u8)nvme_mi_dtyp_subsys_info << 24; + len = sizeof(*s); + + rc = nvme_mi_read_data(ep, cdw0, s, &len); + if (rc) + return rc; + + if (len != sizeof(*s)) { + nvme_msg(ep->root, LOG_WARNING, + "MI read data length mismatch: " + "got %zd bytes, expected %zd\n", + len, sizeof(*s)); + errno = EPROTO; + return -1; + } + + return 0; +} + +int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid, + struct nvme_mi_read_port_info *p) +{ + size_t len; + __u32 cdw0; + int rc; + + cdw0 = ((__u8)nvme_mi_dtyp_port_info << 24) | (portid << 16); + len = sizeof(*p); + + rc = nvme_mi_read_data(ep, cdw0, p, &len); + if (rc) + return rc; + + if (len != sizeof(*p)) { + errno = EPROTO; + return -1; + } + + return 0; +} + +int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid, + struct nvme_ctrl_list *list) +{ + size_t len; + __u32 cdw0; + int rc; + + cdw0 = ((__u8)nvme_mi_dtyp_ctrl_list << 24) | (start_ctrlid << 16); + len = sizeof(*list); + + rc = nvme_mi_read_data(ep, cdw0, list, &len); + if (rc) + return rc; + + return 0; +} + +int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id, + struct nvme_mi_read_ctrl_info *ctrl) +{ + size_t len; + __u32 cdw0; + int rc; + + cdw0 = ((__u8)nvme_mi_dtyp_ctrl_info << 24) | cpu_to_le16(ctrl_id); + len = sizeof(*ctrl); + + rc = nvme_mi_read_data(ep, cdw0, ctrl, &len); + if (rc) + return rc; + + if (len != sizeof(*ctrl)) { + errno = EPROTO; + return -1; + } + + return 0; +} + +int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear, + struct nvme_mi_nvm_ss_health_status *sshs) +{ + struct nvme_mi_mi_resp_hdr resp_hdr; + struct nvme_mi_mi_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + memset(&req_hdr, 0, sizeof(req_hdr)); + req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;; + req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_MI << 3); + req_hdr.opcode = nvme_mi_mi_opcode_subsys_health_status_poll; + req_hdr.cdw1 = (clear ? 1 : 0) << 31; + + memset(&req, 0, sizeof(req)); + req.hdr = &req_hdr.hdr; + req.hdr_len = sizeof(req_hdr); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &resp_hdr.hdr; + resp.hdr_len = sizeof(resp_hdr); + resp.data = sshs; + resp.data_len = sizeof(*sshs); + + rc = nvme_mi_submit(ep, &req, &resp); + if (rc) + return rc; + + if (resp_hdr.status) + return resp_hdr.status; + + if (resp.data_len != sizeof(*sshs)) { + nvme_msg(ep->root, LOG_WARNING, + "MI Subsystem Health Status length mismatch: " + "got %zd bytes, expected %zd\n", + resp.data_len, sizeof(*sshs)); + errno = EPROTO; + return -1; + } + + return 0; +} + +int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, + __u32 *nmresp) +{ + struct nvme_mi_mi_resp_hdr resp_hdr; + struct nvme_mi_mi_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + memset(&req_hdr, 0, sizeof(req_hdr)); + req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME; + req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3); + req_hdr.opcode = nvme_mi_mi_opcode_configuration_get; + req_hdr.cdw0 = cpu_to_le32(dw0); + req_hdr.cdw1 = cpu_to_le32(dw1); + + memset(&req, 0, sizeof(req)); + req.hdr = &req_hdr.hdr; + req.hdr_len = sizeof(req_hdr); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &resp_hdr.hdr; + resp.hdr_len = sizeof(resp_hdr); + + rc = nvme_mi_submit(ep, &req, &resp); + if (rc) + return rc; + + if (resp_hdr.status) + return resp_hdr.status; + + *nmresp = resp_hdr.nmresp[0] | + resp_hdr.nmresp[1] << 8 | + resp_hdr.nmresp[2] << 16; + + return 0; +} + +int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1) +{ + struct nvme_mi_mi_resp_hdr resp_hdr; + struct nvme_mi_mi_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + memset(&req_hdr, 0, sizeof(req_hdr)); + req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME; + req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3); + req_hdr.opcode = nvme_mi_mi_opcode_configuration_set; + req_hdr.cdw0 = cpu_to_le32(dw0); + req_hdr.cdw1 = cpu_to_le32(dw1); + + memset(&req, 0, sizeof(req)); + req.hdr = &req_hdr.hdr; + req.hdr_len = sizeof(req_hdr); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &resp_hdr.hdr; + resp.hdr_len = sizeof(resp_hdr); + + rc = nvme_mi_submit(ep, &req, &resp); + if (rc) + return rc; + + if (resp_hdr.status) + return resp_hdr.status; + + return 0; +} + +void nvme_mi_close(nvme_mi_ep_t ep) +{ + struct nvme_mi_ctrl *ctrl, *tmp; + + /* don't look for controllers during destruction */ + ep->controllers_scanned = true; + + nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp) + nvme_mi_close_ctrl(ctrl); + + if (ep->transport && ep->transport->close) + ep->transport->close(ep); + list_del(&ep->root_entry); + free(ep); +} + +void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl) +{ + list_del(&ctrl->ep_entry); + free(ctrl); +} + +char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep) +{ + char tsbuf[101], *s = NULL; + size_t tslen; + int rc; + + rc = -1; + memset(tsbuf, 0, sizeof(tsbuf)); + if (ep->transport->desc_ep) + rc = ep->transport->desc_ep(ep, tsbuf, sizeof(tsbuf) - 1); + + if (!rc) { + /* don't overflow if the transport gives us an invalid string */ + tsbuf[sizeof(tsbuf)-1] = '\0'; + tslen = strlen(tsbuf); + } else { + tslen = 0; + } + + if (tslen) + rc = asprintf(&s, "%s: %s", ep->transport->name, tsbuf); + else + rc = asprintf(&s, "%s endpoint", ep->transport->name); + + if (rc < 0) + return NULL; + + return s; +} + +nvme_mi_ep_t nvme_mi_first_endpoint(nvme_root_t m) +{ + return list_top(&m->endpoints, struct nvme_mi_ep, root_entry); +} + +nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t ep) +{ + return ep ? list_next(&m->endpoints, ep, root_entry) : NULL; +} + +nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep) +{ + return list_top(&ep->controllers, struct nvme_mi_ctrl, ep_entry); +} + +nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c) +{ + return c ? list_next(&ep->controllers, c, ep_entry) : NULL; +} + + +static const char *const mi_status[] = { + [NVME_MI_RESP_MPR] = "More Processing Required: The command message is in progress and requires more time to complete processing", + [NVME_MI_RESP_INTERNAL_ERR] = "Internal Error: The request message could not be processed due to a vendor-specific error", + [NVME_MI_RESP_INVALID_OPCODE] = "Invalid Command Opcode", + [NVME_MI_RESP_INVALID_PARAM] = "Invalid Parameter", + [NVME_MI_RESP_INVALID_CMD_SIZE] = "Invalid Command Size: The size of the message body of the request was different than expected", + [NVME_MI_RESP_INVALID_INPUT_SIZE] = "Invalid Command Input Data Size: The command requires data and contains too much or too little data", + [NVME_MI_RESP_ACCESS_DENIED] = "Access Denied. Processing prohibited due to a vendor-specific mechanism of the Command and Feature lockdown function", + [NVME_MI_RESP_VPD_UPDATES_EXCEEDED] = "VPD Updates Exceeded", + [NVME_MI_RESP_PCIE_INACCESSIBLE] = "PCIe Inaccessible. The PCIe functionality is not available at this time", + [NVME_MI_RESP_MEB_SANITIZED] = "Management Endpoint Buffer Cleared Due to Sanitize", + [NVME_MI_RESP_ENC_SERV_FAILURE] = "Enclosure Services Failure", + [NVME_MI_RESP_ENC_SERV_XFER_FAILURE] = "Enclosure Services Transfer Failure: Communication with the Enclosure Services Process has failed", + [NVME_MI_RESP_ENC_FAILURE] = "An unrecoverable enclosure failure has been detected by the Enclosuer Services Process", + [NVME_MI_RESP_ENC_XFER_REFUSED] = "Enclosure Services Transfer Refused: The NVM Subsystem or Enclosure Services Process indicated an error or an invalid format in communication", + [NVME_MI_RESP_ENC_FUNC_UNSUP] = "Unsupported Enclosure Function: An SES Send command has been attempted to a simple Subenclosure", + [NVME_MI_RESP_ENC_SERV_UNAVAIL] = "Enclosure Services Unavailable: The NVM Subsystem or Enclosure Services Process has encountered an error but may become available again", + [NVME_MI_RESP_ENC_DEGRADED] = "Enclosure Degraded: A noncritical failure has been detected by the Enclosure Services Process", + [NVME_MI_RESP_SANITIZE_IN_PROGRESS] = "Sanitize In Progress: The requested command is prohibited while a sanitize operation is in progress", +}; + +/* kept in mi.c while we have a split libnvme/libnvme-mi; consider moving + * to utils.c (with nvme_status_to_string) if we ever merge. */ +const char *nvme_mi_status_to_string(int status) +{ + const char *s = "Unknown status"; + + if (status < ARRAY_SIZE(mi_status) && mi_status[status]) + s = mi_status[status]; + + return s; +} diff --git a/src/nvme/mi.h b/src/nvme/mi.h new file mode 100644 index 0000000..bd26627 --- /dev/null +++ b/src/nvme/mi.h @@ -0,0 +1,2677 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021 Code Construct Pty Ltd + * + * Authors: Jeremy Kerr <jk@codeconstruct.com.au> + */ + +/** + * DOC: mi.h - NVMe Management Interface library (libnvme-mi) definitions. + * + * These provide an abstraction for the MI messaging between controllers + * and a host, typically over an MCTP-over-i2c link to a NVMe device, used + * as part of the out-of-band management of a system. + * + * We have a few data structures define here to reflect the topology + * of a MI connection with an NVMe subsystem: + * + * - &nvme_mi_ep_t: an MI endpoint - our mechanism of communication with a + * NVMe subsystem. For MCTP, an endpoint will be the component that + * holds the MCTP address (EID), and receives our request message. + * + * endpoints are defined in the NVMe-MI spec, and are specific to the MI + * interface. + * + * Each endpoint will provide access to one or more of: + * + * - &nvme_mi_ctrl_t: a NVMe controller, as defined by the NVMe base spec. + * The controllers are responsible for processing any NVMe standard + * commands (eg, the Admin command set). An endpoint (&nvme_mi_ep_t) + * may provide access to multiple controllers - so each of the controller- + * type commands will require a &nvme_mi_ctrl_t to be specified, rather than + * an endpoint + * + * A couple of conventions with the libnvme-mi API: + * + * - All types and functions have the nvme_mi prefix, to distinguish from + * the libnvme core. + * + * - We currently support either MI commands and Admin commands. The + * former adds a _mi prefix, the latter an _admin prefix. [This does + * result in the MI functions having a double _mi, like + * &nvme_mi_mi_subsystem_health_status_poll, which is apparently amusing + * for our German-speaking readers] + * + * For return values: unless specified in the per-function documentation, + * all functions: + * + * - return 0 on success + * + * - return -1, with errno set, for errors communicating with the MI device, + * either in request or response data + * + * - return >1 on MI status errors. This value is the 8-bit MI status + * value, represented by &enum nvme_mi_resp_status. Note that the + * status values may be vendor-defined above 0xe0. + * + * For the second case, we have a few conventions for errno values: + * + * - EPROTO: response data violated the MI protocol, and libnvme cannot + * validly interpret the response + * + * - EIO: Other I/O error communicating with device (eg., valid but + * unexpected response data) + * + * - EINVAL: invalid input arguments for a command + * + * In line with the core NVMe API, the Admin command functions take an + * `_args` structure to provide the command-specific parameters. However, + * for the MI interface, the fd and timeout members of these _args structs + * are ignored. + * + * References to the specifications here will either to be the NVM Express + * Management Interface ("NVMe-MI") or the NVM Express Base specification + * ("NVMe"). At the time of writing, the versions we're referencing here + * are: + * - NVMe-MI 1.2b + * - NVMe 2.0b + * with a couple of accommodations for older spec types, particularly NVMe-MI + * 1.1, where possible. + * + */ + +#ifndef _LIBNVME_MI_MI_H +#define _LIBNVME_MI_MI_H + +#include <endian.h> +#include <stdint.h> + +#include "types.h" +#include "tree.h" + +/** + * NVME_MI_MSGTYPE_NVME - MCTP message type for NVMe-MI messages. + * + * This is defined by MCTP, but is referenced as part of the NVMe-MI message + * spec. This is the MCTP NVMe message type (0x4), with the message-integrity + * bit (0x80) set. + */ +#define NVME_MI_MSGTYPE_NVME 0x84 + +/* Basic MI message definitions */ + +/** + * enum nvme_mi_message_type - NVMe-MI message type field. + * @NVME_MI_MT_CONTROL: NVME-MI Control Primitive + * @NVME_MI_MT_MI: NVMe-MI command + * @NVME_MI_MT_ADMIN: NVMe Admin command + * @NVME_MI_MT_PCIE: PCIe command + * + * Used as byte 1 of both request and response messages (NMIMT bits of NMP + * byte). Not to be confused with the MCTP message type in byte 0. + */ +enum nvme_mi_message_type { + NVME_MI_MT_CONTROL = 0, + NVME_MI_MT_MI = 1, + NVME_MI_MT_ADMIN = 2, + NVME_MI_MT_PCIE = 4, +}; + +/** + * enum nvme_mi_ror: Request or response field. + * @NVME_MI_ROR_REQ: request message + * @NVME_MI_ROR_RSP: response message + */ +enum nvme_mi_ror { + NVME_MI_ROR_REQ = 0, + NVME_MI_ROR_RSP = 1, +}; + +/** + * enum nvme_mi_resp_status - values for the response status field + * @NVME_MI_RESP_SUCCESS: success + * @NVME_MI_RESP_MPR: More Processing Required + * @NVME_MI_RESP_INTERNAL_ERR: Internal Error + * @NVME_MI_RESP_INVALID_OPCODE: Invalid command opcode + * @NVME_MI_RESP_INVALID_PARAM: Invalid command parameter + * @NVME_MI_RESP_INVALID_CMD_SIZE: Invalid command size + * @NVME_MI_RESP_INVALID_INPUT_SIZE: Invalid command input data size + * @NVME_MI_RESP_ACCESS_DENIED: Access Denied + * @NVME_MI_RESP_VPD_UPDATES_EXCEEDED: More VPD updates than allowed + * @NVME_MI_RESP_PCIE_INACCESSIBLE: PCIe functionality currently unavailable + * @NVME_MI_RESP_MEB_SANITIZED: MEB has been cleared due to sanitize + * @NVME_MI_RESP_ENC_SERV_FAILURE: Enclosure services process failed + * @NVME_MI_RESP_ENC_SERV_XFER_FAILURE: Transfer with enclosure services failed + * @NVME_MI_RESP_ENC_FAILURE: Unreoverable enclosure failure + * @NVME_MI_RESP_ENC_XFER_REFUSED: Enclosure services transfer refused + * @NVME_MI_RESP_ENC_FUNC_UNSUP: Unsupported enclosure services function + * @NVME_MI_RESP_ENC_SERV_UNAVAIL: Enclosure services unavailable + * @NVME_MI_RESP_ENC_DEGRADED: Noncritical failure detected by enc. services + * @NVME_MI_RESP_SANITIZE_IN_PROGRESS: Command prohibited during sanitize + */ +enum nvme_mi_resp_status { + NVME_MI_RESP_SUCCESS = 0x00, + NVME_MI_RESP_MPR = 0x01, + NVME_MI_RESP_INTERNAL_ERR = 0x02, + NVME_MI_RESP_INVALID_OPCODE = 0x03, + NVME_MI_RESP_INVALID_PARAM = 0x04, + NVME_MI_RESP_INVALID_CMD_SIZE = 0x05, + NVME_MI_RESP_INVALID_INPUT_SIZE = 0x06, + NVME_MI_RESP_ACCESS_DENIED = 0x07, + /* 0x08 - 0x1f: reserved */ + NVME_MI_RESP_VPD_UPDATES_EXCEEDED = 0x20, + NVME_MI_RESP_PCIE_INACCESSIBLE = 0x21, + NVME_MI_RESP_MEB_SANITIZED = 0x22, + NVME_MI_RESP_ENC_SERV_FAILURE = 0x23, + NVME_MI_RESP_ENC_SERV_XFER_FAILURE = 0x24, + NVME_MI_RESP_ENC_FAILURE = 0x25, + NVME_MI_RESP_ENC_XFER_REFUSED = 0x26, + NVME_MI_RESP_ENC_FUNC_UNSUP = 0x27, + NVME_MI_RESP_ENC_SERV_UNAVAIL = 0x28, + NVME_MI_RESP_ENC_DEGRADED = 0x29, + NVME_MI_RESP_SANITIZE_IN_PROGRESS = 0x2a, + /* 0x2b - 0xdf: reserved */ + /* 0xe0 - 0xff: vendor specific */ +}; + +/** + * struct nvme_mi_msg_hdr - General MI message header. + * @type: MCTP message type, will always be NVME_MI_MSGTYPE_NVME + * @nmp: NVMe-MI message parameters (including MI message type) + * @meb: Management Endpoint Buffer flag; unused for libnvme-mi implementation + * @rsvd0: currently reserved + * + * Wire format shared by both request and response messages, per NVMe-MI + * section 3.1. This is used for all message types, MI and Admin. + */ +struct nvme_mi_msg_hdr { + __u8 type; + __u8 nmp; + __u8 meb; + __u8 rsvd0; +} __attribute__((packed)); + +/** + * struct nvme_mi_msg_resp - Generic response type. + * @hdr: the general request/response message header + * @status: response status value (see &enum nvme_mi_resp_status) + * @rsvd0: reserved data, may be defined by specific response + * + * Every response will start with one of these; command-specific responses + * will define parts of the reserved data, and may add further fields. + */ +struct nvme_mi_msg_resp { + struct nvme_mi_msg_hdr hdr; + __u8 status; + __u8 rsvd0[3]; +}; + +/** + * enum nvme_mi_mi_opcode - Operation code for supported NVMe-MI commands. + * @nvme_mi_mi_opcode_mi_data_read: Read NVMe-MI Data Structure + * @nvme_mi_mi_opcode_subsys_health_status_poll: Subsystem Health Status Poll + * @nvme_mi_mi_opcode_configuration_set: MI Configuration Set + * @nvme_mi_mi_opcode_configuration_get: MI Configuration Get + */ +enum nvme_mi_mi_opcode { + nvme_mi_mi_opcode_mi_data_read = 0x00, + nvme_mi_mi_opcode_subsys_health_status_poll = 0x01, + nvme_mi_mi_opcode_configuration_set = 0x03, + nvme_mi_mi_opcode_configuration_get = 0x04, +}; + +/** + * struct nvme_mi_mi_req_hdr - MI request message header. + * @hdr: generic MI message header + * @opcode: opcode (OPC) for the specific MI command + * @rsvd0: reserved bytes + * @cdw0: Management Request Doubleword 0 - command specific usage + * @cdw1: Management Request Doubleword 1 - command specific usage + * + * Wire format for MI request message headers, defined in section 5 of NVMe-MI. + */ +struct nvme_mi_mi_req_hdr { + struct nvme_mi_msg_hdr hdr; + __u8 opcode; + __u8 rsvd0[3]; + __le32 cdw0, cdw1; +}; + +/** + * struct nvme_mi_mi_resp_hdr - MI response message header. + * @hdr: generic MI message header + * @status: generic response status from command; non-zero on failure. + * @nmresp: NVMe Management Response: command-type-specific response data + * + * Wire format for MI response message header, defined in section 5 of NVMe-MI. + */ +struct nvme_mi_mi_resp_hdr { + struct nvme_mi_msg_hdr hdr; + __u8 status; + __u8 nmresp[3]; +}; + +/** + * enum nvme_mi_dtyp - Data Structure Type field. + * @nvme_mi_dtyp_subsys_info: NVM Subsystem Information + * @nvme_mi_dtyp_port_info: Port information + * @nvme_mi_dtyp_ctrl_list: Controller List + * @nvme_mi_dtyp_ctrl_info: Controller Information + * @nvme_mi_dtyp_opt_cmd_support: Optionally Supported Command List + * @nvme_mi_dtyp_meb_support: Management Endpoint Buffer Command Support List + * + * Data Structure Type field for Read NVMe-MI Data Structure command, used to + * indicate the particular structure to query from the endpoint. + */ +enum nvme_mi_dtyp { + nvme_mi_dtyp_subsys_info = 0x00, + nvme_mi_dtyp_port_info = 0x01, + nvme_mi_dtyp_ctrl_list = 0x02, + nvme_mi_dtyp_ctrl_info = 0x03, + nvme_mi_dtyp_opt_cmd_support = 0x04, + nvme_mi_dtyp_meb_support = 0x05, +}; + +/** + * enum nvme_mi_config_id - NVMe-MI Configuration identifier. + * @NVME_MI_CONFIG_SMBUS_FREQ: Current SMBus/I2C frequency + * @NVME_MI_CONFIG_HEALTH_STATUS_CHANGE: Health Status change - used to clear + * health status bits in CCS bits of + * status poll. Only for Set ops. + * @NVME_MI_CONFIG_MCTP_MTU: MCTP maximum transmission unit size of port + * specified in dw 0 + * + * Configuration parameters for the MI Get/Set Configuration commands. + * + * See &nvme_mi_mi_config_get() and &nvme_mi_config_set(). + */ +enum nvme_mi_config_id { + NVME_MI_CONFIG_SMBUS_FREQ = 0x1, + NVME_MI_CONFIG_HEALTH_STATUS_CHANGE = 0x2, + NVME_MI_CONFIG_MCTP_MTU = 0x3, +}; + +/** + * enum nvme_mi_config_smbus_freq - SMBus/I2C frequency values + * @NVME_MI_CONFIG_SMBUS_FREQ_100kHz: 100kHz + * @NVME_MI_CONFIG_SMBUS_FREQ_400kHz: 400kHz + * @NVME_MI_CONFIG_SMBUS_FREQ_1MHz: 1MHz + * + * Values used in the SMBus Frequency device configuration. See + * &nvme_mi_mi_config_get_smbus_freq() and &nvme_mi_mi_config_set_smbus_freq(). + */ +enum nvme_mi_config_smbus_freq { + NVME_MI_CONFIG_SMBUS_FREQ_100kHz = 0x1, + NVME_MI_CONFIG_SMBUS_FREQ_400kHz = 0x2, + NVME_MI_CONFIG_SMBUS_FREQ_1MHz = 0x3, +}; + +/* Admin command definitions */ + +/** + * struct nvme_mi_admin_req_hdr - Admin command request header. + * @hdr: Generic MI message header + * @opcode: Admin command opcode (using enum nvme_admin_opcode) + * @flags: Command Flags, indicating dlen and doff validity; Only defined in + * NVMe-MI version 1.1, no fields defined in 1.2 (where the dlen/doff + * are always considered valid). + * @ctrl_id: Controller ID target of command + * @cdw1: Submission Queue Entry doubleword 1 + * @cdw2: Submission Queue Entry doubleword 2 + * @cdw3: Submission Queue Entry doubleword 3 + * @cdw4: Submission Queue Entry doubleword 4 + * @cdw5: Submission Queue Entry doubleword 5 + * @doff: Offset of data to return from command + * @dlen: Length of sent/returned data + * @rsvd0: Reserved + * @rsvd1: Reserved + * @cdw10: Submission Queue Entry doubleword 10 + * @cdw11: Submission Queue Entry doubleword 11 + * @cdw12: Submission Queue Entry doubleword 12 + * @cdw13: Submission Queue Entry doubleword 13 + * @cdw14: Submission Queue Entry doubleword 14 + * @cdw15: Submission Queue Entry doubleword 15 + * + * Wire format for Admin command message headers, defined in section 6 of + * NVMe-MI. + */ +struct nvme_mi_admin_req_hdr { + struct nvme_mi_msg_hdr hdr; + __u8 opcode; + __u8 flags; + __le16 ctrl_id; + __le32 cdw1, cdw2, cdw3, cdw4, cdw5; + __le32 doff; + __le32 dlen; + __le32 rsvd0, rsvd1; + __le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15; +} __attribute((packed)); + +/** + * struct nvme_mi_admin_resp_hdr - Admin command response header. + * @hdr: Generic MI message header + * @status: Generic response code, non-zero on failure + * @rsvd0: Reserved + * @cdw0: Completion Queue Entry doubleword 0 + * @cdw1: Completion Queue Entry doubleword 1 + * @cdw3: Completion Queue Entry doubleword 3 + * + * This is the generic response format with the three doublewords of completion + * queue data, plus optional response data. + */ +struct nvme_mi_admin_resp_hdr { + struct nvme_mi_msg_hdr hdr; + __u8 status; + __u8 rsvd0[3]; + __le32 cdw0, cdw1, cdw3; +} __attribute__((packed)); + +/** + * nvme_mi_status_to_string() - return a string representation of the MI + * status. + * @status: MI response status + * + * Gives a string description of @status, as per section 4.1.2 of the NVMe-MI + * spec. The status value should be of type NVME_STATUS_MI, and extracted + * from the return value using nvme_status_get_value(). + * + * Returned string is const, and should not be free()ed. + * + * Returns: A string representing the status value + */ +const char *nvme_mi_status_to_string(int status); + +/** + * nvme_mi_create_root() - Create top-level MI (root) handle. + * @fp: File descriptor for logging messages + * @log_level: Logging level to use + * + * Create the top-level (library) handle for creating subsequent endpoint + * objects. Similar to nvme_create_root(), but we provide this to allow linking + * without the core libnvme. + * + * Return: new root object, or NULL on failure. + * + * See &nvme_create_root. + */ +nvme_root_t nvme_mi_create_root(FILE *fp, int log_level); + +/** + * nvme_mi_free_root() - Free root object. + * @root: root to free + */ +void nvme_mi_free_root(nvme_root_t root); + +/** + * nvme_mi_set_probe_enabled() - enable/disable the probe for new endpoints + * @root: &nvme_root_t object + * @enabled: whether to probe new endpoints + * + * Controls whether newly-created endpoints are probed for quirks on creation. + * Defaults to enabled, which results in some initial messaging with the + * endpoint to determine model-specific details. + */ +void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled); + +/* Top level management object: NVMe-MI Management Endpoint */ +struct nvme_mi_ep; + +/** + * typedef nvme_mi_ep_t - MI Endpoint object. + * + * Represents our communication endpoint on the remote MI-capable device. + * To be used for direct MI commands for the endpoint (through the + * nvme_mi_mi_* functions(), or to communicate with individual controllers + * (see &nvme_mi_init_ctrl). + * + * Endpoints are created through a transport-specific constructor; currently + * only MCTP-connected endpoints are supported, through &nvme_mi_open_mctp. + * Subsequent operations on the endpoint (and related controllers) are + * transport-independent. + */ +typedef struct nvme_mi_ep * nvme_mi_ep_t; + +/** + * nvme_mi_first_endpoint - Start endpoint iterator + * @m: &nvme_root_t object + * + * Return: first MI endpoint object under this root, or NULL if no endpoints + * are present. + * + * See: &nvme_mi_next_endpoint, &nvme_mi_for_each_endpoint + */ +nvme_mi_ep_t nvme_mi_first_endpoint(nvme_root_t m); + +/** + * nvme_mi_next_endpoint - Continue endpoint iterator + * @m: &nvme_root_t object + * @e: &nvme_mi_ep_t current position of iterator + * + * Return: next endpoint MI endpoint object after @e under this root, or NULL + * if no further endpoints are present. + * + * See: &nvme_mi_first_endpoint, &nvme_mi_for_each_endpoint + */ +nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t e); + +/** + * nvme_mi_for_each_endpoint - Iterator for NVMe-MI endpoints. + * @m: &nvme_root_t containing endpoints + * @e: &nvme_mi_ep_t object, set on each iteration + */ +#define nvme_mi_for_each_endpoint(m, e) \ + for (e = nvme_mi_first_endpoint(m); e != NULL; \ + e = nvme_mi_next_endpoint(m, e)) + +/** + * nvme_mi_for_each_endpoint_safe - Iterator for NVMe-MI endpoints, allowing + * deletion during traversal + * @m: &nvme_root_t containing endpoints + * @e: &nvme_mi_ep_t object, set on each iteration + * @_e: &nvme_mi_ep_t object used as temporary storage + */ +#define nvme_mi_for_each_endpoint_safe(m, e, _e) \ + for (e = nvme_mi_first_endpoint(m), _e = nvme_mi_next_endpoint(m, e); \ + e != NULL; \ + e = _e, _e = nvme_mi_next_endpoint(m, e)) + +/** + * nvme_mi_ep_set_timeout - set a timeout for NVMe-MI responses + * @ep: MI endpoint object + * @timeout_ms: Timeout for MI responses, given in milliseconds + */ +int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms); + +/** + * nvme_mi_ep_set_mprt_max - set the maximum wait time for a More Processing + * Required response + * @ep: MI endpoint object + * @mprt_max_ms: Maximum more processing required wait time + * + * NVMe-MI endpoints may respond to a request with a "More Processing Required" + * response; this also includes a hint on the worst-case processing time for + * the eventual response data, with a specification-defined maximum of 65.535 + * seconds. + * + * This function provides a way to limit the maximum time we're prepared to + * wait for the final response. Specify zero in @mprt_max_ms for no limit. + * This should be larger than the command/response timeout set in + * &nvme_mi_ep_set_timeout(). + */ +void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms); + +/** + * nvme_mi_ep_get_timeout - get the current timeout value for NVMe-MI responses + * @ep: MI endpoint object + * + * Returns the current timeout value, in milliseconds, for this endpoint. + */ +unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep); + +struct nvme_mi_ctrl; + +/** + * typedef nvme_mi_ctrl_t - NVMe-MI Controller object. + * + * Provides NVMe command functionality, through the MI interface. + */ +typedef struct nvme_mi_ctrl * nvme_mi_ctrl_t; + +/** + * nvme_mi_first_ctrl - Start controller iterator + * @ep: &nvme_mi_ep_t object + * + * Return: first MI controller object under this root, or NULL if no controllers + * are present. + * + * See: &nvme_mi_next_ctrl, &nvme_mi_for_each_ctrl + */ +nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep); + +/** + * nvme_mi_next_ctrl - Continue ctrl iterator + * @ep: &nvme_mi_ep_t object + * @c: &nvme_mi_ctrl_t current position of iterator + * + * Return: next MI controller object after @c under this endpoint, or NULL + * if no further controllers are present. + * + * See: &nvme_mi_first_ctrl, &nvme_mi_for_each_ctrl + */ +nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c); + +/** + * nvme_mi_for_each_ctrl - Iterator for NVMe-MI controllers. + * @ep: &nvme_mi_ep_t containing endpoints + * @c: &nvme_mi_ctrl_t object, set on each iteration + * + * Allows iteration of the list of controllers behind an endpoint. Unless the + * controllers have already been created explicitly, you'll probably want to + * call &nvme_mi_scan_ep() to scan for the controllers first. + * + * See: &nvme_mi_scan_ep() + */ +#define nvme_mi_for_each_ctrl(ep, c) \ + for (c = nvme_mi_first_ctrl(ep); c != NULL; \ + c = nvme_mi_next_ctrl(ep, c)) + +/** + * nvme_mi_for_each_ctrl_safe - Iterator for NVMe-MI controllers, allowing + * deletion during traversal + * @ep: &nvme_mi_ep_t containing controllers + * @c: &nvme_mi_ctrl_t object, set on each iteration + * @_c: &nvme_mi_ctrl_t object used as temporary storage + * + * Allows iteration of the list of controllers behind an endpoint, safe against + * deletion during iteration. Unless the controllers have already been created + * explicitly (or you're just iterating to destroy controllers) you'll probably + * want to call &nvme_mi_scan_ep() to scan for the controllers first. + * + * See: &nvme_mi_scan_ep() + */ +#define nvme_mi_for_each_ctrl_safe(ep, c, _c) \ + for (c = nvme_mi_first_ctrl(ep), _c = nvme_mi_next_ctrl(ep, c); \ + c != NULL; \ + c = _c, _c = nvme_mi_next_ctrl(ep, c)) + +/** + * nvme_mi_open_mctp() - Create an endpoint using a MCTP connection. + * @root: root object to create under + * @netid: MCTP network ID on this system + * @eid: MCTP endpoint ID + * + * Transport-specific endpoint initialization for MI-connected endpoints. Once + * an endpoint is created, the rest of the API is transport-independent. + * + * Return: New endpoint object for @netid & @eid, or NULL on failure. + * + * See &nvme_mi_close + */ +nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, uint8_t eid); + +/** + * nvme_mi_close() - Close an endpoint connection and release resources, + * including controller objects. + * + * @ep: Endpoint object to close + */ +void nvme_mi_close(nvme_mi_ep_t ep); + +/** + * nvme_mi_scan_mctp - look for MCTP-connected NVMe-MI endpoints. + * + * Description: This function queries the system MCTP daemon ("mctpd") over + * D-Bus, to find MCTP endpoints that report support for NVMe-MI over MCTP. + * + * This requires libvnme-mi to be compiled with D-Bus support; if not, this + * will return NULL. + * + * Return: A @nvme_root_t populated with a set of MCTP-connected endpoints, + * or NULL on failure + */ +nvme_root_t nvme_mi_scan_mctp(void); + +/** + * nvme_mi_scan_ep - query an endpoint for its NVMe controllers. + * @ep: Endpoint to scan + * @force_rescan: close existing controllers and rescan + * + * This function queries an MI endpoint for the controllers available, by + * performing an MI Read MI Data Structure command (requesting the + * controller list). The controllers are stored in the endpoint's internal + * list, and can be iterated with nvme_mi_for_each_ctrl. + * + * This will only scan the endpoint once, unless @force_rescan is set. If + * so, all existing controller objects will be freed - the caller must not + * hold a reference to those across this call. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &nvme_mi_for_each_ctrl + */ +int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan); + +/** + * nvme_mi_init_ctrl() - initialise a NVMe controller. + * @ep: Endpoint to create under + * @ctrl_id: ID of controller to initialize. + * + * Create a connection to a controller behind the endpoint specified in @ep. + * Controller IDs may be queried from the endpoint through + * &nvme_mi_mi_read_mi_data_ctrl_list. + * + * Return: New controller object, or NULL on failure. + * + * See &nvme_mi_close_ctrl + */ +nvme_mi_ctrl_t nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id); + +/** + * nvme_mi_close_ctrl() - free a controller + * @ctrl: controller to free + */ +void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl); + +/** + * nvme_mi_ctrl_id() - get the ID of a controller + * @ctrl: controller to query + * + * Retrieve the ID of the controller, as defined by hardware, and available + * in the Identify (Controller List) data. This is the value passed to + * @nvme_mi_init_ctrl, but may have been created internally via + * @nvme_mi_scan_ep. + * + * Return: the (locally-stored) ID of this controller. + */ +__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl); + + +/** + * nvme_mi_endpoint_desc - Get a string describing a MI endpoint. + * @ep: endpoint to describe + * + * Generates a human-readable string describing the endpoint, with possibly + * transport-specific data. The string is allocated during the call, and the + * caller is responsible for free()-ing the string. + * + * Return: a newly-allocated string containing the endpoint description, or + * NULL on failure. + */ +char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep); + +/* MI Command API: nvme_mi_mi_ prefix */ + +/** + * nvme_mi_mi_read_mi_data_subsys() - Perform a Read MI Data Structure command, + * retrieving subsystem data. + * @ep: endpoint for MI communication + * @s: subsystem information to populate + * + * Retrieves the Subsystem information - number of external ports and + * NVMe version information. See &struct nvme_mi_read_nvm_ss_info. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep, + struct nvme_mi_read_nvm_ss_info *s); + +/** + * nvme_mi_mi_read_mi_data_port() - Perform a Read MI Data Structure command, + * retrieving port data. + * @ep: endpoint for MI communication + * @portid: id of port data to retrieve + * @p: port information to populate + * + * Retrieves the Port information, for the specified port ID. The subsystem + * data (from &nvme_mi_mi_read_mi_data_subsys) nmp field contains the allowed + * range of port IDs. + * + * See &struct nvme_mi_read_port_info. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid, + struct nvme_mi_read_port_info *p); + +/** + * nvme_mi_mi_read_mi_data_ctrl_list() - Perform a Read MI Data Structure + * command, retrieving the list of attached controllers. + * @ep: endpoint for MI communication + * @start_ctrlid: starting controller ID + * @list: controller list to populate + * + * Retrieves the list of attached controllers, with IDs greater than or + * equal to @start_ctrlid. + * + * See &struct nvme_ctrl_list. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid, + struct nvme_ctrl_list *list); + +/** + * nvme_mi_mi_read_mi_data_ctrl() - Perform a Read MI Data Structure command, + * retrieving controller information + * @ep: endpoint for MI communication + * @ctrl_id: ID of controller to query + * @ctrl: controller data to populate + * + * Retrieves the Controller Information Data Structure for the attached + * controller with ID @ctrlid. + * + * See &struct nvme_mi_read_ctrl_info. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id, + struct nvme_mi_read_ctrl_info *ctrl); + +/** + * nvme_mi_mi_subsystem_health_status_poll() - Read the Subsystem Health + * Data Structure from the NVM subsystem + * @ep: endpoint for MI communication + * @clear: flag to clear the Composite Controller Status state + * @nshds: subsystem health status data to populate + * + * Retrieves the Subsystem Health Data Structure into @nshds. If @clear is + * set, requests that the Composite Controller Status bits are cleared after + * the read. See NVMe-MI section 5.6 for details on the CCS bits. + * + * See &struct nvme_mi_nvm_ss_health_status. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear, + struct nvme_mi_nvm_ss_health_status *nshds); + +/** + * nvme_mi_mi_config_get - query a configuration parameter + * @ep: endpoint for MI communication + * @dw0: management doubleword 0, containing configuration identifier, plus + * config-specific fields + * @dw1: management doubleword 0, config-specific. + * @nmresp: set to queried configuration data in NMRESP field of response. + * + * Performs a MI Configuration Get command, with the configuration identifier + * as the LSB of @dw0. Other @dw0 and @dw1 data is configuration-identifier + * specific. + * + * On a successful Configuration Get, the @nmresp pointer will be populated with + * the bytes from the 3-byte NMRESP field, converted to native endian. + * + * See &enum nvme_mi_config_id for identifiers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, + __u32 *nmresp); + +/** + * nvme_mi_mi_config_set - set a configuration parameter + * @ep: endpoint for MI communication + * @dw0: management doubleword 0, containing configuration identifier, plus + * config-specific fields + * @dw1: management doubleword 0, config-specific. + * + * Performs a MI Configuration Set command, with the command as the LSB of + * @dw0. Other @dw0 and @dw1 data is configuration-identifier specific. + * + * See &enum nvme_mi_config_id for identifiers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1); + +/** + * nvme_mi_mi_config_get_smbus_freq - get configuration: SMBus port frequency + * @ep: endpoint for MI communication + * @port: port ID to query + * @freq: output value for current frequency configuration + * + * Performs a MI Configuration Get, to query the current SMBus frequency of + * the port specified in @port. On success, populates @freq with the port + * frequency + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port, + enum nvme_mi_config_smbus_freq *freq) +{ + __u32 tmp, dw0; + int rc; + + dw0 = port << 24 | NVME_MI_CONFIG_SMBUS_FREQ; + + rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp); + if (!rc) + *freq = (enum nvme_mi_config_smbus_freq)(tmp & 0x3); + return rc; +} + +/** + * nvme_mi_mi_config_set_smbus_freq - set configuration: SMBus port frequency + * @ep: endpoint for MI communication + * @port: port ID to set + * @freq: new frequency configuration + * + * Performs a MI Configuration Set, to update the current SMBus frequency of + * the port specified in @port. + * + * See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency + * for the port. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port, + enum nvme_mi_config_smbus_freq freq) +{ + __u32 dw0 = port << 24 | + (freq & 0x3) << 8 | + NVME_MI_CONFIG_SMBUS_FREQ; + + return nvme_mi_mi_config_set(ep, dw0, 0); +} + +/** + * nvme_mi_mi_config_set_health_status_change - clear CCS bits in health status + * @ep: endpoint for MI communication + * @mask: bitmask to clear + * + * Performs a MI Configuration Set, to update the current health status poll + * values of the Composite Controller Status bits. Bits set in @mask will + * be cleared from future health status poll data, and may be re-triggered by + * a future health change event. + * + * See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for + * values in @mask. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep, + __u32 mask) +{ + return nvme_mi_mi_config_set(ep, NVME_MI_CONFIG_HEALTH_STATUS_CHANGE, + mask); +} + +/** + * nvme_mi_mi_config_get_mctp_mtu - get configuration: MCTP MTU + * @ep: endpoint for MI communication + * @port: port ID to query + * @mtu: output value for current MCTP MTU configuration + * + * Performs a MI Configuration Get, to query the current MCTP Maximum + * Transmission Unit size (MTU) of the port specified in @port. On success, + * populates @mtu with the MTU. + * + * The default reset value is 64, corresponding to the MCTP baseline MTU. + * + * Some controllers may also use this as the maximum receive unit size, and + * may not accept MCTP messages larger than the configured MTU. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port, + __u16 *mtu) +{ + __u32 tmp, dw0; + int rc; + + dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU; + + rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp); + if (!rc) + *mtu = tmp & 0xffff; + return rc; +} + +/** + * nvme_mi_mi_config_set_mctp_mtu - set configuration: MCTP MTU + * @ep: endpoint for MI communication + * @port: port ID to set + * @mtu: new MTU configuration + * + * Performs a MI Configuration Set, to update the current MCTP MTU value for + * the port specified in @port. + * + * Some controllers may also use this as the maximum receive unit size, and + * may not accept MCTP messages larger than the configured MTU. When setting + * this value, you will likely need to change the MTU of the local MCTP + * interface(s) to match. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port, + __u16 mtu) +{ + __u32 dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU; + + return nvme_mi_mi_config_set(ep, dw0, mtu); +} + +/* Admin channel functions */ + +/** + * nvme_mi_admin_xfer() - Raw admin transfer interface. + * @ctrl: controller to send the admin command to + * @admin_req: request data + * @req_data_size: size of request data payload + * @admin_resp: buffer for response data + * @resp_data_offset: offset into request data to retrieve from controller + * @resp_data_size: size of response data buffer, updated to received size + * + * Performs an arbitrary NVMe Admin command, using the provided request data, + * in @admin_req. The size of the request data *payload* is specified in + * @req_data_size - this does not include the standard header length (so a + * header-only request would have a size of 0). + * + * On success, response data is stored in @admin_resp, which has an optional + * appended payload buffer of @resp_data_size bytes. The actual payload + * transferred will be stored in @resp_data_size. These sizes do not include + * the Admin request header, so 0 represents no payload. + * + * As with all Admin commands, we can request partial data from the Admin + * Response payload, offset by @resp_data_offset. + * + * See: &struct nvme_mi_admin_req_hdr and &struct nvme_mi_admin_resp_hdr. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. + */ +int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, + struct nvme_mi_admin_req_hdr *admin_req, + size_t req_data_size, + struct nvme_mi_admin_resp_hdr *admin_resp, + off_t resp_data_offset, + size_t *resp_data_size); + +/** + * nvme_mi_admin_admin_passthru() - Submit an nvme admin passthrough command + * @ctrl: Controller to send command to + * @opcode: The nvme admin command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command(not used) + * @metadata: Pointer to user address of the metadata buffer(not used) + * @timeout_ms: How long to wait for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Send a customized NVMe Admin command request message and get the corresponding + * response message. + * + * This interface supports no data, host to controller and controller to + * host but it doesn't support bidirectional data transfer. + * Also this interface only supports data transfer size range [0, 4096] (bytes) + * so the & data_len parameter must be less than 4097. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result); + +/** + * nvme_mi_admin_identify_partial() - Perform an Admin identify command, + * and retrieve partial response data. + * @ctrl: Controller to process identify command + * @args: Identify command arguments + * @offset: offset of identify data to retrieve from response + * @size: size of identify data to return + * + * Perform an Identify command, using the Identify command parameters in @args. + * The @offset and @size arguments allow the caller to retrieve part of + * the identify response. See NVMe-MI section 6.2 for the semantics (and some + * handy diagrams) of the offset & size parameters. + * + * Will return an error if the length of the response data (from the controller) + * did not match @size. + * + * Unless you're performing a vendor-unique identify command, You'll probably + * want to use one of the identify helpers (nvme_mi_admin_identify, + * nvme_mi_admin_identify_cns_nsid, or nvme_mi_admin_identify_<type>) instead + * of this. If the type of your identify command is standardized but not + * yet supported by libnvme-mi, please contact the maintainers. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_identify_args + */ +int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, + struct nvme_identify_args *args, + off_t offset, size_t size); + +/** + * nvme_mi_admin_identify() - Perform an Admin identify command. + * @ctrl: Controller to process identify command + * @args: Identify command arguments + * + * Perform an Identify command, using the Identify command parameters in @args. + * Stores the identify data in ->data, and (if set) the result from cdw0 + * into args->result. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_identify_args + */ +static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl, + struct nvme_identify_args *args) +{ + return nvme_mi_admin_identify_partial(ctrl, args, + 0, NVME_IDENTIFY_DATA_SIZE); +} + +/** + * nvme_mi_admin_identify_cns_nsid() - Perform an Admin identify command using + * specific CNS/NSID parameters. + * @ctrl: Controller to process identify command + * @cns: Controller or Namespace Structure, specifying identified object + * @nsid: namespace ID + * @data: buffer for identify data response + * + * Perform an Identify command, using the CNS specifier @cns, and the + * namespace ID @nsid if required by the CNS type. + * + * Stores the identify data in @data, which is expected to be a buffer of + * &NVME_IDENTIFY_DATA_SIZE bytes. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl, + enum nvme_identify_cns cns, + __u32 nsid, void *data) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = data, + .args_size = sizeof(args), + .cns = cns, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = NVME_CNTLID_NONE, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_ns() - Perform an Admin identify command for a + * namespace + * @ctrl: Controller to process identify command + * @nsid: namespace ID + * @ns: Namespace identification to populate + * + * Perform an Identify (namespace) command, setting the namespace id data + * in @ns. The namespace is expected to active and allocated. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_ns(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_id_ns *ns) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS, + nsid, ns); +} + +/** + * nvme_mi_admin_identify_ns_descs() - Perform an Admin identify Namespace + * Identification Descriptor list command for a namespace + * @ctrl: Controller to process identify command + * @nsid: Namespace ID + * @descs: Namespace Identification Descriptor list to populate + * + * Perform an Identify namespace identification description list command, + * setting the namespace identification description list in @descs + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_ns_descs(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_id_desc *descs) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS_DESC_LIST, + nsid, descs); +} + +/** + * nvme_mi_admin_identify_allocated_ns() - Perform an Admin identify command + * for an allocated namespace + * @ctrl: Controller to process identify command + * @nsid: namespace ID + * @ns: Namespace identification to populate + * + * Perform an Identify (namespace) command, setting the namespace id data + * in @ns. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_allocated_ns(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_id_ns *ns) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, + NVME_IDENTIFY_CNS_ALLOCATED_NS, + nsid, ns); +} + +/** + * nvme_mi_admin_identify_ctrl() - Perform an Admin identify for a controller + * @ctrl: Controller to process identify command + * @id: Controller identify data to populate + * + * Perform an Identify command, for the controller specified by @ctrl, + * writing identify data to @id. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be + * fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_id_ctrl + */ +static inline int nvme_mi_admin_identify_ctrl(nvme_mi_ctrl_t ctrl, + struct nvme_id_ctrl *id) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_CTRL, + NVME_NSID_NONE, id); +} + +/** + * nvme_mi_admin_identify_ctrl_list() - Perform an Admin identify for a + * controller list. + * @ctrl: Controller to process identify command + * @cntid: Controller ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the controller list starting with + * IDs greater than or equal to @cntid. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be + * fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ctrl_list + */ +static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl, + __u16 cntid, + struct nvme_ctrl_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_nsid_ctrl_list() - Perform an Admin identify for a + * controller list with specific namespace ID + * @ctrl: Controller to process identify command + * @nsid: Namespace identifier + * @cntid: Controller ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the controller list for @nsid, starting + * with IDs greater than or equal to @cntid. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be + * fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ctrl_list + */ +static inline int nvme_mi_admin_identify_nsid_ctrl_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, __u16 cntid, + struct nvme_ctrl_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_NS_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_allocated_ns_list() - Perform an Admin identify for + * an allocated namespace list + * @ctrl: Controller to process identify command + * @nsid: Namespace ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the allocated namespace list starting with + * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start + * of the list. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ns_list + */ +static inline int nvme_mi_admin_identify_allocated_ns_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_active_ns_list() - Perform an Admin identify for an + * active namespace list + * @ctrl: Controller to process identify command + * @nsid: Namespace ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the active namespace list starting with + * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start + * of the list. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ns_list + */ +static inline int nvme_mi_admin_identify_active_ns_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_primary_ctrl() - Perform an Admin identify for + * primary controller capabilities data structure. + * @ctrl: Controller to process identify command + * @cntid: Controller ID to specify + * @cap: Primary Controller Capabilities data structure to populate + * + * Perform an Identify command to get the Primary Controller Capabilities data + * for the controller specified by @cntid + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @cap will be + * be fully populated on success. + * + * Return: 0 on success, non-zero on failure + * + * See: &struct nvme_primary_ctrl_cap + */ +static inline int nvme_mi_admin_identify_primary_ctrl(nvme_mi_ctrl_t ctrl, + __u16 cntid, + struct nvme_primary_ctrl_cap *cap) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = cap, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_secondary_ctrl_list() - Perform an Admin identify for + * a secondary controller list. + * @ctrl: Controller to process identify command + * @cntid: Controller ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the secondary controllers associated with + * the current primary controller. Only entries with IDs greater than or + * equal to @cntid are returned. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: 0 on success, non-zero on failure + * + * See: &struct nvme_secondary_ctrl_list + */ +static inline int nvme_mi_admin_identify_secondary_ctrl_list(nvme_mi_ctrl_t ctrl, + __u16 cntid, + struct nvme_secondary_ctrl_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_page() - Retrieve log page data from controller + * @ctrl: Controller to query + * @xfer_len: The chunk size of the read + * @args: Get Log Page command arguments + * + * Performs a Get Log Page Admin command as specified by @args. Response data + * is stored in @args->data, which should be a buffer of @args->data_len bytes. + * Resulting data length is stored in @args->data_len on successful + * command completion. + * + * This request may be implemented as multiple log page commands, in order + * to fit within MI message-size limits. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_get_log_args + */ +int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_len, + struct nvme_get_log_args *args); + +/** + * nvme_mi_admin_get_log() - Retrieve log page data from controller + * @ctrl: Controller to query + * @args: Get Log Page command arguments + * + * Performs a Get Log Page Admin command as specified by @args. Response data + * is stored in @args->data, which should be a buffer of @args->data_len bytes. + * Resulting data length is stored in @args->data_len on successful + * command completion. + * + * This request may be implemented as multiple log page commands, in order + * to fit within MI message-size limits. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_get_log_args + */ +int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args); + +/** + * nvme_mi_admin_get_nsid_log() - Helper for Get Log Page functions + * @ctrl: Controller to query + * @rae: Retain Asynchronous Events + * @lid: Log identifier + * @nsid: Namespace ID + * @len: length of log buffer + * @log: pointer for resulting log data + * + * Performs a Get Log Page Admin command for a specific log ID @lid and + * namespace ID @nsid. Log data is expected to be @len bytes, and is stored + * in @log on success. The @rae flag is passed as-is to the Get Log Page + * command, and is specific to the Log Page requested. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_nsid_log(nvme_mi_ctrl_t ctrl, bool rae, + enum nvme_cmd_get_log_lid lid, + __u32 nsid, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = lid, + .len = len, + .nsid = nsid, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_simple() - Helper for Get Log Page functions with no + * NSID or RAE requirements + * @ctrl: Controller to query + * @lid: Log identifier + * @len: length of log buffer + * @log: pointer for resulting log data + * + * Performs a Get Log Page Admin command for a specific log ID @lid, using + * NVME_NSID_ALL for the namespace identifier, and rae set to false. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_simple(nvme_mi_ctrl_t ctrl, + enum nvme_cmd_get_log_lid lid, + __u32 len, void *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, false, lid, NVME_NSID_ALL, + len, log); +} + +/** + * nvme_mi_admin_get_log_supported_log_pages() - Retrieve nmve supported log + * pages + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: Array of LID supported and Effects data structures + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_supported_log_pages(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_supported_log_pages *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, + NVME_LOG_LID_SUPPORTED_LOG_PAGES, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_error() - Retrieve nvme error log + * @ctrl: Controller to query + * @nr_entries: Number of error log entries allocated + * @rae: Retain asynchronous events + * @err_log: Array of error logs of size 'entries' + * + * This log page describes extended error information for a command that + * completed with error, or may report an error that is not specific to a + * particular command. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_error(nvme_mi_ctrl_t ctrl, + unsigned int nr_entries, bool rae, + struct nvme_error_log_page *err_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_ERROR, + NVME_NSID_ALL, sizeof(*err_log) * nr_entries, + err_log); +} + +/** + * nvme_mi_admin_get_log_smart() - Retrieve nvme smart log + * @ctrl: Controller to query + * @nsid: Optional namespace identifier + * @rae: Retain asynchronous events + * @smart_log: User address to store the smart log + * + * This log page provides SMART and general health information. The information + * provided is over the life of the controller and is retained across power + * cycles. To request the controller log page, the namespace identifier + * specified is FFFFFFFFh. The controller may also support requesting the log + * page on a per namespace basis, as indicated by bit 0 of the LPA field in the + * Identify Controller data structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_smart(nvme_mi_ctrl_t ctrl, __u32 nsid, + bool rae, + struct nvme_smart_log *smart_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SMART, + nsid, sizeof(*smart_log), smart_log); +} + +/** + * nvme_mi_admin_get_log_fw_slot() - Retrieves the controller firmware log + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @fw_log: User address to store the log page + * + * This log page describes the firmware revision stored in each firmware slot + * supported. The firmware revision is indicated as an ASCII string. The log + * page also indicates the active slot number. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_fw_slot(nvme_mi_ctrl_t ctrl, bool rae, + struct nvme_firmware_slot *fw_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_FW_SLOT, + NVME_NSID_ALL, sizeof(*fw_log), fw_log); +} + +/** + * nvme_mi_admin_get_log_changed_ns_list() - Retrieve namespace changed list + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @ns_log: User address to store the log page + * + * This log page describes namespaces attached to this controller that have + * changed since the last time the namespace was identified, been added, or + * deleted. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_changed_ns_list(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_ns_list *ns_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_CHANGED_NS, + NVME_NSID_ALL, sizeof(*ns_log), ns_log); +} + +/** + * nvme_mi_admin_get_log_cmd_effects() - Retrieve nvme command effects log + * @ctrl: Controller to query + * @csi: Command Set Identifier + * @effects_log: User address to store the effects log + * + * This log page describes the commands that the controller supports and the + * effects of those commands on the state of the NVM subsystem. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_cmd_effects(nvme_mi_ctrl_t ctrl, + enum nvme_csi csi, + struct nvme_cmd_effects_log *effects_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = effects_log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_CMD_EFFECTS, + .len = sizeof(*effects_log), + .nsid = NVME_NSID_ALL, + .csi = csi, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_device_self_test() - Retrieve the device self test log + * @ctrl: Controller to query + * @log: Userspace address of the log payload + * + * The log page indicates the status of an in progress self test and the + * percent complete of that operation, and the results of the previous 20 + * self-test operations. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_device_self_test(nvme_mi_ctrl_t ctrl, + struct nvme_self_test_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, false, + NVME_LOG_LID_DEVICE_SELF_TEST, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_create_telemetry_host() - Create host telemetry log + * @ctrl: Controller to query + * @log: Userspace address of the log payload + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_create_telemetry_host(nvme_mi_ctrl_t ctrl, + struct nvme_telemetry_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_telemetry_host() - Get Telemetry Host-Initiated log + * page + * @ctrl: Controller to query + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Host-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_telemetry_host(nvme_mi_ctrl_t ctrl, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_RETAIN, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_telemetry_ctrl() - Get Telemetry Controller-Initiated + * log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Controller-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_telemetry_ctrl(nvme_mi_ctrl_t ctrl, + bool rae, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_CTRL, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_endurance_group() - Get Endurance Group log + * @ctrl: Controller to query + * @endgid: Starting group identifier to return in the list + * @log: User address to store the endurance log + * + * This log page indicates if an Endurance Group Event has occurred for a + * particular Endurance Group. If an Endurance Group Event has occurred, the + * details of the particular event are included in the Endurance Group + * Information log page for that Endurance Group. An asynchronous event is + * generated when an entry for an Endurance Group is newly added to this log + * page. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_endurance_group(nvme_mi_ctrl_t ctrl, + __u16 endgid, + struct nvme_endurance_group_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ENDURANCE_GROUP, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = endgid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_predictable_lat_nvmset() - Predictable Latency Per NVM + * Set + * @ctrl: Controller to query + * @nvmsetid: NVM set id + * @log: User address to store the predictable latency log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_predictable_lat_nvmset(nvme_mi_ctrl_t ctrl, + __u16 nvmsetid, + struct nvme_nvmset_predictable_lat_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PREDICTABLE_LAT_NVMSET, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = nvmsetid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_predictable_lat_event() - Retrieve Predictable Latency + * Event Aggregate Log Page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset into the predictable latency event + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_predictable_lat_event(nvme_mi_ctrl_t ctrl, + bool rae, + __u32 offset, + __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PREDICTABLE_LAT_AGG, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_ana() - Retrieve Asymmetric Namespace Access log page + * @ctrl: Controller to query + * @lsp: Log specific, see &enum nvme_get_log_ana_lsp + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the ana log + * + * This log consists of a header describing the log and descriptors containing + * the asymmetric namespace access information for ANA Groups that contain + * namespaces that are attached to the controller processing the command. + * + * See &struct nvme_ana_rsp_hdr for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_ana(nvme_mi_ctrl_t ctrl, + enum nvme_log_ana_lsp lsp, bool rae, + __u64 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ANA, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)lsp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_ana_groups() - Retrieve Asymmetric Namespace Access + * groups only log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @len: The allocated length of the log page + * @log: User address to store the ana group log + * + * See &struct nvme_ana_group_desc for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_ana_groups(nvme_mi_ctrl_t ctrl, + bool rae, __u32 len, + struct nvme_ana_group_desc *log) +{ + return nvme_mi_admin_get_log_ana(ctrl, NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY, rae, 0, + len, log); +} + +/** + * nvme_mi_admin_get_log_lba_status() - Retrieve LBA Status + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_lba_status(nvme_mi_ctrl_t ctrl, bool rae, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_LBA_STATUS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_endurance_grp_evt() - Retrieve Rotational Media + * Information + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_endurance_grp_evt(nvme_mi_ctrl_t ctrl, + bool rae, + __u32 offset, + __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ENDURANCE_GRP_EVT, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_fid_supported_effects() - Retrieve Feature Identifiers + * Supported and Effects + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: FID Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_fid_supported_effects(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_fid_supported_effects_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, + NVME_LOG_LID_FID_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_mi_cmd_supported_effects() - displays the MI Commands + * Supported by the controller + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: MI Command Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_mi_cmd_supported_effects(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_mi_cmd_supported_effects_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_boot_partition() - Retrieve Boot Partition + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @lsp: The log specified field of LID + * @len: The allocated size, minimum + * struct nvme_boot_partition + * @part: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_boot_partition(nvme_mi_ctrl_t ctrl, + bool rae, __u8 lsp, + __u32 len, + struct nvme_boot_partition *part) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = part, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_BOOT_PARTITION, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_phy_rx_eom() - Retrieve Physical Interface Receiver Eye Opening Measurement Log + * @ctrl: Controller to query + * @lsp: Log specific, controls action and measurement quality + * @controller: Target controller ID + * @len: The allocated size, minimum + * struct nvme_phy_rx_eom_log + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise + */ +static inline int nvme_mi_admin_get_log_phy_rx_eom(nvme_mi_ctrl_t ctrl, + __u8 lsp, __u16 controller, + __u32 len, + struct nvme_phy_rx_eom_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PHY_RX_EOM, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = controller, + .lsp = lsp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_discovery() - Retrieve Discovery log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset of this log to retrieve + * @len: The allocated size for this portion of the log + * @log: User address to store the discovery log + * + * Supported only by fabrics discovery controllers, returning discovery + * records. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_discovery(nvme_mi_ctrl_t ctrl, bool rae, + __u32 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_DISCOVER, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_media_unit_stat() - Retrieve Media Unit Status + * @ctrl: Controller to query + * @domid: Domain Identifier selection, if supported + * @mus: User address to store the Media Unit statistics log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_media_unit_stat(nvme_mi_ctrl_t ctrl, + __u16 domid, + struct nvme_media_unit_stat_log *mus) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = mus, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_MEDIA_UNIT_STATUS, + .len = sizeof(*mus), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_support_cap_config_list() - Retrieve Supported + * Capacity Configuration List + * @ctrl: Controller to query + * @domid: Domain Identifier selection, if supported + * @cap: User address to store supported capabilities config list + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_support_cap_config_list(nvme_mi_ctrl_t ctrl, + __u16 domid, + struct nvme_supported_cap_config_list_log *cap) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = cap, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST, + .len = sizeof(*cap), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_reservation() - Retrieve Reservation Notification + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: User address to store the reservation log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_reservation(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_resv_notification_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_RESERVATION, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_sanitize() - Retrieve Sanitize Status + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: User address to store the sanitize log + * + * The Sanitize Status log page reports sanitize operation time estimates and + * information about the most recent sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_sanitize(nvme_mi_ctrl_t ctrl, bool rae, + struct nvme_sanitize_log_page *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SANITIZE, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_zns_changed_zones() - Retrieve list of zones that have + * changed + * @ctrl: Controller to query + * @nsid: Namespace ID + * @rae: Retain asynchronous events + * @log: User address to store the changed zone log + * + * The list of zones that have changed state due to an exceptional event. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_zns_changed_zones(nvme_mi_ctrl_t ctrl, + __u32 nsid, bool rae, + struct nvme_zns_changed_zone_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ZNS_CHANGED_ZONES, + .len = sizeof(*log), + .nsid = nsid, + .csi = NVME_CSI_ZNS, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_persistent_event() - Retrieve Persistent Event Log + * @ctrl: Controller to query + * @action: Action the controller should take during processing this command + * @size: Size of @pevent_log + * @pevent_log: User address to store the persistent event log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_persistent_event(nvme_mi_ctrl_t ctrl, + enum nvme_pevent_log_action action, + __u32 size, void *pevent_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = pevent_log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PERSISTENT_EVENT, + .len = size, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)action, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_security_send() - Perform a Security Send command on a + * controller. + * @ctrl: Controller to send command to + * @args: Security Send command arguments + * + * Performs a Security Send Admin command as specified by @args. Response data + * is stored in @args->data, which should be a buffer of @args->data_len bytes. + * Resulting data length is stored in @args->data_len on successful + * command completion. + * + * Security Send data length should not be greater than 4096 bytes to + * comply with specification limits. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_get_log_args + */ +int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, + struct nvme_security_send_args *args); + +/** + * nvme_mi_admin_security_recv() - Perform a Security Receive command on a + * controller. + * @ctrl: Controller to send command to + * @args: Security Receive command arguments + * + * Performs a Security Receive Admin command as specified by @args. Response + * data is stored in @args->data, which should be a buffer of @args->data_len + * bytes. Resulting data length is stored in @args->data_len on successful + * command completion. + * + * Security Receive data length should not be greater than 4096 bytes to + * comply with specification limits. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_get_log_args + */ +int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, + struct nvme_security_receive_args *args); + +/** + * nvme_mi_admin_get_features - Perform a Get Feature command on a controller + * @ctrl: Controller to send command to + * @args: Get Features command arguments + * + * Performs a Get Features Admin command as specified by @args. Returned + * feature data will be stored in @args->result and @args->data, depending + * on the specification of the feature itself; most features do not return + * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for + * feature-specific information. + * + * On success, @args->data_len will be updated with the actual data length + * received. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl, + struct nvme_get_features_args *args); + +/** + * nvme_mi_admin_get_features_data() - Helper function for &nvme_mi_admin_get_features() + * @ctrl: Controller to send command to + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable for @fid + * @data_len: Length of feature data, if applicable for @fid, in bytes + * @data: User address of feature data, if applicable + * @result: The command completion result from CQE dword0 + * + * Helper for optionally features that optionally return data, using the + * SEL_CURRENT selector value. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_features_data(nvme_mi_ctrl_t ctrl, + enum nvme_features_id fid, + __u32 nsid, __u32 data_len, + void *data, __u32 *result) +{ + struct nvme_get_features_args args = { + .result = result, + .data = data, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_GET_FEATURES_SEL_CURRENT, + .cdw11 = 0, + .data_len = data_len, + .fid = (__u8)fid, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_get_features(ctrl, &args); +} + +/** + * nvme_mi_admin_get_features_simple - Get a simple feature value with no data + * @ctrl: Controller to send command to + * @fid: Feature identifier + * @nsid: Namespace id, if required by @fid + * @result: output feature data + */ +static inline int nvme_mi_admin_get_features_simple(nvme_mi_ctrl_t ctrl, + enum nvme_features_id fid, + __u32 nsid, + __u32 *result) +{ + return nvme_mi_admin_get_features_data(ctrl, fid, nsid, + 0, NULL, result); +} + +/** + * nvme_mi_admin_set_features - Perform a Set Features command on a controller + * @ctrl: Controller to send command to + * @args: Set Features command arguments + * + * Performs a Set Features Admin command as specified by @args. Result + * data will be stored in @args->result. + * on the specification of the feature itself; most features do not return + * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for + * feature-specific information. + * + * On success, @args->data_len will be updated with the actual data length + * received. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl, + struct nvme_set_features_args *args); + +/** + * nvme_mi_admin_ns_mgmt - Issue a Namespace Management command + * @ctrl: Controller to send command to + * @args: Namespace management command arguments + * + * Issues a Namespace Management command to @ctrl, with arguments specified + * from @args. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, + struct nvme_ns_mgmt_args *args); + +/** + * nvme_mi_admin_ns_mgmt_create - Helper for Namespace Management Create command + * @ctrl: Controller to send command to + * @ns: New namespace parameters + * @csi: Command Set Identifier for new NS + * @nsid: Set to new namespace ID on create + * @data: Host Software Specified Fields that defines ns creation parameters + * + * Issues a Namespace Management (Create) command to @ctrl, to create a + * new namespace specified by @ns, using command set @csi. On success, + * the new namespace ID will be written to @nsid. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_mgmt_create(nvme_mi_ctrl_t ctrl, + struct nvme_id_ns *ns, __u8 csi, __u32 *nsid, + struct nvme_ns_mgmt_host_sw_specified *data) +{ + struct nvme_ns_mgmt_args args = { + .result = nsid, + .ns = ns, + .args_size = sizeof(args), + .nsid = NVME_NSID_NONE, + .sel = NVME_NS_MGMT_SEL_CREATE, + .csi = csi, + .data = data, + }; + + return nvme_mi_admin_ns_mgmt(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_mgmt_delete - Helper for Namespace Management Delete command + * @ctrl: Controller to send command to + * @nsid: Namespace ID to delete + * + * Issues a Namespace Management (Delete) command to @ctrl, to delete the + * namespace with id @nsid. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_mgmt_delete(nvme_mi_ctrl_t ctrl, __u32 nsid) +{ + struct nvme_ns_mgmt_args args = { + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_MGMT_SEL_DELETE, + }; + + return nvme_mi_admin_ns_mgmt(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_attach() - Attach or detach namespace to controller(s) + * @ctrl: Controller to send command to + * @args: Namespace Attach command arguments + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl, + struct nvme_ns_attach_args *args); + +/** + * nvme_mi_admin_ns_attach_ctrls() - Attach namespace to controllers + * @ctrl: Controller to send command to + * @nsid: Namespace ID to attach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_attach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH, + }; + + return nvme_mi_admin_ns_attach(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_detach_ctrls() - Detach namespace from controllers + * @ctrl: Controller to send command to + * @nsid: Namespace ID to detach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_detach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + }; + + return nvme_mi_admin_ns_attach(ctrl, &args); +} + +/** + * nvme_mi_admin_fw_download() - Download part or all of a firmware image to + * the controller + * @ctrl: Controller to send firmware data to + * @args: &struct nvme_fw_download_args argument structure + * + * The Firmware Image Download command downloads all or a portion of an image + * for a future update to the controller. The Firmware Image Download command + * downloads a new image (in whole or in part) to the controller. + * + * The image may be constructed of multiple pieces that are individually + * downloaded with separate Firmware Image Download commands. Each Firmware + * Image Download command includes a Dword Offset and Number of Dwords that + * specify a dword range. + * + * The new firmware image is not activated as part of the Firmware Image + * Download command. Use the nvme_mi_admin_fw_commit() to activate a newly + * downloaded image. + * + * Return: 0 on success, non-zero on failure + */ +int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl, + struct nvme_fw_download_args *args); + +/** + * nvme_mi_admin_fw_commit() - Commit firmware using the specified action + * @ctrl: Controller to send firmware data to + * @args: &struct nvme_fw_download_args argument structure + * + * The Firmware Commit command modifies the firmware image or Boot Partitions. + * + * Return: 0 on success, non-zero on failure + */ +int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl, + struct nvme_fw_commit_args *args); + +/** + * nvme_mi_admin_format_nvm() - Format NVMe namespace + * @ctrl: Controller to send command to + * @args: Format NVM command arguments + * + * Perform a low-level format to set the LBA data & metadata size. May destroy + * data & metadata on the specified namespaces + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args); + +/** + * nvme_mi_admin_sanitize_nvm() - Start a subsystem Sanitize operation + * @ctrl: Controller to send command to + * @args: Sanitize command arguments + * + * A sanitize operation alters all user data in the NVM subsystem such that + * recovery of any previous user data from any cache, the non-volatile media, + * or any Controller Memory Buffer is not possible. + * + * The Sanitize command starts a sanitize operation or to recover from a + * previously failed sanitize operation. The sanitize operation types that may + * be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize + * operations are processed in the background, i.e., completion of the sanitize + * command does not indicate completion of the sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_sanitize_nvm_args *args); + +#endif /* _LIBNVME_MI_MI_H */ diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c new file mode 100644 index 0000000..f2ffc21 --- /dev/null +++ b/src/nvme/nbft.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved. + * + * Authors: Stuart Hayes <Stuart_Hayes@Dell.com> + * + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <arpa/inet.h> +#include <ccan/endian/endian.h> + +#include "private.h" +#include "nbft.h" +#include "log.h" + + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +static __u8 csum(const __u8 *buffer, ssize_t length) +{ + int n; + __u8 sum = 0; + + for (n = 0; n < length; n++) + sum = (__u8)(sum + ((__u8 *)buffer)[n]); + return sum; +} + +static void format_ip_addr(char *buf, size_t buflen, __u8 *addr) +{ + struct in6_addr addr_ipv6; + + memcpy(&addr_ipv6, addr, sizeof(addr_ipv6)); + if (IN6_IS_ADDR_V4MAPPED(&addr_ipv6)) + /* ipv4 */ + inet_ntop(AF_INET, &addr_ipv6.s6_addr32[3], buf, buflen); + else + /* ipv6 */ + inet_ntop(AF_INET6, &addr_ipv6, buf, buflen); +} + +static bool in_heap(struct nbft_header *header, struct nbft_heap_obj obj) +{ + if (le16_to_cpu(obj.length) == 0) + return true; + if (le32_to_cpu(obj.offset) < le32_to_cpu(header->heap_offset)) + return false; + if (le32_to_cpu(obj.offset) > + le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length)) + return false; + if (le32_to_cpu(obj.offset) + le16_to_cpu(obj.length) > + le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length)) + return false; + return true; +} + +/* + * Return transport_type string (NBFT Table 2) + */ +static char *trtype_to_string(__u8 transport_type) +{ + switch (transport_type) { + case 3: + return "tcp"; + default: + return "invalid"; + } +} + +#define verify(condition, message) \ + do { \ + if (!(condition)) { \ + nvme_msg(NULL, LOG_DEBUG, "file %s: " message "\n", \ + nbft->filename); \ + return -EINVAL; \ + } \ + } while (0) + +static int __get_heap_obj(struct nbft_header *header, const char *filename, + const char *descriptorname, const char *fieldname, + struct nbft_heap_obj obj, bool is_string, + char **output) +{ + if (le16_to_cpu(obj.length) == 0) + return -ENOENT; + + if (!in_heap(header, obj)) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: field '%s' in descriptor '%s' has invalid offset or length\n", + filename, fieldname, descriptorname); + return -EINVAL; + } + + /* check that string is zero terminated correctly */ + *output = (char *)header + le32_to_cpu(obj.offset); + + if (is_string) { + if (strnlen(*output, le16_to_cpu(obj.length) + 1) < le16_to_cpu(obj.length)) + nvme_msg(NULL, LOG_DEBUG, + "file %s: string '%s' in descriptor '%s' is shorter (%zd) than specified length (%d)\n", + filename, fieldname, descriptorname, + strnlen(*output, le16_to_cpu(obj.length) + 1), + le16_to_cpu(obj.length)); + else if (strnlen(*output, le16_to_cpu(obj.length) + 1) > + le16_to_cpu(obj.length)) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: string '%s' in descriptor '%s' is not zero terminated\n", + filename, fieldname, descriptorname); + return -EINVAL; + } + } + + return 0; +} + +#define get_heap_obj(descriptor, obj, is_string, output) \ + __get_heap_obj(header, nbft->filename, \ + stringify(descriptor), stringify(obj), \ + descriptor->obj, is_string, \ + output) + +static struct nbft_info_discovery *discovery_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_discovery **d; + + for (d = nbft->discovery_list; d && *d; d++) { + if ((*d)->index == i) + return *d; + } + return NULL; +} + +static struct nbft_info_hfi *hfi_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_hfi **h; + + for (h = nbft->hfi_list; h && *h; h++) { + if ((*h)->index == i) + return *h; + } + return NULL; +} + +static struct nbft_info_security *security_from_index(struct nbft_info *nbft, int i) +{ + struct nbft_info_security **s; + + for (s = nbft->security_list; s && *s; s++) { + if ((*s)->index == i) + return *s; + } + return NULL; +} + +static int read_ssns_exended_info(struct nbft_info *nbft, + struct nbft_info_subsystem_ns *ssns, + struct nbft_ssns_ext_info *raw_ssns_ei) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + verify(raw_ssns_ei->structure_id == NBFT_DESC_SSNS_EXT_INFO, + "invalid ID in SSNS extended info descriptor"); + verify(raw_ssns_ei->version == 1, + "invalid version in SSNS extended info descriptor"); + verify(le16_to_cpu(raw_ssns_ei->ssns_index) == ssns->index, + "SSNS index doesn't match extended info descriptor index"); + + if (!(le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_VALID)) + return -EINVAL; + + if (le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ) + ssns->asqsz = le16_to_cpu(raw_ssns_ei->asqsz); + ssns->controller_id = le16_to_cpu(raw_ssns_ei->cntlid); + get_heap_obj(raw_ssns_ei, dhcp_root_path_str_obj, 1, &ssns->dhcp_root_path_string); + + return 0; +} + +static int read_ssns(struct nbft_info *nbft, + struct nbft_ssns *raw_ssns, + struct nbft_info_subsystem_ns **s) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + struct nbft_info_subsystem_ns *ssns; + __u8 *ss_hfi_indexes = NULL; + __u8 *tmp = NULL; + int i, ret; + + if (!(le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_VALID)) + return -EINVAL; + + verify(raw_ssns->structure_id == NBFT_DESC_SSNS, + "invalid ID in SSNS descriptor"); + + /* verify transport type */ + verify(raw_ssns->trtype == NBFT_TRTYPE_TCP, + "invalid transport type in SSNS descriptor"); + + ssns = calloc(1, sizeof(*ssns)); + if (!ssns) + return -ENOMEM; + + ssns->index = le16_to_cpu(raw_ssns->index); + strncpy(ssns->transport, trtype_to_string(raw_ssns->trtype), sizeof(ssns->transport)); + + /* transport specific flags */ + if (raw_ssns->trtype == NBFT_TRTYPE_TCP) { + if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_PDU_HEADER_DIGEST) + ssns->pdu_header_digest_required = true; + if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_DATA_DIGEST) + ssns->data_digest_required = true; + } + + /* primary discovery controller */ + if (raw_ssns->primary_discovery_ctrl_index) { + ssns->discovery = discovery_from_index(nbft, + raw_ssns->primary_discovery_ctrl_index); + if (!ssns->discovery) + nvme_msg(NULL, LOG_DEBUG, + "file %s: namespace %d discovery controller not found\n", + nbft->filename, ssns->index); + } + + /* subsystem transport address */ + ret = get_heap_obj(raw_ssns, subsys_traddr_obj, 0, (char **)&tmp); + if (ret) + goto fail; + + format_ip_addr(ssns->traddr, sizeof(ssns->traddr), tmp); + + /* subsystem transport service identifier */ + ret = get_heap_obj(raw_ssns, subsys_trsvcid_obj, 1, &ssns->trsvcid); + if (ret) + goto fail; + + /* subsystem port ID */ + ssns->subsys_port_id = le16_to_cpu(raw_ssns->subsys_port_id); + + /* NSID, NID type, & NID */ + ssns->nsid = le32_to_cpu(raw_ssns->nsid); + ssns->nid_type = raw_ssns->nidt; + ssns->nid = raw_ssns->nid; + + /* security profile */ + if (raw_ssns->security_desc_index) { + ssns->security = security_from_index(nbft, raw_ssns->security_desc_index); + if (!ssns->security) + nvme_msg(NULL, LOG_DEBUG, + "file %s: namespace %d security controller not found\n", + nbft->filename, ssns->index); + } + + /* HFI descriptors */ + ret = get_heap_obj(raw_ssns, secondary_hfi_assoc_obj, 0, (char **)&ss_hfi_indexes); + if (ret) + goto fail; + + ssns->hfis = calloc(le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length) + 2, + sizeof(*ssns->hfis)); + if (!ssns->hfis) { + ret = -ENOMEM; + goto fail; + } + ssns->hfis[0] = hfi_from_index(nbft, raw_ssns->primary_hfi_desc_index); + if (!ssns->hfis[0]) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: SSNS %d: HFI %d not found\n", + nbft->filename, ssns->index, raw_ssns->primary_hfi_desc_index); + ret = -EINVAL; + goto fail; + } + ssns->num_hfis = 1; + for (i = 0; i < le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length); i++) { + bool duplicate = false; + int j; + + for (j = 0; j < i; j++) { + if (ss_hfi_indexes[i] == ss_hfi_indexes[j]) { + duplicate = true; + break; + } + } + + if (!duplicate && + ss_hfi_indexes[i] == raw_ssns->primary_hfi_desc_index) + duplicate = true; + + if (duplicate) { + nvme_msg(NULL, LOG_DEBUG, + "file %s: SSNS %d skipping duplicate HFI index %d\n", + nbft->filename, ssns->index, ss_hfi_indexes[i]); + continue; + } + + ssns->hfis[i + 1] = hfi_from_index(nbft, ss_hfi_indexes[i]); + if (ss_hfi_indexes[i] && !ssns->hfis[i + 1]) + nvme_msg(NULL, LOG_DEBUG, + "file %s: SSNS %d HFI %d not found\n", + nbft->filename, ssns->index, ss_hfi_indexes[i]); + else + ssns->num_hfis++; + } + + /* SSNS NQN */ + ret = get_heap_obj(raw_ssns, subsys_ns_nqn_obj, 1, &ssns->subsys_nqn); + if (ret) + goto fail; + + /* SSNS extended info */ + if (le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_EXTENDED_INFO_IN_USE) { + struct nbft_ssns_ext_info *ssns_extended_info; + + if (!get_heap_obj(raw_ssns, ssns_extended_info_desc_obj, 0, + (char **)&ssns_extended_info)) + read_ssns_exended_info(nbft, ssns, ssns_extended_info); + } + + *s = ssns; + return 0; + +fail: + free(ssns); + return ret; +} + +static int read_hfi_info_tcp(struct nbft_info *nbft, + struct nbft_hfi_info_tcp *raw_hfi_info_tcp, + struct nbft_info_hfi *hfi) +{ + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + if ((raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_VALID) == 0) + return -EINVAL; + + verify(raw_hfi_info_tcp->structure_id == NBFT_DESC_HFI_TRINFO, + "invalid ID in HFI transport descriptor"); + verify(raw_hfi_info_tcp->version == 1, + "invalid version in HFI transport descriptor"); + if (le16_to_cpu(raw_hfi_info_tcp->hfi_index) != hfi->index) + nvme_msg(NULL, LOG_DEBUG, + "file %s: HFI descriptor index %d does not match index in HFI transport descriptor\n", + nbft->filename, hfi->index); + + hfi->tcp_info.pci_sbdf = le32_to_cpu(raw_hfi_info_tcp->pci_sbdf); + memcpy(hfi->tcp_info.mac_addr, raw_hfi_info_tcp->mac_addr, + sizeof(raw_hfi_info_tcp->mac_addr)); + hfi->tcp_info.vlan = le16_to_cpu(raw_hfi_info_tcp->vlan); + hfi->tcp_info.ip_origin = raw_hfi_info_tcp->ip_origin; + format_ip_addr(hfi->tcp_info.ipaddr, sizeof(hfi->tcp_info.ipaddr), + raw_hfi_info_tcp->ip_address); + hfi->tcp_info.subnet_mask_prefix = raw_hfi_info_tcp->subnet_mask_prefix; + format_ip_addr(hfi->tcp_info.gateway_ipaddr, sizeof(hfi->tcp_info.ipaddr), + raw_hfi_info_tcp->ip_gateway); + hfi->tcp_info.route_metric = le16_to_cpu(raw_hfi_info_tcp->route_metric); + format_ip_addr(hfi->tcp_info.primary_dns_ipaddr, + sizeof(hfi->tcp_info.primary_dns_ipaddr), + raw_hfi_info_tcp->primary_dns); + format_ip_addr(hfi->tcp_info.secondary_dns_ipaddr, + sizeof(hfi->tcp_info.secondary_dns_ipaddr), + raw_hfi_info_tcp->secondary_dns); + if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_DHCP_OVERRIDE) { + hfi->tcp_info.dhcp_override = true; + format_ip_addr(hfi->tcp_info.dhcp_server_ipaddr, + sizeof(hfi->tcp_info.dhcp_server_ipaddr), + raw_hfi_info_tcp->dhcp_server); + } + get_heap_obj(raw_hfi_info_tcp, host_name_obj, 1, &hfi->tcp_info.host_name); + if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_GLOBAL_ROUTE) + hfi->tcp_info.this_hfi_is_default_route = true; + + return 0; +} + +static int read_hfi(struct nbft_info *nbft, + struct nbft_hfi *raw_hfi, + struct nbft_info_hfi **h) +{ + int ret; + struct nbft_info_hfi *hfi; + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + + if (!(raw_hfi->flags & NBFT_HFI_VALID)) + return -EINVAL; + + verify(raw_hfi->structure_id == NBFT_DESC_HFI, + "invalid ID in HFI descriptor"); + + hfi = calloc(1, sizeof(struct nbft_info_hfi)); + if (!hfi) + return -ENOMEM; + + hfi->index = raw_hfi->index; + + /* + * read HFI transport descriptor for this HFI + */ + if (raw_hfi->trtype == NBFT_TRTYPE_TCP) { + /* TCP */ + struct nbft_hfi_info_tcp *raw_hfi_info_tcp; + + strncpy(hfi->transport, trtype_to_string(raw_hfi->trtype), + sizeof(hfi->transport)); + + ret = get_heap_obj(raw_hfi, trinfo_obj, 0, (char **)&raw_hfi_info_tcp); + if (ret) + goto fail; + + ret = read_hfi_info_tcp(nbft, raw_hfi_info_tcp, hfi); + if (ret) + goto fail; + } else { + nvme_msg(NULL, LOG_DEBUG, + "file %s: invalid transport type %d\n", + nbft->filename, raw_hfi->trtype); + ret = -EINVAL; + goto fail; + } + + *h = hfi; + return 0; + +fail: + free(hfi); + return ret; +} + +static int read_discovery(struct nbft_info *nbft, + struct nbft_discovery *raw_discovery, + struct nbft_info_discovery **d) +{ + struct nbft_info_discovery *discovery = NULL; + struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft; + int r = -EINVAL; + + if (!(raw_discovery->flags & NBFT_DISCOVERY_VALID)) + goto error; + + verify(raw_discovery->structure_id == NBFT_DESC_DISCOVERY, + "invalid ID in discovery descriptor"); + + discovery = calloc(1, sizeof(struct nbft_info_discovery)); + if (!discovery) { + r = -ENOMEM; + goto error; + } + + discovery->index = raw_discovery->index; + + if (get_heap_obj(raw_discovery, discovery_ctrl_addr_obj, 1, &discovery->uri)) + goto error; + + if (get_heap_obj(raw_discovery, discovery_ctrl_nqn_obj, 1, &discovery->nqn)) + goto error; + + discovery->hfi = hfi_from_index(nbft, raw_discovery->hfi_index); + if (raw_discovery->hfi_index && !discovery->hfi) + nvme_msg(NULL, LOG_DEBUG, + "file %s: discovery %d HFI not found\n", + nbft->filename, discovery->index); + + discovery->security = security_from_index(nbft, raw_discovery->sec_index); + if (raw_discovery->sec_index && !discovery->security) + nvme_msg(NULL, LOG_DEBUG, + "file %s: discovery %d security descriptor not found\n", + nbft->filename, discovery->index); + + *d = discovery; + r = 0; + +error: + if (r) + free(discovery); + return r; +} + +static int read_security(struct nbft_info *nbft, + struct nbft_security *raw_security, + struct nbft_info_security **s) +{ + return -EINVAL; +} + +static void read_hfi_descriptors(struct nbft_info *nbft, int num_hfi, + struct nbft_hfi *raw_hfi_array, int hfi_len) +{ + int i, cnt; + + nbft->hfi_list = calloc(num_hfi + 1, sizeof(struct nbft_info_hfi)); + for (i = 0, cnt = 0; i < num_hfi; i++) { + if (read_hfi(nbft, &raw_hfi_array[i], &nbft->hfi_list[cnt]) == 0) + cnt++; + } +} + +static void read_security_descriptors(struct nbft_info *nbft, int num_sec, + struct nbft_security *raw_sec_array, int sec_len) +{ + int i, cnt; + + nbft->security_list = calloc(num_sec + 1, sizeof(struct nbft_info_security)); + for (i = 0, cnt = 0; i < num_sec; i++) { + if (read_security(nbft, &raw_sec_array[i], &nbft->security_list[cnt]) == 0) + cnt++; + } +} + +static void read_discovery_descriptors(struct nbft_info *nbft, int num_disc, + struct nbft_discovery *raw_disc_array, int disc_len) +{ + int i, cnt; + + nbft->discovery_list = calloc(num_disc + 1, sizeof(struct nbft_info_discovery)); + for (i = 0, cnt = 0; i < num_disc; i++) { + if (read_discovery(nbft, &raw_disc_array[i], &nbft->discovery_list[cnt]) == 0) + cnt++; + } +} + +static void read_ssns_descriptors(struct nbft_info *nbft, int num_ssns, + struct nbft_ssns *raw_ssns_array, int ssns_len) +{ + int i, cnt; + + nbft->subsystem_ns_list = calloc(num_ssns + 1, sizeof(struct nbft_info_subsystem_ns)); + for (i = 0, cnt = 0; i < num_ssns; i++) { + if (read_ssns(nbft, &raw_ssns_array[i], &nbft->subsystem_ns_list[cnt]) == 0) + cnt++; + } +} + +/** + * parse_raw_nbft - parses raw ACPI NBFT table and fill in abstracted nbft_info structure + * @nbft: nbft_info struct containing only raw_nbft and raw_nbft_size + * + * Returns 0 on success, errno otherwise. + */ +static int parse_raw_nbft(struct nbft_info *nbft) +{ + __u8 *raw_nbft = nbft->raw_nbft; + int raw_nbft_size = nbft->raw_nbft_size; + + struct nbft_header *header; + struct nbft_control *control; + struct nbft_host *host; + + verify(raw_nbft_size >= sizeof(struct nbft_header) + sizeof(struct nbft_control), + "table is too short"); + verify(csum(raw_nbft, raw_nbft_size) == 0, "invalid checksum"); + + /* + * header + */ + header = (struct nbft_header *)raw_nbft; + + verify(strncmp(header->signature, NBFT_HEADER_SIG, 4) == 0, "invalid signature"); + verify(le32_to_cpu(header->length) <= raw_nbft_size, "length in header exceeds table length"); + verify(header->major_revision == 1, "unsupported major revision"); + verify(header->minor_revision == 0, "unsupported minor revision"); + verify(le32_to_cpu(header->heap_length) + le32_to_cpu(header->heap_offset) <= + le32_to_cpu(header->length), "heap exceeds table length"); + + /* + * control + */ + control = (struct nbft_control *)(raw_nbft + sizeof(struct nbft_header)); + + if ((control->flags & NBFT_CONTROL_VALID) == 0) + return 0; + verify(control->structure_id == NBFT_DESC_CONTROL, + "invalid ID in control structure"); + + /* + * host + */ + verify(le32_to_cpu(control->hdesc.offset) + sizeof(struct nbft_host) <= + le32_to_cpu(header->length) && + le32_to_cpu(control->hdesc.offset) >= sizeof(struct nbft_host), + "host descriptor offset/length is invalid"); + host = (struct nbft_host *)(raw_nbft + le32_to_cpu(control->hdesc.offset)); + + verify(host->flags & NBFT_HOST_VALID, "host descriptor valid flag not set"); + verify(host->structure_id == NBFT_DESC_HOST, "invalid ID in HOST descriptor"); + nbft->host.id = (unsigned char *) &(host->host_id); + if (get_heap_obj(host, host_nqn_obj, 1, &nbft->host.nqn) != 0) + return -EINVAL; + nbft->host.host_id_configured = host->flags & NBFT_HOST_HOSTID_CONFIGURED; + nbft->host.host_nqn_configured = host->flags & NBFT_HOST_HOSTNQN_CONFIGURED; + + /* + * HFI + */ + if (control->num_hfi > 0) { + struct nbft_hfi *raw_hfi_array; + + verify(le32_to_cpu(control->hfio) + sizeof(struct nbft_hfi) * + control->num_hfi <= le32_to_cpu(header->length), + "invalid hfi descriptor list offset"); + raw_hfi_array = (struct nbft_hfi *)(raw_nbft + le32_to_cpu(control->hfio)); + read_hfi_descriptors(nbft, control->num_hfi, raw_hfi_array, + le16_to_cpu(control->hfil)); + } + + /* + * security + */ + if (control->num_sec > 0) { + struct nbft_security *raw_security_array; + + verify(le32_to_cpu(control->seco) + le16_to_cpu(control->secl) * + control->num_sec <= le32_to_cpu(header->length), + "invalid security profile desciptor list offset"); + raw_security_array = (struct nbft_security *)(raw_nbft + + le32_to_cpu(control->seco)); + read_security_descriptors(nbft, control->num_sec, + raw_security_array, + le16_to_cpu(control->secl)); + } + + /* + * discovery + */ + if (control->num_disc > 0) { + struct nbft_discovery *raw_discovery_array; + + verify(le32_to_cpu(control->disco) + le16_to_cpu(control->discl) * + control->num_disc <= le32_to_cpu(header->length), + "invalid discovery profile descriptor list offset"); + raw_discovery_array = (struct nbft_discovery *)(raw_nbft + + le32_to_cpu(control->disco)); + read_discovery_descriptors(nbft, control->num_disc, raw_discovery_array, + le16_to_cpu(control->discl)); + } + + /* + * subsystem namespace + */ + if (control->num_ssns > 0) { + struct nbft_ssns *raw_ssns_array; + + verify(le32_to_cpu(control->ssnso) + le16_to_cpu(control->ssnsl) * + control->num_ssns <= le32_to_cpu(header->length), + "invalid subsystem namespace descriptor list offset"); + raw_ssns_array = (struct nbft_ssns *)(raw_nbft + + le32_to_cpu(control->ssnso)); + read_ssns_descriptors(nbft, control->num_ssns, raw_ssns_array, + le16_to_cpu(control->ssnsl)); + } + + return 0; +} + +void nvme_nbft_free(struct nbft_info *nbft) +{ + struct nbft_info_hfi **hfi; + struct nbft_info_security **sec; + struct nbft_info_discovery **disc; + struct nbft_info_subsystem_ns **ns; + + for (hfi = nbft->hfi_list; hfi && *hfi; hfi++) + free(*hfi); + free(nbft->hfi_list); + for (disc = nbft->discovery_list; disc && *disc; disc++) + free(*disc); + free(nbft->discovery_list); + for (sec = nbft->security_list; sec && *sec; sec++) + free(*sec); + free(nbft->security_list); + for (ns = nbft->subsystem_ns_list; ns && *ns; ns++) { + free((*ns)->hfis); + free(*ns); + } + free(nbft->subsystem_ns_list); + free(nbft->raw_nbft); + free(nbft->filename); + free(nbft); +} + +int nvme_nbft_read(struct nbft_info **nbft, const char *filename) +{ + __u8 *raw_nbft = NULL; + size_t raw_nbft_size; + FILE *raw_nbft_fp = NULL; + int i; + + /* + * read in raw nbft file + */ + raw_nbft_fp = fopen(filename, "rb"); + if (raw_nbft_fp == NULL) { + nvme_msg(NULL, LOG_ERR, "Failed to open %s: %s\n", + filename, strerror(errno)); + errno = EINVAL; + return 1; + } + + i = fseek(raw_nbft_fp, 0L, SEEK_END); + if (i) { + nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n", + filename, strerror(errno)); + fclose(raw_nbft_fp); + errno = EINVAL; + return 1; + } + + raw_nbft_size = ftell(raw_nbft_fp); + rewind(raw_nbft_fp); + + raw_nbft = malloc(raw_nbft_size); + if (!raw_nbft) { + nvme_msg(NULL, LOG_ERR, "Failed to allocate memory for NBFT table"); + fclose(raw_nbft_fp); + errno = ENOMEM; + return 1; + } + + i = fread(raw_nbft, sizeof(*raw_nbft), raw_nbft_size, raw_nbft_fp); + if (i != raw_nbft_size) { + nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n", + filename, strerror(errno)); + fclose(raw_nbft_fp); + free(raw_nbft); + errno = EINVAL; + return 1; + } + fclose(raw_nbft_fp); + + /* + * alloc new struct nbft_info, add raw nbft & filename to it, and add it to the list + */ + *nbft = calloc(1, sizeof(struct nbft_info)); + if (!*nbft) { + nvme_msg(NULL, LOG_ERR, "Could not allocate memory for NBFT\n"); + free(raw_nbft); + errno = ENOMEM; + return 1; + } + + (*nbft)->filename = strdup(filename); + (*nbft)->raw_nbft = raw_nbft; + (*nbft)->raw_nbft_size = raw_nbft_size; + + if (parse_raw_nbft(*nbft)) { + nvme_msg(NULL, LOG_ERR, "Failed to parse %s\n", filename); + nvme_nbft_free(*nbft); + errno = EINVAL; + return 1; + } + return 0; +} diff --git a/src/nvme/nbft.h b/src/nvme/nbft.h new file mode 100644 index 0000000..6012e16 --- /dev/null +++ b/src/nvme/nbft.h @@ -0,0 +1,1238 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved. + * + * Authors: Stuart Hayes <Stuart_Hayes@Dell.com> + * + */ +#ifndef _NBFT_H +#define _NBFT_H + +#include <sys/types.h> +#include "util.h" + +/* + * ACPI NBFT table structures (TP8012 Boot Specification rev. 1.0) + */ + +/** + * enum nbft_desc_type - NBFT Elements - Descriptor Types (Figure 5) + * @NBFT_DESC_HEADER: Header: an ACPI structure header with some additional + * NBFT specific info. + * @NBFT_DESC_CONTROL: Control Descriptor: indicates the location of host, + * HFI, SSNS, security, and discovery descriptors. + * @NBFT_DESC_HOST: Host Descriptor: host information. + * @NBFT_DESC_HFI: HFI Descriptor: an indexable table of HFI Descriptors, + * one for each fabric interface on the host. + * @NBFT_DESC_SSNS: Subsystem Namespace Descriptor: an indexable table + * of SSNS Descriptors. + * @NBFT_DESC_SECURITY: Security Descriptor: an indexable table of Security + * descriptors. + * @NBFT_DESC_DISCOVERY: Discovery Descriptor: an indexable table of Discovery + * Descriptors. + * @NBFT_DESC_HFI_TRINFO: HFI Transport Descriptor: indicated by an HFI Descriptor, + * corresponds to a specific transport for a single HFI. + * @NBFT_DESC_RESERVED_8: Reserved. + * @NBFT_DESC_SSNS_EXT_INFO: SSNS Extended Info Descriptor: indicated by an SSNS + * Descriptor if required. + */ +enum nbft_desc_type { + NBFT_DESC_HEADER = 0, + NBFT_DESC_CONTROL = 1, + NBFT_DESC_HOST = 2, + NBFT_DESC_HFI = 3, + NBFT_DESC_SSNS = 4, + NBFT_DESC_SECURITY = 5, + NBFT_DESC_DISCOVERY = 6, + NBFT_DESC_HFI_TRINFO = 7, + NBFT_DESC_RESERVED_8 = 8, + NBFT_DESC_SSNS_EXT_INFO = 9, +}; + +/** + * enum nbft_trtype - NBFT Interface Transport Types (Figure 7) + * @NBFT_TRTYPE_TCP: NVMe/TCP (802.3 + TCP/IP). String Designator "tcp". + */ +enum nbft_trtype { + NBFT_TRTYPE_TCP = 3, +}; + +#define NBFT_HEADER_SIG "NBFT" + +/** + * struct nbft_heap_obj - NBFT Header Driver Signature + * @offset: Offset in bytes of the heap object, if any, from byte offset 0h + * of the NBFT Table Header. + * @length: Length in bytes of the heap object, if any. + */ +struct nbft_heap_obj { + __le32 offset; + __le16 length; +} __attribute__((packed)); + +/** + * struct nbft_header - NBFT Table - Header (Figure 8) + * @signature: Signature: An ASCII string representation of the table + * identifier. This field shall be set to the value 4E424654h + * (i.e. "NBFT", see #NBFT_HEADER_SIG). + * @length: Length: The length of the table, in bytes, including the + * header, starting from offset 0h. This field is used to record + * the size of the entire table. + * @major_revision: Major Revision: The major revision of the structure + * corresponding to the Signature field. Larger major revision + * numbers should not be assumed backward compatible to lower + * major revision numbers with the same signature. + * @checksum: Checksum: The entire table, including the Checksum field, + * shall sum to 0h to be considered valid. + * @oem_id: OEMID shall be populated by the NBFT driver writer by + * an OEM-supplied string that identifies the OEM. All + * trailing bytes shall be NULL. + * @oem_table_id: OEM Table ID: This field shall be populated by the NBFT + * driver writer with an OEM-supplied string that the OEM + * uses to identify the particular data table. This field is + * particularly useful when defining a definition block to + * distinguish definition block functions. The OEM assigns + * each dissimilar table a new OEM Table ID. + * @oem_revision: OEM Revision: An OEM-supplied revision number. Larger + * numbers are assumed to be newer revisions. + * @creator_id: Creator ID: Vendor ID of utility that created the table. + * For instance, this may be the ID for the ASL Compiler. + * @creator_revision: Creator Revision: Revision of utility that created the + * table. For instance, this may be the ID for the ASL Compiler. + * @heap_offset: Heap Offset (HO): This field indicates the offset in bytes + * of the heap, if any, from byte offset 0h of the NBFT + * Table Header. + * @heap_length: Heap Length (HL): The length of the heap, if any. + * @driver_dev_path_sig: Driver Signature Heap Object Reference: This field indicates + * the offset in bytes of a heap object containing the Driver + * Signature, if any, from byte offset 0h of the NBFT Table + * Header. + * @minor_revision: Minor Revision: The minor revision of the structure + * corresponding to the Signature field. If the major revision + * numbers are the same, any minor revision number differences + * shall be backwards compatible with the same signature. + * @reserved: Reserved. + */ +struct nbft_header { + char signature[4]; + __le32 length; + __u8 major_revision; + __u8 checksum; + char oem_id[6]; + char oem_table_id[8]; + __le32 oem_revision; + __le32 creator_id; + __le32 creator_revision; + __le32 heap_offset; + __le32 heap_length; + struct nbft_heap_obj driver_dev_path_sig; + __u8 minor_revision; + __u8 reserved[13]; +}; + +/** + * struct nbft_control - NBFT Table - Control Descriptor (Figure 8) + * @structure_id: Structure ID: This field specifies the element (refer to + * &enum nbft_desc_type). This field shall be set to 1h (i.e., + * Control, #NBFT_DESC_CONTROL). + * @major_revision: Major Revision: The major revision of the structure corresponding + * to the Signature field. Larger major revision numbers should + * not be assumed backward compatible to lower major revision + * numbers with the same signature. + * @minor_revision: Minor Revision: The minor revision of the structure corresponding + * to the signature field. If the major revision numbers are + * the same, any minor revision number differences shall be backwards + * compatible with the same signature. + * @reserved1: Reserved. + * @csl: Control Structure Length (CSL): This field indicates the length + * in bytes of the Control Descriptor. + * @flags: Flags, see &enum nbft_control_flags. + * @reserved2: Reserved. + * @hdesc: Host Descriptor (HDESC): This field indicates the location + * and length of the Host Descriptor (see &struct nbft_host). + * @hsv: Host Descriptor Version (HSV): This field indicates the version + * of the Host Descriptor. + * @reserved3: Reserved. + * @hfio: HFI Descriptor List Offset (HFIO): If this field is set to + * a non-zero value, then this field indicates the offset in bytes + * of the HFI Descriptor List, if any, from byte offset 0h of the + * NBFT Table Header. If the @num_hfi field is cleared to 0h, + * then this field is reserved. + * @hfil: HFI Descriptor Length (HFIL): This field indicates the length + * in bytes of each HFI Descriptor, if any. If the @num_hfi field + * is cleared to 0h, then this field is reserved. + * @hfiv: HFI Descriptor Version (HFIV): This field indicates the version + * of each HFI Descriptor. + * @num_hfi: Number of Host Fabric Interface Descriptors (NumHFI): This field + * indicates the number of HFI Descriptors (see &struct nbft_hfi) + * in the HFI Descriptor List, if any. If no interfaces have been + * configured, then this field shall be cleared to 0h. + * @ssnso: SSNS Descriptor List Offset (SSNSO):: This field indicates + * the offset in bytes of the SSNS Descriptor List, if any, from + * byte offset 0h of the NBFT Table Header. If the @num_ssns field + * is cleared to 0h, then this field is reserved. + * @ssnsl: SSNS Descriptor Length (SSNSL): This field indicates the length + * in bytes of each SSNS Descriptor, if any. If the @num_ssns + * field is cleared to 0h, then this field is reserved. + * @ssnsv: SSNS Descriptor Version (SSNSV): This field indicates the version + * of the SSNS Descriptor. + * @num_ssns: Number of Subsystem and Namespace Descriptors (NumSSNS): This + * field indicates the number of Subsystem Namespace (SSNS) + * Descriptors (see &struct nbft_ssns) in the SSNS Descriptor List, + * if any. + * @seco: Security Profile Descriptor List Offset (SECO): This field + * indicates the offset in bytes of the Security Profile Descriptor + * List, if any, from byte offset 0h of the NBFT Table Header. + * If the @num_sec field is cleared to 0h, then this field + * is reserved. + * @secl: Security Profile Descriptor Length (SECL): This field indicates + * the length in bytes of each Security Profile Descriptor, if any. + * If the @num_sec field is cleared to 0h, then this field + * is reserved. + * @secv: Security Profile Descriptor Version (SECV): This field indicates + * the version of the Security Profile Descriptor. + * @num_sec: Number of Security Profile Descriptors (NumSec): This field + * indicates the number of Security Profile Descriptors + * (see &struct nbft_security), if any, in the Security Profile + * Descriptor List. + * @disco: Discovery Descriptor Offset (DISCO): This field indicates + * the offset in bytes of the Discovery Descriptor List, if any, + * from byte offset 0h of the NBFT Table Header. If the @num_disc + * field is cleared to 0h, then this field is reserved. + * @discl: Discovery Descriptor Length (DISCL): This field indicates + * the length in bytes of each Discovery Descriptor, if any. + * If the @num_disc field is cleared to 0h, then this field + * is reserved. + * @discv: Discovery Descriptor Version (DISCV): This field indicates + * the version of the Discovery Descriptor. + * @num_disc: Number of Discovery Descriptors (NumDisc): This field indicates + * the number of Discovery Descriptors (see &struct nbft_discovery), + * if any, in the Discovery Descriptor List, if any. + * @reserved4: Reserved. + */ +struct nbft_control { + __u8 structure_id; + __u8 major_revision; + __u8 minor_revision; + __u8 reserved1; + __le16 csl; + __u8 flags; + __u8 reserved2; + struct nbft_heap_obj hdesc; + __u8 hsv; + __u8 reserved3; + __le32 hfio; + __le16 hfil; + __u8 hfiv; + __u8 num_hfi; + __le32 ssnso; + __le16 ssnsl; + __u8 ssnsv; + __u8 num_ssns; + __le32 seco; + __le16 secl; + __u8 secv; + __u8 num_sec; + __le32 disco; + __le16 discl; + __u8 discv; + __u8 num_disc; + __u8 reserved4[16]; +}; + +/** + * enum nbft_control_flags - Control Descriptor Flags + * @NBFT_CONTROL_VALID: Block Valid: indicates that the structure is valid. + */ +enum nbft_control_flags { + NBFT_CONTROL_VALID = 1 << 0, +}; + +/** + * struct nbft_host - Host Descriptor (Figure 9) + * @structure_id: Structure ID: This field shall be set to 2h (i.e., + * Host Descriptor; #NBFT_DESC_HOST). + * @flags: Host Flags, see &enum nbft_host_flags. + * @host_id: Host ID: This field shall be set to the Host Identifier. This + * field shall not be empty if the NBFT and NVMe Boot are supported + * by the Platform. + * @host_nqn_obj: Host NQN Heap Object Reference: this field indicates a heap + * object containing a Host NQN. This object shall not be empty + * if the NBFT and NVMe Boot are supported by the Platform. + * @reserved: Reserved. + */ +struct nbft_host { + __u8 structure_id; + __u8 flags; + __u8 host_id[16]; + struct nbft_heap_obj host_nqn_obj; + __u8 reserved[8]; +}; + +/** + * enum nbft_host_flags - Host Flags + * @NBFT_HOST_VALID: Descriptor Valid: If set to 1h, then this + * descriptor is valid. If cleared to 0h, then + * this descriptor is reserved. + * @NBFT_HOST_HOSTID_CONFIGURED: HostID Configured: If set to 1h, then the + * Host ID field contains an administratively-configured + * value. If cleared to 0h, then the Host ID + * field contains a driver default value. + * @NBFT_HOST_HOSTNQN_CONFIGURED: Host NQN Configured: If set to 1h, then the + * Host NQN indicated by the Host NQN Heap Object + * Reference field (&struct nbft_host.host_nqn) + * contains an administratively-configured value. + * If cleared to 0h, then the Host NQN indicated + * by the Host NQN Offset field contains a driver + * default value. + * @NBFT_HOST_PRIMARY_ADMIN_MASK: Mask to get Primary Administrative Host Descriptor: + * indicates whether the Host Descriptor in this + * NBFT was selected as the primary NBFT for + * administrative purposes of platform identity + * as a hint to the OS. If multiple NBFT tables + * are present, only one NBFT should be administratively + * selected. There is no enforcement mechanism + * for this to be coordinated between multiple NBFT + * tables, but this field should be set to Selected + * (#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if + * more than one NBFT is present. + * @NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED: Not Indicated by Driver: The driver that created + * this NBFT provided no administrative priority + * hint for this NBFT. + * @NBFT_HOST_PRIMARY_ADMIN_UNSELECTED: Unselected: The driver that created this NBFT + * explicitly indicated that this NBFT should + * not be prioritized over any other NBFT. + * @NBFT_HOST_PRIMARY_ADMIN_SELECTED: Selected: The driver that created this NBFT + * explicitly indicated that this NBFT should + * be prioritized over any other NBFT. + */ +enum nbft_host_flags { + NBFT_HOST_VALID = 1 << 0, + NBFT_HOST_HOSTID_CONFIGURED = 1 << 1, + NBFT_HOST_HOSTNQN_CONFIGURED = 1 << 2, + NBFT_HOST_PRIMARY_ADMIN_MASK = 0x18, + NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED = 0x00, + NBFT_HOST_PRIMARY_ADMIN_UNSELECTED = 0x08, + NBFT_HOST_PRIMARY_ADMIN_SELECTED = 0x10, +}; + +/** + * struct nbft_hfi - Host Fabric Interface (HFI) Descriptor (Figure 11) + * @structure_id: Structure ID: This field shall be set to 3h (i.e., Host Fabric + * Interface Descriptor; #NBFT_DESC_HFI). + * @index: HFI Descriptor Index: This field indicates the number of this + * HFI Descriptor in the Host Fabric Interface Descriptor List. + * @flags: HFI Descriptor Flags, see &enum nbft_hfi_flags. + * @trtype: HFI Transport Type, see &enum nbft_trtype. + * @reserved1: Reserved. + * @trinfo_obj: HFI Transport Info Descriptor Heap Object Reference: If this + * field is set to a non-zero value, then this field indicates + * the location and size of a heap object containing + * a HFI Transport Info. + * @reserved2: Reserved. + */ +struct nbft_hfi { + __u8 structure_id; + __u8 index; + __u8 flags; + __u8 trtype; + __u8 reserved1[12]; + struct nbft_heap_obj trinfo_obj; + __u8 reserved2[10]; +}; + +/** + * enum nbft_hfi_flags - HFI Descriptor Flags + * @NBFT_HFI_VALID: Descriptor Valid: If set to 1h, then this descriptor is valid. + * If cleared to 0h, then this descriptor is reserved. + */ +enum nbft_hfi_flags { + NBFT_HFI_VALID = 1 << 0, +}; + +/** + * struct nbft_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP (Figure 13) + * @structure_id: Structure ID: This field shall be set to 7h (i.e., + * HFI Transport Info; #NBFT_DESC_HFI_TRINFO). + * @version: Version: This field shall be set to 1h. + * @trtype: HFI Transport Type, see &enum nbft_trtype: This field + * shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP). + * @trinfo_version: Transport Info Version: Implementations compliant to this + * specification shall set this field to 1h. + * @hfi_index: HFI Descriptor Index: The value of the HFI Descriptor Index + * field of the HFI Descriptor (see &struct nbft_hfi.index) + * whose HFI Transport Info Descriptor Heap Object Reference + * field indicates this HFI Transport Info Descriptor. + * @flags: HFI Transport Flags, see &enum nbft_hfi_info_tcp_flags. + * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function: + * This field indicates the PCI Express Routing ID as specified + * in the PCI Express Base Specification. + * @mac_addr: MAC Address: The MAC address of this HFI, in EUI-48TM format, + * as defined in the IEEE Guidelines for Use of Extended Unique + * Identifiers. This field shall be set to a non-zero value. + * @vlan: VLAN: If this field is set to a non-zero value, then this + * field contains the VLAN identifier if the VLAN associated + * with this HFI, as defined in IEEE 802.1q-2018. If no VLAN + * is associated with this HFI, then this field shall be cleared + * to 0h. + * @ip_origin: IP Origin: If this field is set to a non-zero value, then + * this field indicates the source of Ethernet L3 configuration + * information used by the driver for this interface. Valid + * values are defined in the Win 32 API: NL_PREFIX_ORIGIN + * enumeration specification. This field should be cleared + * to 0h if the IP Origin field is unused by driver. + * @ip_address: IP Address: This field indicates the IPv4 or IPv6 address + * of this HFI. This field shall be set to a non-zero value. + * @subnet_mask_prefix: Subnet Mask Prefix: This field indicates the IPv4 or IPv6 + * subnet mask in CIDR routing prefix notation. + * @ip_gateway: IP Gateway: If this field is set to a non-zero value, this + * field indicates the IPv4 or IPv6 address of the IP gateway + * for this HFI. If this field is cleared to 0h, then + * no IP gateway is specified. + * @reserved1: Reserved. + * @route_metric: Route Metric: If this field is set to a non-zero value, + * this field indicates the cost value for the route indicated + * by this HF. This field contains the value utilized by the + * pre-OS driver when chosing among all available routes. Lower + * values relate to higher priority. Refer to IETF RFC 4249. + * If the pre-OS driver supports routing and did not configure + * a specific route metric for this interface, then the pre-OS + * driver should set this value to 500. If the pre-OS driver + * does not support routing, then this field should be cleared + * to 0h. + * @primary_dns: Primary DNS: If this field is set to a non-zero value, + * this field indicates the IPv4 or IPv6 address of the + * Primary DNS server for this HFI, if any, from byte offset + * 0h of the NBFT Table Header. If this field is cleared to 0h, + * then no Primary DNS is specified. + * @secondary_dns: Secondary DNS: If this field is set to a non-zero value, + * this field indicates the IPv4 or IPv6 address of + * the Secondary DNS server for this HFI, if any, from byte + * offset 0h of the NBFT Table Header. If this field is + * cleared to 0h, then no Secondary DNS is specified. + * @dhcp_server: DHCP Server: If the DHCP Override bit is set to 1h, then + * this field indicates the IPv4 or IPv6 address of the DHCP + * server used to assign this HFI address. If that bit is + * cleared to 0h, then this field is reserved. + * @host_name_obj: Host Name Heap Object Reference: If this field is set + * to a non-zero value, then this field indicates the location + * and size of a heap object containing a Host Name string. + * @reserved2: Reserved. + */ +struct nbft_hfi_info_tcp { + __u8 structure_id; + __u8 version; + __u8 trtype; + __u8 trinfo_version; + __le16 hfi_index; + __u8 flags; + __le32 pci_sbdf; + __u8 mac_addr[6]; + __le16 vlan; + __u8 ip_origin; + __u8 ip_address[16]; + __u8 subnet_mask_prefix; + __u8 ip_gateway[16]; + __u8 reserved1; + __le16 route_metric; + __u8 primary_dns[16]; + __u8 secondary_dns[16]; + __u8 dhcp_server[16]; + struct nbft_heap_obj host_name_obj; + __u8 reserved2[18]; +} __attribute__((packed)); + +/** + * enum nbft_hfi_info_tcp_flags - HFI Transport Flags + * @NBFT_HFI_INFO_TCP_VALID: Descriptor Valid: if set to 1h, then this + * descriptor is valid. If cleared to 0h, then + * this descriptor is reserved. + * @NBFT_HFI_INFO_TCP_GLOBAL_ROUTE: Global Route vs. Link Local Override Flag: + * if set to 1h, then the BIOS utilized this + * interface described by HFI to be the default + * route with highest priority. If cleared to 0h, + * then routes are local to their own scope. + * @NBFT_HFI_INFO_TCP_DHCP_OVERRIDE: DHCP Override: if set to 1, then HFI information + * was populated by consuming the DHCP on this + * interface. If cleared to 0h, then the HFI + * information was set administratively by + * a configuration interface to the driver and + * pre-OS envrionment. + */ +enum nbft_hfi_info_tcp_flags { + NBFT_HFI_INFO_TCP_VALID = 1 << 0, + NBFT_HFI_INFO_TCP_GLOBAL_ROUTE = 1 << 1, + NBFT_HFI_INFO_TCP_DHCP_OVERRIDE = 1 << 2, +}; + +/** + * struct nbft_ssns - Subsystem Namespace (SSNS) Descriptor (Figure 15) + * @structure_id: Structure ID: This field shall be set to 4h + * (i.e., SSNS; #NBFT_DESC_SSNS). + * @index: SSNS Descriptor Index: This field indicates the number + * of this Subsystem Namespace Descriptor in the + * Subsystem Namespace Descriptor List. + * @flags: SSNS Flags, see &enum nbft_ssns_flags. + * @trtype: Transport Type, see &enum nbft_trtype. + * @trflags: Transport Specific Flags, see &enum nbft_ssns_trflags. + * @primary_discovery_ctrl_index: Primary Discovery Controller Index: The Discovery + * Descriptor Index field of the Discovery Descriptor + * (see &struct nbft_discovery) that is associated with + * this SSNS Descriptor. If a Discovery controller was + * used to establish this record this value shall + * be set to a non-zero value. If this namespace was + * associated with multiple Discovery controllers, + * those Discovery controllers shall have records + * in the Discovery Descriptor to facilitate multi-path + * rediscovery as required. If no Discovery controller + * was utilized to inform this namespace record, + * this field shall be cleared to 0h. + * @reserved1: Reserved. + * @subsys_traddr_obj: Subsystem Transport Address Heap Object Reference: + * This field indicates the location and size of a heap + * object containing the Subsystem Transport Address. + * For IP based transports types, shall be an IP Address. + * @subsys_trsvcid_obj: Subsystem Transport Service Identifier Heap Object Reference: + * This field indicates the location and size of a heap + * object containing an array of bytes indicating + * the Subsystem Transport Service Identifier. + * See &enum nbft_trtype. + * @subsys_port_id: Subsystem Port ID: Port in the NVM subsystem + * associated with this transport address used by + * the pre-OS driver. + * @nsid: Namespace ID: This field indicates the namespace + * identifier (NSID) of the namespace indicated by + * this descriptor. This field shall be cleared to 0h + * if not specified by the user. If this value is cleared + * to 0h, then consumers of the NBFT shall rely + * on the NID. + * @nidt: Namespace Identifier Type (NIDT): This field + * contains the value of the Namespace Identifier Type (NIDT) + * field in the Namespace Identification Descriptor + * for the namespace indicated by this descriptor. + * If a namespace supports multiple NIDT entries + * for uniqueness, the order of preference is NIDT field + * value of 3h (i.e., UUID) before 2h (i.e., NSGUID), + * and 2h before 1h (i.e., EUI-64). + * @nid: Namespace Identifier (NID): This field contains + * the value of the Namespace Identifier (NID) field + * in the Namespace Identification Descriptor for + * the namespace indicated by this descriptor. + * @security_desc_index: Security Profile Descriptor Index: If the Use Security + * Flag bit in the SSNS Flags field is set to 1h, then + * this field indicates the value of the Security Profile + * Descriptor Index field of the Security Profile + * Descriptor (see &struct nbft_security) associated + * with this namespace. If the Use Security Flag bit + * is cleared to 0h, then no Security Profile Descriptor + * is associated with this namespace and this field + * is reserved. + * @primary_hfi_desc_index: Primary HFI Descriptor Index: This field indicates + * the value of the HFI Descriptor Index field of the + * HFI Descriptor (see &struct nbft_hfi) for the + * interface associated with this namespace. If multiple + * HFIs are associated with this record, subsequent + * interfaces should be populated in the Secondary + * HFI Associations field. + * @reserved2: Reserved. + * @secondary_hfi_assoc_obj: Secondary HFI Associations Heap Object Reference: + * If this field is set to a non-zero value, then + * this field indicates an array of bytes, in which + * each byte contains the value of the HFI Descriptor + * Index field of an HFI Descriptor in the HFI Descriptor + * List. If this field is cleared to 0h, then no + * secondary HFI associations are specified. + * @subsys_ns_nqn_obj: Subsystem and Namespace NQN Heap Object Reference: + * This field indicates the location and size of + * a heap object containing the Subsystem and Namespace NQN. + * @ssns_extended_info_desc_obj: SSNS Extended Information Descriptor Heap Object + * Reference: If the SSNS Extended Info In-use Flag + * bit is set to 1h, then this field indicates the + * offset in bytes of a heap object containing an + * SSNS Extended Information Descriptor + * (see &struct nbft_ssns_ext_info) heap object + * from byte offset 0h of the NBFT Table Header. + * If the SSNS Extended Info In-use Flag bit is cleared + * to 0h, then this field is reserved. + * @reserved3: Reserved. + */ +struct nbft_ssns { + __u8 structure_id; + __le16 index; + __le16 flags; + __u8 trtype; + __le16 trflags; + __u8 primary_discovery_ctrl_index; + __u8 reserved1; + struct nbft_heap_obj subsys_traddr_obj; + struct nbft_heap_obj subsys_trsvcid_obj; + __le16 subsys_port_id; + __le32 nsid; + __u8 nidt; + __u8 nid[16]; + __u8 security_desc_index; + __u8 primary_hfi_desc_index; + __u8 reserved2; + struct nbft_heap_obj secondary_hfi_assoc_obj; + struct nbft_heap_obj subsys_ns_nqn_obj; + struct nbft_heap_obj ssns_extended_info_desc_obj; + __u8 reserved3[62]; +} __attribute__((packed)); + +/** + * enum nbft_ssns_flags - Subsystem and Namespace Specific Flags Field (Figure 16) + * @NBFT_SSNS_VALID: Descriptor Valid: If set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is not valid. A host that supports NVMe-oF Boot, + * but does not currently have a remote Subsystem + * and Namespace assigned may clear this bit to 0h. + * @NBFT_SSNS_NON_BOOTABLE_ENTRY: Non-bootable Entry Flag: If set to 1h, this flag + * indicates that this SSNS Descriptor contains + * a namespace of administrative purpose to the boot + * process, but the pre-OS may not have established + * connectivity to or evaluated the contents of this + * Descriptor. Such namespaces may contain supplemental + * data deemed relevant by the Administrator as part + * of the pre-OS to OS hand off. This may include + * properties such as a UEFI device path that may + * not have been created for this namespace. This means + * an OS runtime may still require the contents + * of such a namespace to complete later stages + * of boot. If cleared to 0h, then this namespace did + * not have any special administrative intent. + * @NBFT_SSNS_USE_SECURITY_FIELD: Use Security Flag: If set to 1h, then there is + * a Security Profile Descriptor associated with this + * SSNS record and the Security Profile Descriptor Index + * field is valid. If cleared to 0h, then there is + * no Security Profile Descriptor associated with this + * SSNS record and the Security Profile Descriptor Index + * field is not valid. + * @NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE: DHCP Root-Path Override Flag: If set to 1h, then + * this SSNS descriptor was populated by consuming + * the DHCP Root-Path on this interface. If cleared + * to 0h, then the DHCP Root-Path was not used + * in populating the SSNS descriptor. + * @NBFT_SSNS_EXTENDED_INFO_IN_USE: SSNS Extended Info In-use Flag: If set to 1h, + * then the SSNS Extended Information Offset field + * and the SSNS Extended Information Length field + * are valid. This flag, if set to 1h, indicates + * that a Subsystem and Namespace Extended Information + * Descriptor corresponding to this descriptor is present. + * @NBFT_SSNS_SEPARATE_DISCOVERY_CTRL: Separate Discovery Controller Flag: If set to 1h, + * then the Discovery controller associated with + * this volume is on a different transport address + * than the specified in the Subsystem Transport + * Address Heap Object Reference. If cleared to 0h, + * then the Discovery controller is the same as the + * Subsystem Transport Address Heap Object Reference. + * @NBFT_SSNS_DISCOVERED_NAMESPACE: Discovered Namespace Flag: If set to 1h, then + * this namespace was acquired through discovery. + * If cleared to 0h, then this namespace was + * explicitly configured in the system. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_MASK: Mask to get Unavailable Namespace Flag: This + * field indicates the availability of the namespace + * at a specific point in time. Such use is only + * a hint and its use does not guarantee the availability + * of that referenced namespace at any future point in time. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND: Not Indicated by Driver: No information is provided. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL: Available: A referenced namespace described by this + * flag was previously accessible by the pre-OS driver. + * @NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL: Unavailable: This namespace was administratively + * configured but unattempted, unavailable or + * inaccessible when establishing connectivity + * by the pre-OS driver. + */ +enum nbft_ssns_flags { + NBFT_SSNS_VALID = 1 << 0, + NBFT_SSNS_NON_BOOTABLE_ENTRY = 1 << 1, + NBFT_SSNS_USE_SECURITY_FIELD = 1 << 2, + NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE = 1 << 3, + NBFT_SSNS_EXTENDED_INFO_IN_USE = 1 << 4, + NBFT_SSNS_SEPARATE_DISCOVERY_CTRL = 1 << 5, + NBFT_SSNS_DISCOVERED_NAMESPACE = 1 << 6, + NBFT_SSNS_UNAVAIL_NAMESPACE_MASK = 0x0180, + NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND = 0x0000, + NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL = 0x0080, + NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL = 0x0100, +}; + +/** + * enum nbft_ssns_trflags - SSNS Transport Specific Flags Field (Figure 17) + * @NBFT_SSNS_TRFLAG_VALID: Transport Specific Flags in Use: If set to 1h, then + * this descriptor is valid. If cleared to 0h, then + * this descriptor is not valid. + * @NBFT_SSNS_PDU_HEADER_DIGEST: PDU Header Digest (HDGST) Flag: If set to 1h, then + * the host or administrator required the connection + * described by this Subsystem and Namespace Descriptor + * to use the NVM Header Digest Enabled. A consumer + * of this information should attempt to use NVM Header + * Digest when recreating this connection if enabled. + * If cleared to 0h, then the host or administrator + * did not require the connection described by this + * Subsystem and Namespace Descriptor to use the + * NVM Header Digest Enabled. + * @NBFT_SSNS_DATA_DIGEST: Data Digest (DDGST) Flag: If set to 1h, then + * the host or administrator required the connection + * described by this Subsystem and Namespace Descriptor + * to use the NVM Data Digest Enabled. If cleared + * to 0h, then the host or administrator did not + * require the connection described by this Subsystem + * and Namespace Descriptor to use the NVM Data Digest + * Enabled. A consumer of this field should attempt + * to use NVM Data Digest when recreating this + * connection if enabled. + */ +enum nbft_ssns_trflags { + NBFT_SSNS_TRFLAG_VALID = 1 << 0, + NBFT_SSNS_PDU_HEADER_DIGEST = 1 << 1, + NBFT_SSNS_DATA_DIGEST = 1 << 2, +}; + +/** + * struct nbft_ssns_ext_info - Subsystem and Namespace Extended Information + * Descriptor (Figure 19) + * @structure_id: Structure ID: This field shall be set to 9h + * (i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO). + * @version: Version: This field shall be set to 1h. + * @ssns_index: SSNS Descriptor Index: This field indicates the value + * of the SSNS Descriptor Index field of the Subsystem + * and Namespace Descriptor (see &struct nbft_ssns) whose + * SSNS Extended Information Descriptor Heap Object + * Reference field indicates this descriptor. + * @flags: Flags, see &enum nbft_ssns_ext_info_flags. + * @cntlid: Controller ID: The controller identifier of the first + * controller associated with the Admin Queue by the driver. + * If a controller identifier is not administratively + * specified or direct configuration is not supported + * by the driver, then this field shall be cleared to 0h. + * @asqsz: Admin Submission Queue Size (ASQSZ): The Admin Submission + * Queue Size utilized for the respective SSNS by the driver. + * @dhcp_root_path_str_obj: DHCP Root Path String Heap Object Reference: If the + * SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE) + * flag bit is set to 1h, then this field indicates + * the offset in bytes of a heap object containing + * an DHCP Root Path String used by the driver. If the + * SNSS DHCP Root Path Override flag bit is cleared to 0h, + * then this field is reserved. + */ +struct nbft_ssns_ext_info { + __u8 structure_id; + __u8 version; + __le16 ssns_index; + __le32 flags; + __le16 cntlid; + __le16 asqsz; + struct nbft_heap_obj dhcp_root_path_str_obj; +} __attribute__((packed)); + +/** + * enum nbft_ssns_ext_info_flags - Subsystem and Namespace Extended Information + * Descriptor Flags + * @NBFT_SSNS_EXT_INFO_VALID: Descriptor Valid: If set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is reserved. + * @NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ: Administrative ASQSZ: If set to 1h, then the value + * of the ASQSZ field was provided by administrative + * configuration for this SSNS record. If cleared + * to 0h, then the value of the ASQSZ field was + * either obtained by discovery or assumed + * by the driver. + */ +enum nbft_ssns_ext_info_flags { + NBFT_SSNS_EXT_INFO_VALID = 1 << 0, + NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ = 1 << 1, +}; + +/** + * struct nbft_security - Security Profile Descriptor (Figure 21) + * @structure_id: Structure ID: This field shall be set to 5h + * (i.e., Security; #NBFT_DESC_SECURITY). + * @index: Security Profile Descriptor Index: This field indicates + * the number of this Security Profile Descriptor in the + * Security Profile Descriptor List. + * @flags: Security Profile Descriptor Flags, see &enum nbft_security_flags. + * @secret_type: Secret Type, see &enum nbft_security_secret_type. + * @reserved1: Reserved. + * @sec_chan_alg_obj: Secure Channel Algorithm Heap Object Reference: If the + * Security Policy List field is set to 1h, then this field + * indicates the location and size of a heap object containing + * a list of secure channel algorithms. The list is an array + * of bytes and the values are defined in the Security Type + * (SECTYPE) field in the Transport Specific Address Subtype + * Definition in the NVMe TCP Transport Specification. + * If the Security Policy List field is cleared to 0h, then + * this field is reserved. + * @auth_proto_obj: Authentication Protocols Heap Object Reference: If the + * Authentication Policy List field is set to 1h, then this + * field indicates the location and size of a heap object + * containing a list of authentication protocol identifiers. + * If the Authentication Policy List field is cleared to 0h, + * then this field is reserved. + * @cipher_suite_obj: Cipher Suite Offset Heap Object Reference: If the Cipher + * Suites Restricted by Policy bit is set to 1h, then this + * field indicates the location and size of a heap object + * containing a list of cipher suite identifiers. The list, + * if any, is an array of bytes and the values are defined + * in the IANA TLS Parameters Registry. If the Cipher Suites + * Restricted by Policy bit is cleared to 0h, then this field + * is reserved. + * @dh_grp_obj: DH Groups Heap Object Reference: If the Authentication DH Groups + * Restricted by Policy List bit is set to 1h, then this field + * indicates the location and size of a heap object containing + * a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers. + * If the Authentication DH Groups Restricted by Policy List + * bit is cleared to 0h, then this field is reserved. + * @sec_hash_func_obj: Secure Hash Functions Offset Heap Object Reference: If the + * Secure Hash Functions Policy List bit is set to 1h, then + * this field indicates the offset in bytes of a heap object + * containing a list of DH-HMAC-CHAP hash function identifiers. + * The list is an array of bytes and the values are defined + * in the NVM Express Base Specification. If the Secure Hash + * Functions Policy List bit is cleared to 0h, then this + * field is reserved. + * @sec_keypath_obj: Secret Keypath Offset Heap Object Reference: if this field + * is set to a non-zero value, then this field indicates + * the location and size of a heap object containing a URI. + * The type of the URI is specified in the Secret Type field. + * If this field is cleared to 0h, then this field is reserved. + * @reserved2: Reserved. + */ +struct nbft_security { + __u8 structure_id; + __u8 index; + __le16 flags; + __u8 secret_type; + __u8 reserved1; + struct nbft_heap_obj sec_chan_alg_obj; + struct nbft_heap_obj auth_proto_obj; + struct nbft_heap_obj cipher_suite_obj; + struct nbft_heap_obj dh_grp_obj; + struct nbft_heap_obj sec_hash_func_obj; + struct nbft_heap_obj sec_keypath_obj; + __u8 reserved2[22]; +}; + +/** + * enum nbft_security_flags - Security Profile Descriptor Flags (Figure 22) + * @NBFT_SECURITY_VALID: Descriptor Valid: If set to 1h, then + * this descriptor is valid. If cleared + * to 0h, then this descriptor is not valid. + * @NBFT_SECURITY_IN_BAND_AUTH_MASK: Mask to get the In-Band Authentication + * Required field. + * @NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED: In-band authentication is not supported + * by the NVM subsystem. + * @NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED: In-band authentication is supported by + * the NVM subsystem and is not required. + * @NBFT_SECURITY_IN_BAND_AUTH_REQUIRED: In-band authentication is supported by + * the NVM subsystem and is required. + * @NBFT_SECURITY_AUTH_POLICY_LIST_MASK: Mask to get the Authentication Policy List + * flag: This field indicates whether + * authentication protocols were indicated + * by policy from driver defaults or + * administrative configuration. + * @NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED: Authentication Protocols Heap Object Reference + * field Offset and Length are reserved. + * @NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER: Authentication Protocols Offset field and + * the Authentication Protocols Length field + * indicate a list of authentication protocols + * used by the driver. + * @NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN: Authentication Protocols Offset field and + * the Authentication Protocols Length field + * indicate a list of authentication protocols + * that were administratively set and used + * by the driver. + * @NBFT_SECURITY_SEC_CHAN_NEG_MASK: Mask to get the Secure Channel Negotiation + * Required flag: This field indicates whether + * secure channel negotiation (e.g. TLS) + * is required. + * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED: Secure channel negotiation is not supported + * by the NVM subsystem. + * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED: Secure channel negotiation is supported + * by the NVM subsystem and is not required. + * @NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED: Secure channel negotiation is supported + * by the NVM subsystem and is required. + * @NBFT_SECURITY_SEC_POLICY_LIST_MASK: Mask to get the Security Policy List flag: + * This field indicates whether secure channel + * protocols were indicated by policy from driver + * defaults or administrative configuration. + * @NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED: The Offset field and Length field in the + * Secure Channel Algorithm Heap Object Reference + * field are reserved. + * @NBFT_SECURITY_SEC_POLICY_LIST_DRIVER: The Heap Object specified by the Secure Channel + * Algorithm Heap Object Reference field indicates + * a list of authentication protocols used + * by the driver. + * @NBFT_SECURITY_SEC_POLICY_LIST_ADMIN: The Heap Object specified by the Secure Channel + * Algorithm Heap Object Reference field indicates + * a list of authentication protocols that were + * administratively set and used by the driver. + * @NBFT_SECURITY_CIPHER_RESTRICTED: Cipher Suites Restricted by Policy: If set to 1h, + * then the Cipher Suite Offset field and the + * Ciper Suite Length field indicate a list + * of supported cipher suites by the driver. + * If cleared to 0h, then the Cipher Suite Offset + * field and the Cipher Suite Length field + * are reserved. + * @NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED: Authentication DH Groups Restricted + * by Policy List: If set to 1h, then connections + * shall use one of the authentication DH groups + * in the Authentication DH Groups List is required. + * If cleared to 0h, then no Authentication DH Groups + * List is indicated and use of an authentication + * DH Group is not required. + * @NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST: Secure Hash Functions Policy List: If set to 1h, + * then connections shall use one of the secure + * hash functions in the Secure Hash Functions + * Policy List is required. If cleared to 0h, + * then no Secure Hash Functions Policy + * List is indicated and use of a secure + * hash function is not required. + */ +enum nbft_security_flags { + NBFT_SECURITY_VALID = 1 << 0, + NBFT_SECURITY_IN_BAND_AUTH_MASK = 0x0006, + NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED = 0x0002, + NBFT_SECURITY_IN_BAND_AUTH_REQUIRED = 0x0004, + NBFT_SECURITY_AUTH_POLICY_LIST_MASK = 0x0018, + NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER = 0x0008, + NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN = 0x0010, + NBFT_SECURITY_SEC_CHAN_NEG_MASK = 0x0060, + NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED = 0x0020, + NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED = 0x0040, + NBFT_SECURITY_SEC_POLICY_LIST_MASK = 0x0180, + NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED = 0x0000, + NBFT_SECURITY_SEC_POLICY_LIST_DRIVER = 0x0080, + NBFT_SECURITY_SEC_POLICY_LIST_ADMIN = 0x0100, + NBFT_SECURITY_CIPHER_RESTRICTED = 1 << 9, + NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED = 1 << 10, + NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST = 1 << 11, +}; + +/** + * enum nbft_security_secret_type - Security Profile Descriptor Secret Type + * @NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI: Redfish Host Interface URI: + * If set to 1h, then the Secret Keypath + * Object Reference is a URI pointing + * to a Redfish Key Collection Object + * that contains the PSK. + */ +enum nbft_security_secret_type { + NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI = 1 << 1, +}; + +/** + * struct nbft_discovery - Discovery Descriptor (Figure 24) + * @structure_id: Structure ID: This field shall be set to 6h + * (i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY). + * @flags: Discovery Descriptor Flags, see &enum nbft_discovery_flags. + * @index: Discovery Descriptor Index: This field indicates + * the number of this Discovery Descriptor in + * the Discovery Descriptor List. + * @hfi_index: HFI Descriptor Index: This field indicates the value + * of the HFI Descriptor Index field of the HFI Descriptor + * associated with this Discovery Descriptor. If multiple + * HFIs share a common Discovery controller, there shall + * be multiple Discovery Descriptor entries with one per HFI. + * @sec_index: Security Profile Descriptor Index: This field indicates + * the value of the Security Profile Descriptor Index + * field of the Security Descriptor associated with + * this Discovery Descriptor. + * @reserved1: Reserved. + * @discovery_ctrl_addr_obj: Discovery Controller Address Heap Object Reference: + * This field indicates the location and size of a heap + * object containing a URI which indicates an NVMe Discovery + * controller associated with this Discovery Descriptor. + * If this field is cleared to 0h, then no URI is specified. + * @discovery_ctrl_nqn_obj: Discovery Controller NQN Heap Object Reference: + * If set to a non-zero value, this field indicates + * the location and size of a heap object containing + * an NVMe Discovery controller NQN. If the NVMe Discovery + * controller referenced by this record requires secure + * authentication with a well known Subsystem NQN, this + * field indicates the unique NQN for that NVMe Discovery + * controller. This record is involved formatted as an NQN + * string. If this field is cleared to 0h, then this + * field is reserved and the OS shall use the well + * known discovery NQN for this record. + * @reserved2: Reserved. + */ +struct nbft_discovery { + __u8 structure_id; + __u8 flags; + __u8 index; + __u8 hfi_index; + __u8 sec_index; + __u8 reserved1; + struct nbft_heap_obj discovery_ctrl_addr_obj; + struct nbft_heap_obj discovery_ctrl_nqn_obj; + __u8 reserved2[14]; +}; + +/** + * enum nbft_discovery_flags - Discovery Descriptor Flags + * @NBFT_DISCOVERY_VALID: Descriptor Valid: if set to 1h, then this descriptor + * is valid. If cleared to 0h, then this descriptor + * is reserved. + */ +enum nbft_discovery_flags { + NBFT_DISCOVERY_VALID = 1 << 0, +}; + +/* + * End of NBFT ACPI table definitions + */ + + +/* + * Convenient NBFT table parser ('nbft_info' prefix) + */ + +/** + * enum nbft_info_primary_admin_host_flag - Primary Administrative Host Descriptor Flags + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED: Not Indicated by Driver: The driver + * that created this NBFT provided no + * administrative priority hint for + * this NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED: Unselected: The driver that created + * this NBFT explicitly indicated that + * this NBFT should not be prioritized + * over any other NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED: Selected: The driver that created + * this NBFT explicitly indicated that + * this NBFT should be prioritized over + * any other NBFT. + * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED: Reserved. + */ +enum nbft_info_primary_admin_host_flag { + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED, + NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED, +}; + +/** + * struct nbft_info_host - Host Descriptor + * @id: Host ID (raw UUID, length = 16 bytes). + * @nqn: Host NQN. + * @host_id_configured: HostID Configured Flag: value of True indicates that @id + * contains administratively-configured value, or driver + * default value if False. + * @host_nqn_configured: Host NQN Configured Flag: value of True indicates that + * @nqn contains administratively-configured value, + * or driver default value if False. + * @primary: Primary Administrative Host Descriptor, see + * &enum nbft_info_primary_admin_host_flag. + */ +struct nbft_info_host { + unsigned char *id; + char *nqn; + bool host_id_configured; + bool host_nqn_configured; + enum nbft_info_primary_admin_host_flag primary; +}; + +/** + * struct nbft_info_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP + * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function. + * @mac_addr: MAC Address: The MAC address of this HFI, + * in EUI-48TM format. + * @vlan: The VLAN identifier if the VLAN is associated with + * this HFI, as defined in IEEE 802.1q-2018 or zeroes + * if no VLAN is associated with this HFI. + * @ip_origin: The source of Ethernet L3 configuration information + * used by the driver or 0 if not used. + * @ipaddr: The IPv4 or IPv6 address of this HFI. + * @subnet_mask_prefix: The IPv4 or IPv6 subnet mask in CIDR routing prefix + * notation. + * @gateway_ipaddr: The IPv4 or IPv6 address of the IP gateway for this + * HFI or zeroes if no IP gateway is specified. + * @route_metric: The cost value for the route indicated by this HFI. + * @primary_dns_ipaddr: The IPv4 or IPv6 address of the Primary DNS server + * for this HFI. + * @secondary_dns_ipaddr: The IPv4 or IPv6 address of the Secondary DNS server + * for this HFI. + * @dhcp_server_ipaddr: The IPv4 or IPv6 address of the DHCP server used + * to assign this HFI address. + * @host_name: The Host Name string. + * @this_hfi_is_default_route: If True, then the BIOS utilized this interface + * described by HFI to be the default route with highest + * priority. If False, then routes are local to their + * own scope. + * @dhcp_override: If True, then HFI information was populated + * by consuming the DHCP on this interface. If False, + * then the HFI information was set administratively + * by a configuration interface to the driver and + * pre-OS envrionment. + */ +struct nbft_info_hfi_info_tcp { + __u32 pci_sbdf; + __u8 mac_addr[6]; + __u16 vlan; + __u8 ip_origin; + char ipaddr[40]; + __u8 subnet_mask_prefix; + char gateway_ipaddr[40]; + __u16 route_metric; + char primary_dns_ipaddr[40]; + char secondary_dns_ipaddr[40]; + char dhcp_server_ipaddr[40]; + char *host_name; + bool this_hfi_is_default_route; + bool dhcp_override; +}; + +/** + * struct nbft_info_hfi - Host Fabric Interface (HFI) Descriptor + * @index: HFI Descriptor Index: indicates the number of this HFI Descriptor + * in the Host Fabric Interface Descriptor List. + * @transport: Transport Type string (e.g. 'tcp'). + * @tcp_info: The HFI Transport Info Descriptor, see &struct nbft_info_hfi_info_tcp. + */ +struct nbft_info_hfi { + int index; + char transport[8]; + struct nbft_info_hfi_info_tcp tcp_info; +}; + +/** + * struct nbft_info_discovery - Discovery Descriptor + * @index: The number of this Discovery Descriptor in the Discovery + * Descriptor List. + * @security: The Security Profile Descriptor, see &struct nbft_info_security. + * @hfi: The HFI Descriptor associated with this Discovery Descriptor. + * See &struct nbft_info_hfi. + * @uri: A URI which indicates an NVMe Discovery controller associated + * with this Discovery Descriptor. + * @nqn: An NVMe Discovery controller NQN. + */ +struct nbft_info_discovery { + int index; + struct nbft_info_security *security; + struct nbft_info_hfi *hfi; + char *uri; + char *nqn; +}; + +/** + * struct nbft_info_security - Security Profile Descriptor + * @index: The number of this Security Profile Descriptor in the Security + * Profile Descriptor List. + */ +struct nbft_info_security { + int index; + /* TODO add fields */ +}; + +/** + * enum nbft_info_nid_type - Namespace Identifier Type (NIDT) + * @NBFT_INFO_NID_TYPE_NONE: No identifier available. + * @NBFT_INFO_NID_TYPE_EUI64: The EUI-64 identifier. + * @NBFT_INFO_NID_TYPE_NGUID: The NSGUID identifier. + * @NBFT_INFO_NID_TYPE_NS_UUID: The UUID identifier. + */ +enum nbft_info_nid_type { + NBFT_INFO_NID_TYPE_NONE = 0, + NBFT_INFO_NID_TYPE_EUI64 = 1, + NBFT_INFO_NID_TYPE_NGUID = 2, + NBFT_INFO_NID_TYPE_NS_UUID = 3, +}; + +/** + * struct nbft_info_subsystem_ns - Subsystem Namespace (SSNS) info + * @index: SSNS Descriptor Index in the descriptor list. + * @discovery: Primary Discovery Controller associated with + * this SSNS Descriptor. + * @security: Security Profile Descriptor associated with + * this namespace. + * @num_hfis: Number of HFIs. + * @hfis: List of HFIs associated with this namespace. + * Includes the primary HFI at the first position + * and all secondary HFIs. This array is null-terminated. + * @transport: Transport Type string (e.g. 'tcp'). + * @traddr: Subsystem Transport Address. + * @trsvcid: Subsystem Transport Service Identifier. + * @subsys_port_id: The Subsystem Port ID. + * @nsid: The Namespace ID of this descriptor or when @nid + * should be used instead. + * @nid_type: Namespace Identifier Type, see &enum nbft_info_nid_type. + * @nid: The Namespace Identifier value. + * @subsys_nqn: Subsystem and Namespace NQN. + * @pdu_header_digest_required: PDU Header Digest (HDGST) Flag: the use of NVM Header + * Digest Enabled is required. + * @data_digest_required: Data Digest (DDGST) Flag: the use of NVM Data Digest + * Enabled is required. + * @controller_id: Controller ID (SSNS Extended Information Descriptor): + * The controller ID associated with the Admin Queue + * or 0 if not supported. + * @asqsz: Admin Submission Queue Size (SSNS Extended Information + * Descriptor) or 0 if not supported. + * @dhcp_root_path_string: DHCP Root Path Override string (SSNS Extended + * Information Descriptor). + */ +struct nbft_info_subsystem_ns { + int index; + struct nbft_info_discovery *discovery; + struct nbft_info_security *security; + int num_hfis; + struct nbft_info_hfi **hfis; + char transport[8]; + char traddr[40]; + char *trsvcid; + __u16 subsys_port_id; + __u32 nsid; + enum nbft_info_nid_type nid_type; + __u8 *nid; + char *subsys_nqn; + bool pdu_header_digest_required; + bool data_digest_required; + int controller_id; + int asqsz; + char *dhcp_root_path_string; +}; + +/** + * struct nbft_info - The parsed NBFT table data. + * @filename: Path to the NBFT table. + * @raw_nbft: The original NBFT table contents. + * @raw_nbft_size: Size of @raw_nbft. + * @host: The Host Descriptor (should match other NBFTs). + * @hfi_list: The HFI Descriptor List (null-terminated array). + * @security_list: The Security Profile Descriptor List (null-terminated array). + * @discovery_list: The Discovery Descriptor List (null-terminated array). + * @subsystem_ns_list: The SSNS Descriptor List (null-terminated array). + */ +struct nbft_info { + char *filename; + __u8 *raw_nbft; + ssize_t raw_nbft_size; + struct nbft_info_host host; + struct nbft_info_hfi **hfi_list; + struct nbft_info_security **security_list; + struct nbft_info_discovery **discovery_list; + struct nbft_info_subsystem_ns **subsystem_ns_list; +}; + +/** + * nvme_nbft_read() - Read and parse contents of an ACPI NBFT table + * + * @nbft: Parsed NBFT table data. + * @filename: Filename of the raw NBFT table to read. + * + * Read and parse the specified NBFT file into a struct nbft_info. + * Free with nvme_nbft_free(). + * + * Return: 0 on success, errno otherwise. + */ +int nvme_nbft_read(struct nbft_info **nbft, const char *filename); + +/** + * nvme_nbft_free() - Free the struct nbft_info and its contents + * @nbft: Parsed NBFT table data. + */ +void nvme_nbft_free(struct nbft_info *nbft); + +#endif diff --git a/src/nvme/no-json.c b/src/nvme/no-json.c new file mode 100644 index 0000000..171144e --- /dev/null +++ b/src/nvme/no-json.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2023 SUSE Software Solutions + * + * Authors: Daniel Wagner <dwagner@suse.de> + */ + +#include "tree.h" + +#include <errno.h> + +int json_read_config(nvme_root_t r, const char *config_file) +{ + return -ENOTSUP; +} + +int json_update_config(nvme_root_t r, const char *config_file) +{ + return -ENOTSUP; +} + +int json_dump_tree(nvme_root_t r) +{ + return -ENOTSUP; +} diff --git a/src/nvme/private.h b/src/nvme/private.h new file mode 100644 index 0000000..3505802 --- /dev/null +++ b/src/nvme/private.h @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2021 SUSE Software Solutions + * + * Authors: Hannes Reinecke <hare@suse.de> + */ + +#ifndef _LIBNVME_PRIVATE_H +#define _LIBNVME_PRIVATE_H + +#include <ccan/list/list.h> +#include <poll.h> +#include <sys/socket.h> + +#include "fabrics.h" +#include "mi.h" + + +char *nvme_ctrl_sysfs_dir(void); +char *nvme_subsys_sysfs_dir(void); +char *nvme_ns_sysfs_dir(void); + +struct nvme_path { + struct list_node entry; + struct list_node nentry; + + struct nvme_ctrl *c; + struct nvme_ns *n; + + char *name; + char *sysfs_dir; + char *ana_state; + int grpid; +}; + +struct nvme_ns { + struct list_node entry; + struct list_head paths; + + struct nvme_subsystem *s; + struct nvme_ctrl *c; + + int fd; + __u32 nsid; + char *name; + char *generic_name; + char *sysfs_dir; + + int lba_shift; + int lba_size; + int meta_size; + uint64_t lba_count; + uint64_t lba_util; + + uint8_t eui64[8]; + uint8_t nguid[16]; + unsigned char uuid[NVME_UUID_LEN]; + enum nvme_csi csi; +}; + +struct nvme_ctrl { + struct list_node entry; + struct list_head paths; + struct list_head namespaces; + struct nvme_subsystem *s; + + int fd; + char *name; + char *sysfs_dir; + char *address; + char *firmware; + char *model; + char *state; + char *numa_node; + char *queue_count; + char *serial; + char *sqsize; + char *transport; + char *subsysnqn; + char *traddr; + char *trsvcid; + char *dhchap_key; + char *dhchap_ctrl_key; + char *cntrltype; + char *dctype; + char *phy_slot; + bool discovery_ctrl; + bool unique_discovery_ctrl; + bool discovered; + bool persistent; + struct nvme_fabrics_config cfg; +}; + +struct nvme_subsystem { + struct list_node entry; + struct list_head ctrls; + struct list_head namespaces; + struct nvme_host *h; + + char *name; + char *sysfs_dir; + char *subsysnqn; + char *model; + char *serial; + char *firmware; + char *subsystype; + char *application; + char *iopolicy; +}; + +struct nvme_host { + struct list_node entry; + struct list_head subsystems; + struct nvme_root *r; + + char *hostnqn; + char *hostid; + char *dhchap_key; + char *hostsymname; + bool pdc_enabled; + bool pdc_enabled_valid; /* set if pdc_enabled doesn't have an undefined + * value */ +}; + +struct nvme_fabric_options { + bool cntlid; + bool concat; + bool ctrl_loss_tmo; + bool data_digest; + bool dhchap_ctrl_secret; + bool dhchap_secret; + bool disable_sqflow; + bool discovery; + bool duplicate_connect; + bool fast_io_fail_tmo; + bool hdr_digest; + bool host_iface; + bool host_traddr; + bool hostid; + bool hostnqn; + bool instance; + bool keep_alive_tmo; + bool keyring; + bool nqn; + bool nr_io_queues; + bool nr_poll_queues; + bool nr_write_queues; + bool queue_size; + bool reconnect_delay; + bool tls; + bool tls_key; + bool tos; + bool traddr; + bool transport; + bool trsvcid; +}; + +struct nvme_root { + char *config_file; + char *application; + struct list_head hosts; + struct list_head endpoints; /* MI endpoints */ + FILE *fp; + int log_level; + bool log_pid; + bool log_timestamp; + bool modified; + bool mi_probe_enabled; + struct nvme_fabric_options *options; +}; + +int nvme_set_attr(const char *dir, const char *attr, const char *value); + +int json_read_config(nvme_root_t r, const char *config_file); + +int json_update_config(nvme_root_t r, const char *config_file); + +int json_dump_tree(nvme_root_t r); + +nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid, + const char *subsysnqn, nvme_ctrl_t p); + +void *__nvme_alloc(size_t len); + +#if (LOG_FUNCNAME == 1) +#define __nvme_log_func __func__ +#else +#define __nvme_log_func NULL +#endif + +void __attribute__((format(printf, 4, 5))) +__nvme_msg(nvme_root_t r, int lvl, const char *func, const char *format, ...); + +#define nvme_msg(r, lvl, format, ...) \ + do { \ + if ((lvl) <= MAX_LOGLEVEL) \ + __nvme_msg(r, lvl, __nvme_log_func, \ + format, ##__VA_ARGS__); \ + } while (0) + +#define root_from_ctrl(c) ((c)->s && (c)->s->h ? (c)->s->h->r : NULL) +#define root_from_ns(n) ((n)->s && (n)->s->h ? (n)->s->h->r : \ + (n)->c && (n)->c->s && (n)->c->s->h ? (n)->c->s->h->r : \ + NULL) + +/* mi internal headers */ + +/* internal transport API */ +struct nvme_mi_req { + struct nvme_mi_msg_hdr *hdr; + size_t hdr_len; + void *data; + size_t data_len; + __u32 mic; +}; + +struct nvme_mi_resp { + struct nvme_mi_msg_hdr *hdr; + size_t hdr_len; + void *data; + size_t data_len; + __u32 mic; +}; + +struct nvme_mi_transport { + const char *name; + bool mic_enabled; + int (*submit)(struct nvme_mi_ep *ep, + struct nvme_mi_req *req, + struct nvme_mi_resp *resp); + void (*close)(struct nvme_mi_ep *ep); + int (*desc_ep)(struct nvme_mi_ep *ep, char *buf, size_t len); + int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout); +}; + +/* quirks */ + +/* Set a minimum time between receiving a response from one command and + * sending the next request. Some devices may ignore new commands sent too soon + * after the previous request, so manually insert a delay + */ +#define NVME_QUIRK_MIN_INTER_COMMAND_TIME (1 << 0) + +struct nvme_mi_ep { + struct nvme_root *root; + const struct nvme_mi_transport *transport; + void *transport_data; + struct list_node root_entry; + struct list_head controllers; + bool controllers_scanned; + unsigned int timeout; + unsigned int mprt_max; + unsigned long quirks; + + /* inter-command delay, for NVME_QUIRK_MIN_INTER_COMMAND_TIME */ + unsigned int inter_command_us; + struct timespec last_resp_time; + bool last_resp_time_valid; +}; + +struct nvme_mi_ctrl { + struct nvme_mi_ep *ep; + __u16 id; + struct list_node ep_entry; +}; + +struct nvme_mi_ep *nvme_mi_init_ep(struct nvme_root *root); +void nvme_mi_ep_probe(struct nvme_mi_ep *ep); + +/* for tests, we need to calculate the correct MICs */ +__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len); + +/* we have a facility to mock MCTP socket operations in the mi-mctp transport, + * using this ops type. This should only be used for test, and isn't exposed + * in the shared lib */; +struct mctp_ioc_tag_ctl; +struct __mi_mctp_socket_ops { + int (*socket)(int, int, int); + ssize_t (*sendmsg)(int, const struct msghdr *, int); + ssize_t (*recvmsg)(int, struct msghdr *, int); + int (*poll)(struct pollfd *, nfds_t, int); + int (*ioctl_tag)(int, unsigned long, struct mctp_ioc_tag_ctl *); +}; +void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops); + +#endif /* _LIBNVME_PRIVATE_H */ diff --git a/src/nvme/tree.c b/src/nvme/tree.c new file mode 100644 index 0000000..344f8bc --- /dev/null +++ b/src/nvme/tree.c @@ -0,0 +1,2713 @@ +// 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 <dirent.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <libgen.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <ccan/endian/endian.h> +#include <ccan/list/list.h> + +#include "cleanup.h" +#include "ioctl.h" +#include "linux.h" +#include "filters.h" +#include "tree.h" +#include "filters.h" +#include "util.h" +#include "fabrics.h" +#include "log.h" +#include "private.h" + +/** + * struct candidate_args - Used to look for a controller matching these parameters + * @transport: Transport type: loop, fc, rdma, tcp + * @traddr: Transport address (destination address) + * @trsvcid: Transport service ID + * @subsysnqn: Subsystem NQN + * @host_traddr: Host transport address (source address) + * @host_iface: Host interface for connection (tcp only) + * @iface_list: Interface list (tcp only) + * @addreq: Address comparison function (for traddr, host-traddr) + * @well_known_nqn: Set to "true" when @subsysnqn is the well-known NQN + */ +struct candidate_args { + const char *transport; + const char *traddr; + const char *trsvcid; + const char *subsysnqn; + const char *host_traddr; + const char *host_iface; + struct ifaddrs *iface_list; + bool (*addreq)(const char *, const char *); + bool well_known_nqn; +}; +typedef bool (*ctrl_match_t)(struct nvme_ctrl *c, struct candidate_args *candidate); + +#define PATH_SYSFS_SLOTS "/sys/bus/pci/slots" + +static char *nvme_slots_sysfs_dir(void) +{ + char *basepath = getenv("LIBNVME_SYSFS_PATH"); + char *str; + + if (!basepath) + return strdup(PATH_SYSFS_SLOTS); + + if (!asprintf(&str, "%s" PATH_SYSFS_SLOTS, basepath)) + return NULL; + + return str; +} + +static struct nvme_host *default_host; + +static void __nvme_free_host(nvme_host_t h); +static void __nvme_free_ctrl(nvme_ctrl_t c); +static int nvme_subsystem_scan_namespace(nvme_root_t r, + struct nvme_subsystem *s, char *name, + nvme_scan_filter_t f, void *f_args); +static int nvme_init_subsystem(nvme_subsystem_t s, const char *name); +static int nvme_scan_subsystem(nvme_root_t r, const char *name, + nvme_scan_filter_t f, void *f_args); +static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c, + char *name); +static int nvme_ctrl_scan_path(nvme_root_t r, struct nvme_ctrl *c, char *name); + +/** + * Compare two C strings and handle NULL pointers gracefully. + * Return true if both pointers are equal (including both set to NULL). + * Return false if one and only one of the two pointers is NULL. + * Perform string comparisong only if both pointers are not NULL and + * return true if both strings are the same, false otherwise. + */ +static bool streq0(const char *s1, const char *s2) +{ + if (s1 == s2) + return true; + if (!s1 || !s2) + return false; + return !strcmp(s1, s2); +} + +/** + * Same as streq0() but ignore the case of the characters. + */ +static bool streqcase0(const char *s1, const char *s2) +{ + if (s1 == s2) + return true; + if (!s1 || !s2) + return false; + return !strcasecmp(s1, s2); +} + +struct dirents { + struct dirent **ents; + int num; +}; + +static void cleanup_dirents(struct dirents *ents) +{ + while (ents->num > 0) + free(ents->ents[--ents->num]); + free(ents->ents); +} + +#define _cleanup_dirents_ __cleanup__(cleanup_dirents) + +nvme_host_t nvme_default_host(nvme_root_t r) +{ + struct nvme_host *h; + _cleanup_free_ char *hostnqn = NULL; + _cleanup_free_ char *hostid = NULL; + + hostnqn = nvmf_hostnqn_from_file(); + if (!hostnqn) + hostnqn = nvmf_hostnqn_generate(); + hostid = nvmf_hostid_from_file(); + + h = nvme_lookup_host(r, hostnqn, hostid); + + nvme_host_set_hostsymname(h, NULL); + + default_host = h; + return h; +} + +int nvme_scan_topology(struct nvme_root *r, nvme_scan_filter_t f, void *f_args) +{ + _cleanup_dirents_ struct dirents subsys = {}, ctrls = {}; + int i, ret; + + if (!r) + return 0; + + ctrls.num = nvme_scan_ctrls(&ctrls.ents); + if (ctrls.num < 0) { + nvme_msg(r, LOG_DEBUG, "failed to scan ctrls: %s\n", + strerror(errno)); + return ctrls.num; + } + + for (i = 0; i < ctrls.num; i++) { + nvme_ctrl_t c = nvme_scan_ctrl(r, ctrls.ents[i]->d_name); + if (!c) { + nvme_msg(r, LOG_DEBUG, "failed to scan ctrl %s: %s\n", + ctrls.ents[i]->d_name, strerror(errno)); + continue; + } + if ((f) && !f(NULL, c, NULL, f_args)) { + nvme_msg(r, LOG_DEBUG, "filter out controller %s\n", + ctrls.ents[i]->d_name); + nvme_free_ctrl(c); + } + } + + subsys.num = nvme_scan_subsystems(&subsys.ents); + if (subsys.num < 0) { + nvme_msg(r, LOG_DEBUG, "failed to scan subsystems: %s\n", + strerror(errno)); + return subsys.num; + } + + for (i = 0; i < subsys.num; i++) { + ret = nvme_scan_subsystem( + r, subsys.ents[i]->d_name, f, f_args); + if (ret < 0) { + nvme_msg(r, LOG_DEBUG, + "failed to scan subsystem %s: %s\n", + subsys.ents[i]->d_name, strerror(errno)); + } + } + + return 0; +} + +nvme_root_t nvme_create_root(FILE *fp, int log_level) +{ + struct nvme_root *r = calloc(1, sizeof(*r)); + + if (!r) { + errno = ENOMEM; + return NULL; + } + r->log_level = log_level; + r->fp = stderr; + if (fp) + r->fp = fp; + list_head_init(&r->hosts); + list_head_init(&r->endpoints); + nvme_set_root(r); + return r; +} + +int nvme_read_config(nvme_root_t r, const char *config_file) +{ + int err = -1; + + if (!r || !config_file) { + errno = ENODEV; + return err; + } + + r->config_file = strdup(config_file); + if (!r->config_file) { + errno = ENOMEM; + return err; + } + err = json_read_config(r, config_file); + /* + * The json configuration file is optional, + * so ignore errors when opening the file. + */ + if (err < 0 && errno != EPROTO) + err = 0; + + return err; +} + +nvme_root_t nvme_scan(const char *config_file) +{ + nvme_root_t r = nvme_create_root(NULL, DEFAULT_LOGLEVEL); + + nvme_scan_topology(r, NULL, NULL); + nvme_read_config(r, config_file); + return r; +} + +int nvme_update_config(nvme_root_t r) +{ + if (!r->modified || !r->config_file) + return 0; + + return json_update_config(r, r->config_file); +} + +int nvme_dump_config(nvme_root_t r) +{ + return json_update_config(r, NULL); +} + +int nvme_dump_tree(nvme_root_t r) +{ + return json_dump_tree(r); +} + +const char *nvme_root_get_application(nvme_root_t r) +{ + return r->application; +} + +void nvme_root_set_application(nvme_root_t r, const char *a) +{ + if (r->application) { + free(r->application); + r->application = NULL; + } + if (a) + r->application = strdup(a); +} + +nvme_host_t nvme_first_host(nvme_root_t r) +{ + return list_top(&r->hosts, struct nvme_host, entry); +} + +nvme_host_t nvme_next_host(nvme_root_t r, nvme_host_t h) +{ + return h ? list_next(&r->hosts, h, entry) : NULL; +} + +nvme_root_t nvme_host_get_root(nvme_host_t h) +{ + return h->r; +} + +const char *nvme_host_get_hostnqn(nvme_host_t h) +{ + return h->hostnqn; +} + +const char *nvme_host_get_hostid(nvme_host_t h) +{ + return h->hostid; +} + +const char *nvme_host_get_hostsymname(nvme_host_t h) +{ + return h->hostsymname; +} + +void nvme_host_set_hostsymname(nvme_host_t h, const char *hostsymname) +{ + if (h->hostsymname) { + free(h->hostsymname); + h->hostsymname = NULL; + } + if (hostsymname) + h->hostsymname = strdup(hostsymname); +} + +const char *nvme_host_get_dhchap_key(nvme_host_t h) +{ + return h->dhchap_key; +} + +void nvme_host_set_dhchap_key(nvme_host_t h, const char *key) +{ + if (h->dhchap_key) { + free(h->dhchap_key); + h->dhchap_key = NULL; + } + if (key) + h->dhchap_key = strdup(key); +} + +void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled) +{ + h->pdc_enabled_valid = true; + h->pdc_enabled = enabled; +} + +bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback) +{ + if (h->pdc_enabled_valid) + return h->pdc_enabled; + return fallback; +} + +nvme_subsystem_t nvme_first_subsystem(nvme_host_t h) +{ + return list_top(&h->subsystems, struct nvme_subsystem, entry); +} + +nvme_subsystem_t nvme_next_subsystem(nvme_host_t h, nvme_subsystem_t s) +{ + return s ? list_next(&h->subsystems, s, entry) : NULL; +} + +void nvme_refresh_topology(nvme_root_t r) +{ + struct nvme_host *h, *_h; + + nvme_for_each_host_safe(r, h, _h) + __nvme_free_host(h); + nvme_scan_topology(r, NULL, NULL); +} + +void nvme_free_tree(nvme_root_t r) +{ + struct nvme_host *h, *_h; + + free(r->options); + nvme_for_each_host_safe(r, h, _h) + __nvme_free_host(h); + if (r->config_file) + free(r->config_file); + if (r->application) + free(r->application); + nvme_set_root(NULL); + free(r); +} + +void nvme_root_release_fds(nvme_root_t r) +{ + struct nvme_host *h, *_h; + + nvme_for_each_host_safe(r, h, _h) + nvme_host_release_fds(h); +} + +const char *nvme_subsystem_get_nqn(nvme_subsystem_t s) +{ + return s->subsysnqn; +} + +const char *nvme_subsystem_get_sysfs_dir(nvme_subsystem_t s) +{ + return s->sysfs_dir; +} + +const char *nvme_subsystem_get_name(nvme_subsystem_t s) +{ + return s->name; +} + +const char *nvme_subsystem_get_type(nvme_subsystem_t s) +{ + return s->subsystype; +} + +const char *nvme_subsystem_get_application(nvme_subsystem_t s) +{ + return s->application; +} + +void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a) +{ + if (s->application) { + free(s->application); + s->application = NULL; + } + if (a) + s->application = strdup(a); +} + +const char *nvme_subsystem_get_iopolicy(nvme_subsystem_t s) +{ + return s->iopolicy; +} + +nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s) +{ + return list_top(&s->ctrls, struct nvme_ctrl, entry); +} + +nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c) +{ + return c ? list_next(&s->ctrls, c, entry) : NULL; +} + +nvme_host_t nvme_subsystem_get_host(nvme_subsystem_t s) +{ + return s->h; +} + +nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s) +{ + return list_top(&s->namespaces, struct nvme_ns, entry); +} + +nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n) +{ + return n ? list_next(&s->namespaces, n, entry) : NULL; +} + +nvme_path_t nvme_namespace_first_path(nvme_ns_t ns) +{ + return list_top(&ns->paths, struct nvme_path, nentry); +} + +nvme_path_t nvme_namespace_next_path(nvme_ns_t ns, nvme_path_t p) +{ + return p ? list_next(&ns->paths, p, nentry) : NULL; +} + +static void __nvme_free_ns(struct nvme_ns *n) +{ + list_del_init(&n->entry); + nvme_ns_release_fd(n); + free(n->generic_name); + free(n->name); + free(n->sysfs_dir); + free(n); +} + +/* Stub for SWIG */ +void nvme_free_ns(struct nvme_ns *n) +{ + __nvme_free_ns(n); +} + +static void __nvme_free_subsystem(struct nvme_subsystem *s) +{ + struct nvme_ctrl *c, *_c; + struct nvme_ns *n, *_n; + + list_del_init(&s->entry); + nvme_subsystem_for_each_ctrl_safe(s, c, _c) + __nvme_free_ctrl(c); + + nvme_subsystem_for_each_ns_safe(s, n, _n) + __nvme_free_ns(n); + + if (s->name) + free(s->name); + free(s->sysfs_dir); + free(s->subsysnqn); + if (s->model) + free(s->model); + if (s->serial) + free(s->serial); + if (s->firmware) + free(s->firmware); + if (s->subsystype) + free(s->subsystype); + if (s->application) + free(s->application); + if (s->iopolicy) + free(s->iopolicy); + free(s); +} + +void nvme_subsystem_release_fds(struct nvme_subsystem *s) +{ + struct nvme_ctrl *c, *_c; + struct nvme_ns *n, *_n; + + nvme_subsystem_for_each_ctrl_safe(s, c, _c) + nvme_ctrl_release_fd(c); + + nvme_subsystem_for_each_ns_safe(s, n, _n) + nvme_ns_release_fd(n); +} + +/* + * Stub for SWIG + */ +void nvme_free_subsystem(nvme_subsystem_t s) +{ +} + +struct nvme_subsystem *nvme_alloc_subsystem(struct nvme_host *h, + const char *name, + const char *subsysnqn) +{ + struct nvme_subsystem *s; + + s = calloc(1, sizeof(*s)); + if (!s) + return NULL; + + s->h = h; + s->subsysnqn = strdup(subsysnqn); + if (name) + nvme_init_subsystem(s, name); + list_head_init(&s->ctrls); + list_head_init(&s->namespaces); + list_node_init(&s->entry); + list_add(&h->subsystems, &s->entry); + h->r->modified = true; + return s; +} + +struct nvme_subsystem *nvme_lookup_subsystem(struct nvme_host *h, + const char *name, + const char *subsysnqn) +{ + struct nvme_subsystem *s; + + nvme_for_each_subsystem(h, s) { + if (subsysnqn && s->subsysnqn && + strcmp(s->subsysnqn, subsysnqn)) + continue; + if (name && s->name && + strcmp(s->name, name)) + continue; + if (h->r->application) { + if (!s->application) + continue; + if (strcmp(h->r->application, s->application)) + continue; + } + return s; + } + return nvme_alloc_subsystem(h, name, subsysnqn); +} + +static void __nvme_free_host(struct nvme_host *h) +{ + struct nvme_subsystem *s, *_s; + + list_del_init(&h->entry); + nvme_for_each_subsystem_safe(h, s, _s) + __nvme_free_subsystem(s); + free(h->hostnqn); + if (h->hostid) + free(h->hostid); + if (h->dhchap_key) + free(h->dhchap_key); + nvme_host_set_hostsymname(h, NULL); + h->r->modified = true; + free(h); +} + +void nvme_host_release_fds(struct nvme_host *h) +{ + struct nvme_subsystem *s, *_s; + + nvme_for_each_subsystem_safe(h, s, _s) + nvme_subsystem_release_fds(s); +} + +/* Stub for SWIG */ +void nvme_free_host(struct nvme_host *h) +{ + __nvme_free_host(h); +} + +struct nvme_host *nvme_lookup_host(nvme_root_t r, const char *hostnqn, + const char *hostid) +{ + struct nvme_host *h; + + if (!hostnqn) + return NULL; + nvme_for_each_host(r, h) { + if (strcmp(h->hostnqn, hostnqn)) + continue; + if (hostid && (!h->hostid || + strcmp(h->hostid, hostid))) + continue; + return h; + } + h = calloc(1,sizeof(*h)); + if (!h) + return NULL; + h->hostnqn = strdup(hostnqn); + if (hostid) + h->hostid = strdup(hostid); + list_head_init(&h->subsystems); + list_node_init(&h->entry); + h->r = r; + list_add(&r->hosts, &h->entry); + r->modified = true; + + return h; +} + +static int nvme_subsystem_scan_namespaces(nvme_root_t r, nvme_subsystem_t s, + nvme_scan_filter_t f, void *f_args) +{ + _cleanup_dirents_ struct dirents namespaces = {}; + int i, ret; + + namespaces.num = nvme_scan_subsystem_namespaces(s, &namespaces.ents); + if (namespaces.num < 0) { + nvme_msg(r, LOG_DEBUG, + "failed to scan namespaces for subsys %s: %s\n", + s->subsysnqn, strerror(errno)); + return namespaces.num; + } + + for (i = 0; i < namespaces.num; i++) { + ret = nvme_subsystem_scan_namespace(r, s, + namespaces.ents[i]->d_name, f, f_args); + if (ret < 0) + nvme_msg(r, LOG_DEBUG, + "failed to scan namespace %s: %s\n", + namespaces.ents[i]->d_name, strerror(errno)); + } + + return 0; +} + +static int nvme_init_subsystem(nvme_subsystem_t s, const char *name) +{ + _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); + char *path; + + if (asprintf(&path, "%s/%s", subsys_dir, name) < 0) + return -1; + + s->model = nvme_get_attr(path, "model"); + if (!s->model) + s->model = strdup("undefined"); + s->serial = nvme_get_attr(path, "serial"); + s->firmware = nvme_get_attr(path, "firmware_rev"); + s->subsystype = nvme_get_attr(path, "subsystype"); + if (!s->subsystype) { + if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME)) + s->subsystype = strdup("discovery"); + else + s->subsystype = strdup("nvm"); + } + s->name = strdup(name); + s->sysfs_dir = (char *)path; + if (s->h->r->application) + s->application = strdup(s->h->r->application); + s->iopolicy = nvme_get_attr(path, "iopolicy"); + + return 0; +} + +static bool __nvme_scan_subsystem(struct nvme_root *r, nvme_subsystem_t s, + nvme_scan_filter_t f, void *f_args) +{ + if (f && !f(s, NULL, NULL, f_args)) { + nvme_msg(r, LOG_DEBUG, "filter out subsystem %s\n", s->name); + __nvme_free_subsystem(s); + return false; + } + nvme_subsystem_scan_namespaces(r, s, f, f_args); + return true; +} + +static int nvme_scan_subsystem(struct nvme_root *r, const char *name, + nvme_scan_filter_t f, void *f_args) +{ + struct nvme_subsystem *s = NULL, *_s; + _cleanup_free_ char *path = NULL, *subsysnqn = NULL; + _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); + nvme_host_t h = NULL; + int ret; + + nvme_msg(r, LOG_DEBUG, "scan subsystem %s\n", name); + ret = asprintf(&path, "%s/%s", subsys_dir, name); + if (ret < 0) + return ret; + + subsysnqn = nvme_get_attr(path, "subsysnqn"); + if (!subsysnqn) { + errno = ENODEV; + return -1; + } + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, _s) { + /* + * We are always called after nvme_scan_ctrl(), + * so any subsystem we're interested at _must_ + * have a name. + */ + if (!_s->name) + continue; + if (strcmp(_s->name, name)) + continue; + if (!__nvme_scan_subsystem(r, _s, f, f_args)) { + errno = EINVAL; + return -1; + } + s = _s; + } + } + if (!s) { + /* + * Subsystem with non-matching controller. odd. + * Create a subsystem with the default host + * and hope for the best. + */ + nvme_msg(r, LOG_DEBUG, "creating detached subsystem '%s'\n", + name); + h = nvme_default_host(r); + s = nvme_alloc_subsystem(h, name, subsysnqn); + if (!s) { + errno = ENOMEM; + return -1; + } + if (!__nvme_scan_subsystem(r, s, f, f_args)) { + errno = EINVAL; + return -1; + } + } else if (strcmp(s->subsysnqn, subsysnqn)) { + nvme_msg(r, LOG_DEBUG, "NQN mismatch for subsystem '%s'\n", + name); + errno = EINVAL; + return -1; + } + + return 0; +} + +nvme_ctrl_t nvme_path_get_ctrl(nvme_path_t p) +{ + return p->c; +} + +nvme_ns_t nvme_path_get_ns(nvme_path_t p) +{ + return p->n; +} + +const char *nvme_path_get_sysfs_dir(nvme_path_t p) +{ + return p->sysfs_dir; +} + +const char *nvme_path_get_name(nvme_path_t p) +{ + return p->name; +} + +const char *nvme_path_get_ana_state(nvme_path_t p) +{ + return p->ana_state; +} + +void nvme_free_path(struct nvme_path *p) +{ + list_del_init(&p->entry); + list_del_init(&p->nentry); + free(p->name); + free(p->sysfs_dir); + free(p->ana_state); + free(p); +} + +static void nvme_subsystem_set_path_ns(nvme_subsystem_t s, nvme_path_t p) +{ + char n_name[32] = { }; + int i, c, nsid, ret; + nvme_ns_t n; + + ret = sscanf(nvme_path_get_name(p), "nvme%dc%dn%d", &i, &c, &nsid); + if (ret != 3) + return; + + sprintf(n_name, "nvme%dn%d", i, nsid); + nvme_subsystem_for_each_ns(s, n) { + if (!strcmp(n_name, nvme_ns_get_name(n))) { + list_add(&n->paths, &p->nentry); + p->n = n; + } + } +} + +static int nvme_ctrl_scan_path(nvme_root_t r, struct nvme_ctrl *c, char *name) +{ + struct nvme_path *p; + _cleanup_free_ char *path = NULL, *grpid = NULL; + int ret; + + nvme_msg(r, LOG_DEBUG, "scan controller %s path %s\n", + c->name, name); + if (!c->s) { + errno = ENXIO; + return -1; + } + ret = asprintf(&path, "%s/%s", c->sysfs_dir, name); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + p = calloc(1, sizeof(*p)); + if (!p) { + errno = ENOMEM; + return -1; + } + + p->c = c; + p->name = strdup(name); + p->sysfs_dir = path; + path = NULL; + p->ana_state = nvme_get_path_attr(p, "ana_state"); + if (!p->ana_state) + p->ana_state = strdup("optimized"); + + grpid = nvme_get_path_attr(p, "ana_grpid"); + if (grpid) { + sscanf(grpid, "%d", &p->grpid); + } + + list_node_init(&p->nentry); + nvme_subsystem_set_path_ns(c->s, p); + list_node_init(&p->entry); + list_add(&c->paths, &p->entry); + return 0; +} + +int nvme_ctrl_get_fd(nvme_ctrl_t c) +{ + if (c->fd < 0) { + c->fd = nvme_open(c->name); + if (c->fd < 0) + nvme_msg(root_from_ctrl(c), LOG_ERR, + "Failed to open ctrl %s, errno %d\n", + c->name, errno); + } + return c->fd; +} + +void nvme_ctrl_release_fd(nvme_ctrl_t c) +{ + if (c->fd < 0) + return; + + close(c->fd); + c->fd = -1; +} + +nvme_subsystem_t nvme_ctrl_get_subsystem(nvme_ctrl_t c) +{ + return c->s; +} + +const char *nvme_ctrl_get_name(nvme_ctrl_t c) +{ + return c->name; +} + +const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c) +{ + return c->sysfs_dir; +} + +const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c) +{ + return c->s ? c->s->subsysnqn : c->subsysnqn; +} + +const char *nvme_ctrl_get_address(nvme_ctrl_t c) +{ + return c->address ? c->address : ""; +} + +char *nvme_ctrl_get_src_addr(nvme_ctrl_t c, char *src_addr, size_t src_addr_len) +{ + size_t l; + char *p; + + if (!c->address) + return NULL; + + p = strstr(c->address, "src_addr="); + if (!p) + return NULL; + + p += strlen("src_addr="); + l = strcspn(p, ",%"); /* % to eliminate IPv6 scope (if present) */ + if (l >= src_addr_len) { + nvme_msg(root_from_ctrl(c), LOG_ERR, + "Buffer for src_addr is too small (%zu must be > %zu)\n", + src_addr_len, l); + return NULL; + } + + strncpy(src_addr, p, l); + src_addr[l] = '\0'; + return src_addr; +} + +const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c) +{ + return c->phy_slot ? c->phy_slot : ""; +} + +const char *nvme_ctrl_get_firmware(nvme_ctrl_t c) +{ + return c->firmware; +} + +const char *nvme_ctrl_get_model(nvme_ctrl_t c) +{ + return c->model; +} + +const char *nvme_ctrl_get_state(nvme_ctrl_t c) +{ + char *state = c->state; + + c->state = nvme_get_ctrl_attr(c, "state"); + if (state) + free(state); + return c->state; +} + +const char *nvme_ctrl_get_numa_node(nvme_ctrl_t c) +{ + return c->numa_node; +} + +const char *nvme_ctrl_get_queue_count(nvme_ctrl_t c) +{ + return c->queue_count; +} + +const char *nvme_ctrl_get_serial(nvme_ctrl_t c) +{ + return c->serial; +} + +const char *nvme_ctrl_get_sqsize(nvme_ctrl_t c) +{ + return c->sqsize; +} + +const char *nvme_ctrl_get_transport(nvme_ctrl_t c) +{ + return c->transport; +} + +const char *nvme_ctrl_get_traddr(nvme_ctrl_t c) +{ + return c->traddr; +} + +const char *nvme_ctrl_get_trsvcid(nvme_ctrl_t c) +{ + return c->trsvcid; +} + +const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c) +{ + return c->cfg.host_traddr; +} + +const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c) +{ + return c->cfg.host_iface; +} + +struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c) +{ + return &c->cfg; +} + +const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c) +{ + return c->dhchap_key; +} + +void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key) +{ + if (c->dhchap_key) { + free(c->dhchap_key); + c->dhchap_key = NULL; + } + if (key) + c->dhchap_key = strdup(key); +} + +const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c) +{ + return c->dhchap_ctrl_key; +} + +void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key) +{ + if (c->dhchap_ctrl_key) { + free(c->dhchap_ctrl_key); + c->dhchap_ctrl_key = NULL; + } + if (key) + c->dhchap_ctrl_key = strdup(key); +} + +void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered) +{ + c->discovered = discovered; +} + +bool nvme_ctrl_is_discovered(nvme_ctrl_t c) +{ + return c->discovered; +} + +void nvme_ctrl_set_persistent(nvme_ctrl_t c, bool persistent) +{ + c->persistent = persistent; +} + +bool nvme_ctrl_is_persistent(nvme_ctrl_t c) +{ + return c->persistent; +} + +void nvme_ctrl_set_discovery_ctrl(nvme_ctrl_t c, bool discovery) +{ + c->discovery_ctrl = discovery; +} + +bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c) +{ + return c->discovery_ctrl; +} + +void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique) +{ + c->unique_discovery_ctrl = unique; +} + +bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c) +{ + return c->unique_discovery_ctrl; +} + +int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id) +{ + return nvme_identify_ctrl(nvme_ctrl_get_fd(c), id); +} + +nvme_ns_t nvme_ctrl_first_ns(nvme_ctrl_t c) +{ + return list_top(&c->namespaces, struct nvme_ns, entry); +} + +nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n) +{ + return n ? list_next(&c->namespaces, n, entry) : NULL; +} + +nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c) +{ + return list_top(&c->paths, struct nvme_path, entry); +} + +nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p) +{ + return p ? list_next(&c->paths, p, entry) : NULL; +} + +#define FREE_CTRL_ATTR(a) \ + do { if (a) { free(a); (a) = NULL; } } while (0) +void nvme_deconfigure_ctrl(nvme_ctrl_t c) +{ + nvme_ctrl_release_fd(c); + FREE_CTRL_ATTR(c->name); + FREE_CTRL_ATTR(c->sysfs_dir); + FREE_CTRL_ATTR(c->firmware); + FREE_CTRL_ATTR(c->model); + FREE_CTRL_ATTR(c->state); + FREE_CTRL_ATTR(c->numa_node); + FREE_CTRL_ATTR(c->queue_count); + FREE_CTRL_ATTR(c->serial); + FREE_CTRL_ATTR(c->sqsize); + FREE_CTRL_ATTR(c->dhchap_key); + FREE_CTRL_ATTR(c->dhchap_ctrl_key); + FREE_CTRL_ATTR(c->address); + FREE_CTRL_ATTR(c->dctype); + FREE_CTRL_ATTR(c->cntrltype); + FREE_CTRL_ATTR(c->phy_slot); +} + +int nvme_disconnect_ctrl(nvme_ctrl_t c) +{ + nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL; + int ret; + + ret = nvme_set_attr(nvme_ctrl_get_sysfs_dir(c), + "delete_controller", "1"); + if (ret < 0) { + nvme_msg(r, LOG_ERR, "%s: failed to disconnect, error %d\n", + c->name, errno); + return ret; + } + nvme_msg(r, LOG_INFO, "%s: %s disconnected\n", c->name, c->subsysnqn); + nvme_deconfigure_ctrl(c); + return 0; +} + +void nvme_unlink_ctrl(nvme_ctrl_t c) +{ + list_del_init(&c->entry); + c->s = NULL; +} + +static void __nvme_free_ctrl(nvme_ctrl_t c) +{ + struct nvme_path *p, *_p; + struct nvme_ns *n, *_n; + + nvme_unlink_ctrl(c); + + nvme_ctrl_for_each_path_safe(c, p, _p) + nvme_free_path(p); + + nvme_ctrl_for_each_ns_safe(c, n, _n) + __nvme_free_ns(n); + + nvme_deconfigure_ctrl(c); + + FREE_CTRL_ATTR(c->transport); + FREE_CTRL_ATTR(c->subsysnqn); + FREE_CTRL_ATTR(c->traddr); + FREE_CTRL_ATTR(c->cfg.host_traddr); + FREE_CTRL_ATTR(c->cfg.host_iface); + FREE_CTRL_ATTR(c->trsvcid); + free(c); +} + +void nvme_free_ctrl(nvme_ctrl_t c) +{ + __nvme_free_ctrl(c); +} + +static bool traddr_is_hostname(const char *transport, const char *traddr) +{ + char addrstr[NVMF_TRADDR_SIZE]; + + if (!traddr || !transport) + return false; + if (!strcmp(traddr, "none")) + return false; + if (strcmp(transport, "tcp") && + strcmp(transport, "rdma")) + return false; + if (inet_pton(AF_INET, traddr, addrstr) > 0 || + inet_pton(AF_INET6, traddr, addrstr) > 0) + return false; + return true; +} + +struct nvme_ctrl *nvme_create_ctrl(nvme_root_t r, + const char *subsysnqn, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid) +{ + struct nvme_ctrl *c; + + if (!transport) { + nvme_msg(r, LOG_ERR, "No transport specified\n"); + errno = EINVAL; + return NULL; + } + if (strncmp(transport, "loop", 4) && + strncmp(transport, "pcie", 4) && !traddr) { + nvme_msg(r, LOG_ERR, "No transport address for '%s'\n", + transport); + errno = EINVAL; + return NULL; + } + if (!subsysnqn) { + nvme_msg(r, LOG_ERR, "No subsystem NQN specified\n"); + errno = EINVAL; + return NULL; + } + c = calloc(1, sizeof(*c)); + if (!c) { + errno = ENOMEM; + return NULL; + } + c->fd = -1; + nvmf_default_config(&c->cfg); + list_head_init(&c->namespaces); + list_head_init(&c->paths); + list_node_init(&c->entry); + c->transport = strdup(transport); + c->subsysnqn = strdup(subsysnqn); + if (traddr) + c->traddr = strdup(traddr); + if (host_traddr) { + if (traddr_is_hostname(transport, host_traddr)) + c->cfg.host_traddr = hostname2traddr(r, host_traddr); + if (!c->cfg.host_traddr) + c->cfg.host_traddr = strdup(host_traddr); + } + if (host_iface) + c->cfg.host_iface = strdup(host_iface); + if (trsvcid) + c->trsvcid = strdup(trsvcid); + + return c; +} + +/** + * _tcp_ctrl_match_host_traddr_no_src_addr() - Match host_traddr w/o src_addr + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * On kernels prior to 6.1 (i.e. src_addr is not available), try to match + * a candidate controller's host_traddr to that of an existing controller. + * + * This function takes an optimistic approach. In doubt, it will declare a + * match and return true. + * + * Return: true if @c->host_traddr matches @candidate->host_traddr. false otherwise. + */ +static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + if (c->cfg.host_traddr) + return candidate->addreq(candidate->host_traddr, c->cfg.host_traddr); + + /* If c->cfg.host_traddr is NULL, then the controller (c) + * uses the interface's primary address as the source + * address. If c->cfg.host_iface is defined we can + * determine the primary address associated with that + * interface and compare that to the candidate->host_traddr. + */ + if (c->cfg.host_iface) + return nvme_iface_primary_addr_matches(candidate->iface_list, + c->cfg.host_iface, + candidate->host_traddr); + + /* If both c->cfg.host_traddr and c->cfg.host_iface are + * NULL, we don't have enough information to make a + * 100% positive match. Regardless, let's be optimistic + * and assume that we have a match. + */ + nvme_msg(root_from_ctrl(c), LOG_DEBUG, + "Not enough data, but assume %s matches candidate's host_traddr: %s\n", + nvme_ctrl_get_name(c), candidate->host_traddr); + + return true; +} + +/** + * _tcp_ctrl_match_host_iface_no_src_addr() - Match host_iface w/o src_addr + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * On kernels prior to 6.1 (i.e. src_addr is not available), try to match + * a candidate controller's host_iface to that of an existing controller. + * + * This function takes an optimistic approach. In doubt, it will declare a + * match and return true. + * + * Return: true if @c->host_iface matches @candidate->host_iface. false otherwise. + */ +static bool _tcp_ctrl_match_host_iface_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + if (c->cfg.host_iface) + return streq0(candidate->host_iface, c->cfg.host_iface); + + /* If c->cfg.host_traddr is not NULL we can infer the controller's (c) + * interface from it and compare it to the candidate->host_iface. + */ + if (c->cfg.host_traddr) { + const char *c_host_iface; + + c_host_iface = nvme_iface_matching_addr(candidate->iface_list, c->cfg.host_traddr); + return streq0(candidate->host_iface, c_host_iface); + } + + /* If both c->cfg.host_traddr and c->cfg.host_iface are + * NULL, we don't have enough information to make a + * 100% positive match. Regardless, let's be optimistic + * and assume that we have a match. + */ + nvme_msg(root_from_ctrl(c), LOG_DEBUG, + "Not enough data, but assume %s matches candidate's host_iface: %s\n", + nvme_ctrl_get_name(c), candidate->host_iface); + + return true; +} + +/** + * _tcp_opt_params_match_no_src_addr() - Match optional host_traddr/host_iface w/o src_addr + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * Before kernel 6.1, the src_addr was not reported by the kernel which makes + * it hard to match a candidate's host_traddr and host_iface to an existing + * controller if that controller was created without specifying the + * host_traddr and/or host_iface. This function tries its best in the absense + * of a src_addr to match @c to @candidate. This may not be 100% accurate. + * Only the src_addr can provide 100% accuracy. + * + * This function takes an optimistic approach. In doubt, it will declare a + * match and return true. + * + * Return: true if @c matches @candidate. false otherwise. + */ +static bool _tcp_opt_params_match_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + /* Check host_traddr only if candidate is interested */ + if (candidate->host_traddr) { + if (!_tcp_ctrl_match_host_traddr_no_src_addr(c, candidate)) + return false; + } + + /* Check host_iface only if candidate is interested */ + if (candidate->host_iface) { + if (!_tcp_ctrl_match_host_iface_no_src_addr(c, candidate)) + return false; + } + + return true; +} + +/** + * _tcp_opt_params_match() - Match optional host_traddr/host_iface + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * The host_traddr and host_iface are optional for TCP. When they are not + * specified, the kernel looks up the destination IP address (traddr) in the + * routing table to determine the best interface for the connection. The + * kernel then retrieves the primary IP address assigned to that interface + * and uses that as the connection’s source address. + * + * An interface’s primary address is the default source address used for + * all connections made on that interface unless host-traddr is used to + * override the default. Kernel-selected interfaces and/or source addresses + * are hidden from user-space applications unless the kernel makes that + * information available through the "src_addr" attribute in the + * sysfs (kernel 6.1 or later). + * + * Sometimes, an application may force the interface by specifying the + * "host-iface" or may force a different source address (instead of the + * primary address) by providing the "host-traddr". + * + * If the candidate specifies the host_traddr and/or host_iface but they + * do not match the existing controller's host_traddr and/or host_iface + * (they could be NULL), we may still be able to find a match by taking + * the existing controller's src_addr into consideration since that + * parameter identifies the actual source address of the connection and + * therefore can be used to infer the interface of the connection. However, + * the src_addr can only be read from the nvme device's sysfs "address" + * attribute starting with kernel 6.1 (or kernels that backported the + * src_addr patch). + * + * For legacy kernels that do not provide the src_addr we must use a + * different algorithm to match the host_traddr and host_iface, but + * it's not 100% accurate. + * + * Return: true if @c matches @candidate. false otherwise. + */ +static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + char *src_addr, buffer[INET6_ADDRSTRLEN]; + + /* Check if src_addr is available (kernel 6.1 or later) */ + src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer)); + if (!src_addr) + return _tcp_opt_params_match_no_src_addr(c, candidate); + + /* Check host_traddr only if candidate is interested */ + if (candidate->host_traddr && + !candidate->addreq(candidate->host_traddr, src_addr)) + return false; + + /* Check host_iface only if candidate is interested */ + if (candidate->host_iface && + !streq0(candidate->host_iface, + nvme_iface_matching_addr(candidate->iface_list, src_addr))) + return false; + + return true; +} + +/** + * _tcp_match_ctrl() - Check if controller matches candidate (TCP only) + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * We want to determine if an existing controller can be re-used + * for the candidate controller we're trying to instantiate. + * + * For TCP, we do not have a match if the candidate's transport, traddr, + * trsvcid are not identical to those of the the existing controller. + * These 3 parameters are mandatory for a match. + * + * The host_traddr and host_iface are optional. When the candidate does + * not specify them (both NULL), we can ignore them. Otherwise, we must + * employ advanced investigation techniques to determine if there's a match. + * + * Return: true if a match is found, false otherwise. + */ +static bool _tcp_match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + if (!streq0(c->transport, candidate->transport)) + return false; + + if (!streq0(c->trsvcid, candidate->trsvcid)) + return false; + + if (!candidate->addreq(c->traddr, candidate->traddr)) + return false; + + if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c)) + return false; + + if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn)) + return false; + + /* Check host_traddr / host_iface only if candidate is interested */ + if ((candidate->host_iface || candidate->host_traddr) && + !_tcp_opt_params_match(c, candidate)) + return false; + + return true; +} + +/** + * _match_ctrl() - Check if controller matches candidate (non TCP transport) + * @c: An existing controller instance + * @candidate: Candidate ctrl we're trying to match with @c. + * + * We want to determine if an existing controller can be re-used + * for the candidate controller we're trying to instantiate. This function + * is used for all transports except TCP. + * + * Return: true if a match is found, false otherwise. + */ +static bool _match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate) +{ + if (!streq0(c->transport, candidate->transport)) + return false; + + if (candidate->traddr && c->traddr && + !candidate->addreq(c->traddr, candidate->traddr)) + return false; + + if (candidate->host_traddr && c->cfg.host_traddr && + !candidate->addreq(c->cfg.host_traddr, candidate->host_traddr)) + return false; + + if (candidate->host_iface && c->cfg.host_iface && + !streq0(c->cfg.host_iface, candidate->host_iface)) + return false; + + if (candidate->trsvcid && c->trsvcid && + !streq0(c->trsvcid, candidate->trsvcid)) + return false; + + if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c)) + return false; + + if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn)) + return false; + + return true; +} +/** + * _candidate_init() - Init candidate and get the matching function + * + * @candidate: Candidate struct to initialize + * @transport: Transport name + * @traddr: Transport address + * @trsvcid: Transport service identifier + * @subsysnqn: Subsystem NQN + * @host_traddr: Host transport address + * @host_iface: Host interface name + * @host_iface: Host interface name + * + * The function _candidate_free() must be called to release resources once + * the candidate object is not longer required. + * + * Return: The matching function to use when comparing an existing + * controller to the candidate controller. + */ +static ctrl_match_t _candidate_init(struct candidate_args *candidate, + const char *transport, + const char *traddr, + const char *trsvcid, + const char *subsysnqn, + const char *host_traddr, + const char *host_iface) +{ + memset(candidate, 0, sizeof(*candidate)); + + candidate->traddr = traddr; + candidate->trsvcid = trsvcid; + candidate->transport = transport; + candidate->subsysnqn = subsysnqn; + candidate->host_iface = host_iface; + candidate->host_traddr = host_traddr; + + if (streq0(subsysnqn, NVME_DISC_SUBSYS_NAME)) { + /* Since TP8013, the NQN of discovery controllers can be the + * well-known NQN (i.e. nqn.2014-08.org.nvmexpress.discovery) or + * a unique NQN. A DC created using the well-known NQN may later + * display a unique NQN when looked up in the sysfs. Therefore, + * ignore (i.e. set to NULL) the well-known NQN when looking for + * a match. + */ + candidate->subsysnqn = NULL; + candidate->well_known_nqn = true; + } + + if (streq0(transport, "tcp")) { + /* For TCP we may need to access the interface map. + * Let's retrieve and cache the map. + */ + if (getifaddrs(&candidate->iface_list) == -1) + candidate->iface_list = NULL; + + candidate->addreq = nvme_ipaddrs_eq; + return _tcp_match_ctrl; + } + + if (streq0(transport, "rdma")) { + candidate->addreq = nvme_ipaddrs_eq; + return _match_ctrl; + } + + /* All other transport types */ + candidate->addreq = streqcase0; + return _match_ctrl; +} + +/** + * _candidate_free() - Release resources allocated by _candidate_init() + * + * @candidate: data to free. + */ +static void _candidate_free(struct candidate_args *candidate) +{ + freeifaddrs(candidate->iface_list); /* This is NULL-safe */ +} + +#define _cleanup_candidate_ __cleanup__(_candidate_free) + +nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid, + const char *subsysnqn, nvme_ctrl_t p) +{ + struct nvme_ctrl *c, *matching_c = NULL; + _cleanup_candidate_ struct candidate_args candidate; + ctrl_match_t ctrl_match; + + /* Init candidate and get the matching function to use */ + ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid, + subsysnqn, host_traddr, host_iface); + + c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s); + for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) { + if (ctrl_match(c, &candidate)) { + matching_c = c; + break; + } + } + + return matching_c; +} + +bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport, + const char *traddr, const char *trsvcid, + const char *subsysnqn, const char *host_traddr, + const char *host_iface) +{ + ctrl_match_t ctrl_match; + _cleanup_candidate_ struct candidate_args candidate; + + /* Init candidate and get the matching function to use */ + ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid, + subsysnqn, host_traddr, host_iface); + + return ctrl_match(c, &candidate); +} + +nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *trsvcid, + const char *subsysnqn, const char *host_traddr, + const char *host_iface) +{ + return __nvme_lookup_ctrl(s, transport, traddr, host_traddr, host_iface, + trsvcid, subsysnqn, NULL/*p*/); +} + +nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid, + nvme_ctrl_t p) +{ + nvme_root_t r; + struct nvme_ctrl *c; + + if (!s || !transport) + return NULL; + + c = __nvme_lookup_ctrl(s, transport, traddr, host_traddr, + host_iface, trsvcid, NULL, p); + if (c) + return c; + + r = s->h ? s->h->r : NULL; + c = nvme_create_ctrl(r, s->subsysnqn, transport, traddr, + host_traddr, host_iface, trsvcid); + if (c) { + c->s = s; + list_add(&s->ctrls, &c->entry); + s->h->r->modified = true; + } + return c; +} + +static int nvme_ctrl_scan_paths(nvme_root_t r, struct nvme_ctrl *c) +{ + _cleanup_dirents_ struct dirents paths = {}; + int i; + + paths.num = nvme_scan_ctrl_namespace_paths(c, &paths.ents); + if (paths.num < 0) + return paths.num; + + for (i = 0; i < paths.num; i++) + nvme_ctrl_scan_path(r, c, paths.ents[i]->d_name); + + return 0; +} + +static int nvme_ctrl_scan_namespaces(nvme_root_t r, struct nvme_ctrl *c) +{ + _cleanup_dirents_ struct dirents namespaces = {}; + int i; + + namespaces.num = nvme_scan_ctrl_namespaces(c, &namespaces.ents); + for (i = 0; i < namespaces.num; i++) + nvme_ctrl_scan_namespace(r, c, namespaces.ents[i]->d_name); + + return 0; +} + +static char *nvme_ctrl_lookup_subsystem_name(nvme_root_t r, + const char *ctrl_name) +{ + _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir(); + _cleanup_dirents_ struct dirents subsys = {}; + int i; + + subsys.num = nvme_scan_subsystems(&subsys.ents); + if (subsys.num < 0) + return NULL; + for (i = 0; i < subsys.num; i++) { + struct stat st; + _cleanup_free_ char *path = NULL; + + if (asprintf(&path, "%s/%s/%s", subsys_dir, + subsys.ents[i]->d_name, ctrl_name) < 0) { + errno = ENOMEM; + return NULL; + } + nvme_msg(r, LOG_DEBUG, "lookup subsystem %s\n", path); + if (stat(path, &st) < 0) { + continue; + } + return strdup(subsys.ents[i]->d_name); + } + return NULL; +} + +static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address) +{ + _cleanup_free_ char *slots_sysfs_dir = nvme_slots_sysfs_dir(); + _cleanup_free_ char *target_addr = NULL; + _cleanup_dir_ DIR *slots_dir = NULL; + int ret; + struct dirent *entry; + + if (!address) + return NULL; + + slots_dir = opendir(slots_sysfs_dir); + if (!slots_dir) { + nvme_msg(r, LOG_WARNING, "failed to open slots dir %s\n", + slots_sysfs_dir); + return NULL; + } + + target_addr = strndup(address, 10); + while (!(entry = readdir(slots_dir))) { + if (entry->d_type == DT_DIR && + strncmp(entry->d_name, ".", 1) != 0 && + strncmp(entry->d_name, "..", 2) != 0) { + _cleanup_free_ char *path = NULL; + _cleanup_free_ char *addr = NULL; + + ret = asprintf(&path, "%s/%s", + slots_sysfs_dir, entry->d_name); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + addr = nvme_get_attr(path, "address"); + if (strcmp(addr, target_addr) == 0) + return strdup(entry->d_name); + } + } + return NULL; +} + +static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, + const char *name) +{ + DIR *d; + char *host_key; + + d = opendir(path); + if (!d) { + nvme_msg(r, LOG_ERR, "Failed to open ctrl dir %s, error %d\n", + path, errno); + errno = ENODEV; + return -1; + } + closedir(d); + + c->fd = -1; + c->name = strdup(name); + c->sysfs_dir = (char *)path; + c->firmware = nvme_get_ctrl_attr(c, "firmware_rev"); + c->model = nvme_get_ctrl_attr(c, "model"); + c->state = nvme_get_ctrl_attr(c, "state"); + c->numa_node = nvme_get_ctrl_attr(c, "numa_node"); + c->queue_count = nvme_get_ctrl_attr(c, "queue_count"); + c->serial = nvme_get_ctrl_attr(c, "serial"); + c->sqsize = nvme_get_ctrl_attr(c, "sqsize"); + host_key = nvme_get_ctrl_attr(c, "dhchap_secret"); + if (host_key && c->s && c->s->h && c->s->h->dhchap_key && + (!strcmp(c->s->h->dhchap_key, host_key) || + !strcmp("none", host_key))) { + free(host_key); + host_key = NULL; + } + if (host_key) + c->dhchap_key = host_key; + c->dhchap_ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret"); + if (c->dhchap_ctrl_key && !strcmp(c->dhchap_ctrl_key, "none")) { + free(c->dhchap_ctrl_key); + c->dhchap_ctrl_key = NULL; + } + c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype"); + c->dctype = nvme_get_ctrl_attr(c, "dctype"); + c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address); + + errno = 0; /* cleanup after nvme_get_ctrl_attr() */ + return 0; +} + +int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance) +{ + _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir(); + _cleanup_free_ char *subsys_name = NULL; + _cleanup_free_ char *name = NULL; + nvme_subsystem_t s; + char *path; + int ret; + + ret = asprintf(&name, "nvme%d", instance); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + ret = asprintf(&path, "%s/nvme%d", ctrl_dir, instance); + if (ret < 0) { + errno = ENOMEM; + return ret; + } + + ret = nvme_configure_ctrl(h->r, c, path, name); + if (ret < 0) { + free(path); + return ret; + } + + c->address = nvme_get_attr(path, "address"); + if (!c->address && strcmp(c->transport, "loop")) { + errno = ENVME_CONNECT_INVAL_TR; + return -1; + } + + subsys_name = nvme_ctrl_lookup_subsystem_name(h->r, name); + if (!subsys_name) { + nvme_msg(h->r, LOG_ERR, + "Failed to lookup subsystem name for %s\n", + c->name); + errno = ENVME_CONNECT_LOOKUP_SUBSYS_NAME; + return -1; + } + s = nvme_lookup_subsystem(h, subsys_name, c->subsysnqn); + if (!s) { + errno = ENVME_CONNECT_LOOKUP_SUBSYS; + return -1; + } + if (s->subsystype && !strcmp(s->subsystype, "discovery")) + c->discovery_ctrl = true; + c->s = s; + list_add(&s->ctrls, &c->entry); + return ret; +} + +static nvme_ctrl_t nvme_ctrl_alloc(nvme_root_t r, nvme_subsystem_t s, + const char *path, const char *name) +{ + nvme_ctrl_t c, p; + _cleanup_free_ char *addr = NULL, *address = NULL; + char *a, *e; + _cleanup_free_ char *transport = NULL; + char *traddr = NULL, *trsvcid = NULL; + char *host_traddr = NULL, *host_iface = NULL; + int ret; + + transport = nvme_get_attr(path, "transport"); + if (!transport) { + errno = ENXIO; + return NULL; + } + /* Parse 'address' string into components */ + addr = nvme_get_attr(path, "address"); + if (!addr) { + _cleanup_free_ char *rpath = NULL; + char *p = NULL, *_a = NULL; + + /* loop transport might not have an address */ + if (!strcmp(transport, "loop")) + goto skip_address; + + /* Older kernel don't support pcie transport addresses */ + if (strcmp(transport, "pcie")) { + errno = ENXIO; + return NULL; + } + /* Figure out the PCI address from the attribute path */ + rpath = realpath(path, NULL); + if (!rpath) { + errno = ENOMEM; + return NULL; + } + a = strtok_r(rpath, "/", &e); + while(a && strlen(a)) { + if (_a) + p = _a; + _a = a; + if (!strncmp(a, "nvme", 4)) + break; + a = strtok_r(NULL, "/", &e); + } + if (p) + addr = strdup(p); + } else if (!strcmp(transport, "pcie")) { + /* The 'address' string is the transport address */ + traddr = addr; + } else { + address = strdup(addr); + a = strtok_r(address, ",", &e); + while (a && strlen(a)) { + if (!strncmp(a, "traddr=", 7)) + traddr = a + 7; + else if (!strncmp(a, "trsvcid=", 8)) + trsvcid = a + 8; + else if (!strncmp(a, "host_traddr=", 12)) + host_traddr = a + 12; + else if (!strncmp(a, "host_iface=", 11)) + host_iface = a + 11; + a = strtok_r(NULL, ",", &e); + } + } +skip_address: + p = NULL; + do { + c = nvme_lookup_ctrl(s, transport, traddr, + host_traddr, host_iface, trsvcid, p); + if (c) { + if (!c->name) + break; + if (!strcmp(c->name, name)) { + nvme_msg(r, LOG_DEBUG, + "found existing ctrl %s\n", c->name); + break; + } + nvme_msg(r, LOG_DEBUG, "skipping ctrl %s\n", c->name); + p = c; + } + } while (c); + if (!c) + c = p; + if (!c && !p) { + nvme_msg(r, LOG_ERR, "failed to lookup ctrl\n"); + errno = ENODEV; + return NULL; + } + c->address = addr; + addr = NULL; + if (s->subsystype && !strcmp(s->subsystype, "discovery")) + c->discovery_ctrl = true; + ret = nvme_configure_ctrl(r, c, path, name); + return (ret < 0) ? NULL : c; +} + +nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name) +{ + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c; + _cleanup_free_ char *path = NULL; + _cleanup_free_ char *hostnqn = NULL, *hostid = NULL; + _cleanup_free_ char *subsysnqn = NULL, *subsysname = NULL; + _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir(); + int ret; + + nvme_msg(r, LOG_DEBUG, "scan controller %s\n", name); + ret = asprintf(&path, "%s/%s", ctrl_dir, name); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + + hostnqn = nvme_get_attr(path, "hostnqn"); + hostid = nvme_get_attr(path, "hostid"); + h = nvme_lookup_host(r, hostnqn, hostid); + if (h) { + if (h->dhchap_key) + free(h->dhchap_key); + h->dhchap_key = nvme_get_attr(path, "dhchap_secret"); + if (h->dhchap_key && !strcmp(h->dhchap_key, "none")) { + free(h->dhchap_key); + h->dhchap_key = NULL; + } + } + if (!h) { + h = nvme_default_host(r); + if (!h) { + errno = ENOMEM; + return NULL; + } + } + + subsysnqn = nvme_get_attr(path, "subsysnqn"); + if (!subsysnqn) { + errno = ENXIO; + return NULL; + } + subsysname = nvme_ctrl_lookup_subsystem_name(r, name); + if (!subsysname) { + nvme_msg(r, LOG_DEBUG, + "failed to lookup subsystem for controller %s\n", + name); + errno = ENXIO; + return NULL; + } + s = nvme_lookup_subsystem(h, subsysname, subsysnqn); + + if (!s) { + errno = ENOMEM; + return NULL; + } + + c = nvme_ctrl_alloc(r, s, path, name); + if (!c) + return NULL; + + path = NULL; + nvme_ctrl_scan_namespaces(r, c); + nvme_ctrl_scan_paths(r, c); + return c; +} + +void nvme_rescan_ctrl(struct nvme_ctrl *c) +{ + nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL; + if (!c->s) + return; + nvme_ctrl_scan_namespaces(r, c); + nvme_ctrl_scan_paths(r, c); + nvme_subsystem_scan_namespaces(r, c->s, NULL, NULL); +} + +static int nvme_bytes_to_lba(nvme_ns_t n, off_t offset, size_t count, + __u64 *lba, __u16 *nlb) +{ + int bs; + + bs = nvme_ns_get_lba_size(n); + if (!count || offset & (bs - 1) || count & (bs - 1)) { + errno = EINVAL; + return -1; + } + + *lba = offset >> n->lba_shift; + *nlb = (count >> n->lba_shift) - 1; + return 0; +} + +int nvme_ns_get_fd(nvme_ns_t n) +{ + if (n->fd < 0) { + n->fd = nvme_open(n->name); + if (n->fd < 0) + nvme_msg(root_from_ns(n), LOG_ERR, + "Failed to open ns %s, errno %d\n", + n->name, errno); + } + + return n->fd; +} + +void nvme_ns_release_fd(nvme_ns_t n) +{ + if (n->fd < 0) + return; + + close(n->fd); + n->fd = -1; +} + +nvme_subsystem_t nvme_ns_get_subsystem(nvme_ns_t n) +{ + return n->s; +} + +nvme_ctrl_t nvme_ns_get_ctrl(nvme_ns_t n) +{ + return n->c; +} + +int nvme_ns_get_nsid(nvme_ns_t n) +{ + return n->nsid; +} + +const char *nvme_ns_get_sysfs_dir(nvme_ns_t n) +{ + return n->sysfs_dir; +} + +const char *nvme_ns_get_name(nvme_ns_t n) +{ + return n->name; +} + +const char *nvme_ns_get_generic_name(nvme_ns_t n) +{ + return n->generic_name; +} + +const char *nvme_ns_get_model(nvme_ns_t n) +{ + return n->c ? n->c->model : n->s->model; +} + +const char *nvme_ns_get_serial(nvme_ns_t n) +{ + return n->c ? n->c->serial : n->s->serial; +} + +const char *nvme_ns_get_firmware(nvme_ns_t n) +{ + return n->c ? n->c->firmware : n->s->firmware; +} + +int nvme_ns_get_lba_size(nvme_ns_t n) +{ + return n->lba_size; +} + +int nvme_ns_get_meta_size(nvme_ns_t n) +{ + return n->meta_size; +} + +uint64_t nvme_ns_get_lba_count(nvme_ns_t n) +{ + return n->lba_count; +} + +uint64_t nvme_ns_get_lba_util(nvme_ns_t n) +{ + return n->lba_util; +} + +enum nvme_csi nvme_ns_get_csi(nvme_ns_t n) +{ + return n->csi; +} + +const uint8_t *nvme_ns_get_eui64(nvme_ns_t n) +{ + return n->eui64; +} + +const uint8_t *nvme_ns_get_nguid(nvme_ns_t n) +{ + return n->nguid; +} + +void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN]) +{ + memcpy(out, n->uuid, NVME_UUID_LEN); +} + +int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns) +{ + return nvme_identify_ns(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), ns); +} + +int nvme_ns_identify_descs(nvme_ns_t n, struct nvme_ns_id_desc *descs) +{ + return nvme_identify_ns_descs(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), descs); +} + +int nvme_ns_verify(nvme_ns_t n, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = 0, + .data = NULL, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_verify(&args); +} + +int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = 0, + .data = NULL, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_write_uncorrectable(&args); +} + +int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = 0, + .data = NULL, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_write_zeros(&args); +} + +int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = count, + .data = buf, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_write(&args); +} + +int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = count, + .data = buf, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_read(&args); +} + +int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = nvme_ns_get_fd(n), + .nsid = nvme_ns_get_nsid(n), + .control = 0, + .dsm = 0, + .dspec = 0, + .reftag = 0, + .apptag = 0, + .appmask = 0, + .storage_tag = 0, + .data_len = count, + .data = buf, + .metadata_len = 0, + .metadata = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb)) + return -1; + + return nvme_compare(&args); +} + +int nvme_ns_flush(nvme_ns_t n) +{ + return nvme_flush(nvme_ns_get_fd(n), nvme_ns_get_nsid(n)); +} + +static int nvme_strtou64(const char *str, void *res) +{ + char *endptr; + __u64 v; + + errno = 0; + v = strtoull(str, &endptr, 0); + + if (errno != 0) + return -errno; + + if (endptr == str) { + /* no digits found */ + return -EINVAL; + } + + *(__u64 *)res = v; + return 0; +} + +static int nvme_strtou32(const char *str, void *res) +{ + char *endptr; + __u32 v; + + errno = 0; + v = strtol(str, &endptr, 0); + + if (errno != 0) + return -errno; + + if (endptr == str) { + /* no digits found */ + return -EINVAL; + } + + *(__u32 *)res = v; + return 0; +} + +static int nvme_strtoi(const char *str, void *res) +{ + char *endptr; + int v; + + errno = 0; + v = strtol(str, &endptr, 0); + + if (errno != 0) + return -errno; + + if (endptr == str) { + /* no digits found */ + return -EINVAL; + } + + *(int *)res = v; + return 0; +} + +static int nvme_strtoeuid(const char *str, void *res) +{ + memcpy(res, str, 8); + return 0; +} + +static int nvme_strtouuid(const char *str, void *res) +{ + memcpy(res, str, NVME_UUID_LEN); + return 0; +} + +struct sysfs_attr_table { + void *var; + int (*parse)(const char *str, void *res); + bool mandatory; + const char *name; +}; + +#define GETSHIFT(x) (__builtin_ffsll(x) - 1) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size) +{ + char *str; + int ret, i; + + for (i = 0; i < size; i++) { + struct sysfs_attr_table *e = &tbl[i]; + + str = nvme_get_attr(path, e->name); + if (!str) { + if (!e->mandatory) + continue; + return -ENOENT; + } + ret = e->parse(str, e->var); + free(str); + if (ret) + return ret; + } + + return 0; +} + +static int nvme_ns_init(const char *path, struct nvme_ns *ns) +{ + _cleanup_free_ char *attr = NULL; + struct stat sb; + int ret; + + struct sysfs_attr_table base[] = { + { &ns->nsid, nvme_strtou32, true, "nsid" }, + { &ns->lba_count, nvme_strtou64, true, "size" }, + { &ns->lba_size, nvme_strtou64, true, "queue/logical_block_size" }, + { ns->eui64, nvme_strtoeuid, false, "eui" }, + { ns->nguid, nvme_strtouuid, false, "nguid" }, + { ns->uuid, nvme_strtouuid, false, "uuid" } + }; + + ret = parse_attrs(path, base, ARRAY_SIZE(base)); + if (ret) + return ret; + + ns->lba_shift = GETSHIFT(ns->lba_size); + + if (asprintf(&attr, "%s/csi", path) < 0) + return -errno; + ret = stat(attr, &sb); + if (ret == 0) { + /* only available on kernels >= 6.8 */ + struct sysfs_attr_table ext[] = { + { &ns->csi, nvme_strtoi, true, "csi" }, + { &ns->lba_util, nvme_strtou64, true, "nuse" }, + { &ns->meta_size, nvme_strtoi, true, "metadata_bytes"}, + + }; + + ret = parse_attrs(path, ext, ARRAY_SIZE(ext)); + if (ret) + return ret; + } else { + struct nvme_id_ns *id; + uint8_t flbas; + + id = __nvme_alloc(sizeof(*ns)); + if (!id) + return -ENOMEM; + + ret = nvme_ns_identify(ns, id); + if (ret) + return ret; + + nvme_id_ns_flbas_to_lbaf_inuse(id->flbas, &flbas); + ns->lba_count = le64_to_cpu(id->nsze); + ns->lba_util = le64_to_cpu(id->nuse); + ns->meta_size = le16_to_cpu(id->lbaf[flbas].ms); + } + + return 0; +} + +static void nvme_ns_set_generic_name(struct nvme_ns *n, const char *name) +{ + char generic_name[PATH_MAX]; + int instance, head_instance; + int ret; + + ret = sscanf(name, "nvme%dn%d", &instance, &head_instance); + if (ret != 2) + return; + + sprintf(generic_name, "ng%dn%d", instance, head_instance); + n->generic_name = strdup(generic_name); +} + +static nvme_ns_t nvme_ns_open(const char *sys_path, const char *name) +{ + struct nvme_ns *n; + + n = calloc(1, sizeof(*n)); + if (!n) { + errno = ENOMEM; + return NULL; + } + + n->fd = -1; + n->name = strdup(name); + + nvme_ns_set_generic_name(n, name); + + if (nvme_ns_init(sys_path, n) != 0) + goto free_ns; + + list_head_init(&n->paths); + list_node_init(&n->entry); + + nvme_ns_release_fd(n); /* Do not leak fds */ + return n; + +free_ns: + free(n->generic_name); + free(n->name); + free(n); + return NULL; +} + +static inline bool nvme_ns_is_generic(const char *name) +{ + int instance, head_instance; + + if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2) + return false; + return true; +} + +static char *nvme_ns_generic_to_blkdev(const char *generic) +{ + + int instance, head_instance; + char blkdev[PATH_MAX]; + + if (!nvme_ns_is_generic(generic)) + return strdup(generic); + + sscanf(generic, "ng%dn%d", &instance, &head_instance); + sprintf(blkdev, "nvme%dn%d", instance, head_instance); + + return strdup(blkdev); +} + +static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char *name) +{ + struct nvme_ns *n; + _cleanup_free_ char *path = NULL; + int ret; + _cleanup_free_ char *blkdev = NULL; + + blkdev = nvme_ns_generic_to_blkdev(name); + if (!blkdev) { + errno = ENOMEM; + return NULL; + } + + ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + + n = nvme_ns_open(path, blkdev); + if (!n) + return NULL; + + n->sysfs_dir = path; + path = NULL; + + return n; +} + +nvme_ns_t nvme_scan_namespace(const char *name) +{ + _cleanup_free_ char *ns_dir = nvme_ns_sysfs_dir(); + + return __nvme_scan_namespace(ns_dir, name); +} + +static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c, + char *name) +{ + struct nvme_ns *n, *_n, *__n; + + nvme_msg(r, LOG_DEBUG, "scan controller %s namespace %s\n", + c->name, name); + if (!c->s) { + nvme_msg(r, LOG_DEBUG, "no subsystem for %s\n", name); + errno = EINVAL; + return -1; + } + n = __nvme_scan_namespace(c->sysfs_dir, name); + if (!n) { + nvme_msg(r, LOG_DEBUG, "failed to scan namespace %s\n", name); + return -1; + } + nvme_ctrl_for_each_ns_safe(c, _n, __n) { + if (strcmp(n->name, _n->name)) + continue; + __nvme_free_ns(_n); + } + n->s = c->s; + n->c = c; + list_add(&c->namespaces, &n->entry); + return 0; +} + +static void nvme_subsystem_set_ns_path(nvme_subsystem_t s, nvme_ns_t n) +{ + nvme_ctrl_t c; + nvme_path_t p; + int ns_ctrl, ns_nsid, ret; + + ret = sscanf(nvme_ns_get_name(n), "nvme%dn%d", &ns_ctrl, &ns_nsid); + if (ret != 2) + return; + + nvme_subsystem_for_each_ctrl(s, c) { + nvme_ctrl_for_each_path(c, p) { + int p_subsys, p_ctrl, p_nsid; + + ret = sscanf(nvme_path_get_name(p), "nvme%dc%dn%d", + &p_subsys, &p_ctrl, &p_nsid); + if (ret != 3) + continue; + if (ns_ctrl == p_subsys && ns_nsid == p_nsid) { + list_add(&n->paths, &p->nentry); + p->n = n; + } + } + } +} + +static int nvme_subsystem_scan_namespace(nvme_root_t r, nvme_subsystem_t s, + char *name, nvme_scan_filter_t f, void *f_args) +{ + struct nvme_ns *n, *_n, *__n; + + nvme_msg(r, LOG_DEBUG, "scan subsystem %s namespace %s\n", + s->name, name); + n = __nvme_scan_namespace(s->sysfs_dir, name); + if (!n) { + nvme_msg(r, LOG_DEBUG, "failed to scan namespace %s\n", name); + return -1; + } + if (f && !f(NULL, NULL, n, f_args)) { + nvme_msg(r, LOG_DEBUG, "filter out namespace %s\n", name); + __nvme_free_ns(n); + return 0; + } + nvme_subsystem_for_each_ns_safe(s, _n, __n) { + struct nvme_path *p, *_p; + + if (strcmp(n->name, _n->name)) + continue; + /* Detach paths */ + nvme_namespace_for_each_path_safe(_n, p, _p) { + list_del_init(&p->nentry); + p->n = NULL; + } + list_head_init(&_n->paths); + __nvme_free_ns(_n); + } + n->s = s; + list_add(&s->namespaces, &n->entry); + nvme_subsystem_set_ns_path(s, n); + return 0; +} + +struct nvme_ns *nvme_subsystem_lookup_namespace(struct nvme_subsystem *s, + __u32 nsid) +{ + struct nvme_ns *n; + + nvme_subsystem_for_each_ns(s, n) { + if (nvme_ns_get_nsid(n) == nsid) + return n; + } + return NULL; +} diff --git a/src/nvme/tree.h b/src/nvme/tree.h new file mode 100644 index 0000000..a30e8eb --- /dev/null +++ b/src/nvme/tree.h @@ -0,0 +1,1454 @@ +// 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> + */ + +#ifndef _LIBNVME_TREE_H +#define _LIBNVME_TREE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stddef.h> + +#include <sys/types.h> +#include <netinet/in.h> + +#include "ioctl.h" +#include "util.h" + +/** + * DOC: tree.h + * + * libnvme tree object interface + */ + +typedef struct nvme_ns *nvme_ns_t; +typedef struct nvme_path *nvme_path_t; +typedef struct nvme_ctrl *nvme_ctrl_t; +typedef struct nvme_subsystem *nvme_subsystem_t; +typedef struct nvme_host *nvme_host_t; +typedef struct nvme_root *nvme_root_t; + +typedef bool (*nvme_scan_filter_t)(nvme_subsystem_t, nvme_ctrl_t, + nvme_ns_t, void *); + +/** + * nvme_create_root() - Initialize root object + * @fp: File descriptor for logging messages + * @log_level: Logging level to use + * + * Return: Initialized &nvme_root_t object + */ +nvme_root_t nvme_create_root(FILE *fp, int log_level); + +/** + * nvme_root_set_application - Specify managing application + * @r: &nvme_root_t object + * @a: Application string + * + * Sets the managing application string for @r. + */ +void nvme_root_set_application(nvme_root_t r, const char *a); + +/** + * nvme_root_get_application - Get managing application + * @r: &nvme_root_t object + * + * Returns the managing application string for @r or NULL if not set. + */ +const char *nvme_root_get_application(nvme_root_t r); + +/** + * nvme_root_release_fds - Close all opened file descriptors in the tree + * @r: &nvme_root_t object + * + * Controller and Namespace objects cache the file descriptors + * of opened nvme devices. This API can be used to close and + * clear all cached fds in the tree. + * + */ +void nvme_root_release_fds(nvme_root_t r); + +/** + * nvme_free_tree() - Free root object + * @r: &nvme_root_t object + * + * Free an &nvme_root_t object and all attached objects + */ +void nvme_free_tree(nvme_root_t r); + +/** + * nvme_first_host() - Start host iterator + * @r: &nvme_root_t object + * + * Return: First &nvme_host_t object in an iterator + */ +nvme_host_t nvme_first_host(nvme_root_t r); + +/** + * nvme_next_host() - Next host iterator + * @r: &nvme_root_t object + * @h: Previous &nvme_host_t iterator + * + * Return: Next &nvme_host_t object in an iterator + */ +nvme_host_t nvme_next_host(nvme_root_t r, nvme_host_t h); + +/** + * nvme_host_get_root() - Returns nvme_root_t object + * @h: &nvme_host_t object + * + * Return: &nvme_root_t object from @h + */ +nvme_root_t nvme_host_get_root(nvme_host_t h); + +/** + * nvme_lookup_host() - Lookup nvme_host_t object + * @r: &nvme_root_t object + * @hostnqn: Host NQN + * @hostid: Host ID + * + * Lookup a nvme_host_t object based on @hostnqn and @hostid + * or create one if not found. + * + * Return: &nvme_host_t object + */ +nvme_host_t nvme_lookup_host(nvme_root_t r, const char *hostnqn, + const char *hostid); + +/** + * nvme_host_get_dhchap_key() - Return host key + * @h: Host for which the key should be returned + * + * Return: DH-HMAC-CHAP host key or NULL if not set + */ +const char *nvme_host_get_dhchap_key(nvme_host_t h); + +/** + * nvme_host_set_dhchap_key() - set host key + * @h: Host for which the key should be set + * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key + */ +void nvme_host_set_dhchap_key(nvme_host_t h, const char *key); + +/** + * nvme_host_set_pdc_enabled() - Set Persistent Discovery Controller flag + * @h: Host for which the falg should be set + * @enabled: The bool to set the enabled flag + * + * When nvme_host_set_pdc_enabled() is not used to set the PDC flag, + * nvme_host_is_pdc_enabled() will return the default value which was + * passed into the function and not the undefined flag value. + */ +void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled); + +/** + * nvme_host_is_pdc_enabled() - Is Persistenct Discovery Controller enabled + * @h: Host which to check if PDC is enabled + * @fallback: The fallback default value of the flag when + * @nvme_host_set_pdc_enabled has not be used + * to set the flag. + * + * Return: true if PDC is enabled for @h, else false + */ +bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback); + +/** + * nvme_default_host() - Initializes the default host + * @r: &nvme_root_t object + * + * Initializes the default host object based on the values in + * /etc/nvme/hostnqn and /etc/nvme/hostid and attaches it to @r. + * + * Return: &nvme_host_t object + */ +nvme_host_t nvme_default_host(nvme_root_t r); + +/** + * nvme_first_subsystem() - Start subsystem iterator + * @h: &nvme_host_t object + * + * Return: first &nvme_subsystem_t object in an iterator + */ +nvme_subsystem_t nvme_first_subsystem(nvme_host_t h); + +/** + * nvme_next_subsystem() - Next subsystem iterator + * @h: &nvme_host_t object + * @s: Previous &nvme_subsystem_t iterator + * + * Return: next &nvme_subsystem_t object in an iterator + */ +nvme_subsystem_t nvme_next_subsystem(nvme_host_t h, nvme_subsystem_t s); + +/** + * nvme_lookup_subsystem() - Lookup nvme_subsystem_t object + * @h: &nvme_host_t object + * @name: Name of the subsystem (may be NULL) + * @subsysnqn: Subsystem NQN + * + * Lookup a &nvme_subsystem_t object in @h base on @name (if present) + * and @subsysnqn or create one if not found. + * + * Return: nvme_subsystem_t object + */ +nvme_subsystem_t nvme_lookup_subsystem(struct nvme_host *h, + const char *name, + const char *subsysnqn); + +/** + * nvme_free_subsystem() - Free a subsystem + * @s: subsystem + * + * Frees @s and all related objects. + */ +void nvme_free_subsystem(struct nvme_subsystem *s); + +/** + * nvme_subsystem_get_host() - Returns nvme_host_t object + * @s: subsystem + * + * Return: &nvme_host_t object from @s + */ +nvme_host_t nvme_subsystem_get_host(nvme_subsystem_t s); + +/** + * nvme_ctrl_first_ns() - Start namespace iterator + * @c: Controller instance + * + * Return: First &nvme_ns_t object of an @c iterator + */ +nvme_ns_t nvme_ctrl_first_ns(nvme_ctrl_t c); + +/** + * nvme_ctrl_next_ns() - Next namespace iterator + * @c: Controller instance + * @n: Previous nvme_ns_t iterator + * + * Return: Next nvme_ns_t object of an @c iterator + */ +nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n); + +/** + * nvme_ctrl_first_path() - Start path iterator + * @c: Controller instance + * + * Return: First &nvme_path_t object of an @c iterator + */ +nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c); + +/** + * nvme_ctrl_next_path() - Next path iterator + * @c: Controller instance + * @p: Previous &nvme_path_t object of an @c iterator + * + * Return: Next &nvme_path_t object of an @c iterator + */ +nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p); + +/** + * nvme_subsystem_first_ctrl() - First ctrl iterator + * @s: &nvme_subsystem_t object + * + * Return: First controller of an @s iterator + */ +nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s); + +/** + * nvme_subsystem_next_ctrl() - Next ctrl iterator + * @s: &nvme_subsystem_t object + * @c: Previous controller instance of an @s iterator + * + * Return: Next controller of an @s iterator + */ +nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c); + +/** + * nvme_namespace_first_path() - Start path iterator + * @ns: Namespace instance + * + * Return: First &nvme_path_t object of an @ns iterator + */ +nvme_path_t nvme_namespace_first_path(nvme_ns_t ns); + +/** + * nvme_namespace_next_path() - Next path iterator + * @ns: Namespace instance + * @p: Previous &nvme_path_t object of an @ns iterator + * + * Return: Next &nvme_path_t object of an @ns iterator + */ +nvme_path_t nvme_namespace_next_path(nvme_ns_t ns, nvme_path_t p); + +/** + * nvme_lookup_ctrl() - Lookup nvme_ctrl_t object + * @s: &nvme_subsystem_t object + * @transport: Transport name + * @traddr: Transport address + * @host_traddr: Host transport address + * @host_iface: Host interface name + * @trsvcid: Transport service identifier + * @p: Previous controller instance + * + * Lookup a controller in @s based on @transport, @traddr, + * @host_traddr, @host_iface, and @trsvcid. @transport must be specified, + * other fields may be required depending on the transport. A new + * object is created if none is found. If @p is specified the lookup + * will start at @p instead of the first controller. + * + * Return: Controller instance + */ +nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid, + nvme_ctrl_t p); + +/** + * nvme_ctrl_find() - Locate an existing controller + * @s: &nvme_subsystem_t object + * @transport: Transport name + * @traddr: Transport address + * @trsvcid: Transport service identifier + * @subsysnqn: Subsystem NQN + * @host_traddr: Host transport address + * @host_iface: Host interface name + * + * Lookup a controller in @s based on @transport, @traddr, @trsvcid, + * @subsysnqn, @host_traddr, and @host_iface. @transport must be specified, + * other fields may be required depending on the transport. Parameters set + * to NULL will be ignored. + * + * Unlike nvme_lookup_ctrl(), this function does not create a new object if + * an existing controller cannot be found. + * + * Return: Controller instance on success, NULL otherwise. + */ +nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *trsvcid, + const char *subsysnqn, const char *host_traddr, + const char *host_iface); + +/** + * nvme_ctrl_config_match() - Check if ctrl @c matches config params + * @c: An existing controller instance + * @transport: Transport name + * @traddr: Transport address + * @trsvcid: Transport service identifier + * @subsysnqn: Subsystem NQN + * @host_traddr: Host transport address + * @host_iface: Host interface name + * + * Check that controller @c matches parameters: @transport, @traddr, + * @trsvcid, @subsysnqn, @host_traddr, and @host_iface. Parameters set + * to NULL will be ignored. + * + * Return: true if there's a match, false otherwise. + */ +bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport, + const char *traddr, const char *trsvcid, + const char *subsysnqn, const char *host_traddr, + const char *host_iface); + +/** + * nvme_create_ctrl() - Allocate an unconnected NVMe controller + * @r: NVMe root element + * @subsysnqn: Subsystem NQN + * @transport: Transport type + * @traddr: Transport address + * @host_traddr: Host transport address + * @host_iface: Host interface name + * @trsvcid: Transport service ID + * + * Creates an unconnected controller to be used for nvme_add_ctrl(). + * + * Return: Controller instance + */ +nvme_ctrl_t nvme_create_ctrl(nvme_root_t r, + const char *subsysnqn, const char *transport, + const char *traddr, const char *host_traddr, + const char *host_iface, const char *trsvcid); + + +/** + * nvme_subsystem_first_ns() - Start namespace iterator + * @s: &nvme_subsystem_t object + * + * Return: First &nvme_ns_t object of an @s iterator + */ +nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s); + +/** + * nvme_subsystem_next_ns() - Next namespace iterator + * @s: &nvme_subsystem_t object + * @n: Previous &nvme_ns_t iterator + * + * Return: Next &nvme_ns_t object of an @s iterator + */ +nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n); + +/** + * nvme_for_each_host_safe() - Traverse host list + * @r: &nvme_root_t object + * @h: &nvme_host_t object + * @_h: Temporary &nvme_host_t object + */ +#define nvme_for_each_host_safe(r, h, _h) \ + for (h = nvme_first_host(r), \ + _h = nvme_next_host(r, h); \ + h != NULL; \ + h = _h, _h = nvme_next_host(r, h)) + +/** + * nvme_for_each_host() - Traverse host list + * @r: &nvme_root_t object + * @h: &nvme_host_t object + */ +#define nvme_for_each_host(r, h) \ + for (h = nvme_first_host(r); h != NULL; \ + h = nvme_next_host(r, h)) + +/** + * nvme_for_each_subsystem_safe() - Traverse subsystems + * @h: &nvme_host_t object + * @s: &nvme_subsystem_t object + * @_s: Temporary &nvme_subsystem_t object + */ +#define nvme_for_each_subsystem_safe(h, s, _s) \ + for (s = nvme_first_subsystem(h), \ + _s = nvme_next_subsystem(h, s); \ + s != NULL; \ + s = _s, _s = nvme_next_subsystem(h, s)) + +/** + * nvme_for_each_subsystem() - Traverse subsystems + * @h: &nvme_host_t object + * @s: &nvme_subsystem_t object + */ +#define nvme_for_each_subsystem(h, s) \ + for (s = nvme_first_subsystem(h); s != NULL; \ + s = nvme_next_subsystem(h, s)) + +/** + * nvme_subsystem_for_each_ctrl_safe() - Traverse controllers + * @s: &nvme_subsystem_t object + * @c: Controller instance + * @_c: A &nvme_ctrl_t_node to use as temporary storage + */ +#define nvme_subsystem_for_each_ctrl_safe(s, c, _c) \ + for (c = nvme_subsystem_first_ctrl(s), \ + _c = nvme_subsystem_next_ctrl(s, c); \ + c != NULL; \ + c = _c, _c = nvme_subsystem_next_ctrl(s, c)) + +/** + * nvme_subsystem_for_each_ctrl() - Traverse controllers + * @s: &nvme_subsystem_t object + * @c: Controller instance + */ +#define nvme_subsystem_for_each_ctrl(s, c) \ + for (c = nvme_subsystem_first_ctrl(s); c != NULL; \ + c = nvme_subsystem_next_ctrl(s, c)) + +/** + * nvme_ctrl_for_each_ns_safe() - Traverse namespaces + * @c: Controller instance + * @n: &nvme_ns_t object + * @_n: A &nvme_ns_t_node to use as temporary storage + */ +#define nvme_ctrl_for_each_ns_safe(c, n, _n) \ + for (n = nvme_ctrl_first_ns(c), \ + _n = nvme_ctrl_next_ns(c, n); \ + n != NULL; \ + n = _n, _n = nvme_ctrl_next_ns(c, n)) + +/** + * nvme_ctrl_for_each_ns() - Traverse namespaces + * @c: Controller instance + * @n: &nvme_ns_t object + */ +#define nvme_ctrl_for_each_ns(c, n) \ + for (n = nvme_ctrl_first_ns(c); n != NULL; \ + n = nvme_ctrl_next_ns(c, n)) + +/** + * nvme_ctrl_for_each_path_safe() - Traverse paths + * @c: Controller instance + * @p: &nvme_path_t object + * @_p: A &nvme_path_t_node to use as temporary storage + */ +#define nvme_ctrl_for_each_path_safe(c, p, _p) \ + for (p = nvme_ctrl_first_path(c), \ + _p = nvme_ctrl_next_path(c, p); \ + p != NULL; \ + p = _p, _p = nvme_ctrl_next_path(c, p)) + +/** + * nvme_ctrl_for_each_path() - Traverse paths + * @c: Controller instance + * @p: &nvme_path_t object + */ +#define nvme_ctrl_for_each_path(c, p) \ + for (p = nvme_ctrl_first_path(c); p != NULL; \ + p = nvme_ctrl_next_path(c, p)) + +/** + * nvme_subsystem_for_each_ns_safe() - Traverse namespaces + * @s: &nvme_subsystem_t object + * @n: &nvme_ns_t object + * @_n: A &nvme_ns_t_node to use as temporary storage + */ +#define nvme_subsystem_for_each_ns_safe(s, n, _n) \ + for (n = nvme_subsystem_first_ns(s), \ + _n = nvme_subsystem_next_ns(s, n); \ + n != NULL; \ + n = _n, _n = nvme_subsystem_next_ns(s, n)) + +/** + * nvme_subsystem_for_each_ns() - Traverse namespaces + * @s: &nvme_subsystem_t object + * @n: &nvme_ns_t object + */ +#define nvme_subsystem_for_each_ns(s, n) \ + for (n = nvme_subsystem_first_ns(s); n != NULL; \ + n = nvme_subsystem_next_ns(s, n)) + +/** + * nvme_namespace_for_each_path_safe() - Traverse paths + * @n: Namespace instance + * @p: &nvme_path_t object + * @_p: A &nvme_path_t_node to use as temporary storage + */ +#define nvme_namespace_for_each_path_safe(n, p, _p) \ + for (p = nvme_namespace_first_path(n), \ + _p = nvme_namespace_next_path(n, p); \ + p != NULL; \ + p = _p, _p = nvme_namespace_next_path(n, p)) + +/** + * nvme_namespace_for_each_path() - Traverse paths + * @n: Namespace instance + * @p: &nvme_path_t object + */ +#define nvme_namespace_for_each_path(n, p) \ + for (p = nvme_namespace_first_path(n); p != NULL; \ + p = nvme_namespace_next_path(n, p)) + +/** + * nvme_ns_get_fd() - Get associated file descriptor + * @n: Namespace instance + * + * libnvme will open() the file (if not already opened) and keep + * an internal copy of the file descriptor. Following calls to + * this API retrieve the internal cached copy of the file + * descriptor. The file will remain opened and the fd will + * remain cached until the ns object is deleted or + * nvme_ns_release_fd() is called. + * + * Return: File descriptor associated with @n or -1 + */ +int nvme_ns_get_fd(nvme_ns_t n); + +/** + * nvme_ns_release_fd() - Close fd and clear fd from ns object + * @n: Namespace instance + * + */ +void nvme_ns_release_fd(nvme_ns_t n); + +/** + * nvme_ns_get_nsid() - NSID of a namespace + * @n: Namespace instance + * + * Return: NSID of @n + */ +int nvme_ns_get_nsid(nvme_ns_t n); + +/** + * nvme_ns_get_lba_size() - LBA size of a namespace + * @n: Namespace instance + * + * Return: LBA size of @n + */ +int nvme_ns_get_lba_size(nvme_ns_t n); + +/** + * nvme_ns_get_meta_size() - Metadata size of a namespace + * @n: Namespace instance + * + * Return: Metadata size of @n + */ +int nvme_ns_get_meta_size(nvme_ns_t n); + +/** + * nvme_ns_get_lba_count() - LBA count of a namespace + * @n: Namespace instance + * + * Return: LBA count of @n + */ +uint64_t nvme_ns_get_lba_count(nvme_ns_t n); + +/** + * nvme_ns_get_lba_util() - LBA utilization of a namespace + * @n: Namespace instance + * + * Return: LBA utilization of @n + */ +uint64_t nvme_ns_get_lba_util(nvme_ns_t n); + +/** + * nvme_ns_get_csi() - Command set identifier of a namespace + * @n: Namespace instance + * + * Return: The namespace's command set identifier in use + */ +enum nvme_csi nvme_ns_get_csi(nvme_ns_t n); + +/** + * nvme_ns_get_eui64() - 64-bit eui of a namespace + * @n: Namespace instance + * + * Return: A pointer to the 64-bit eui + */ +const uint8_t *nvme_ns_get_eui64(nvme_ns_t n); + +/** + * nvme_ns_get_nguid() - 128-bit nguid of a namespace + * @n: Namespace instance + * + * Return: A pointer to the 128-bit nguid + */ +const uint8_t *nvme_ns_get_nguid(nvme_ns_t n); + +/** + * nvme_ns_get_uuid() - UUID of a namespace + * @n: Namespace instance + * @out: buffer for the UUID + * + * Copies the namespace's uuid into @out + */ +void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN]); + +/** + * nvme_ns_get_sysfs_dir() - sysfs directory of a namespace + * @n: Namespace instance + * + * Return: sysfs directory name of @n + */ +const char *nvme_ns_get_sysfs_dir(nvme_ns_t n); + +/** + * nvme_ns_get_name() - sysfs name of a namespace + * @n: Namespace instance + * + * Return: sysfs name of @n + */ +const char *nvme_ns_get_name(nvme_ns_t n); + +/** + * nvme_ns_get_generic_name() - Returns name of generic namespace chardev. + * @n: Namespace instance + * + * Return: Name of generic namespace chardev + */ +const char *nvme_ns_get_generic_name(nvme_ns_t n); + +/** + * nvme_ns_get_firmware() - Firmware string of a namespace + * @n: Namespace instance + * + * Return: Firmware string of @n + */ +const char *nvme_ns_get_firmware(nvme_ns_t n); + +/** + * nvme_ns_get_serial() - Serial number of a namespace + * @n: Namespace instance + * + * Return: Serial number string of @n + */ +const char *nvme_ns_get_serial(nvme_ns_t n); + +/** + * nvme_ns_get_model() - Model of a namespace + * @n: Namespace instance + * + * Return: Model string of @n + */ +const char *nvme_ns_get_model(nvme_ns_t n); + +/** + * nvme_ns_get_subsystem() - &nvme_subsystem_t of a namespace + * @n: Namespace instance + * + * Return: nvme_subsystem_t object of @n + */ +nvme_subsystem_t nvme_ns_get_subsystem(nvme_ns_t n); + +/** + * nvme_ns_get_ctrl() - &nvme_ctrl_t of a namespace + * @n: Namespace instance + * + * nvme_ctrl_t object may be NULL for a multipathed namespace + * + * Return: nvme_ctrl_t object of @n if present + */ +nvme_ctrl_t nvme_ns_get_ctrl(nvme_ns_t n); + +/** + * nvme_free_ns() - Free a namespace object + * @n: Namespace instance + */ +void nvme_free_ns(struct nvme_ns *n); + +/** + * nvme_ns_read() - Read from a namespace + * @n: Namespace instance + * @buf: Buffer into which the data will be transferred + * @offset: LBA offset of @n + * @count: Number of sectors in @buf + * + * Return: Number of sectors read or -1 on error. + */ +int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_write() - Write to a namespace + * @n: Namespace instance + * @buf: Buffer with data to be written + * @offset: LBA offset of @n + * @count: Number of sectors in @buf + * + * Return: Number of sectors written or -1 on error + */ +int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_verify() - Verify data on a namespace + * @n: Namespace instance + * @offset: LBA offset of @n + * @count: Number of sectors to be verified + * + * Return: Number of sectors verified + */ +int nvme_ns_verify(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_compare() - Compare data on a namespace + * @n: Namespace instance + * @buf: Buffer with data to be compared + * @offset: LBA offset of @n + * @count: Number of sectors in @buf + * + * Return: Number of sectors compared + */ +int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_write_zeros() - Write zeros to a namespace + * @n: Namespace instance + * @offset: LBA offset in @n + * @count: Number of sectors to be written + * + * Return: Number of sectors written + */ +int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_write_uncorrectable() - Issus a 'write uncorrectable' command + * @n: Namespace instance + * @offset: LBA offset in @n + * @count: Number of sectors to be written + * + * Return: Number of sectors written + */ +int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_flush() - Flush data to a namespace + * @n: Namespace instance + * + * Return: 0 on success, -1 on error. + */ +int nvme_ns_flush(nvme_ns_t n); + +/** + * nvme_ns_identify() - Issue an 'identify namespace' command + * @n: Namespace instance + * @ns: &nvme_id_ns buffer + * + * Writes the data returned by the 'identify namespace' command + * into @ns. + * + * Return: 0 on success, -1 on error. + */ +int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns); + +/** + * nvme_ns_identify_descs() - Issue an 'identify descriptors' command + * @n: Namespace instance + * @descs: List of identify descriptors + * + * Writes the data returned by the 'identify descriptors' command + * into @descs. + * + * Return: 0 on success, -1 on error. + */ +int nvme_ns_identify_descs(nvme_ns_t n, struct nvme_ns_id_desc *descs); + +/** + * nvme_path_get_name() - sysfs name of an &nvme_path_t object + * @p: &nvme_path_t object + * + * Return: sysfs name of @p + */ +const char *nvme_path_get_name(nvme_path_t p); + +/** + * nvme_path_get_sysfs_dir() - sysfs directory of an nvme_path_t object + * @p: &nvme_path_t object + * + * Return: sysfs directory of @p + */ +const char *nvme_path_get_sysfs_dir(nvme_path_t p); + +/** + * nvme_path_get_ana_state() - ANA state of an nvme_path_t object + * @p: &nvme_path_t object + * + * Return: ANA (Asynchronous Namespace Access) state of @p + */ +const char *nvme_path_get_ana_state(nvme_path_t p); + +/** + * nvme_path_get_ctrl() - Parent controller of an nvme_path_t object + * @p: &nvme_path_t object + * + * Return: Parent controller if present + */ +nvme_ctrl_t nvme_path_get_ctrl(nvme_path_t p); + +/** + * nvme_path_get_ns() - Parent namespace of an nvme_path_t object + * @p: &nvme_path_t object + * + * Return: Parent namespace if present + */ +nvme_ns_t nvme_path_get_ns(nvme_path_t p); + +/** + * nvme_ctrl_get_fd() - Get associated file descriptor + * @c: Controller instance + * + * libnvme will open() the file (if not already opened) and keep + * an internal copy of the file descriptor. Following calls to + * this API retrieve the internal cached copy of the file + * descriptor. The file will remain opened and the fd will + * remain cached until the controller object is deleted or + * nvme_ctrl_release_fd() is called. + * + * Return: File descriptor associated with @c or -1 + */ +int nvme_ctrl_get_fd(nvme_ctrl_t c); + +/** + * nvme_ctrl_release_fd() - Close fd and clear fd from controller object + * @c: Controller instance + * + */ +void nvme_ctrl_release_fd(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_name() - sysfs name of a controller + * @c: Controller instance + * + * Return: sysfs name of @c + */ +const char *nvme_ctrl_get_name(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_sysfs_dir() - sysfs directory of a controller + * @c: Controller instance + * + * Return: sysfs directory name of @c + */ +const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_address() - Address string of a controller + * @c: Controller instance + * + * Return: NVMe-over-Fabrics address string of @c or empty string + * of no address is present. + */ +const char *nvme_ctrl_get_address(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_src_addr() - Extract src_addr from the c->address string + * @c: Controller instance + * @src_addr: Where to copy the src_addr. Size must be at least INET6_ADDRSTRLEN. + * @src_addr_len: Length of the buffer @src_addr. + * + * Return: Pointer to @src_addr on success. NULL on failure to extract the src_addr. + */ +char *nvme_ctrl_get_src_addr(nvme_ctrl_t c, char *src_addr, size_t src_addr_len); + +/** + * nvme_ctrl_get_phy_slot() - PCI physical slot number of a controller + * @c: Controller instance + * + * Return: PCI physical slot number of @c or empty string if slot + * number is not present. + */ +const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_firmware() - Firmware string of a controller + * @c: Controller instance + * + * Return: Firmware string of @c + */ +const char *nvme_ctrl_get_firmware(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_model() - Model of a controller + * @c: Controller instance + * + * Return: Model string of @c + */ +const char *nvme_ctrl_get_model(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_state() - Running state of a controller + * @c: Controller instance + * + * Return: String indicating the running state of @c + */ +const char *nvme_ctrl_get_state(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_numa_node() - NUMA node of a controller + * @c: Controller instance + * + * Return: String indicating the NUMA node + */ +const char *nvme_ctrl_get_numa_node(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_queue_count() - Queue count of a controller + * @c: Controller instance + * + * Return: Queue count of @c + */ +const char *nvme_ctrl_get_queue_count(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_serial() - Serial number of a controller + * @c: Controller instance + * + * Return: Serial number string of @c + */ +const char *nvme_ctrl_get_serial(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_sqsize() - SQ size of a controller + * @c: Controller instance + * + * Return: SQ size (as string) of @c + */ +const char *nvme_ctrl_get_sqsize(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_transport() - Transport type of a controller + * @c: Controller instance + * + * Return: Transport type of @c + */ +const char *nvme_ctrl_get_transport(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_subsysnqn() - Subsystem NQN of a controller + * @c: Controller instance + * + * Return: Subsystem NQN of @c + */ +const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_subsystem() - Parent subsystem of a controller + * @c: Controller instance + * + * Return: Parent nvme_subsystem_t object + */ +nvme_subsystem_t nvme_ctrl_get_subsystem(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_traddr() - Transport address of a controller + * @c: Controller instance + * + * Return: Transport address of @c + */ +const char *nvme_ctrl_get_traddr(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_trsvcid() - Transport service identifier of a controller + * @c: Controller instance + * + * Return: Transport service identifier of @c (if present) + */ +const char *nvme_ctrl_get_trsvcid(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_host_traddr() - Host transport address of a controller + * @c: Controller instance + * + * Return: Host transport address of @c (if present) + */ +const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_host_iface() - Host interface name of a controller + * @c: Controller instance + * + * Return: Host interface name of @c (if present) + */ +const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_dhchap_host_key() - Return host key + * @c: Controller to be checked + * + * Return: DH-HMAC-CHAP host key or NULL if not set + */ +const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_dhchap_host_key() - Set host key + * @c: Host for which the key should be set + * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key + */ +void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key); + +/** + * nvme_ctrl_get_dhchap_key() - Return controller key + * @c: Controller for which the key should be set + * + * Return: DH-HMAC-CHAP controller key or NULL if not set + */ +const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_dhchap_key() - Set controller key + * @c: Controller for which the key should be set + * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key + */ +void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key); + +/** + * nvme_ctrl_get_config() - Fabrics configuration of a controller + * @c: Controller instance + * + * Return: Fabrics configuration of @c + */ +struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_discovered() - Set the 'discovered' flag + * @c: nvme_ctrl_t object + * @discovered: Value of the 'discovered' flag + * + * Set the 'discovered' flag of @c to @discovered + */ +void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered); + +/** + * nvme_ctrl_is_discovered() - Returns the value of the 'discovered' flag + * @c: Controller instance + * + * Return: Value of the 'discovered' flag of @c + */ +bool nvme_ctrl_is_discovered(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_persistent() - Set the 'persistent' flag + * @c: Controller instance + * @persistent: value of the 'persistent' flag + * + * Set the 'persistent' flag of @c to @persistent + */ +void nvme_ctrl_set_persistent(nvme_ctrl_t c, bool persistent); + +/** + * nvme_ctrl_is_persistent() - Returns the value of the 'persistent' flag + * @c: Controller instance + * + * Return: Value of the 'persistent' flag of @c + */ +bool nvme_ctrl_is_persistent(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_discovery_ctrl() - Set the 'discovery_ctrl' flag + * @c: Controller to be modified + * @discovery: value of the discovery_ctrl flag + * + * Sets the 'discovery_ctrl' flag in @c to specify whether + * @c connects to a discovery subsystem. + * + */ +void nvme_ctrl_set_discovery_ctrl(nvme_ctrl_t c, bool discovery); + +/** + * nvme_ctrl_is_discovery_ctrl() - Check the 'discovery_ctrl' flag + * @c: Controller to be checked + * + * Returns the value of the 'discovery_ctrl' flag which specifies whether + * @c connects to a discovery subsystem. + * + * Return: Value of the 'discover_ctrl' flag + */ +bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_unique_discovery_ctrl() - Set the 'unique_discovery_ctrl' flag + * @c: Controller to be modified + * @unique: value of the unique_disc_ctrl flag + * + * Sets the 'unique_discovery_ctrl' flag in @c to specify wheter + * @c is a unique discovery controller + * + */ +void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique); + +/** + * nvme_ctrl_is_unique_discovery_ctrl() - Check the 'unique_discovery_ctrl' flag + * @c: Controller to be checked + * + * Return: Value of the 'unique_discovery_ctrl' flag + */ +bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c); + +/** + * nvme_ctrl_identify() - Issues an 'identify controller' command + * @c: Controller instance + * @id: Identify controller data structure + * + * Issues an 'identify controller' command to @c and copies the + * data into @id. + * + * Return: 0 on success or -1 on failure. + */ +int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id); + +/** + * nvme_disconnect_ctrl() - Disconnect a controller + * @c: Controller instance + * + * Issues a 'disconnect' fabrics command to @c + * + * Return: 0 on success, -1 on failure. + */ +int nvme_disconnect_ctrl(nvme_ctrl_t c); + +/** + * nvme_scan_ctrl() - Scan on a controller + * @r: nvme_root_t object + * @name: Name of the controller + * + * Scans a controller with sysfs name @name and add it to @r. + * + * Return: nvme_ctrl_t object + */ +nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name); + +/** + * nvme_rescan_ctrl() - Rescan an existing controller + * @c: Controller instance + */ +void nvme_rescan_ctrl(nvme_ctrl_t c); + +/** + * nvme_init_ctrl() - Initialize nvme_ctrl_t object for an existing controller. + * @h: nvme_host_t object + * @c: nvme_ctrl_t object + * @instance: Instance number (e.g. 1 for nvme1) + * + * Return: The ioctl() return code. Typically 0 on success. + */ +int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance); + +/** + * nvme_free_ctrl() - Free controller + * @c: Controller instance + */ +void nvme_free_ctrl(struct nvme_ctrl *c); + +/** + * nvme_unlink_ctrl() - Unlink controller + * @c: Controller instance + */ +void nvme_unlink_ctrl(struct nvme_ctrl *c); + +/** + * nvme_subsystem_get_nqn() - Retrieve NQN from subsystem + * @s: nvme_subsystem_t object + * + * Return: NQN of subsystem + */ +const char *nvme_subsystem_get_nqn(nvme_subsystem_t s); + +/** + * nvme_subsystem_get_sysfs_dir() - sysfs directory of an nvme_subsystem_t object + * @s: nvme_subsystem_t object + * + * Return: sysfs directory name of @s + */ +const char *nvme_subsystem_get_sysfs_dir(nvme_subsystem_t s); + +/** + * nvme_subsystem_get_name() - sysfs name of an nvme_subsystem_t object + * @s: nvme_subsystem_t object + * + * Return: sysfs name of @s + */ +const char *nvme_subsystem_get_name(nvme_subsystem_t s); + +/** + * nvme_subsystem_get_type() - Returns the type of a subsystem + * @s: nvme_subsystem_t object + * + * Returns the subsystem type of @s. + * + * Return: 'nvm' or 'discovery' + */ +const char *nvme_subsystem_get_type(nvme_subsystem_t s); + +/** + * nvme_subsystem_get_application() - Return the application string + * @s: nvme_subsystem_t object + * + * Return: Managing application string or NULL if not set. + */ +const char *nvme_subsystem_get_application(nvme_subsystem_t s); + +/** + * nvme_subsystem_set_application() - Set the application string + * @s: nvme_subsystem_t object + * @a: application string + * + * Sets the managing application string for @s. + */ +void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a); + +/** + * nvme_subsystem_get_iopolicy() - Return the IO policy of subsytem + * @s: nvme_subsystem_t object + * + * Return: IO policy used by current subsystem + */ +const char *nvme_subsystem_get_iopolicy(nvme_subsystem_t s); + +/** + * nvme_scan_topology() - Scan NVMe topology and apply filter + * @r: nvme_root_t object + * @f: filter to apply + * @f_args: user-specified argument to @f + * + * Scans the NVMe topology and filters out the resulting elements + * by applying @f. + * + * Return: Number of elements scanned + */ +int nvme_scan_topology(nvme_root_t r, nvme_scan_filter_t f, void *f_args); + +/** + * nvme_host_get_hostnqn() - Host NQN of an nvme_host_t object + * @h: nvme_host_t object + * + * Return: Host NQN of @h + */ +const char *nvme_host_get_hostnqn(nvme_host_t h); + +/** + * nvme_host_get_hostid() - Host ID of an nvme_host_t object + * @h: nvme_host_t object + * + * Return: Host ID of @h + */ +const char *nvme_host_get_hostid(nvme_host_t h); + +/** + * nvme_host_release_fds() - Close all opened file descriptors under host + * @h: nvme_host_t object + * + * Controller and Namespace objects cache the file descriptors + * of opened nvme devices. This API can be used to close and + * clear all cached fds under this host. + */ +void nvme_host_release_fds(struct nvme_host *h); + +/** + * nvme_free_host() - Free nvme_host_t object + * @h: nvme_host_t object + */ +void nvme_free_host(nvme_host_t h); + +/** + * nvme_scan() - Scan NVMe topology + * @config_file: Configuration file + * + * Return: nvme_root_t object of found elements + */ +nvme_root_t nvme_scan(const char *config_file); + +/** + * nvme_read_config() - Read NVMe JSON configuration file + * @r: nvme_root_t object + * @config_file: JSON configuration file + * + * Read in the contents of @config_file and merge them with + * the elements in @r. + * + * Returns: 0 on success, -1 on failure with errno set. + */ +int nvme_read_config(nvme_root_t r, const char *config_file); + +/** + * nvme_refresh_topology() - Refresh nvme_root_t object contents + * @r: nvme_root_t object + * + * Removes all elements in @r and rescans the existing topology. + */ +void nvme_refresh_topology(nvme_root_t r); + +/** + * nvme_update_config() - Update JSON configuration + * @r: nvme_root_t object + * + * Updates the JSON configuration file with the contents of @r. + * + * Return: 0 on success, -1 on failure. + */ +int nvme_update_config(nvme_root_t r); + +/** + * nvme_dump_config() - Print the JSON configuration + * @r: nvme_root_t object + * + * Prints the current contents of the JSON configuration + * file to stdout. + * + * Return: 0 on success, -1 on failure. + */ +int nvme_dump_config(nvme_root_t r); + +/** + * nvme_dump_tree() - Dump internal object tree + * @r: nvme_root_t object + * + * Prints the internal object tree in JSON format + * to stdout. + * + * Return: 0 on success, -1 on failure. + */ +int nvme_dump_tree(nvme_root_t r); + +/** + * nvme_get_attr() - Read sysfs attribute + * @d: sysfs directory + * @attr: sysfs attribute name + * + * Return: String with the contents of @attr or %NULL in case of an empty value + * or in case of an error (indicated by non-zero errno code). + */ +char *nvme_get_attr(const char *d, const char *attr); + +/** + * nvme_get_subsys_attr() - Read subsystem sysfs attribute + * @s: nvme_subsystem_t object + * @attr: sysfs attribute name + * + * Return: String with the contents of @attr or %NULL in case of an empty value + * or in case of an error (indicated by non-zero errno code). + */ +char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr); + +/** + * nvme_get_ctrl_attr() - Read controller sysfs attribute + * @c: Controller instance + * @attr: sysfs attribute name + * + * Return: String with the contents of @attr or %NULL in case of an empty value + * or in case of an error (indicated by non-zero errno code). + */ +char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr); + +/** + * nvme_get_ns_attr() - Read namespace sysfs attribute + * @n: nvme_ns_t object + * @attr: sysfs attribute name + * + * Return: String with the contents of @attr or %NULL in case of an empty value + * or in case of an error (indicated by non-zero errno code). + */ +char *nvme_get_ns_attr(nvme_ns_t n, const char *attr); + +/** + * nvme_subsystem_lookup_namespace() - lookup namespace by NSID + * @s: nvme_subsystem_t object + * @nsid: Namespace id + * + * Return: nvme_ns_t of the namespace with id @nsid in subsystem @s + */ +nvme_ns_t nvme_subsystem_lookup_namespace(struct nvme_subsystem *s, + __u32 nsid); + +/** + * nvme_subsystem_release_fds() - Close all opened fds under subsystem + * @s: nvme_subsystem_t object + * + * Controller and Namespace objects cache the file descriptors + * of opened nvme devices. This API can be used to close and + * clear all cached fds under this subsystem. + * + */ +void nvme_subsystem_release_fds(struct nvme_subsystem *s); + + +/** + * nvme_get_path_attr() - Read path sysfs attribute + * @p: nvme_path_t object + * @attr: sysfs attribute name + * + * Return: String with the contents of @attr or %NULL in case of an empty value + * or in case of an error (indicated by non-zero errno code). + */ +char *nvme_get_path_attr(nvme_path_t p, const char *attr); + +/** + * nvme_scan_namespace() - scan namespace based on sysfs name + * @name: sysfs name of the namespace to scan + * + * Return: nvme_ns_t object or NULL if not found. + */ +nvme_ns_t nvme_scan_namespace(const char *name); + +/** + * nvme_host_get_hostsymname() - Get the host's symbolic name + * @h: Host for which the symbolic name should be returned. + * + * Return: The symbolic name or NULL if a symbolic name hasn't been + * configure. + */ +const char *nvme_host_get_hostsymname(nvme_host_t h); + +/** + * nvme_host_set_hostsymname() - Set the host's symbolic name + * @h: Host for which the symbolic name should be set. + * @hostsymname: Symbolic name + */ +void nvme_host_set_hostsymname(nvme_host_t h, const char *hostsymname); + +#endif /* _LIBNVME_TREE_H */ diff --git a/src/nvme/types.h b/src/nvme/types.h new file mode 100644 index 0000000..fe79b6e --- /dev/null +++ b/src/nvme/types.h @@ -0,0 +1,7996 @@ +// 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> + */ + +#ifndef _LIBNVME_TYPES_H +#define _LIBNVME_TYPES_H + +#include <stdbool.h> +#include <stdint.h> + +#include <linux/types.h> + +/** + * DOC: types.h + * + * NVMe standard definitions + */ + +/** + * NVME_GET() - extract field from complex value + * @value: The original value of a complex field + * @name: The name of the sub-field within an nvme value + * + * By convention, this library defines _SHIFT and _MASK such that mask can be + * applied after the shift to isolate a specific set of bits that decode to a + * sub-field. + * + * Returns: The 'name' field from 'value' + */ +#define NVME_GET(value, name) \ + (((value) >> NVME_##name##_SHIFT) & NVME_##name##_MASK) + +/** + * NVME_SET() - set field into complex value + * @value: The value to be set in its completed position + * @name: The name of the sub-field within an nvme value + * + * Returns: The 'name' field from 'value' + */ +#define NVME_SET(value, name) \ + (((__u32)(value) & NVME_##name##_MASK) << NVME_##name##_SHIFT) + +/** + * enum nvme_constants - A place to stash various constant nvme values + * @NVME_NSID_ALL: A broadcast value that is used to specify all + * namespaces + * @NVME_NSID_NONE: The invalid namespace id, for when the nsid + * parameter is not used in a command + * @NVME_UUID_NONE: Use to omit a uuid command parameter + * @NVME_CNTLID_NONE: Use to omit a cntlid command parameter + * @NVME_CNSSPECID_NONE: Use to omit a cns_specific_id command parameter + * @NVME_LOG_LSP_NONE: Use to omit a log lsp command parameter + * @NVME_LOG_LSI_NONE: Use to omit a log lsi command parameter + * @NVME_LOG_LPO_NONE: Use to omit a log lpo command parameter + * @NVME_IDENTIFY_DATA_SIZE: The transfer size for nvme identify commands + * @NVME_LOG_SUPPORTED_LOG_PAGES_MAX: The largest possible index in the supported + * log pages log. + * @NVME_ID_NVMSET_LIST_MAX: The largest possible nvmset index in identify + * nvmeset + * @NVME_ID_UUID_LIST_MAX: The largest possible uuid index in identify + * uuid list + * @NVME_ID_CTRL_LIST_MAX: The largest possible controller index in + * identify controller list + * @NVME_ID_NS_LIST_MAX: The largest possible namespace index in + * identify namespace list + * @NVME_ID_SECONDARY_CTRL_MAX: The largest possible secondary controller index + * in identify secondary controller + * @NVME_ID_DOMAIN_LIST_MAX: The largest possible domain index in the + * in domain list + * @NVME_ID_ENDURANCE_GROUP_LIST_MAX: The largest possible endurance group + * index in the endurance group list + * @NVME_ID_ND_DESCRIPTOR_MAX: The largest possible namespace granularity + * index in the namespace granularity descriptor + * list + * @NVME_FEAT_LBA_RANGE_MAX: The largest possible LBA range index in feature + * lba range type + * @NVME_LOG_ST_MAX_RESULTS: The largest possible self test result index in the + * device self test log + * @NVME_LOG_FID_SUPPORTED_EFFECTS_MAX: The largest possible FID index in the + * feature identifiers effects log. + * @NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX: The largest possible MI Command index + * in the MI Command effects log. + * @NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED: The reserved space in the MI Command + * effects log. + * @NVME_LOG_TELEM_BLOCK_SIZE: Specification defined size of Telemetry Data Blocks + * @NVME_DSM_MAX_RANGES: The largest possible range index in a data-set + * management command + * @NVME_NQN_LENGTH: Max length for NVMe Qualified Name + * @NVMF_TRADDR_SIZE: Max Transport Address size + * @NVMF_TSAS_SIZE: Max Transport Specific Address Subtype size + * @NVME_ZNS_CHANGED_ZONES_MAX: Max number of zones in the changed zones log + * page + */ +enum nvme_constants { + NVME_NSID_ALL = 0xffffffff, + NVME_NSID_NONE = 0, + NVME_UUID_NONE = 0, + NVME_CNTLID_NONE = 0, + NVME_CNSSPECID_NONE = 0, + NVME_LOG_LSP_NONE = 0, + NVME_LOG_LSI_NONE = 0, + NVME_LOG_LPO_NONE = 0, + NVME_IDENTIFY_DATA_SIZE = 4096, + NVME_LOG_SUPPORTED_LOG_PAGES_MAX = 256, + NVME_ID_NVMSET_LIST_MAX = 31, + NVME_ID_UUID_LIST_MAX = 127, + NVME_ID_CTRL_LIST_MAX = 2047, + NVME_ID_NS_LIST_MAX = 1024, + NVME_ID_SECONDARY_CTRL_MAX = 127, + NVME_ID_DOMAIN_LIST_MAX = 31, + NVME_ID_ENDURANCE_GROUP_LIST_MAX = 2047, + NVME_ID_ND_DESCRIPTOR_MAX = 16, + NVME_FEAT_LBA_RANGE_MAX = 64, + NVME_LOG_ST_MAX_RESULTS = 20, + NVME_LOG_TELEM_BLOCK_SIZE = 512, + NVME_LOG_FID_SUPPORTED_EFFECTS_MAX = 256, + NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX = 256, + NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED = 768, + NVME_DSM_MAX_RANGES = 256, + NVME_NQN_LENGTH = 256, + NVMF_TRADDR_SIZE = 256, + NVMF_TSAS_SIZE = 256, + NVME_ZNS_CHANGED_ZONES_MAX = 511, +}; + +/** + * enum nvme_csi - Defined command set indicators + * @NVME_CSI_NVM: NVM Command Set Indicator + * @NVME_CSI_KV: Key Value Command Set + * @NVME_CSI_ZNS: Zoned Namespace Command Set + */ +enum nvme_csi { + NVME_CSI_NVM = 0, + NVME_CSI_KV = 1, + NVME_CSI_ZNS = 2, +}; + +/** + * enum nvme_register_offsets - controller registers for all transports. This + * is the layout of BAR0/1 for PCIe, and + * properties for fabrics. + * @NVME_REG_CAP: Controller Capabilities + * @NVME_REG_VS: Version + * @NVME_REG_INTMS: Interrupt Mask Set + * @NVME_REG_INTMC: Interrupt Mask Clear + * @NVME_REG_CC: Controller Configuration + * @NVME_REG_CSTS: Controller Status + * @NVME_REG_NSSR: NVM Subsystem Reset + * @NVME_REG_AQA: Admin Queue Attributes + * @NVME_REG_ASQ: Admin SQ Base Address + * @NVME_REG_ACQ: Admin CQ Base Address + * @NVME_REG_CMBLOC: Controller Memory Buffer Location + * @NVME_REG_CMBSZ: Controller Memory Buffer Size + * @NVME_REG_BPINFO: Boot Partition Information + * @NVME_REG_BPRSEL: Boot Partition Read Select + * @NVME_REG_BPMBL: Boot Partition Memory Buffer Location + * @NVME_REG_CMBMSC: Controller Memory Buffer Memory Space Control + * @NVME_REG_CMBSTS: Controller Memory Buffer Status + * @NVME_REG_CMBEBS: Controller Memory Buffer Elasticity Buffer Size + * @NVME_REG_CMBSWTP: Controller Memory Buffer Sustained Write Throughput + * @NVME_REG_NSSD: NVM Subsystem Shutdown + * @NVME_REG_CRTO: Controller Ready Timeouts + * @NVME_REG_PMRCAP: Persistent Memory Capabilities + * @NVME_REG_PMRCTL: Persistent Memory Region Control + * @NVME_REG_PMRSTS: Persistent Memory Region Status + * @NVME_REG_PMREBS: Persistent Memory Region Elasticity Buffer Size + * @NVME_REG_PMRSWTP: Memory Region Sustained Write Throughput + * @NVME_REG_PMRMSCL: Persistent Memory Region Controller Memory Space Control Lower + * @NVME_REG_PMRMSCU: Persistent Memory Region Controller Memory Space Control Upper + */ +enum nvme_register_offsets { + NVME_REG_CAP = 0x0000, + NVME_REG_VS = 0x0008, + NVME_REG_INTMS = 0x000c, + NVME_REG_INTMC = 0x0010, + NVME_REG_CC = 0x0014, + NVME_REG_CSTS = 0x001c, + NVME_REG_NSSR = 0x0020, + NVME_REG_AQA = 0x0024, + NVME_REG_ASQ = 0x0028, + NVME_REG_ACQ = 0x0030, + NVME_REG_CMBLOC = 0x0038, + NVME_REG_CMBSZ = 0x003c, + NVME_REG_BPINFO = 0x0040, + NVME_REG_BPRSEL = 0x0044, + NVME_REG_BPMBL = 0x0048, + NVME_REG_CMBMSC = 0x0050, + NVME_REG_CMBSTS = 0x0058, + NVME_REG_CMBEBS = 0x005c, + NVME_REG_CMBSWTP = 0x0060, + NVME_REG_NSSD = 0x0064, + NVME_REG_CRTO = 0x0068, + NVME_REG_PMRCAP = 0x0e00, + NVME_REG_PMRCTL = 0x0e04, + NVME_REG_PMRSTS = 0x0e08, + NVME_REG_PMREBS = 0x0e0c, + NVME_REG_PMRSWTP = 0x0e10, + NVME_REG_PMRMSCL = 0x0e14, + NVME_REG_PMRMSCU = 0x0e18, +}; + +/** + * nvme_is_64bit_reg() - Checks if offset of the controller register is a know + * 64bit value. + * @offset: Offset of controller register field in bytes + * + * This function does not care about transport so that the offset is not going + * to be checked inside of this function for the unsupported fields in a + * specific transport. For example, BPMBL(Boot Partition Memory Buffer + * Location) register is not supported by fabrics, but it can be checked here. + * + * Returns: true if given offset is 64bit register, otherwise it returns false. + */ +static inline bool nvme_is_64bit_reg(__u32 offset) +{ + switch (offset) { + case NVME_REG_CAP: + case NVME_REG_ASQ: + case NVME_REG_ACQ: + case NVME_REG_BPMBL: + case NVME_REG_CMBMSC: + return true; + default: + return false; + } +} + +enum nvme_cap { + NVME_CAP_MQES_SHIFT = 0, + NVME_CAP_CQR_SHIFT = 16, + NVME_CAP_AMS_SHIFT = 17, + NVME_CAP_TO_SHIFT = 24, + NVME_CAP_DSTRD_SHIFT = 32, + NVME_CAP_NSSRC_SHIFT = 36, + NVME_CAP_CSS_SHIFT = 37, + NVME_CAP_BPS_SHIFT = 45, + NVME_CAP_MPSMIN_SHIFT = 48, + NVME_CAP_MPSMAX_SHIFT = 52, + NVME_CAP_PMRS_SHIFT = 56, + NVME_CAP_CMBS_SHIFT = 57, + NVME_CAP_CRMS_SHIFT = 59, + NVME_CAP_MQES_MASK = 0xffff, + NVME_CAP_CQR_MASK = 0x1, + NVME_CAP_AMS_MASK = 0x3, + NVME_CAP_TO_MASK = 0xff, + NVME_CAP_DSTRD_MASK = 0xf, + NVME_CAP_NSSRC_MASK = 0x1, + NVME_CAP_CSS_MASK = 0xff, + NVME_CAP_BPS_MASK = 0x1, + NVME_CAP_MPSMIN_MASK = 0xf, + NVME_CAP_MPSMAX_MASK = 0xf, + NVME_CAP_PMRS_MASK = 0x1, + NVME_CAP_CMBS_MASK = 0x1, + NVME_CAP_CRMS_MASK = 0x3, + NVME_CAP_AMS_WRR = 1 << 0, + NVME_CAP_AMS_VS = 1 << 1, + NVME_CAP_CSS_NVM = 1 << 0, + NVME_CAP_CSS_CSI = 1 << 6, + NVME_CAP_CSS_ADMIN = 1 << 7, + NVME_CAP_CRWMS = 1 << 0, + NVME_CAP_CRIMS = 1 << 1, +}; + +#define NVME_CAP_MQES(cap) NVME_GET(cap, CAP_MQES) +#define NVME_CAP_CQR(cap) NVME_GET(cap, CAP_CQR) +#define NVME_CAP_AMS(cap) NVME_GET(cap, CAP_AMS) +#define NVME_CAP_TO(cap) NVME_GET(cap, CAP_TO) +#define NVME_CAP_DSTRD(cap) NVME_GET(cap, CAP_DSTRD) +#define NVME_CAP_NSSRC(cap) NVME_GET(cap, CAP_NSSRC) +#define NVME_CAP_CSS(cap) NVME_GET(cap, CAP_CSS) +#define NVME_CAP_BPS(cap) NVME_GET(cap, CAP_BPS) +#define NVME_CAP_MPSMIN(cap) NVME_GET(cap, CAP_MPSMIN) +#define NVME_CAP_MPSMAX(cap) NVME_GET(cap, CAP_MPSMAX) +#define NVME_CAP_PMRS(cap) NVME_GET(cap, CAP_PMRS) +#define NVME_CAP_CMBS(cap) NVME_GET(cap, CAP_CMBS) +#define NVME_CAP_CRMS(cap) NVME_GET(cap, CAP_CRMS) + +enum nvme_vs { + NVME_VS_TER_SHIFT = 0, + NVME_VS_MNR_SHIFT = 8, + NVME_VS_MJR_SHIFT = 16, + NVME_VS_TER_MASK = 0xff, + NVME_VS_MNR_MASK = 0xff, + NVME_VS_MJR_MASK = 0xffff, +}; + +#define NVME_VS_TER(vs) NVME_GET(vs, VS_TER) +#define NVME_VS_MNR(vs) NVME_GET(vs, VS_MNR) +#define NVME_VS_MJR(vs) NVME_GET(vs, VS_MJR) + +#define NVME_MAJOR(ver) NVME_VS_MJR(ver) +#define NVME_MINOR(ver) NVME_VS_MNR(ver) +#define NVME_TERTIARY(ver) NVME_VS_TER(ver) + +enum nvme_cc { + NVME_CC_EN_SHIFT = 0, + NVME_CC_CSS_SHIFT = 4, + NVME_CC_MPS_SHIFT = 7, + NVME_CC_AMS_SHIFT = 11, + NVME_CC_SHN_SHIFT = 14, + NVME_CC_IOSQES_SHIFT = 16, + NVME_CC_IOCQES_SHIFT = 20, + NVME_CC_CRIME_SHIFT = 24, + NVME_CC_EN_MASK = 0x1, + NVME_CC_CSS_MASK = 0x7, + NVME_CC_MPS_MASK = 0xf, + NVME_CC_AMS_MASK = 0x7, + NVME_CC_SHN_MASK = 0x3, + NVME_CC_CRIME_MASK = 0x1, + NVME_CC_IOSQES_MASK = 0xf, + NVME_CC_IOCQES_MASK = 0xf, + NVME_CC_CSS_NVM = 0, + NVME_CC_CSS_CSI = 6, + NVME_CC_CSS_ADMIN = 7, + NVME_CC_AMS_RR = 0, + NVME_CC_AMS_WRRU = 1, + NVME_CC_AMS_VS = 7, + NVME_CC_SHN_NONE = 0, + NVME_CC_SHN_NORMAL = 1, + NVME_CC_SHN_ABRUPT = 2, + NVME_CC_CRWME = 0, + NVME_CC_CRIME = 1, +}; + +#define NVME_CC_EN(cc) NVME_GET(cc, CC_EN) +#define NVME_CC_CSS(cc) NVME_GET(cc, CC_CSS) +#define NVME_CC_MPS(cc) NVME_GET(cc, CC_MPS) +#define NVME_CC_AMS(cc) NVME_GET(cc, CC_AMS) +#define NVME_CC_SHN(cc) NVME_GET(cc, CC_SHN) +#define NVME_CC_IOSQES(cc) NVME_GET(cc, CC_IOSQES) +#define NVME_CC_IOCQES(cc) NVME_GET(cc, CC_IOCQES) +#define NVME_CC_CRIME(cc) NVME_GET(cc, CC_CRIME) + +enum nvme_csts { + NVME_CSTS_RDY_SHIFT = 0, + NVME_CSTS_CFS_SHIFT = 1, + NVME_CSTS_SHST_SHIFT = 2, + NVME_CSTS_NSSRO_SHIFT = 4, + NVME_CSTS_PP_SHIFT = 5, + NVME_CSTS_RDY_MASK = 0x1, + NVME_CSTS_CFS_MASK = 0x1, + NVME_CSTS_SHN_MASK = 0x3, + NVME_CSTS_NSSRO_MASK = 0x1, + NVME_CSTS_PP_MASK = 0x1, + NVME_CSTS_SHST_NORMAL = 0, + NVME_CSTS_SHST_OCCUR = 1, + NVME_CSTS_SHST_CMPLT = 2, + NVME_CSTS_SHST_MASK = 3, +}; + +#define NVME_CSTS_RDY(csts) NVME_GET(csts, CSTS_RDY) +#define NVME_CSTS_CFS(csts) NVME_GET(csts, CSTS_CFS) +#define NVME_CSTS_SHST(csts) NVME_GET(csts, CSTS_SHST) +#define NVME_CSTS_NSSRO(csts) NVME_GET(csts, CSTS_NSSRO) +#define NVME_CSTS_PP(csts) NVME_GET(csts, CSTS_PP) + +enum nvme_aqa { + NVME_AQA_ASQS_SHIFT = 0, + NVME_AQA_ACQS_SHIFT = 16, + NVME_AQA_ASQS_MASK = 0xfff, + NVME_AQA_ACQS_MASK = 0xfff, +}; + +#define NVME_AQA_ASQS(aqa) NVME_GET(aqa, AQA_ASQS) +#define NVME_AQA_ACQS(aqa) NVME_GET(aqa, AQA_ACQS) + +enum nvme_cmbloc { + NVME_CMBLOC_BIR_SHIFT = 0, + NVME_CMBLOC_CQMMS_SHIFT = 3, + NVME_CMBLOC_CQPDS_SHIFT = 4, + NVME_CMBLOC_CDPLMS_SHIFT = 5, + NVME_CMBLOC_CDPCILS_SHIFT = 6, + NVME_CMBLOC_CDMMMS_SHIFT = 7, + NVME_CMBLOC_CQDA_SHIFT = 8, + NVME_CMBLOC_OFST_SHIFT = 12, + NVME_CMBLOC_BIR_MASK = 0x7, + NVME_CMBLOC_CQMMS_MASK = 0x1, + NVME_CMBLOC_CQPDS_MASK = 0x1, + NVME_CMBLOC_CDPLMS_MASK = 0x1, + NVME_CMBLOC_CDPCILS_MASK = 0x1, + NVME_CMBLOC_CDMMMS_MASK = 0x1, + NVME_CMBLOC_CQDA_MASK = 0x1, + NVME_CMBLOC_OFST_MASK = 0xfffff, +}; + +#define NVME_CMBLOC_BIR(cmbloc) NVME_GET(cmbloc, CMBLOC_BIR) +#define NVME_CMBLOC_CQMMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CQMMS) +#define NVME_CMBLOC_CQPDS(cmbloc) NVME_GET(cmbloc, CMBLOC_CQPDS) +#define NVME_CMBLOC_CDPLMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDPLMS) +#define NVME_CMBLOC_CDPCILS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDPCILS) +#define NVME_CMBLOC_CDMMMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDMMMS) +#define NVME_CMBLOC_CQDA(cmbloc) NVME_GET(cmbloc, CMBLOC_CQDA) +#define NVME_CMBLOC_OFST(cmbloc) NVME_GET(cmbloc, CMBLOC_OFST) + +enum nvme_cmbsz { + NVME_CMBSZ_SQS_SHIFT = 0, + NVME_CMBSZ_CQS_SHIFT = 1, + NVME_CMBSZ_LISTS_SHIFT = 2, + NVME_CMBSZ_RDS_SHIFT = 3, + NVME_CMBSZ_WDS_SHIFT = 4, + NVME_CMBSZ_SZU_SHIFT = 8, + NVME_CMBSZ_SZ_SHIFT = 12, + NVME_CMBSZ_SQS_MASK = 0x1, + NVME_CMBSZ_CQS_MASK = 0x1, + NVME_CMBSZ_LISTS_MASK = 0x1, + NVME_CMBSZ_RDS_MASK = 0x1, + NVME_CMBSZ_WDS_MASK = 0x1, + NVME_CMBSZ_SZU_MASK = 0xf, + NVME_CMBSZ_SZ_MASK = 0xfffff, + NVME_CMBSZ_SZU_4K = 0, + NVME_CMBSZ_SZU_64K = 1, + NVME_CMBSZ_SZU_1M = 2, + NVME_CMBSZ_SZU_16M = 3, + NVME_CMBSZ_SZU_256M = 4, + NVME_CMBSZ_SZU_4G = 5, + NVME_CMBSZ_SZU_64G = 6, +}; + +#define NVME_CMBSZ_SQS(cmbsz) NVME_GET(cmbsz, CMBSZ_SQS) +#define NVME_CMBSZ_CQS(cmbsz) NVME_GET(cmbsz, CMBSZ_CQS) +#define NVME_CMBSZ_LISTS(cmbsz) NVME_GET(cmbsz, CMBSZ_LISTS) +#define NVME_CMBSZ_RDS(cmbsz) NVME_GET(cmbsz, CMBSZ_RDS) +#define NVME_CMBSZ_WDS(cmbsz) NVME_GET(cmbsz, CMBSZ_WDS) +#define NVME_CMBSZ_SZU(cmbsz) NVME_GET(cmbsz, CMBSZ_SZU) +#define NVME_CMBSZ_SZ(cmbsz) NVME_GET(cmbsz, CMBSZ_SZ) + +/** + * nvme_cmb_size() - Calculate size of the controller memory buffer + * @cmbsz: Value from controller register %NVME_REG_CMBSZ + * + * Returns: size of controller memory buffer in bytes + */ +static inline __u64 nvme_cmb_size(__u32 cmbsz) +{ + return ((__u64)NVME_CMBSZ_SZ(cmbsz)) * + (1ULL << (12 + 4 * NVME_CMBSZ_SZU(cmbsz))); +} + +enum nvme_bpinfo { + NVME_BPINFO_BPSZ_SHIFT = 0, + NVME_BPINFO_BRS_SHIFT = 24, + NVME_BPINFO_ABPID_SHIFT = 31, + NVME_BPINFO_BPSZ_MASK = 0x7fff, + NVME_BPINFO_BRS_MASK = 0x3, + NVME_BPINFO_ABPID_MASK = 0x1, + NVME_BPINFO_BRS_NONE = 0, + NVME_BPINFO_BRS_READ_IN_PROGRESS = 1, + NVME_BPINFO_BRS_READ_SUCCESS = 2, + NVME_BPINFO_BRS_READ_ERROR = 3, +}; + +#define NVME_BPINFO_BPSZ(bpinfo) NVME_GET(bpinfo, BPINFO_BPSZ) +#define NVME_BPINFO_BRS(bpinfo) NVME_GET(bpinfo, BPINFO_BRS) +#define NVME_BPINFO_ABPID(bpinfo) NVME_GET(bpinfo, BPINFO_ABPID) + +enum nvme_bprsel { + NVME_BPRSEL_BPRSZ_SHIFT = 0, + NVME_BPRSEL_BPROF_SHIFT = 10, + NVME_BPRSEL_BPID_SHIFT = 31, + NVME_BPRSEL_BPRSZ_MASK = 0x3ff, + NVME_BPRSEL_BPROF_MASK = 0xfff, + NVME_BPRSEL_BPID_MASK = 0x1, +}; + +#define NVME_BPRSEL_BPRSZ(bprsel) NVME_GET(bprsel, BPRSEL_BPRSZ) +#define NVME_BPRSEL_BPROF(bprsel) NVME_GET(bprsel, BPRSEL_BPROF) +#define NVME_BPRSEL_BPID(bprsel) NVME_GET(bprsel, BPRSEL_BPID) + +enum nvme_cmbmsc { + NVME_CMBMSC_CRE_SHIFT = 0, + NVME_CMBMSC_CMSE_SHIFT = 1, + NVME_CMBMSC_CBA_SHIFT = 12, + NVME_CMBMSC_CRE_MASK = 0x1, + NVME_CMBMSC_CMSE_MASK = 0x1, +}; +static const __u64 NVME_CMBMSC_CBA_MASK = 0xfffffffffffffull; + +#define NVME_CMBMSC_CRE(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CRE) +#define NVME_CMBMSC_CMSE(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CMSE) +#define NVME_CMBMSC_CBA(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CBA) + +enum nvme_cmbsts { + NVME_CMBSTS_CBAI_SHIFT = 0, + NVME_CMBSTS_CBAI_MASK = 0x1, +}; + +#define NVME_CMBSTS_CBAI(cmbsts) NVME_GET(cmbsts, CMBSTS_CBAI) + +enum nvme_crto { + NVME_CRTO_CRIMT_SHIFT = 16, + NVME_CRTO_CRIMT_MASK = 0xffff0000, + NVME_CRTO_CRWMT_SHIFT = 0, + NVME_CRTO_CRWMT_MASK = 0x0000ffff, +}; + +#define NVME_CRTO_CRIMT(crto) NVME_GET(crto, CRTO_CRIMT) +#define NVME_CRTO_CRWMT(crto) NVME_GET(crto, CRTO_CRWMT) + +enum nvme_pmrcap { + NVME_PMRCAP_RDS_SHIFT = 3, + NVME_PMRCAP_WDS_SHIFT = 4, + NVME_PMRCAP_BIR_SHIFT = 5, + NVME_PMRCAP_PMRTU_SHIFT = 8, + NVME_PMRCAP_PMRWMB_SHIFT = 10, + NVME_PMRCAP_PMRTO_SHIFT = 16, + NVME_PMRCAP_CMSS_SHIFT = 24, + NVME_PMRCAP_RDS_MASK = 0x1, + NVME_PMRCAP_WDS_MASK = 0x1, + NVME_PMRCAP_BIR_MASK = 0x7, + NVME_PMRCAP_PMRTU_MASK = 0x3, + NVME_PMRCAP_PMRWMB_MASK = 0xf, + NVME_PMRCAP_PMRTO_MASK = 0xff, + NVME_PMRCAP_CMSS_MASK = 0x1, + NVME_PMRCAP_PMRTU_500MS = 0, + NVME_PMRCAP_PMRTU_60S = 1, +}; + +#define NVME_PMRCAP_RDS(pmrcap) NVME_GET(pmrcap, PMRCAP_RDS) +#define NVME_PMRCAP_WDS(pmrcap) NVME_GET(pmrcap, PMRCAP_WDS) +#define NVME_PMRCAP_BIR(pmrcap) NVME_GET(pmrcap, PMRCAP_BIR) +#define NVME_PMRCAP_PMRTU(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTU) +#define NVME_PMRCAP_PMRWMB(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRWMB) +#define NVME_PMRCAP_PMRTO(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTO) +#define NVME_PMRCAP_CMSS(pmrcap) NVME_GET(pmrcap, PMRCAP_CMSS) + +enum nvme_pmrctl { + NVME_PMRCTL_EN_SHIFT = 0, + NVME_PMRCTL_EN_MASK = 0x1, +}; + +#define NVME_PMRCTL_EN(pmrctl) NVME_GET(pmrctl, PMRCTL_EN) + +enum nvme_pmrsts { + NVME_PMRSTS_ERR_SHIFT = 0, + NVME_PMRSTS_NRDY_SHIFT = 8, + NVME_PMRSTS_HSTS_SHIFT = 9, + NVME_PMRSTS_CBAI_SHIFT = 12, + NVME_PMRSTS_ERR_MASK = 0xff, + NVME_PMRSTS_NRDY_MASK = 0x1, + NVME_PMRSTS_HSTS_MASK = 0x7, + NVME_PMRSTS_CBAI_MASK = 0x1, +}; + +#define NVME_PMRSTS_ERR(pmrsts) NVME_GET(pmrsts, PMRSTS_ERR) +#define NVME_PMRSTS_NRDY(pmrsts) NVME_GET(pmrsts, PMRSTS_NRDY) +#define NVME_PMRSTS_HSTS(pmrsts) NVME_GET(pmrsts, PMRSTS_HSTS) +#define NVME_PMRSTS_CBAI(pmrsts) NVME_GET(pmrsts, PMRSTS_CBAI) + +enum nvme_pmrebs { + NVME_PMREBS_PMRSZU_SHIFT = 0, + NVME_PMREBS_RBB_SHIFT = 4, + NVME_PMREBS_PMRWBZ_SHIFT = 8, + NVME_PMREBS_PMRSZU_MASK = 0xf, + NVME_PMREBS_RBB_MASK = 0x1, + NVME_PMREBS_PMRWBZ_MASK = 0xffffff, + NVME_PMREBS_PMRSZU_B = 0, + NVME_PMREBS_PMRSZU_1K = 1, + NVME_PMREBS_PMRSZU_1M = 2, + NVME_PMREBS_PMRSZU_1G = 3, +}; + +#define NVME_PMREBS_PMRSZU(pmrebs) NVME_GET(pmrebs, PMREBS_PMRSZU) +#define NVME_PMREBS_RBB(pmrebs) NVME_GET(pmrebs, PMREBS_RBB) +#define NVME_PMREBS_PMRWBZ(pmrebs) NVME_GET(pmrebs, PMREBS_PMRWBZ) + +/** + * nvme_pmr_size() - Calculate size of persistent memory region elasticity + * buffer + * @pmrebs: Value from controller register %NVME_REG_PMREBS + * + * Returns: size of controller persistent memory buffer in bytes + */ +static inline __u64 nvme_pmr_size(__u32 pmrebs) +{ + return ((__u64)NVME_PMREBS_PMRWBZ(pmrebs)) * + (1ULL << (10 * NVME_PMREBS_PMRSZU(pmrebs))); +} + +enum nvme_pmrswtp { + NVME_PMRSWTP_PMRSWTU_SHIFT = 0, + NVME_PMRSWTP_PMRSWTV_SHIFT = 8, + NVME_PMRSWTP_PMRSWTU_MASK = 0xf, + NVME_PMRSWTP_PMRSWTV_MASK = 0xffffff, + NVME_PMRSWTP_PMRSWTU_BPS = 0, + NVME_PMRSWTP_PMRSWTU_KBPS = 1, + NVME_PMRSWTP_PMRSWTU_MBPS = 2, + NVME_PMRSWTP_PMRSWTU_GBPS = 3, +}; + +#define NVME_PMRSWTP_PMRSWTU(pmrswtp) NVME_GET(pmrswtp, PMRSWTP_PMRSWTU) +#define NVME_PMRSWTP_PMRSWTV(pmrswtp) NVME_GET(pmrswtp, PMRSWTP_PMRSWTU) + +/** + * nvme_pmr_throughput() - Calculate throughput of persistent memory buffer + * @pmrswtp: Value from controller register %NVME_REG_PMRSWTP + * + * Returns: throughput of controller persistent memory buffer in bytes/second + */ +static inline __u64 nvme_pmr_throughput(__u32 pmrswtp) +{ + return ((__u64)NVME_PMRSWTP_PMRSWTV(pmrswtp)) * + (1ULL << (10 * NVME_PMRSWTP_PMRSWTU(pmrswtp))); +} + +enum nvme_pmrmsc { + NVME_PMRMSC_CMSE_SHIFT = 1, + NVME_PMRMSC_CBA_SHIFT = 12, + NVME_PMRMSC_CMSE_MASK = 0x1, +}; +static const __u64 NVME_PMRMSC_CBA_MASK = 0xfffffffffffffull; + +#define NVME_PMRMSC_CMSE(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CMSE) +#define NVME_PMRMSC_CBA(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CBA) + +enum nvme_flbas { + NVME_FLBAS_LOWER_SHIFT = 0, + NVME_FLBAS_META_EXT_SHIFT = 4, + NVME_FLBAS_HIGHER_SHIFT = 5, + NVME_FLBAS_LOWER_MASK = 0xf, + NVME_FLBAS_META_EXT_MASK = 0x1, + NVME_FLBAS_HIGHER_MASK = 0x3, +}; + +#define NVME_FLBAS_LOWER(flbas) NVME_GET(flbas, FLBAS_LOWER) +#define NVME_FLBAS_META_EXT(flbas) NVME_GET(flbas, FLBAS_META_EXT) +#define NVME_FLBAS_HIGHER(flbas) NVME_GET(flbas, FLBAS_HIGHER) + +/** + * enum nvme_psd_flags - Possible flag values in nvme power state descriptor + * @NVME_PSD_FLAGS_MXPS: Indicates the scale for the Maximum Power + * field. If this bit is cleared, then the scale of the + * Maximum Power field is in 0.01 Watts. If this bit is + * set, then the scale of the Maximum Power field is in + * 0.0001 Watts. + * @NVME_PSD_FLAGS_NOPS: Indicates whether the controller processes I/O + * commands in this power state. If this bit is cleared, + * then the controller processes I/O commands in this + * power state. If this bit is set, then the controller + * does not process I/O commands in this power state. + */ +enum nvme_psd_flags { + NVME_PSD_FLAGS_MXPS = 1 << 0, + NVME_PSD_FLAGS_NOPS = 1 << 1, +}; + +/** + * enum nvme_psd_ps - Known values for &struct nvme_psd %ips and %aps. Use with + * nvme_psd_power_scale() to extract the power scale field + * to match this enum. + * @NVME_PSD_PS_NOT_REPORTED: Not reported + * @NVME_PSD_PS_100_MICRO_WATT: 0.0001 watt scale + * @NVME_PSD_PS_10_MILLI_WATT: 0.01 watt scale + */ +enum nvme_psd_ps { + NVME_PSD_PS_NOT_REPORTED = 0, + NVME_PSD_PS_100_MICRO_WATT = 1, + NVME_PSD_PS_10_MILLI_WATT = 2, +}; + +/** + * nvme_psd_power_scale() - power scale occupies the upper 3 bits + * @ps: power scale value + * + * Returns: power scale value + */ +static inline unsigned int nvme_psd_power_scale(__u8 ps) +{ + return ps >> 6; +} + +/** + * enum nvme_psd_workload - Specifies a workload hint in the Power Management + * Feature (see &struct nvme_psd.apw) to inform the + * NVM subsystem or indicate the conditions for the + * active power level. + * @NVME_PSD_WORKLOAD_NP: The workload is unknown or not provided. + * @NVME_PSD_WORKLOAD_1: Extended Idle Period with a Burst of Random Write + * consists of five minutes of idle followed by + * thirty-two random write commands of size 1 MiB + * submitted to a single controller while all other + * controllers in the NVM subsystem are idle, and then + * thirty (30) seconds of idle. + * @NVME_PSD_WORKLOAD_2: Heavy Sequential Writes consists of 80,000 + * sequential write commands of size 128 KiB submitted to + * a single controller while all other controllers in the + * NVM subsystem are idle. The submission queue(s) + * should be sufficiently large allowing the host to + * ensure there are multiple commands pending at all + * times during the workload. + */ +enum nvme_psd_workload { + NVME_PSD_WORKLOAD_NP = 0, + NVME_PSD_WORKLOAD_1 = 1, + NVME_PSD_WORKLOAD_2 = 2, +}; + +/** + * struct nvme_id_psd - Power Management data structure + * @mp: Maximum Power indicates the sustained maximum power consumed by the + * NVM subsystem in this power state. The power in Watts is equal to + * the value in this field multiplied by the scale specified in the Max + * Power Scale bit (see &enum nvme_psd_flags). A value of 0 indicates + * Maximum Power is not reported. + * @rsvd2: Reserved + * @flags: Additional decoding flags, see &enum nvme_psd_flags. + * @enlat: Entry Latency indicates the maximum latency in microseconds + * associated with entering this power state. A value of 0 indicates + * Entry Latency is not reported. + * @exlat: Exit Latency indicates the maximum latency in microseconds + * associated with exiting this power state. A value of 0 indicates + * Exit Latency is not reported. + * @rrt: Relative Read Throughput indicates the read throughput rank + * associated with this power state relative to others. The value in + * this is less than the number of supported power states. + * @rrl: Relative Read Latency indicates the read latency rank associated + * with this power state relative to others. The value in this field is + * less than the number of supported power states. + * @rwt: Relative Write Throughput indicates write throughput rank associated + * with this power state relative to others. The value in this field is + * less than the number of supported power states + * @rwl: Relative Write Latency indicates the write latency rank associated + * with this power state relative to others. The value in this field is + * less than the number of supported power states + * @idlp: Idle Power indicates the typical power consumed by the NVM + * subsystem over 30 seconds in this power state when idle. + * @ips: Idle Power Scale indicates the scale for &struct nvme_id_psd.idlp, + * see &enum nvme_psd_ps for decoding this field. + * @rsvd19: Reserved + * @actp: Active Power indicates the largest average power consumed by the + * NVM subsystem over a 10 second period in this power state with + * the workload indicated in the Active Power Workload field. + * @apws: Bits 7-6: Active Power Scale(APS) indicates the scale for the &struct + * nvme_id_psd.actp, see &enum nvme_psd_ps for decoding this value. + * Bits 2-0: Active Power Workload(APW) indicates the workload + * used to calculate maximum power for this power state. + * See &enum nvme_psd_workload for decoding this field. + * @rsvd23: Reserved + */ +struct nvme_id_psd { + __le16 mp; + __u8 rsvd2; + __u8 flags; + __le32 enlat; + __le32 exlat; + __u8 rrt; + __u8 rrl; + __u8 rwt; + __u8 rwl; + __le16 idlp; + __u8 ips; + __u8 rsvd19; + __le16 actp; + __u8 apws; + __u8 rsvd23[9]; +}; + +/** + * struct nvme_id_ctrl - Identify Controller data structure + * @vid: PCI Vendor ID, the company vendor identifier that is assigned by + * the PCI SIG. + * @ssvid: PCI Subsystem Vendor ID, the company vendor identifier that is + * assigned by the PCI SIG for the subsystem. + * @sn: Serial Number in ASCII + * @mn: Model Number in ASCII + * @fr: Firmware Revision in ASCII, the currently active firmware + * revision for the NVM subsystem + * @rab: Recommended Arbitration Burst, reported as a power of two + * @ieee: IEEE assigned Organization Unique Identifier + * @cmic: Controller Multipath IO and Namespace Sharing Capabilities of + * the controller and NVM subsystem. See &enum nvme_id_ctrl_cmic. + * @mdts: Max Data Transfer Size is the largest data transfer size. The + * host should not submit a command that exceeds this maximum data + * transfer size. The value is in units of the minimum memory page + * size (CAP.MPSMIN) and is reported as a power of two + * @cntlid: Controller ID, the NVM subsystem unique controller identifier + * associated with the controller. + * @ver: Version, this field contains the value reported in the Version + * register, or property (see &enum nvme_registers %NVME_REG_VS). + * @rtd3r: RTD3 Resume Latency, the expected latency in microseconds to resume + * from Runtime D3 + * @rtd3e: RTD3 Exit Latency, the typical latency in microseconds to enter + * Runtime D3. + * @oaes: Optional Async Events Supported, see @enum nvme_id_ctrl_oaes. + * @ctratt: Controller Attributes, see @enum nvme_id_ctrl_ctratt. + * @rrls: Read Recovery Levels. If a bit is set, then the corresponding + * Read Recovery Level is supported. If a bit is cleared, then the + * corresponding Read Recovery Level is not supported. + * @rsvd102: Reserved + * @cntrltype: Controller Type, see &enum nvme_id_ctrl_cntrltype + * @fguid: FRU GUID, a 128-bit value that is globally unique for a given + * Field Replaceable Unit + * @crdt1: Controller Retry Delay time in 100 millisecond units if CQE CRD + * field is 1 + * @crdt2: Controller Retry Delay time in 100 millisecond units if CQE CRD + * field is 2 + * @crdt3: Controller Retry Delay time in 100 millisecond units if CQE CRD + * field is 3 + * @rsvd134: Reserved + * @nvmsr: NVM Subsystem Report, see &enum nvme_id_ctrl_nvmsr + * @vwci: VPD Write Cycle Information, see &enum nvme_id_ctrl_vwci + * @mec: Management Endpoint Capabilities, see &enum nvme_id_ctrl_mec + * @oacs: Optional Admin Command Support,the optional Admin commands and + * features supported by the controller, see &enum nvme_id_ctrl_oacs. + * @acl: Abort Command Limit, the maximum number of concurrently + * executing Abort commands supported by the controller. This is a + * 0's based value. + * @aerl: Async Event Request Limit, the maximum number of concurrently + * outstanding Asynchronous Event Request commands supported by the + * controller This is a 0's based value. + * @frmw: Firmware Updates indicates capabilities regarding firmware + * updates. See &enum nvme_id_ctrl_frmw. + * @lpa: Log Page Attributes, see &enum nvme_id_ctrl_lpa. + * @elpe: Error Log Page Entries, the maximum number of Error Information + * log entries that are stored by the controller. This field is a + * 0's based value. + * @npss: Number of Power States Supported, the number of NVM Express + * power states supported by the controller, indicating the number + * of valid entries in &struct nvme_id_ctrl.psd. This is a 0's + * based value. + * @avscc: Admin Vendor Specific Command Configuration, see + * &enum nvme_id_ctrl_avscc. + * @apsta: Autonomous Power State Transition Attributes, see + * &enum nvme_id_ctrl_apsta. + * @wctemp: Warning Composite Temperature Threshold indicates + * the minimum Composite Temperature field value (see &struct + * nvme_smart_log.critical_comp_time) that indicates an overheating + * condition during which controller operation continues. + * @cctemp: Critical Composite Temperature Threshold, field indicates the + * minimum Composite Temperature field value (see &struct + * nvme_smart_log.critical_comp_time) that indicates a critical + * overheating condition. + * @mtfa: Maximum Time for Firmware Activation indicates the maximum time + * the controller temporarily stops processing commands to activate + * the firmware image, specified in 100 millisecond units. This + * field is always valid if the controller supports firmware + * activation without a reset. + * @hmpre: Host Memory Buffer Preferred Size indicates the preferred size + * that the host is requested to allocate for the Host Memory + * Buffer feature in 4 KiB units. + * @hmmin: Host Memory Buffer Minimum Size indicates the minimum size that + * the host is requested to allocate for the Host Memory Buffer + * feature in 4 KiB units. + * @tnvmcap: Total NVM Capacity, the total NVM capacity in the NVM subsystem. + * The value is in bytes. + * @unvmcap: Unallocated NVM Capacity, the unallocated NVM capacity in the + * NVM subsystem. The value is in bytes. + * @rpmbs: Replay Protected Memory Block Support, see + * &enum nvme_id_ctrl_rpmbs. + * @edstt: Extended Device Self-test Time, if Device Self-test command is + * supported (see &struct nvme_id_ctrl.oacs, %NVME_CTRL_OACS_SELF_TEST), + * then this field indicates the nominal amount of time in one + * minute units that the controller takes to complete an extended + * device self-test operation when in power state 0. + * @dsto: Device Self-test Options, see &enum nvme_id_ctrl_dsto. + * @fwug: Firmware Update Granularity indicates the granularity and + * alignment requirement of the firmware image being updated by the + * Firmware Image Download command. The value is reported in 4 KiB + * units. A value of 0h indicates no information on granularity is + * provided. A value of FFh indicates no restriction + * @kas: Keep Alive Support indicates the granularity of the Keep Alive + * Timer in 100 millisecond units. + * @hctma: Host Controlled Thermal Management Attributes, see + * &enum nvme_id_ctrl_hctm. + * @mntmt: Minimum Thermal Management Temperature indicates the minimum + * temperature, in degrees Kelvin, that the host may request in the + * Thermal Management Temperature 1 field and Thermal Management + * Temperature 2 field of a Set Features command with the Feature + * Identifier field set to %NVME_FEAT_FID_HCTM. + * @mxtmt: Maximum Thermal Management Temperature indicates the maximum + * temperature, in degrees Kelvin, that the host may request in the + * Thermal Management Temperature 1 field and Thermal Management + * Temperature 2 field of the Set Features command with the Feature + * Identifier set to %NVME_FEAT_FID_HCTM. + * @sanicap: Sanitize Capabilities, see &enum nvme_id_ctrl_sanicap + * @hmminds: Host Memory Buffer Minimum Descriptor Entry Size indicates the + * minimum usable size of a Host Memory Buffer Descriptor Entry in + * 4 KiB units. + * @hmmaxd: Host Memory Maximum Descriptors Entries indicates the number of + * usable Host Memory Buffer Descriptor Entries. + * @nsetidmax: NVM Set Identifier Maximum, defines the maximum value of a valid + * NVM Set Identifier for any controller in the NVM subsystem. + * @endgidmax: Endurance Group Identifier Maximum, defines the maximum value of + * a valid Endurance Group Identifier for any controller in the NVM + * subsystem. + * @anatt: ANA Transition Time indicates the maximum amount of time, in + * seconds, for a transition between ANA states or the maximum + * amount of time, in seconds, that the controller reports the ANA + * change state. + * @anacap: Asymmetric Namespace Access Capabilities, see + * &enum nvme_id_ctrl_anacap. + * @anagrpmax: ANA Group Identifier Maximum indicates the maximum value of a + * valid ANA Group Identifier for any controller in the NVM + * subsystem. + * @nanagrpid: Number of ANA Group Identifiers indicates the number of ANA + * groups supported by this controller. + * @pels: Persistent Event Log Size indicates the maximum reportable size + * for the Persistent Event Log. + * @domainid: Domain Identifier indicates the identifier of the domain + * that contains this controller. + * @rsvd358: Reserved + * @megcap: Max Endurance Group Capacity indicates the maximum capacity + * of a single Endurance Group. + * @rsvd384: Reserved + * @sqes: Submission Queue Entry Size, see &enum nvme_id_ctrl_sqes. + * @cqes: Completion Queue Entry Size, see &enum nvme_id_ctrl_cqes. + * @maxcmd: Maximum Outstanding Commands indicates the maximum number of + * commands that the controller processes at one time for a + * particular queue. + * @nn: Number of Namespaces indicates the maximum value of a valid + * nsid for the NVM subsystem. If the MNAN (&struct nvme_id_ctrl.mnan + * field is cleared to 0h, then this field also indicates the + * maximum number of namespaces supported by the NVM subsystem. + * @oncs: Optional NVM Command Support, see &enum nvme_id_ctrl_oncs. + * @fuses: Fused Operation Support, see &enum nvme_id_ctrl_fuses. + * @fna: Format NVM Attributes, see &enum nvme_id_ctrl_fna. + * @vwc: Volatile Write Cache, see &enum nvme_id_ctrl_vwc. + * @awun: Atomic Write Unit Normal indicates the size of the write + * operation guaranteed to be written atomically to the NVM across + * all namespaces with any supported namespace format during normal + * operation. This field is specified in logical blocks and is a + * 0's based value. + * @awupf: Atomic Write Unit Power Fail indicates the size of the write + * operation guaranteed to be written atomically to the NVM across + * all namespaces with any supported namespace format during a + * power fail or error condition. This field is specified in + * logical blocks and is a 0’s based value. + * @icsvscc: NVM Vendor Specific Command Configuration, see + * &enum nvme_id_ctrl_nvscc. + * @nwpc: Namespace Write Protection Capabilities, see + * &enum nvme_id_ctrl_nwpc. + * @acwu: Atomic Compare & Write Unit indicates the size of the write + * operation guaranteed to be written atomically to the NVM across + * all namespaces with any supported namespace format for a Compare + * and Write fused operation. This field is specified in logical + * blocks and is a 0’s based value. + * @ocfs: Optional Copy Formats Supported, each bit n means controller + * supports Copy Format n. + * @sgls: SGL Support, see &enum nvme_id_ctrl_sgls + * @mnan: Maximum Number of Allowed Namespaces indicates the maximum + * number of namespaces supported by the NVM subsystem. + * @maxdna: Maximum Domain Namespace Attachments indicates the maximum + * of the sum of the number of namespaces attached to each I/O + * controller in the Domain. + * @maxcna: Maximum I/O Controller Namespace Attachments indicates the + * maximum number of namespaces that are allowed to be attached to + * this I/O controller. + * @oaqd: Optimal Aggregated Queue Depth indicates the recommended maximum + * total number of outstanding I/O commands across all I/O queues + * on the controller for optimal operation. + * @rsvd568: Reserved + * @subnqn: NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string + * @rsvd1024: Reserved + * @ioccsz: I/O Queue Command Capsule Supported Size, defines the maximum + * I/O command capsule size in 16 byte units. + * @iorcsz: I/O Queue Response Capsule Supported Size, defines the maximum + * I/O response capsule size in 16 byte units. + * @icdoff: In Capsule Data Offset, defines the offset where data starts + * within a capsule. This value is applicable to I/O Queues only. + * @fcatt: Fabrics Controller Attributes, see &enum nvme_id_ctrl_fcatt. + * @msdbd: Maximum SGL Data Block Descriptors indicates the maximum + * number of SGL Data Block or Keyed SGL Data Block descriptors + * that a host is allowed to place in a capsule. A value of 0h + * indicates no limit. + * @ofcs: Optional Fabric Commands Support, see &enum nvme_id_ctrl_ofcs. + * @dctype: Discovery Controller Type (DCTYPE). This field indicates what + * type of Discovery controller the controller is (see enum + * nvme_id_ctrl_dctype) + * @rsvd1807: Reserved + * @psd: Power State Descriptors, see &struct nvme_id_psd. + * @vs: Vendor Specific + */ +struct nvme_id_ctrl { + __le16 vid; + __le16 ssvid; + char sn[20]; + char mn[40]; + char fr[8]; + __u8 rab; + __u8 ieee[3]; + __u8 cmic; + __u8 mdts; + __le16 cntlid; + __le32 ver; + __le32 rtd3r; + __le32 rtd3e; + __le32 oaes; + __le32 ctratt; + __le16 rrls; + __u8 rsvd102[9]; + __u8 cntrltype; + __u8 fguid[16]; + __le16 crdt1; + __le16 crdt2; + __le16 crdt3; + __u8 rsvd134[119]; + __u8 nvmsr; + __u8 vwci; + __u8 mec; + __le16 oacs; + __u8 acl; + __u8 aerl; + __u8 frmw; + __u8 lpa; + __u8 elpe; + __u8 npss; + __u8 avscc; + __u8 apsta; + __le16 wctemp; + __le16 cctemp; + __le16 mtfa; + __le32 hmpre; + __le32 hmmin; + __u8 tnvmcap[16]; + __u8 unvmcap[16]; + __le32 rpmbs; + __le16 edstt; + __u8 dsto; + __u8 fwug; + __le16 kas; + __le16 hctma; + __le16 mntmt; + __le16 mxtmt; + __le32 sanicap; + __le32 hmminds; + __le16 hmmaxd; + __le16 nsetidmax; + __le16 endgidmax; + __u8 anatt; + __u8 anacap; + __le32 anagrpmax; + __le32 nanagrpid; + __le32 pels; + __le16 domainid; + __u8 rsvd358[10]; + __u8 megcap[16]; + __u8 rsvd384[128]; + __u8 sqes; + __u8 cqes; + __le16 maxcmd; + __le32 nn; + __le16 oncs; + __le16 fuses; + __u8 fna; + __u8 vwc; + __le16 awun; + __le16 awupf; + __u8 icsvscc; + __u8 nwpc; + __le16 acwu; + __le16 ocfs; + __le32 sgls; + __le32 mnan; + __u8 maxdna[16]; + __le32 maxcna; + __le32 oaqd; + __u8 rsvd568[200]; + char subnqn[NVME_NQN_LENGTH]; + __u8 rsvd1024[768]; + + /* Fabrics Only */ + __le32 ioccsz; + __le32 iorcsz; + __le16 icdoff; + __u8 fcatt; + __u8 msdbd; + __le16 ofcs; + __u8 dctype; + __u8 rsvd1807[241]; + + struct nvme_id_psd psd[32]; + __u8 vs[1024]; +}; + +/** + * enum nvme_id_ctrl_cmic - Controller Multipath IO and Namespace Sharing + * Capabilities of the controller and NVM subsystem. + * @NVME_CTRL_CMIC_MULTI_PORT: If set, then the NVM subsystem may contain + * more than one NVM subsystem port, otherwise + * the NVM subsystem contains only a single + * NVM subsystem port. + * @NVME_CTRL_CMIC_MULTI_CTRL: If set, then the NVM subsystem may contain + * two or more controllers, otherwise the + * NVM subsystem contains only a single + * controller. An NVM subsystem that contains + * multiple controllers may be used by + * multiple hosts, or may provide multiple + * paths for a single host. + * @NVME_CTRL_CMIC_MULTI_SRIOV: If set, then the controller is associated + * with an SR-IOV Virtual Function, otherwise + * it is associated with a PCI Function + * or a Fabrics connection. + * @NVME_CTRL_CMIC_MULTI_ANA_REPORTING: If set, then the NVM subsystem supports + * Asymmetric Namespace Access Reporting. + */ +enum nvme_id_ctrl_cmic { + NVME_CTRL_CMIC_MULTI_PORT = 1 << 0, + NVME_CTRL_CMIC_MULTI_CTRL = 1 << 1, + NVME_CTRL_CMIC_MULTI_SRIOV = 1 << 2, + NVME_CTRL_CMIC_MULTI_ANA_REPORTING = 1 << 3, +}; + +/** + * enum nvme_id_ctrl_oaes - Optional Asynchronous Events Supported + * @NVME_CTRL_OAES_NA: Namespace Attribute Notices event supported + * @NVME_CTRL_OAES_FA: Firmware Activation Notices event supported + * @NVME_CTRL_OAES_ANA: ANA Change Notices supported + * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log + * Change Notices event supported + * @NVME_CTRL_OAES_LBAS: LBA Status Information Notices event supported + * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change + * Notices event supported + * @NVME_CTRL_OAES_NS: Normal NVM Subsystem Shutdown event supported + * @NVME_CTRL_OAES_ZD: Zone Descriptor Change Notifications supported + * @NVME_CTRL_OAES_DL: Discover Log Page Change Notifications supported + */ +enum nvme_id_ctrl_oaes { + NVME_CTRL_OAES_NA = 1 << 8, + NVME_CTRL_OAES_FA = 1 << 9, + NVME_CTRL_OAES_ANA = 1 << 11, + NVME_CTRL_OAES_PLEA = 1 << 12, + NVME_CTRL_OAES_LBAS = 1 << 13, + NVME_CTRL_OAES_EGE = 1 << 14, + NVME_CTRL_OAES_NS = 1 << 15, + NVME_CTRL_OAES_ZD = 1 << 27, + NVME_CTRL_OAES_DL = 1 << 31, +}; + +/** + * enum nvme_id_ctrl_ctratt - Controller attributes + * @NVME_CTRL_CTRATT_128_ID: 128-bit Host Identifier supported + * @NVME_CTRL_CTRATT_NON_OP_PSP: Non-Operational Poser State Permissive Mode + * supported + * @NVME_CTRL_CTRATT_NVM_SETS: NVM Sets supported + * @NVME_CTRL_CTRATT_READ_RECV_LVLS: Read Recovery Levels supported + * @NVME_CTRL_CTRATT_ENDURANCE_GROUPS: Endurance Groups supported + * @NVME_CTRL_CTRATT_PREDICTABLE_LAT: Predictable Latency Mode supported + * @NVME_CTRL_CTRATT_TBKAS: Traffic Based Keep Alive Support + * @NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY: Namespace Granularity reporting + * supported + * @NVME_CTRL_CTRATT_SQ_ASSOCIATIONS: SQ Associations supported + * @NVME_CTRL_CTRATT_UUID_LIST: UUID List reporting supported + * @NVME_CTRL_CTRATT_MDS: Multi-Domain Subsystem supported + * @NVME_CTRL_CTRATT_FIXED_CAP: Fixed Capacity Management supported + * @NVME_CTRL_CTRATT_VARIABLE_CAP: Variable Capacity Management supported + * @NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS: Delete Endurance Groups supported + * @NVME_CTRL_CTRATT_DEL_NVM_SETS: Delete NVM Sets supported + * @NVME_CTRL_CTRATT_ELBAS: Extended LBA Formats supported + * @NVME_CTRL_CTRATT_FDPS: Flexible Data Placement supported + */ +enum nvme_id_ctrl_ctratt { + NVME_CTRL_CTRATT_128_ID = 1 << 0, + NVME_CTRL_CTRATT_NON_OP_PSP = 1 << 1, + NVME_CTRL_CTRATT_NVM_SETS = 1 << 2, + NVME_CTRL_CTRATT_READ_RECV_LVLS = 1 << 3, + NVME_CTRL_CTRATT_ENDURANCE_GROUPS = 1 << 4, + NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5, + NVME_CTRL_CTRATT_TBKAS = 1 << 6, + NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 1 << 7, + NVME_CTRL_CTRATT_SQ_ASSOCIATIONS = 1 << 8, + NVME_CTRL_CTRATT_UUID_LIST = 1 << 9, + NVME_CTRL_CTRATT_MDS = 1 << 10, + NVME_CTRL_CTRATT_FIXED_CAP = 1 << 11, + NVME_CTRL_CTRATT_VARIABLE_CAP = 1 << 12, + NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS = 1 << 13, + NVME_CTRL_CTRATT_DEL_NVM_SETS = 1 << 14, + NVME_CTRL_CTRATT_ELBAS = 1 << 15, + NVME_CTRL_CTRATT_FDPS = 1 << 19, +}; + +/** + * enum nvme_id_ctrl_cntrltype - Controller types + * @NVME_CTRL_CNTRLTYPE_IO: NVM I/O controller + * @NVME_CTRL_CNTRLTYPE_DISCOVERY: Discovery controller + * @NVME_CTRL_CNTRLTYPE_ADMIN: Admin controller + */ +enum nvme_id_ctrl_cntrltype { + NVME_CTRL_CNTRLTYPE_IO = 1, + NVME_CTRL_CNTRLTYPE_DISCOVERY = 2, + NVME_CTRL_CNTRLTYPE_ADMIN = 3, +}; + +/** + * enum nvme_id_ctrl_dctype - Discovery Controller types + * @NVME_CTRL_DCTYPE_NOT_REPORTED: Not reported (I/O, Admin, and pre-TP8010) + * @NVME_CTRL_DCTYPE_DDC: Direct Discovery controller + * @NVME_CTRL_DCTYPE_CDC: Central Discovery controller + */ +enum nvme_id_ctrl_dctype { + NVME_CTRL_DCTYPE_NOT_REPORTED = 0, + NVME_CTRL_DCTYPE_DDC = 1, + NVME_CTRL_DCTYPE_CDC = 2, +}; + +/** + * enum nvme_id_ctrl_nvmsr - This field reports information associated with the + * NVM Subsystem, see &struct nvme_id_ctrl.nvmsr. + * @NVME_CTRL_NVMSR_NVMESD: If set, then the NVM Subsystem is part of an NVMe + * Storage Device; if cleared, then the NVM Subsystem + * is not part of an NVMe Storage Device. + * @NVME_CTRL_NVMSR_NVMEE: If set’, then the NVM Subsystem is part of an NVMe + * Enclosure; if cleared, then the NVM Subsystem is + * not part of an NVMe Enclosure. + */ +enum nvme_id_ctrl_nvmsr { + NVME_CTRL_NVMSR_NVMESD = 1 << 0, + NVME_CTRL_NVMSR_NVMEE = 1 << 1, +}; + +/** + * enum nvme_id_ctrl_vwci - This field indicates information about remaining + * number of times that VPD contents are able to be + * updated using the VPD Write command, see &struct + * nvme_id_ctrl.vwci. + * @NVME_CTRL_VWCI_VWCR: Mask to get value of VPD Write Cycles Remaining. If + * the VPD Write Cycle Remaining Valid bit is set, then + * this field contains a value indicating the remaining + * number of times that VPD contents are able to be + * updated using the VPD Write command. If this field is + * set to 7Fh, then the remaining number of times that + * VPD contents are able to be updated using the VPD + * Write command is greater than or equal to 7Fh. + * @NVME_CTRL_VWCI_VWCRV: VPD Write Cycle Remaining Valid. If this bit is set, + * then the VPD Write Cycle Remaining field is valid. If + * this bit is cleared, then the VPD Write Cycles + * Remaining field is invalid and cleared to 0h. + */ +enum nvme_id_ctrl_vwci { + NVME_CTRL_VWCI_VWCR = 0x7f << 0, + NVME_CTRL_VWCI_VWCRV = 1 << 7, +}; + +/** + * enum nvme_id_ctrl_mec - Flags indicating the capabilities of the Management + * Endpoint in the Controller, &struct nvme_id_ctrl.mec. + * @NVME_CTRL_MEC_SMBUSME: If set, then the NVM Subsystem contains a Management + * Endpoint on an SMBus/I2C port. + * @NVME_CTRL_MEC_PCIEME: If set, then the NVM Subsystem contains a Management + * Endpoint on a PCIe port. + */ +enum nvme_id_ctrl_mec { + NVME_CTRL_MEC_SMBUSME = 1 << 0, + NVME_CTRL_MEC_PCIEME = 1 << 1, +}; + +/** + * enum nvme_id_ctrl_oacs - Flags indicating the optional Admin commands and + * features supported by the controller, see + * &struct nvme_id_ctrl.oacs. + * @NVME_CTRL_OACS_SECURITY: If set, then the controller supports the + * Security Send and Security Receive commands. + * @NVME_CTRL_OACS_FORMAT: If set then the controller supports the Format + * NVM command. + * @NVME_CTRL_OACS_FW: If set, then the controller supports the + * Firmware Commit and Firmware Image Download commands. + * @NVME_CTRL_OACS_NS_MGMT: If set, then the controller supports the + * Namespace Management capability + * @NVME_CTRL_OACS_SELF_TEST: If set, then the controller supports the Device + * Self-test command. + * @NVME_CTRL_OACS_DIRECTIVES: If set, then the controller supports Directives + * and the Directive Send and Directive Receive + * commands. + * @NVME_CTRL_OACS_NVME_MI: If set, then the controller supports the NVMe-MI + * Send and NVMe-MI Receive commands. + * @NVME_CTRL_OACS_VIRT_MGMT: If set, then the controller supports the + * Virtualization Management command. + * @NVME_CTRL_OACS_DBBUF_CFG: If set, then the controller supports the + * Doorbell Buffer Config command. + * @NVME_CTRL_OACS_LBA_STATUS: If set, then the controller supports the Get LBA + * Status capability. + * @NVME_CTRL_OACS_CMD_FEAT_LD: If set, then the controller supports the command + * and feature lockdown capability. + */ +enum nvme_id_ctrl_oacs { + NVME_CTRL_OACS_SECURITY = 1 << 0, + NVME_CTRL_OACS_FORMAT = 1 << 1, + NVME_CTRL_OACS_FW = 1 << 2, + NVME_CTRL_OACS_NS_MGMT = 1 << 3, + NVME_CTRL_OACS_SELF_TEST = 1 << 4, + NVME_CTRL_OACS_DIRECTIVES = 1 << 5, + NVME_CTRL_OACS_NVME_MI = 1 << 6, + NVME_CTRL_OACS_VIRT_MGMT = 1 << 7, + NVME_CTRL_OACS_DBBUF_CFG = 1 << 8, + NVME_CTRL_OACS_LBA_STATUS = 1 << 9, + NVME_CTRL_OACS_CMD_FEAT_LD = 1 << 10, +}; + +/** + * enum nvme_id_ctrl_frmw - Flags and values indicates capabilities regarding + * firmware updates from &struct nvme_id_ctrl.frmw. + * @NVME_CTRL_FRMW_1ST_RO: If set, the first firmware slot is readonly + * @NVME_CTRL_FRMW_NR_SLOTS: Mask to get the value of the number of + * firmware slots that the controller supports. + * @NVME_CTRL_FRMW_FW_ACT_NO_RESET: If set, the controller supports firmware + * activation without a reset. + * @NVME_CTRL_FRMW_MP_UP_DETECTION: If set, the controller is able to detect + * overlapping firmware/boot partition + * image update. + */ +enum nvme_id_ctrl_frmw { + NVME_CTRL_FRMW_1ST_RO = 1 << 0, + NVME_CTRL_FRMW_NR_SLOTS = 3 << 1, + NVME_CTRL_FRMW_FW_ACT_NO_RESET = 1 << 4, + NVME_CTRL_FRMW_MP_UP_DETECTION = 1 << 5, +}; + +/** + * enum nvme_id_ctrl_lpa - Flags indicating optional attributes for log pages + * that are accessed via the Get Log Page command. + * @NVME_CTRL_LPA_SMART_PER_NS: If set, controller supports SMART/Health log + * page on a per namespace basis. + * @NVME_CTRL_LPA_CMD_EFFECTS: If Set, the controller supports the commands + * supported and effects log page. + * @NVME_CTRL_LPA_EXTENDED: If set, the controller supports extended data + * for log page command including extended number + * of dwords and log page offset fields. + * @NVME_CTRL_LPA_TELEMETRY: If set, the controller supports the telemetry + * host-initiated and telemetry controller-initiated + * log pages and sending telemetry log notices. + * @NVME_CTRL_LPA_PERSETENT_EVENT: If set, the controller supports + * persistent event log. + * @NVME_CTRL_LPA_LI0_LI5_LI12_LI13: If set, the controller supports + * - log pages log page. + * - returning scope of each command in + * commands supported and effects log + * page. + * - feature identifiers supported and + * effects log page. + * - NVMe-MI commands supported and + * effects log page. + * @NVME_CTRL_LPA_DA4_TELEMETRY: If set, the controller supports data + * area 4 for telemetry host-initiated and + * telemetry. + */ +enum nvme_id_ctrl_lpa { + NVME_CTRL_LPA_SMART_PER_NS = 1 << 0, + NVME_CTRL_LPA_CMD_EFFECTS = 1 << 1, + NVME_CTRL_LPA_EXTENDED = 1 << 2, + NVME_CTRL_LPA_TELEMETRY = 1 << 3, + NVME_CTRL_LPA_PERSETENT_EVENT = 1 << 4, + NVME_CTRL_LPA_LI0_LI5_LI12_LI13 = 1 << 5, + NVME_CTRL_LPA_DA4_TELEMETRY = 1 << 6, +}; + +/** + * enum nvme_id_ctrl_avscc - Flags indicating the configuration settings for + * Admin Vendor Specific command handling. + * @NVME_CTRL_AVSCC_AVS: If set, all Admin Vendor Specific Commands use the + * optional vendor specific command format with NDT and + * NDM fields. + */ +enum nvme_id_ctrl_avscc { + NVME_CTRL_AVSCC_AVS = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_apsta - Flags indicating the attributes of the autonomous + * power state transition feature. + * @NVME_CTRL_APSTA_APST: If set, then the controller supports autonomous power + * state transitions. + */ +enum nvme_id_ctrl_apsta { + NVME_CTRL_APSTA_APST = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_rpmbs - This field indicates if the controller supports + * one or more Replay Protected Memory Blocks, from + * &struct nvme_id_ctrl.rpmbs. + * @NVME_CTRL_RPMBS_NR_UNITS: Mask to get the value of the Number of RPMB Units + * @NVME_CTRL_RPMBS_AUTH_METHOD: Mask to get the value of the Authentication Method + * @NVME_CTRL_RPMBS_TOTAL_SIZE: Mask to get the value of Total Size + * @NVME_CTRL_RPMBS_ACCESS_SIZE: Mask to get the value of Access Size + */ +enum nvme_id_ctrl_rpmbs { + NVME_CTRL_RPMBS_NR_UNITS = 7 << 0, + NVME_CTRL_RPMBS_AUTH_METHOD = 7 << 3, + NVME_CTRL_RPMBS_TOTAL_SIZE = 0xff << 16, + NVME_CTRL_RPMBS_ACCESS_SIZE = 0xff << 24, +}; + +/** + * enum nvme_id_ctrl_dsto - Flags indicating the optional Device Self-test + * command or operation behaviors supported by the + * controller or NVM subsystem. + * @NVME_CTRL_DSTO_ONE_DST: If set, then the NVM subsystem supports only one + * device self-test operation in progress at a time. + */ +enum nvme_id_ctrl_dsto { + NVME_CTRL_DSTO_ONE_DST = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_hctm - Flags indicate the attributes of the host + * controlled thermal management feature + * @NVME_CTRL_HCTMA_HCTM: then the controller supports host controlled thermal + * management, and the Set Features command and Get + * Features command with the Feature Identifier field + * set to %NVME_FEAT_FID_HCTM. + */ +enum nvme_id_ctrl_hctm { + NVME_CTRL_HCTMA_HCTM = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_sanicap - Indicates attributes for sanitize operations. + * @NVME_CTRL_SANICAP_CES: Crypto Erase Support. If set, then the + * controller supports the Crypto Erase sanitize operation. + * @NVME_CTRL_SANICAP_BES: Block Erase Support. If set, then the controller + * supports the Block Erase sanitize operation. + * @NVME_CTRL_SANICAP_OWS: Overwrite Support. If set, then the controller + * supports the Overwrite sanitize operation. + * @NVME_CTRL_SANICAP_NDI: No-Deallocate Inhibited. If set and the No- + * Deallocate Response Mode bit is set, then the + * controller deallocates after the sanitize + * operation even if the No-Deallocate After + * Sanitize bit is set in a Sanitize command. + * @NVME_CTRL_SANICAP_NODMMAS: No-Deallocate Modifies Media After Sanitize, + * mask to extract value. + */ +enum nvme_id_ctrl_sanicap { + NVME_CTRL_SANICAP_CES = 1 << 0, + NVME_CTRL_SANICAP_BES = 1 << 1, + NVME_CTRL_SANICAP_OWS = 1 << 2, + NVME_CTRL_SANICAP_NDI = 1 << 29, + NVME_CTRL_SANICAP_NODMMAS = 3 << 30, +}; + +/** + * enum nvme_id_ctrl_anacap - This field indicates the capabilities associated + * with Asymmetric Namespace Access Reporting. + * @NVME_CTRL_ANACAP_OPT: If set, then the controller is able to + * report ANA Optimized state. + * @NVME_CTRL_ANACAP_NON_OPT: If set, then the controller is able to + * report ANA Non-Optimized state. + * @NVME_CTRL_ANACAP_INACCESSIBLE: If set, then the controller is able to + * report ANA Inaccessible state. + * @NVME_CTRL_ANACAP_PERSISTENT_LOSS: If set, then the controller is able to + * report ANA Persistent Loss state. + * @NVME_CTRL_ANACAP_CHANGE: If set, then the controller is able to + * report ANA Change state. + * @NVME_CTRL_ANACAP_GRPID_NO_CHG: If set, then the ANAGRPID field in the + * Identify Namespace data structure + * (&struct nvme_id_ns.anagrpid), does not + * change while the namespace is attached to + * any controller. + * @NVME_CTRL_ANACAP_GRPID_MGMT: If set, then the controller supports a + * non-zero value in the ANAGRPID field of + * the Namespace Management command. + */ +enum nvme_id_ctrl_anacap { + NVME_CTRL_ANACAP_OPT = 1 << 0, + NVME_CTRL_ANACAP_NON_OPT = 1 << 1, + NVME_CTRL_ANACAP_INACCESSIBLE = 1 << 2, + NVME_CTRL_ANACAP_PERSISTENT_LOSS = 1 << 3, + NVME_CTRL_ANACAP_CHANGE = 1 << 4, + NVME_CTRL_ANACAP_GRPID_NO_CHG = 1 << 6, + NVME_CTRL_ANACAP_GRPID_MGMT = 1 << 7, +}; + +/** + * enum nvme_id_ctrl_sqes - Defines the required and maximum Submission Queue + * entry size when using the NVM Command Set. + * @NVME_CTRL_SQES_MIN: Mask to get the value of the required Submission Queue + * Entry size when using the NVM Command Set. + * @NVME_CTRL_SQES_MAX: Mask to get the value of the maximum Submission Queue + * entry size when using the NVM Command Set. + */ +enum nvme_id_ctrl_sqes { + NVME_CTRL_SQES_MIN = 0xf << 0, + NVME_CTRL_SQES_MAX = 0xf << 4, +}; + +/** + * enum nvme_id_ctrl_cqes - Defines the required and maximum Completion Queue + * entry size when using the NVM Command Set. + * @NVME_CTRL_CQES_MIN: Mask to get the value of the required Completion Queue + * Entry size when using the NVM Command Set. + * @NVME_CTRL_CQES_MAX: Mask to get the value of the maximum Completion Queue + * entry size when using the NVM Command Set. + */ +enum nvme_id_ctrl_cqes { + NVME_CTRL_CQES_MIN = 0xf << 0, + NVME_CTRL_CQES_MAX = 0xf << 4, +}; + +/** + * enum nvme_id_ctrl_oncs - This field indicates the optional NVM commands and + * features supported by the controller. + * @NVME_CTRL_ONCS_COMPARE: If set, then the controller supports + * the Compare command. + * @NVME_CTRL_ONCS_WRITE_UNCORRECTABLE: If set, then the controller supports + * the Write Uncorrectable command. + * @NVME_CTRL_ONCS_DSM: If set, then the controller supports + * the Dataset Management command. + * @NVME_CTRL_ONCS_WRITE_ZEROES: If set, then the controller supports + * the Write Zeroes command. + * @NVME_CTRL_ONCS_SAVE_FEATURES: If set, then the controller supports + * the Save field set to a non-zero value + * in the Set Features command and the + * Select field set to a non-zero value in + * the Get Features command. + * @NVME_CTRL_ONCS_RESERVATIONS: If set, then the controller supports + * reservations. + * @NVME_CTRL_ONCS_TIMESTAMP: If set, then the controller supports + * the Timestamp feature. + * @NVME_CTRL_ONCS_VERIFY: If set, then the controller supports + * the Verify command. + * @NVME_CTRL_ONCS_COPY: If set, then the controller supports + * the copy command. + * @NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY: If set, then the write portion of a + * Copy command is performed as a single + * write command to which the same + * atomicity requirements that apply to + * a write command apply. + * @NVME_CTRL_ONCS_ALL_FAST_COPY: If set, then all copy operations for + * the Copy command are fast copy + * operations. + */ +enum nvme_id_ctrl_oncs { + NVME_CTRL_ONCS_COMPARE = 1 << 0, + NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, + NVME_CTRL_ONCS_DSM = 1 << 2, + NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3, + NVME_CTRL_ONCS_SAVE_FEATURES = 1 << 4, + NVME_CTRL_ONCS_RESERVATIONS = 1 << 5, + NVME_CTRL_ONCS_TIMESTAMP = 1 << 6, + NVME_CTRL_ONCS_VERIFY = 1 << 7, + NVME_CTRL_ONCS_COPY = 1 << 8, + NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY = 1 << 9, + NVME_CTRL_ONCS_ALL_FAST_COPY = 1 << 10, +}; + +/** + * enum nvme_id_ctrl_fuses - This field indicates the fused operations that the + * controller supports. + * @NVME_CTRL_FUSES_COMPARE_AND_WRITE: If set, then the controller supports the + * Compare and Write fused operation. + */ +enum nvme_id_ctrl_fuses { + NVME_CTRL_FUSES_COMPARE_AND_WRITE = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_fna - This field indicates attributes for the Format NVM + * command. + * @NVME_CTRL_FNA_FMT_ALL_NAMESPACES: If set, then all namespaces in an NVM + * subsystem shall be configured with the + * same attributes and a format (excluding + * secure erase) of any namespace results in + * a format of all namespaces in an NVM + * subsystem. If cleared, then the + * controller supports format on a per + * namespace basis. + * @NVME_CTRL_FNA_SEC_ALL_NAMESPACES: If set, then any secure erase performed + * as part of a format operation results in + * a secure erase of all namespaces in the + * NVM subsystem. If cleared, then any + * secure erase performed as part of a + * format results in a secure erase of the + * particular namespace specified. + * @NVME_CTRL_FNA_CRYPTO_ERASE: If set, then cryptographic erase is + * supported. If cleared, then cryptographic + * erase is not supported. + * @NVME_CTRL_FNA_NSID_FFFFFFFF: If set, then format does not support + * nsid value set to FFFFFFFFh. If cleared, + * format supports nsid value set to + * FFFFFFFFh. + */ +enum nvme_id_ctrl_fna { + NVME_CTRL_FNA_FMT_ALL_NAMESPACES = 1 << 0, + NVME_CTRL_FNA_SEC_ALL_NAMESPACES = 1 << 1, + NVME_CTRL_FNA_CRYPTO_ERASE = 1 << 2, + NVME_CTRL_FNA_NSID_FFFFFFFF = 1 << 3, +}; + +/** + * enum nvme_id_ctrl_vwc - Volatile write cache + * @NVME_CTRL_VWC_PRESENT: If set, indicates a volatile write cache is present. + * If a volatile write cache is present, then the host + * controls whether the volatile write cache is enabled + * with a Set Features command specifying the value + * %NVME_FEAT_FID_VOLATILE_WC. + * @NVME_CTRL_VWC_FLUSH: Mask to get the value of the flush command behavior. + */ +enum nvme_id_ctrl_vwc { + NVME_CTRL_VWC_PRESENT = 1 << 0, + NVME_CTRL_VWC_FLUSH = 3 << 1, +}; + +/** + * enum nvme_id_ctrl_nvscc - This field indicates the configuration settings + * for NVM Vendor Specific command handling. + * @NVME_CTRL_NVSCC_FMT: If set, all NVM Vendor Specific Commands use the + * format with NDT and NDM fields. + */ +enum nvme_id_ctrl_nvscc { + NVME_CTRL_NVSCC_FMT = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_nwpc - This field indicates the optional namespace write + * protection capabilities supported by the + * controller. + * @NVME_CTRL_NWPC_WRITE_PROTECT: If set, then the controller shall + * support the No Write Protect and + * Write Protect namespace write + * protection states and may support + * the Write Protect Until Power + * Cycle state and Permanent Write + * Protect namespace write + * protection states. + * @NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE: If set, then the controller + * supports the Write Protect Until + * Power Cycle state. + * @NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT: If set, then the controller + * supports the Permanent Write + * Protect state. + */ +enum nvme_id_ctrl_nwpc { + NVME_CTRL_NWPC_WRITE_PROTECT = 1 << 0, + NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE= 1 << 1, + NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT = 1 << 2, +}; + +/** + * enum nvme_id_ctrl_sgls - This field indicates if SGLs are supported for the + * NVM Command Set and the particular SGL types supported. + * @NVME_CTRL_SGLS_SUPPORTED: + * @NVME_CTRL_SGLS_KEYED: + * @NVME_CTRL_SGLS_BIT_BUCKET: + * @NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED: + * @NVME_CTRL_SGLS_OVERSIZE: + * @NVME_CTRL_SGLS_MPTR_SGL: + * @NVME_CTRL_SGLS_OFFSET: + * @NVME_CTRL_SGLS_TPORT: + */ +enum nvme_id_ctrl_sgls { + NVME_CTRL_SGLS_SUPPORTED = 3 << 0, + NVME_CTRL_SGLS_KEYED = 1 << 2, + NVME_CTRL_SGLS_BIT_BUCKET = 1 << 16, + NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED = 1 << 17, + NVME_CTRL_SGLS_OVERSIZE = 1 << 18, + NVME_CTRL_SGLS_MPTR_SGL = 1 << 19, + NVME_CTRL_SGLS_OFFSET = 1 << 20, + NVME_CTRL_SGLS_TPORT = 1 << 21, +}; + +/** + * enum nvme_id_ctrl_fcatt - This field indicates attributes of the controller + * that are specific to NVMe over Fabrics. + * @NVME_CTRL_FCATT_DYNAMIC: If cleared, then the NVM subsystem uses a dynamic + * controller model. If set, then the NVM subsystem + * uses a static controller model. + */ +enum nvme_id_ctrl_fcatt { + NVME_CTRL_FCATT_DYNAMIC = 1 << 0, +}; + +/** + * enum nvme_id_ctrl_ofcs - Indicate whether the controller supports optional + * fabric commands. + * @NVME_CTRL_OFCS_DISCONNECT: If set, then the controller supports the + * Disconnect command and deletion of individual + * I/O Queues. + */ +enum nvme_id_ctrl_ofcs { + NVME_CTRL_OFCS_DISCONNECT = 1 << 0, +}; + +/** + * struct nvme_lbaf - LBA Format Data Structure + * @ms: Metadata Size indicates the number of metadata bytes provided per LBA + * based on the LBA Data Size indicated. + * @ds: LBA Data Size indicates the LBA data size supported, reported as a + * power of two. + * @rp: Relative Performance, see &enum nvme_lbaf_rp. + */ +struct nvme_lbaf { + __le16 ms; + __u8 ds; + __u8 rp; +}; + +/** + * enum nvme_lbaf_rp - This field indicates the relative performance of the LBA + * format indicated relative to other LBA formats supported + * by the controller. + * @NVME_LBAF_RP_BEST: Best performance + * @NVME_LBAF_RP_BETTER: Better performance + * @NVME_LBAF_RP_GOOD: Good performance + * @NVME_LBAF_RP_DEGRADED: Degraded performance + * @NVME_LBAF_RP_MASK: Mask to get the relative performance value from the + * field + */ +enum nvme_lbaf_rp { + NVME_LBAF_RP_BEST = 0, + NVME_LBAF_RP_BETTER = 1, + NVME_LBAF_RP_GOOD = 2, + NVME_LBAF_RP_DEGRADED = 3, + NVME_LBAF_RP_MASK = 3, +}; + +/** + * struct nvme_id_ns - Identify Namespace data structure + * @nsze: Namespace Size indicates the total size of the namespace in + * logical blocks. The number of logical blocks is based on the + * formatted LBA size. + * @ncap: Namespace Capacity indicates the maximum number of logical blocks + * that may be allocated in the namespace at any point in time. The + * number of logical blocks is based on the formatted LBA size. + * @nuse: Namespace Utilization indicates the current number of logical + * blocks allocated in the namespace. This field is smaller than or + * equal to the Namespace Capacity. The number of logical blocks is + * based on the formatted LBA size. + * @nsfeat: Namespace Features, see &enum nvme_id_nsfeat. + * @nlbaf: Number of LBA Formats defines the number of supported LBA data + * size and metadata size combinations supported by the namespace + * and the highest possible index to &struct nvme_id_ns.lbaf. + * @flbas: Formatted LBA Size, see &enum nvme_id_ns_flbas. + * @mc: Metadata Capabilities, see &enum nvme_id_ns_mc. + * @dpc: End-to-end Data Protection Capabilities, see + * &enum nvme_id_ns_dpc. + * @dps: End-to-end Data Protection Type Settings, see + * &enum nvme_id_ns_dps. + * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities, see + * &enum nvme_id_ns_nmic. + * @rescap: Reservation Capabilities, see &enum nvme_id_ns_rescap. + * @fpi: Format Progress Indicator, see &enum nvme_nd_ns_fpi. + * @dlfeat: Deallocate Logical Block Features, see &enum nvme_id_ns_dlfeat. + * @nawun: Namespace Atomic Write Unit Normal indicates the + * namespace specific size of the write operation guaranteed to be + * written atomically to the NVM during normal operation. + * @nawupf: Namespace Atomic Write Unit Power Fail indicates the + * namespace specific size of the write operation guaranteed to be + * written atomically to the NVM during a power fail or error + * condition. + * @nacwu: Namespace Atomic Compare & Write Unit indicates the namespace + * specific size of the write operation guaranteed to be written + * atomically to the NVM for a Compare and Write fused command. + * @nabsn: Namespace Atomic Boundary Size Normal indicates the atomic + * boundary size for this namespace for the NAWUN value. This field + * is specified in logical blocks. + * @nabo: Namespace Atomic Boundary Offset indicates the LBA on this + * namespace where the first atomic boundary starts. + * @nabspf: Namespace Atomic Boundary Size Power Fail indicates the atomic + * boundary size for this namespace specific to the Namespace Atomic + * Write Unit Power Fail value. This field is specified in logical + * blocks. + * @noiob: Namespace Optimal I/O Boundary indicates the optimal I/O boundary + * for this namespace. This field is specified in logical blocks. + * The host should construct Read and Write commands that do not + * cross the I/O boundary to achieve optimal performance. + * @nvmcap: NVM Capacity indicates the total size of the NVM allocated to + * this namespace. The value is in bytes. + * @npwg: Namespace Preferred Write Granularity indicates the smallest + * recommended write granularity in logical blocks for this + * namespace. This is a 0's based value. + * @npwa: Namespace Preferred Write Alignment indicates the recommended + * write alignment in logical blocks for this namespace. This is a + * 0's based value. + * @npdg: Namespace Preferred Deallocate Granularity indicates the + * recommended granularity in logical blocks for the Dataset + * Management command with the Attribute - Deallocate bit. + * @npda: Namespace Preferred Deallocate Alignment indicates the + * recommended alignment in logical blocks for the Dataset + * Management command with the Attribute - Deallocate bit + * @nows: Namespace Optimal Write Size indicates the size in logical blocks + * for optimal write performance for this namespace. This is a 0's + * based value. + * @mssrl: Maximum Single Source Range Length indicates the maximum number + * of logical blocks that may be specified in each valid Source Range + * field of a Copy command. + * @mcl: Maximum Copy Length indicates the maximum number of logical + * blocks that may be specified in a Copy command. + * @msrc: Maximum Source Range Count indicates the maximum number of Source + * Range entries that may be used to specify source data in a Copy + * command. This is a 0’s based value. + * @rsvd81: Reserved + * @nulbaf: Number of Unique Capability LBA Formats defines the number of + * supported user data size and metadata size combinations supported + * by the namespace that may not share the same capabilities. LBA + * formats shall be allocated in order and packed sequentially. + * @rsvd83: Reserved + * @anagrpid: ANA Group Identifier indicates the ANA Group Identifier of the + * ANA group of which the namespace is a member. + * @rsvd96: Reserved + * @nsattr: Namespace Attributes, see &enum nvme_id_ns_attr. + * @nvmsetid: NVM Set Identifier indicates the NVM Set with which this + * namespace is associated. + * @endgid: Endurance Group Identifier indicates the Endurance Group with + * which this namespace is associated. + * @nguid: Namespace Globally Unique Identifier contains a 128-bit value + * that is globally unique and assigned to the namespace when the + * namespace is created. This field remains fixed throughout the + * life of the namespace and is preserved across namespace and + * controller operations + * @eui64: IEEE Extended Unique Identifier contains a 64-bit IEEE Extended + * Unique Identifier (EUI-64) that is globally unique and assigned + * to the namespace when the namespace is created. This field + * remains fixed throughout the life of the namespace and is + * preserved across namespace and controller operations + * @lbaf: LBA Format, see &struct nvme_lbaf. + * @vs: Vendor Specific + */ +struct nvme_id_ns { + __le64 nsze; + __le64 ncap; + __le64 nuse; + __u8 nsfeat; + __u8 nlbaf; + __u8 flbas; + __u8 mc; + __u8 dpc; + __u8 dps; + __u8 nmic; + __u8 rescap; + __u8 fpi; + __u8 dlfeat; + __le16 nawun; + __le16 nawupf; + __le16 nacwu; + __le16 nabsn; + __le16 nabo; + __le16 nabspf; + __le16 noiob; + __u8 nvmcap[16]; + __le16 npwg; + __le16 npwa; + __le16 npdg; + __le16 npda; + __le16 nows; + __le16 mssrl; + __le32 mcl; + __u8 msrc; + __u8 rsvd81; + __u8 nulbaf; + __u8 rsvd83[9]; + __le32 anagrpid; + __u8 rsvd96[3]; + __u8 nsattr; + __le16 nvmsetid; + __le16 endgid; + __u8 nguid[16]; + __u8 eui64[8]; + struct nvme_lbaf lbaf[64]; + __u8 vs[3712]; +}; + +/** + * enum nvme_id_nsfeat - This field defines features of the namespace. + * @NVME_NS_FEAT_THIN: If set, indicates that the namespace supports thin + * provisioning. Specifically, the Namespace Capacity + * reported may be less than the Namespace Size. + * @NVME_NS_FEAT_NATOMIC: If set, indicates that the fields NAWUN, NAWUPF, and + * NACWU are defined for this namespace and should be + * used by the host for this namespace instead of the + * AWUN, AWUPF, and ACWU fields in the Identify + * Controller data structure. + * @NVME_NS_FEAT_DULBE: If set, indicates that the controller supports the + * Deallocated or Unwritten Logical Block error for + * this namespace. + * @NVME_NS_FEAT_ID_REUSE: If set, indicates that the value in the NGUID field + * for this namespace, if non- zero, is never reused by + * the controller and that the value in the EUI64 field + * for this namespace, if non-zero, is never reused by + * the controller. + * @NVME_NS_FEAT_IO_OPT: If set, indicates that the fields NPWG, NPWA, NPDG, + * NPDA, and NOWS are defined for this namespace and + * should be used by the host for I/O optimization + */ +enum nvme_id_nsfeat { + NVME_NS_FEAT_THIN = 1 << 0, + NVME_NS_FEAT_NATOMIC = 1 << 1, + NVME_NS_FEAT_DULBE = 1 << 2, + NVME_NS_FEAT_ID_REUSE = 1 << 3, + NVME_NS_FEAT_IO_OPT = 1 << 4, +}; + +/** + * enum nvme_id_ns_flbas - This field indicates the LBA data size & metadata + * size combination that the namespace has been + * formatted with + * @NVME_NS_FLBAS_LOWER_MASK: Mask to get the index of one of the supported + * LBA Formats's least significant + * 4bits indicated in + * :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf. + * @NVME_NS_FLBAS_META_EXT: Applicable only if format contains metadata. If + * this bit is set, indicates that the metadata is + * transferred at the end of the data LBA, creating an + * extended data LBA. If cleared, indicates that all + * of the metadata for a command is transferred as a + * separate contiguous buffer of data. + * @NVME_NS_FLBAS_HIGHER_MASK: Mask to get the index of one of + * the supported LBA Formats's most significant + * 2bits indicated in + * :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf. + */ +enum nvme_id_ns_flbas { + NVME_NS_FLBAS_LOWER_MASK = 15 << 0, + NVME_NS_FLBAS_META_EXT = 1 << 4, + NVME_NS_FLBAS_HIGHER_MASK = 3 << 5, +}; + +/** + * enum nvme_nvm_id_ns_elbaf - This field indicates the extended LBA format + * @NVME_NVM_ELBAF_STS_MASK: Mask to get the storage tag size used to determine + * the variable-sized storage tag/reference tag fields + * @NVME_NVM_ELBAF_PIF_MASK: Mask to get the protection information format for + * the extended LBA format. + */ +enum nvme_nvm_id_ns_elbaf { + NVME_NVM_ELBAF_STS_MASK = 127 << 0, + NVME_NVM_ELBAF_PIF_MASK = 3 << 7, +}; + +/** + * enum nvme_id_ns_mc - This field indicates the capabilities for metadata. + * @NVME_NS_MC_EXTENDED: If set, indicates the namespace supports the metadata + * being transferred as part of a separate buffer that is + * specified in the Metadata Pointer. + * @NVME_NS_MC_SEPARATE: If set, indicates that the namespace supports the + * metadata being transferred as part of an extended data LBA. + */ +enum nvme_id_ns_mc { + NVME_NS_MC_EXTENDED = 1 << 0, + NVME_NS_MC_SEPARATE = 1 << 1, +}; + +/** + * enum nvme_id_ns_dpc - This field indicates the capabilities for the + * end-to-end data protection feature. + * @NVME_NS_DPC_PI_TYPE1: If set, indicates that the namespace supports + * Protection Information Type 1. + * @NVME_NS_DPC_PI_TYPE2: If set, indicates that the namespace supports + * Protection Information Type 2. + * @NVME_NS_DPC_PI_TYPE3: If set, indicates that the namespace supports + * Protection Information Type 3. + * @NVME_NS_DPC_PI_FIRST: If set, indicates that the namespace supports + * protection information transferred as the first eight + * bytes of metadata. + * @NVME_NS_DPC_PI_LAST: If set, indicates that the namespace supports + * protection information transferred as the last eight + * bytes of metadata. + */ +enum nvme_id_ns_dpc { + NVME_NS_DPC_PI_TYPE1 = 1 << 0, + NVME_NS_DPC_PI_TYPE2 = 1 << 1, + NVME_NS_DPC_PI_TYPE3 = 1 << 2, + NVME_NS_DPC_PI_FIRST = 1 << 3, + NVME_NS_DPC_PI_LAST = 1 << 4, +}; + +/** + * enum nvme_id_ns_dps - This field indicates the Type settings for the + * end-to-end data protection feature. + * @NVME_NS_DPS_PI_NONE: Protection information is not enabled + * @NVME_NS_DPS_PI_TYPE1: Protection information is enabled, Type 1 + * @NVME_NS_DPS_PI_TYPE2: Protection information is enabled, Type 2 + * @NVME_NS_DPS_PI_TYPE3: Protection information is enabled, Type 3 + * @NVME_NS_DPS_PI_MASK: Mask to get the value of the PI type + * @NVME_NS_DPS_PI_FIRST: If set, indicates that the protection information, if + * enabled, is transferred as the first eight bytes of + * metadata. + */ +enum nvme_id_ns_dps { + NVME_NS_DPS_PI_NONE = 0, + NVME_NS_DPS_PI_TYPE1 = 1, + NVME_NS_DPS_PI_TYPE2 = 2, + NVME_NS_DPS_PI_TYPE3 = 3, + NVME_NS_DPS_PI_MASK = 7 << 0, + NVME_NS_DPS_PI_FIRST = 1 << 3, +}; + +/** + * enum nvme_id_ns_nmic - This field specifies multi-path I/O and namespace + * sharing capabilities of the namespace. + * @NVME_NS_NMIC_SHARED: If set, then the namespace may be attached to two or + * more controllers in the NVM subsystem concurrently + */ +enum nvme_id_ns_nmic { + NVME_NS_NMIC_SHARED = 1 << 0, +}; + +/** + * enum nvme_id_ns_rescap - This field indicates the reservation capabilities + * of the namespace. + * @NVME_NS_RESCAP_PTPL: If set, indicates that the namespace supports the + * Persist Through Power Loss capability. + * @NVME_NS_RESCAP_WE: If set, indicates that the namespace supports the + * Write Exclusive reservation type. + * @NVME_NS_RESCAP_EA: If set, indicates that the namespace supports the + * Exclusive Access reservation type. + * @NVME_NS_RESCAP_WERO: If set, indicates that the namespace supports the + * Write Exclusive - Registrants Only reservation type. + * @NVME_NS_RESCAP_EARO: If set, indicates that the namespace supports the + * Exclusive Access - Registrants Only reservation type. + * @NVME_NS_RESCAP_WEAR: If set, indicates that the namespace supports the + * Write Exclusive - All Registrants reservation type. + * @NVME_NS_RESCAP_EAAR: If set, indicates that the namespace supports the + * Exclusive Access - All Registrants reservation type. + * @NVME_NS_RESCAP_IEK_13: If set, indicates that Ignore Existing Key is used + * as defined in revision 1.3 or later of this specification. + */ +enum nvme_id_ns_rescap { + NVME_NS_RESCAP_PTPL = 1 << 0, + NVME_NS_RESCAP_WE = 1 << 1, + NVME_NS_RESCAP_EA = 1 << 2, + NVME_NS_RESCAP_WERO = 1 << 3, + NVME_NS_RESCAP_EARO = 1 << 4, + NVME_NS_RESCAP_WEAR = 1 << 5, + NVME_NS_RESCAP_EAAR = 1 << 6, + NVME_NS_RESCAP_IEK_13 = 1 << 7, +}; + +/** + * enum nvme_nd_ns_fpi - If a format operation is in progress, this field + * indicates the percentage of the namespace that remains + * to be formatted. + * @NVME_NS_FPI_REMAINING: Mask to get the format percent remaining value + * @NVME_NS_FPI_SUPPORTED: If set, indicates that the namespace supports the + * Format Progress Indicator defined for the field. + */ +enum nvme_nd_ns_fpi { + NVME_NS_FPI_REMAINING = 0x7f << 0, + NVME_NS_FPI_SUPPORTED = 1 << 7, +}; + +/** + * enum nvme_id_ns_dlfeat - This field indicates information about features + * that affect deallocating logical blocks for this + * namespace. + * @NVME_NS_DLFEAT_RB: Mask to get the value of the read behavior + * @NVME_NS_DLFEAT_RB_NR: Read behvaior is not reported + * @NVME_NS_DLFEAT_RB_ALL_0S: A deallocated logical block returns all bytes + * cleared to 0h. + * @NVME_NS_DLFEAT_RB_ALL_FS: A deallocated logical block returns all bytes + * set to FFh. + * @NVME_NS_DLFEAT_WRITE_ZEROES: If set, indicates that the controller supports + * the Deallocate bit in the Write Zeroes command + * for this namespace. + * @NVME_NS_DLFEAT_CRC_GUARD: If set, indicates that the Guard field for + * deallocated logical blocks that contain + * protection information is set to the CRC for + * the value read from the deallocated logical + * block and its metadata + */ +enum nvme_id_ns_dlfeat { + NVME_NS_DLFEAT_RB = 7 << 0, + NVME_NS_DLFEAT_RB_NR = 0, + NVME_NS_DLFEAT_RB_ALL_0S = 1, + NVME_NS_DLFEAT_RB_ALL_FS = 2, + NVME_NS_DLFEAT_WRITE_ZEROES = 1 << 3, + NVME_NS_DLFEAT_CRC_GUARD = 1 << 4, +}; + +/** + * enum nvme_id_ns_attr - Specifies attributes of the namespace. + * @NVME_NS_NSATTR_WRITE_PROTECTED: If set, then the namespace is currently + * write protected and all write access to the + * namespace shall fail. + */ +enum nvme_id_ns_attr { + NVME_NS_NSATTR_WRITE_PROTECTED = 1 << 0 +}; + +/** + * struct nvme_ns_id_desc - Namespace identifier type descriptor + * @nidt: Namespace Identifier Type, see &enum nvme_ns_id_desc_nidt + * @nidl: Namespace Identifier Length contains the length in bytes of the + * &struct nvme_id_ns.nid. + * @rsvd: Reserved + * @nid: Namespace Identifier contains a value that is globally unique and + * assigned to the namespace when the namespace is created. The length + * is defined in &struct nvme_id_ns.nidl. + */ +struct nvme_ns_id_desc { + __u8 nidt; + __u8 nidl; + __le16 rsvd; + __u8 nid[]; +}; + +/** + * enum nvme_ns_id_desc_nidt - Known namespace identifier types + * @NVME_NIDT_EUI64: IEEE Extended Unique Identifier, the NID field contains a + * copy of the EUI64 field in the struct nvme_id_ns.eui64. + * @NVME_NIDT_NGUID: Namespace Globally Unique Identifier, the NID field + * contains a copy of the NGUID field in struct nvme_id_ns.nguid. + * @NVME_NIDT_UUID: The NID field contains a 128-bit Universally Unique + * Identifier (UUID) as specified in RFC 4122. + * @NVME_NIDT_CSI: The NID field contains the command set identifier. + */ +enum nvme_ns_id_desc_nidt { + NVME_NIDT_EUI64 = 1, + NVME_NIDT_NGUID = 2, + NVME_NIDT_UUID = 3, + NVME_NIDT_CSI = 4, +}; + +enum nvme_ns_id_desc_nidt_lens { + NVME_NIDT_EUI64_LEN = 8, + NVME_NIDT_NGUID_LEN = 16, + NVME_NIDT_UUID_LEN = 16, + NVME_NIDT_CSI_LEN = 1, +}; + +/** + * struct nvme_nvmset_attr - NVM Set Attributes Entry + * @nvmsetid: NVM Set Identifier + * @endgid: Endurance Group Identifier + * @rsvd4: Reserved + * @rr4kt: Random 4 KiB Read Typical indicates the typical + * time to complete a 4 KiB random read in 100 nanosecond units + * when the NVM Set is in a Predictable Latency Mode Deterministic + * Window and there is 1 outstanding command per NVM Set. + * @ows: Optimal Write Size + * @tnvmsetcap: Total NVM Set Capacity + * @unvmsetcap: Unallocated NVM Set Capacity + * @rsvd48: Reserved + */ +struct nvme_nvmset_attr { + __le16 nvmsetid; + __le16 endgid; + __u8 rsvd4[4]; + __le32 rr4kt; + __le32 ows; + __u8 tnvmsetcap[16]; + __u8 unvmsetcap[16]; + __u8 rsvd48[80]; +}; + +/** + * struct nvme_id_nvmset_list - NVM set list + * @nid: Nvmset id + * @rsvd1: Reserved + * @ent: nvmset id list + */ +struct nvme_id_nvmset_list { + __u8 nid; + __u8 rsvd1[127]; + struct nvme_nvmset_attr ent[NVME_ID_NVMSET_LIST_MAX]; +}; + +/** + * struct nvme_id_independent_id_ns - Identify - I/O Command Set Independent Identify Namespace Data Structure + * @nsfeat: common namespace features + * @nmic: Namespace Multi-path I/O and Namespace + * Sharing Capabilities + * @rescap: Reservation Capabilities + * @fpi: Format Progress Indicator + * @anagrpid: ANA Group Identifier + * @nsattr: Namespace Attributes + * @rsvd9: reserved + * @nvmsetid: NVM Set Identifier + * @endgid: Endurance Group Identifier + * @nstat: Namespace Status + * @rsvd15: reserved + */ +struct nvme_id_independent_id_ns { + __u8 nsfeat; + __u8 nmic; + __u8 rescap; + __u8 fpi; + __le32 anagrpid; + __u8 nsattr; + __u8 rsvd9; + __le16 nvmsetid; + __le16 endgid; + __u8 nstat; + __u8 rsvd15[4081]; +}; + +/** + * struct nvme_id_ns_granularity_desc - Namespace Granularity Descriptor + * @nszegran: Namespace Size Granularity + * @ncapgran: Namespace Capacity Granularity + */ +struct nvme_id_ns_granularity_desc { + __le64 nszegran; + __le64 ncapgran; +}; + +/** + * struct nvme_id_ns_granularity_list - Namespace Granularity List + * @attributes: Namespace Granularity Attributes + * @num_descriptors: Number of Descriptors + * @rsvd5: reserved + * @entry: Namespace Granularity Descriptor + * @rsvd288: reserved + */ +struct nvme_id_ns_granularity_list { + __le32 attributes; + __u8 num_descriptors; + __u8 rsvd5[27]; + struct nvme_id_ns_granularity_desc entry[NVME_ID_ND_DESCRIPTOR_MAX]; + __u8 rsvd288[3808]; +}; + +/** + * struct nvme_id_uuid_list_entry - UUID List Entry + * @header: UUID Lists Entry Header + * @rsvd1: reserved + * @uuid: 128-bit Universally Unique Identifier + */ +struct nvme_id_uuid_list_entry { + __u8 header; + __u8 rsvd1[15]; + __u8 uuid[16]; +}; + +/** + * enum nvme_id_uuid - Identifier Association + * @NVME_ID_UUID_HDR_ASSOCIATION_MASK: + * @NVME_ID_UUID_ASSOCIATION_NONE: + * @NVME_ID_UUID_ASSOCIATION_VENDOR: + * @NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR: + */ +enum nvme_id_uuid { + NVME_ID_UUID_HDR_ASSOCIATION_MASK = 0x3, + NVME_ID_UUID_ASSOCIATION_NONE = 0, + NVME_ID_UUID_ASSOCIATION_VENDOR = 1, + NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR = 2, +}; + +/** + * struct nvme_id_uuid_list - UUID list + * @rsvd0: reserved + * @entry: UUID list entry + */ +struct nvme_id_uuid_list { + __u8 rsvd0[32]; + struct nvme_id_uuid_list_entry entry[NVME_ID_UUID_LIST_MAX]; +}; + +/** + * struct nvme_ctrl_list - Controller List + * @num: Number of Identifiers + * @identifier: NVM subsystem unique controller identifier + */ +struct nvme_ctrl_list { + __le16 num; + __le16 identifier[NVME_ID_CTRL_LIST_MAX]; +}; + +/** + * struct nvme_ns_list - Namespace List + * @ns: Namespace Identifier + */ +struct nvme_ns_list { + __le32 ns[NVME_ID_NS_LIST_MAX]; +}; + +/** + * struct nvme_id_ctrl_nvm - I/O Command Set Specific Identify Controller data structure + * @vsl: Verify Size Limit + * @wzsl: Write Zeroes Size Limit + * @wusl: Write Uncorrectable Size Limit + * @dmrl: Dataset Management Ranges Limit + * @dmrsl: Dataset Management Range Size Limit + * @dmsl: Dataset Management Size Limit + * @rsvd16: reserved + */ +struct nvme_id_ctrl_nvm { + __u8 vsl; + __u8 wzsl; + __u8 wusl; + __u8 dmrl; + __le32 dmrsl; + __le64 dmsl; + __u8 rsvd16[4080]; +}; + +/** + * struct nvme_nvm_id_ns - NVME Command Set I/O Command Set Specific Identify Namespace Data Structure + * @lbstm: Logical Block Storage Tag Mask + * @pic: Protection Information Capabilities + * @rsvd9: Reserved + * @elbaf: List of Extended LBA Format Support + * @rsvd268: Reserved + */ +struct nvme_nvm_id_ns { + __le64 lbstm; + __u8 pic; + __u8 rsvd9[3]; + __le32 elbaf[64]; + __u8 rsvd268[3828]; +}; + +/** + * struct nvme_zns_lbafe - LBA Format Extension Data Structure + * @zsze: Zone Size + * @zdes: Zone Descriptor Extension Size + * @rsvd9: reserved + */ +struct nvme_zns_lbafe { + __le64 zsze; + __u8 zdes; + __u8 rsvd9[7]; +}; + +/** + * struct nvme_zns_id_ns - Zoned Namespace Command Set Specific Identify Namespace Data Structure + * @zoc: Zone Operation Characteristics + * @ozcs: Optional Zoned Command Support + * @mar: Maximum Active Resources + * @mor: Maximum Open Resources + * @rrl: Reset Recommended Limit + * @frl: Finish Recommended Limit + * @rrl1: Reset Recommended Limit 1 + * @rrl2: Reset Recommended Limit 2 + * @rrl3: Reset Recommended Limit 3 + * @frl1: Finish Recommended Limit 1 + * @frl2: Finish Recommended Limit 2 + * @frl3: Finish Recommended Limit 3 + * @numzrwa: Number of ZRWA Resources + * @zrwafg: ZRWA Flush Granularity + * @zrwasz: ZRWA Size + * @zrwacap: ZRWA Capability + * @rsvd53: Reserved + * @lbafe: LBA Format Extension + * @vs: Vendor Specific + */ +struct nvme_zns_id_ns { + __le16 zoc; + __le16 ozcs; + __le32 mar; + __le32 mor; + __le32 rrl; + __le32 frl; + __le32 rrl1; + __le32 rrl2; + __le32 rrl3; + __le32 frl1; + __le32 frl2; + __le32 frl3; + __le32 numzrwa; + __le16 zrwafg; + __le16 zrwasz; + __u8 zrwacap; + __u8 rsvd53[2763]; + struct nvme_zns_lbafe lbafe[64]; + __u8 vs[256]; +}; + +/** + * struct nvme_zns_id_ctrl - I/O Command Set Specific Identify Controller Data Structure for the Zoned Namespace Command Set + * @zasl: Zone Append Size Limit + * @rsvd1: Reserved + */ +struct nvme_zns_id_ctrl { + __u8 zasl; + __u8 rsvd1[4095]; +}; + +/** + * struct nvme_primary_ctrl_cap - Identify - Controller Capabilities Structure + * @cntlid: Controller Identifier + * @portid: Port Identifier + * @crt: Controller Resource Types + * @rsvd5: reserved + * @vqfrt: VQ Resources Flexible Total + * @vqrfa: VQ Resources Flexible Assigned + * @vqrfap: VQ Resources Flexible Allocated to Primary + * @vqprt: VQ Resources Private Total + * @vqfrsm: VQ Resources Flexible Secondary Maximum + * @vqgran: VQ Flexible Resource Preferred Granularity + * @rsvd48: reserved + * @vifrt: VI Resources Flexible Total + * @virfa: VI Resources Flexible Assigned + * @virfap: VI Resources Flexible Allocated to Primary + * @viprt: VI Resources Private Total + * @vifrsm: VI Resources Flexible Secondary Maximum + * @vigran: VI Flexible Resource Preferred Granularity + * @rsvd80: reserved + */ +struct nvme_primary_ctrl_cap { + __le16 cntlid; + __le16 portid; + __u8 crt; + __u8 rsvd5[27]; + __le32 vqfrt; + __le32 vqrfa; + __le16 vqrfap; + __le16 vqprt; + __le16 vqfrsm; + __le16 vqgran; + __u8 rsvd48[16]; + __le32 vifrt; + __le32 virfa; + __le16 virfap; + __le16 viprt; + __le16 vifrsm; + __le16 vigran; + __u8 rsvd80[4016]; +}; + +/** + * struct nvme_secondary_ctrl - Secondary Controller Entry + * @scid: Secondary Controller Identifier + * @pcid: Primary Controller Identifier + * @scs: Secondary Controller State + * @rsvd5: Reserved + * @vfn: Virtual Function Number + * @nvq: Number of VQ Flexible Resources Assigned + * @nvi: Number of VI Flexible Resources Assigned + * @rsvd14: Reserved + */ +struct nvme_secondary_ctrl { + __le16 scid; + __le16 pcid; + __u8 scs; + __u8 rsvd5[3]; + __le16 vfn; + __le16 nvq; + __le16 nvi; + __u8 rsvd14[18]; +}; + +/** + * struct nvme_secondary_ctrl_list - Secondary Controller List + * @num: Number of Identifiers + * @rsvd: Reserved + * @sc_entry: Secondary Controller Entry + */ +struct nvme_secondary_ctrl_list { + __u8 num; + __u8 rsvd[31]; + struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX]; +}; + +/** + * struct nvme_id_iocs - NVMe Identify IO Command Set data structure + * @iocsc: List of supported IO Command Set Combination vectors + */ +struct nvme_id_iocs { + __le64 iocsc[512]; +}; + +/** + * struct nvme_id_domain_attr - Domain Attributes Entry + * @dom_id: Domain Identifier + * @rsvd2: Reserved + * @dom_cap: Total Domain Capacity + * @unalloc_dom_cap: Unallocated Domain Capacity + * @max_egrp_dom_cap: Max Endurance Group Domain Capacity + * @rsvd64: Reserved + */ +struct nvme_id_domain_attr { + __le16 dom_id; + __u8 rsvd2[14]; + __u8 dom_cap[16]; + __u8 unalloc_dom_cap[16]; + __u8 max_egrp_dom_cap[16]; + __u8 rsvd64[64]; +}; + +/** + * struct nvme_id_domain_list - Domain List + * @num: Number of domain attributes + * @rsvd: Reserved + * @domain_attr: List of domain attributes + */ +struct nvme_id_domain_list { + __u8 num; + __u8 rsvd[127]; + struct nvme_id_domain_attr domain_attr[NVME_ID_DOMAIN_LIST_MAX]; +}; + +/** + * struct nvme_id_endurance_group_list - Endurance Group List + * @num: Number of Identifiers + * @identifier: Endurance Group Identifier + */ +struct nvme_id_endurance_group_list { + __le16 num; + __le16 identifier[NVME_ID_ENDURANCE_GROUP_LIST_MAX]; +}; + +/** + * struct nvme_supported_log_pages - Supported Log Pages - Log + * @lid_support: Log Page Identifier Supported + * + * Supported Log Pages (Log Identifier 00h) + */ +struct nvme_supported_log_pages { + __le32 lid_support[NVME_LOG_SUPPORTED_LOG_PAGES_MAX]; +}; + +/** + * struct nvme_error_log_page - Error Information Log Entry (Log Identifier 01h) + * @error_count: Error Count: a 64-bit incrementing error count, + * indicating a unique identifier for this error. The error + * count starts at %1h, is incremented for each unique error + * log entry, and is retained across power off conditions. + * A value of %0h indicates an invalid entry; this value + * is used when there are lost entries or when there are + * fewer errors than the maximum number of entries the + * controller supports. If the value of this field is + * %FFFFFFFFh, then the field shall be set to 1h when + * incremented (i.e., rolls over to %1h). Prior to NVMe + * 1.4, processing of incrementing beyond %FFFFFFFFh is + * unspecified. + * @sqid: Submission Queue ID: indicates the Submission Queue + * Identifier of the command that the error information is + * associated with. If the error is not specific to + * a particular command, then this field shall be set to + * %FFFFh. + * @cmdid: Command ID: indicates the Command Identifier of the + * command that the error is associated with. If the error + * is not specific to a particular command, then this field + * shall be set to %FFFFh. + * @status_field: Bits 15-1: Status Field: indicates the Status Field for + * the command that completed. If the error is not specific + * to a particular command, then this field reports the most + * applicable status value. + * Bit 0: Phase Tag: may indicate the Phase Tag posted for + * the command. + * @parm_error_location: Parameter Error Location: indicates the byte and bit of + * the command parameter that the error is associated with, + * if applicable. If the parameter spans multiple bytes or + * bits, then the location indicates the first byte and bit + * of the parameter. + * Bits 10-8: Bit in command that contained the error. + * Valid values are 0 to 7. + * Bits 7-0: Byte in command that contained the error. + * Valid values are 0 to 63. + * @lba: LBA: This field indicates the first LBA that experienced + * the error condition, if applicable. + * @nsid: Namespace: This field indicates the NSID of the namespace + * that the error is associated with, if applicable. + * @vs: Vendor Specific Information Available: If there is + * additional vendor specific error information available, + * this field provides the log page identifier associated + * with that page. A value of %0h indicates that no additional + * information is available. Valid values are in the range + * of %80h to %FFh. + * @trtype: Transport Type (TRTYPE): indicates the Transport Type of + * the transport associated with the error. The values in + * this field are the same as the TRTYPE values in the + * Discovery Log Page Entry. If the error is not transport + * related, this field shall be cleared to %0h. If the error + * is transport related, this field shall be set to the type + * of the transport - see &enum nvme_trtype. + * @csi: Command Set Indicator: This field contains command set + * indicator for the command that the error is associated + * with. + * @opcode: Opcode: This field contains opcode for the command that + * the error is associated with. + * @cs: Command Specific Information: This field contains command + * specific information. If used, the command definition + * specifies the information returned. + * @trtype_spec_info: Transport Type Specific Information + * @rsvd: Reserved: [62:42] + * @log_page_version: This field shall be set to 1h. If set, @csi and @opcode + * will have valid values. + */ +struct nvme_error_log_page { + __le64 error_count; + __le16 sqid; + __le16 cmdid; + __le16 status_field; + __le16 parm_error_location; + __le64 lba; + __le32 nsid; + __u8 vs; + __u8 trtype; + __u8 csi; + __u8 opcode; + __le64 cs; + __le16 trtype_spec_info; + __u8 rsvd[21]; + __u8 log_page_version; +}; + +enum nvme_err_pel { + NVME_ERR_PEL_BYTE_MASK = 0xf, + NVME_ERR_PEL_BIT_MASK = 0x70, +}; + +/** + * struct nvme_smart_log - SMART / Health Information Log (Log Identifier 02h) + * @critical_warning: This field indicates critical warnings for the state + * of the controller. Critical warnings may result in an + * asynchronous event notification to the host. Bits in + * this field represent the current associated state and + * are not persistent (see &enum nvme_smart_crit). + * @temperature: Composite Temperature: Contains a value corresponding + * to a temperature in Kelvins that represents the current + * composite temperature of the controller and namespace(s) + * associated with that controller. The manner in which + * this value is computed is implementation specific and + * may not represent the actual temperature of any physical + * point in the NVM subsystem. Warning and critical + * overheating composite temperature threshold values are + * reported by the WCTEMP and CCTEMP fields in the Identify + * Controller data structure. + * @avail_spare: Available Spare: Contains a normalized percentage (0% + * to 100%) of the remaining spare capacity available. + * @spare_thresh: Available Spare Threshold: When the Available Spare + * falls below the threshold indicated in this field, an + * asynchronous event completion may occur. The value is + * indicated as a normalized percentage (0% to 100%). + * The values 101 to 255 are reserved. + * @percent_used: Percentage Used: Contains a vendor specific estimate + * of the percentage of NVM subsystem life used based on + * the actual usage and the manufacturer's prediction of + * NVM life. A value of 100 indicates that the estimated + * endurance of the NVM in the NVM subsystem has been + * consumed, but may not indicate an NVM subsystem failure. + * The value is allowed to exceed 100. Percentages greater + * than 254 shall be represented as 255. This value shall + * be updated once per power-on hour (when the controller + * is not in a sleep state). + * @endu_grp_crit_warn_sumry: Endurance Group Critical Warning Summary: This field + * indicates critical warnings for the state of Endurance + * Groups. Bits in this field represent the current associated + * state and are not persistent (see &enum nvme_smart_egcw). + * @rsvd7: Reserved + * @data_units_read: Data Units Read: Contains the number of 512 byte data + * units the host has read from the controller; this value + * does not include metadata. This value is reported in + * thousands (i.e., a value of 1 corresponds to 1000 + * units of 512 bytes read) and is rounded up (e.g., one + * indicates the that number of 512 byte data units read + * is from 1 to 1000, three indicates that the number of + * 512 byte data units read is from 2001 to 3000). When + * the LBA size is a value other than 512 bytes, the + * controller shall convert the amount of data read to + * 512 byte units. For the NVM command set, logical blocks + * read as part of Compare, Read, and Verify operations + * shall be included in this value. A value of %0h in + * this field indicates that the number of Data Units Read + * is not reported. + * @data_units_written: Data Units Written: Contains the number of 512 byte + * data units the host has written to the controller; + * this value does not include metadata. This value is + * reported in thousands (i.e., a value of 1 corresponds + * to 1000 units of 512 bytes written) and is rounded up + * (e.g., one indicates that the number of 512 byte data + * units written is from 1 to 1,000, three indicates that + * the number of 512 byte data units written is from 2001 + * to 3000). When the LBA size is a value other than 512 + * bytes, the controller shall convert the amount of data + * written to 512 byte units. For the NVM command set, + * logical blocks written as part of Write operations shall + * be included in this value. Write Uncorrectable commands + * and Write Zeroes commands shall not impact this value. + * A value of %0h in this field indicates that the number + * of Data Units Written is not reported. + * @host_reads: Host Read Commands: Contains the number of read commands + * completed by the controller. For the NVM command set, + * this value is the sum of the number of Compare commands + * and the number of Read commands. + * @host_writes: Host Write Commands: Contains the number of write + * commands completed by the controller. For the NVM + * command set, this is the number of Write commands. + * @ctrl_busy_time: Controller Busy Time: Contains the amount of time the + * controller is busy with I/O commands. The controller + * is busy when there is a command outstanding to an I/O + * Queue (specifically, a command was issued via an I/O + * Submission Queue Tail doorbell write and the corresponding + * completion queue entry has not been posted yet to the + * associated I/O Completion Queue). This value is + * reported in minutes. + * @power_cycles: Power Cycles: Contains the number of power cycles. + * @power_on_hours: Power On Hours: Contains the number of power-on hours. + * This may not include time that the controller was + * powered and in a non-operational power state. + * @unsafe_shutdowns: Unsafe Shutdowns: Contains the number of unsafe + * shutdowns. This count is incremented when a Shutdown + * Notification (CC.SHN) is not received prior to loss of power. + * @media_errors: Media and Data Integrity Errors: Contains the number + * of occurrences where the controller detected an + * unrecovered data integrity error. Errors such as + * uncorrectable ECC, CRC checksum failure, or LBA tag + * mismatch are included in this field. Errors introduced + * as a result of a Write Uncorrectable command may or + * may not be included in this field. + * @num_err_log_entries: Number of Error Information Log Entries: Contains the + * number of Error Information log entries over the life + * of the controller. + * @warning_temp_time: Warning Composite Temperature Time: Contains the amount + * of time in minutes that the controller is operational + * and the Composite Temperature is greater than or equal + * to the Warning Composite Temperature Threshold (WCTEMP) + * field and less than the Critical Composite Temperature + * Threshold (CCTEMP) field in the Identify Controller + * data structure. If the value of the WCTEMP or CCTEMP + * field is %0h, then this field is always cleared to %0h + * regardless of the Composite Temperature value. + * @critical_comp_time: Critical Composite Temperature Time: Contains the amount + * of time in minutes that the controller is operational + * and the Composite Temperature is greater than or equal + * to the Critical Composite Temperature Threshold (CCTEMP) + * field in the Identify Controller data structure. If + * the value of the CCTEMP field is %0h, then this field + * is always cleared to 0h regardless of the Composite + * Temperature value. + * @temp_sensor: Temperature Sensor 1-8: Contains the current temperature + * in degrees Kelvin reported by temperature sensors 1-8. + * The physical point in the NVM subsystem whose temperature + * is reported by the temperature sensor and the temperature + * accuracy is implementation specific. An implementation + * that does not implement the temperature sensor reports + * a value of %0h. + * @thm_temp1_trans_count: Thermal Management Temperature 1 Transition Count: + * Contains the number of times the controller transitioned + * to lower power active power states or performed vendor + * specific thermal management actions while minimizing + * the impact on performance in order to attempt to reduce + * the Composite Temperature because of the host controlled + * thermal management feature (i.e., the Composite + * Temperature rose above the Thermal Management + * Temperature 1). This counter shall not wrap once the + * value %FFFFFFFFh is reached. A value of %0h, indicates + * that this transition has never occurred or this field + * is not implemented. + * @thm_temp2_trans_count: Thermal Management Temperature 2 Transition Count + * @thm_temp1_total_time: Total Time For Thermal Management Temperature 1: + * Contains the number of seconds that the controller + * had transitioned to lower power active power states or + * performed vendor specific thermal management actions + * while minimizing the impact on performance in order to + * attempt to reduce the Composite Temperature because of + * the host controlled thermal management feature. This + * counter shall not wrap once the value %FFFFFFFFh is + * reached. A value of %0h, indicates that this transition + * has never occurred or this field is not implemented. + * @thm_temp2_total_time: Total Time For Thermal Management Temperature 2 + * @rsvd232: Reserved + */ +struct nvme_smart_log { + __u8 critical_warning; + __u8 temperature[2]; + __u8 avail_spare; + __u8 spare_thresh; + __u8 percent_used; + __u8 endu_grp_crit_warn_sumry; + __u8 rsvd7[25]; + __u8 data_units_read[16]; + __u8 data_units_written[16]; + __u8 host_reads[16]; + __u8 host_writes[16]; + __u8 ctrl_busy_time[16]; + __u8 power_cycles[16]; + __u8 power_on_hours[16]; + __u8 unsafe_shutdowns[16]; + __u8 media_errors[16]; + __u8 num_err_log_entries[16]; + __le32 warning_temp_time; + __le32 critical_comp_time; + __le16 temp_sensor[8]; + __le32 thm_temp1_trans_count; + __le32 thm_temp2_trans_count; + __le32 thm_temp1_total_time; + __le32 thm_temp2_total_time; + __u8 rsvd232[280]; +}; + +/** + * enum nvme_smart_crit - Critical Warning + * @NVME_SMART_CRIT_SPARE: If set, then the available spare capacity has fallen + * below the threshold. + * @NVME_SMART_CRIT_TEMPERATURE: If set, then a temperature is either greater + * than or equal to an over temperature threshold; or + * less than or equal to an under temperature threshold. + * @NVME_SMART_CRIT_DEGRADED: If set, then the NVM subsystem reliability has + * been degraded due to significant media related errors + * or any internal error that degrades NVM subsystem + * reliability. + * @NVME_SMART_CRIT_MEDIA: If set, then all of the media has been placed in read + * only mode. The controller shall not set this bit if + * the read-only condition on the media is a result of + * a change in the write protection state of a namespace. + * @NVME_SMART_CRIT_VOLATILE_MEMORY: If set, then the volatile memory backup + * device has failed. This field is only valid if the + * controller has a volatile memory backup solution. + * @NVME_SMART_CRIT_PMR_RO: If set, then the Persistent Memory Region has become + * read-only or unreliable. + */ +enum nvme_smart_crit { + NVME_SMART_CRIT_SPARE = 1 << 0, + NVME_SMART_CRIT_TEMPERATURE = 1 << 1, + NVME_SMART_CRIT_DEGRADED = 1 << 2, + NVME_SMART_CRIT_MEDIA = 1 << 3, + NVME_SMART_CRIT_VOLATILE_MEMORY = 1 << 4, + NVME_SMART_CRIT_PMR_RO = 1 << 5, +}; + +/** + * enum nvme_smart_egcw - Endurance Group Critical Warning Summary + * @NVME_SMART_EGCW_SPARE: If set, then the available spare capacity of one or + * more Endurance Groups has fallen below the threshold. + * @NVME_SMART_EGCW_DEGRADED: If set, then the reliability of one or more + * Endurance Groups has been degraded due to significant + * media related errors or any internal error that + * degrades NVM subsystem reliability. + * @NVME_SMART_EGCW_RO: If set, then the namespaces in one or more Endurance + * Groups have been placed in read only mode not as + * a result of a change in the write protection state + * of a namespace. + */ +enum nvme_smart_egcw { + NVME_SMART_EGCW_SPARE = 1 << 0, + NVME_SMART_EGCW_DEGRADED = 1 << 2, + NVME_SMART_EGCW_RO = 1 << 3, +}; + +/** + * struct nvme_firmware_slot - Firmware Slot Information Log + * @afi: Active Firmware Info + * @rsvd1: Reserved + * @frs: Firmware Revision for Slot + * @rsvd2: Reserved + */ +struct nvme_firmware_slot { + __u8 afi; + __u8 rsvd1[7]; + char frs[7][8]; + __u8 rsvd2[448]; +}; + +/** + * struct nvme_cmd_effects_log - Commands Supported and Effects Log + * @acs: Admin Command Supported + * @iocs: I/O Command Supported + * @rsvd: Reserved + */ +struct nvme_cmd_effects_log { + __le32 acs[256]; + __le32 iocs[256]; + __u8 rsvd[2048]; +}; + +/** + * enum nvme_cmd_effects - Commands Supported and Effects + * @NVME_CMD_EFFECTS_CSUPP: Command Supported + * @NVME_CMD_EFFECTS_LBCC: Logical Block Content Change + * @NVME_CMD_EFFECTS_NCC: Namespace Capability Change + * @NVME_CMD_EFFECTS_NIC: Namespace Inventory Change + * @NVME_CMD_EFFECTS_CCC: Controller Capability Change + * @NVME_CMD_EFFECTS_CSE_MASK: Command Submission and Execution + * @NVME_CMD_EFFECTS_UUID_SEL: UUID Selection Supported + */ +enum nvme_cmd_effects { + NVME_CMD_EFFECTS_CSUPP = 1 << 0, + NVME_CMD_EFFECTS_LBCC = 1 << 1, + NVME_CMD_EFFECTS_NCC = 1 << 2, + NVME_CMD_EFFECTS_NIC = 1 << 3, + NVME_CMD_EFFECTS_CCC = 1 << 4, + NVME_CMD_EFFECTS_CSE_MASK = 3 << 16, + NVME_CMD_EFFECTS_UUID_SEL = 1 << 19, +}; + +/** + * struct nvme_st_result - Self-test Result + * @dsts: Device Self-test Status: Indicates the device self-test code and the + * status of the operation (see &enum nvme_status_result and &enum nvme_st_code). + * @seg: Segment Number: Iindicates the segment number where the first self-test + * failure occurred. If Device Self-test Status (@dsts) is not set to + * #NVME_ST_RESULT_KNOWN_SEG_FAIL, then this field should be ignored. + * @vdi: Valid Diagnostic Information: Indicates the diagnostic failure + * information that is reported. See &enum nvme_st_valid_diag_info. + * @rsvd: Reserved + * @poh: Power On Hours (POH): Indicates the number of power-on hours at the + * time the device self-test operation was completed or aborted. This + * does not include time that the controller was powered and in a low + * power state condition. + * @nsid: Namespace Identifier (NSID): Indicates the namespace that the Failing + * LBA occurred on. Valid only when the NSID Valid bit + * (#NVME_ST_VALID_DIAG_INFO_NSID) is set in the Valid Diagnostic + * Information (@vdi) field. + * @flba: Failing LBA: indicates the LBA of the logical block that caused the + * test to fail. If the device encountered more than one failed logical + * block during the test, then this field only indicates one of those + * failed logical blocks. Valid only when the NSID Valid bit + * (#NVME_ST_VALID_DIAG_INFO_FLBA) is set in the Valid Diagnostic + * Information (@vdi) field. + * @sct: Status Code Type: This field may contain additional information related + * to errors or conditions. Bits 2:0 may contain additional information + * relating to errors or conditions that occurred during the device + * self-test operation represented in the same format used in the Status + * Code Type field of the completion queue entry (refer to &enum nvme_status_field). + * Valid only when the NSID Valid bit (#NVME_ST_VALID_DIAG_INFO_SCT) is + * set in the Valid Diagnostic Information (@vdi) field. + * @sc: Status Code: This field may contain additional information relating + * to errors or conditions that occurred during the device self-test + * operation represented in the same format used in the Status Code field + * of the completion queue entry. Valid only when the SCT Valid bit + * (#NVME_ST_VALID_DIAG_INFO_SC) is set in the Valid Diagnostic + * Information (@vdi) field. + * @vs: Vendor Specific. + */ +struct nvme_st_result { + __u8 dsts; + __u8 seg; + __u8 vdi; + __u8 rsvd; + __le64 poh; + __le32 nsid; + __le64 flba; + __u8 sct; + __u8 sc; + __u8 vs[2]; +} __attribute__((packed)); + +/** + * enum nvme_status_result - Result of the device self-test operation + * @NVME_ST_RESULT_NO_ERR: Operation completed without error. + * @NVME_ST_RESULT_ABORTED: Operation was aborted by a Device Self-test command. + * @NVME_ST_RESULT_CLR: Operation was aborted by a Controller Level Reset. + * @NVME_ST_RESULT_NS_REMOVED: Operation was aborted due to a removal of + * a namespace from the namespace inventory. + * @NVME_ST_RESULT_ABORTED_FORMAT: Operation was aborted due to the processing + * of a Format NVM command. + * @NVME_ST_RESULT_FATAL_ERR: A fatal error or unknown test error occurred + * while the controller was executing the device + * self-test operation and the operation did + * not complete. + * @NVME_ST_RESULT_UNKNOWN_SEG_FAIL: Operation completed with a segment that failed + * and the segment that failed is not known. + * @NVME_ST_RESULT_KNOWN_SEG_FAIL: Operation completed with one or more failed + * segments and the first segment that failed + * is indicated in the Segment Number field. + * @NVME_ST_RESULT_ABORTED_UNKNOWN: Operation was aborted for unknown reason. + * @NVME_ST_RESULT_ABORTED_SANITIZE: Operation was aborted due to a sanitize operation. + * @NVME_ST_RESULT_NOT_USED: Entry not used (does not contain a test result). + * @NVME_ST_RESULT_MASK: Mask to get the status result value from + * the &struct nvme_st_result.dsts field. + */ +enum nvme_status_result { + NVME_ST_RESULT_NO_ERR = 0x0, + NVME_ST_RESULT_ABORTED = 0x1, + NVME_ST_RESULT_CLR = 0x2, + NVME_ST_RESULT_NS_REMOVED = 0x3, + NVME_ST_RESULT_ABORTED_FORMAT = 0x4, + NVME_ST_RESULT_FATAL_ERR = 0x5, + NVME_ST_RESULT_UNKNOWN_SEG_FAIL = 0x6, + NVME_ST_RESULT_KNOWN_SEG_FAIL = 0x7, + NVME_ST_RESULT_ABORTED_UNKNOWN = 0x8, + NVME_ST_RESULT_ABORTED_SANITIZE = 0x9, + NVME_ST_RESULT_NOT_USED = 0xf, + NVME_ST_RESULT_MASK = 0xf, +}; + +/** + * enum nvme_st_code - Self-test Code value + * @NVME_ST_CODE_RESERVED: Reserved. + * @NVME_ST_CODE_SHORT: Short device self-test operation. + * @NVME_ST_CODE_EXTENDED: Extended device self-test operation. + * @NVME_ST_CODE_VS: Vendor specific. + * @NVME_ST_CODE_ABORT: Abort device self-test operation. + * @NVME_ST_CODE_SHIFT: Shift amount to get the code value from the + * &struct nvme_st_result.dsts field. + */ +enum nvme_st_code { + NVME_ST_CODE_RESERVED = 0x0, + NVME_ST_CODE_SHORT = 0x1, + NVME_ST_CODE_EXTENDED = 0x2, + NVME_ST_CODE_VS = 0xe, + NVME_ST_CODE_ABORT = 0xf, + NVME_ST_CODE_SHIFT = 4, +}; + +/** + * enum nvme_st_curr_op - Current Device Self-Test Operation + * @NVME_ST_CURR_OP_NOT_RUNNING: No device self-test operation in progress. + * @NVME_ST_CURR_OP_SHORT: Short device self-test operation in progress. + * @NVME_ST_CURR_OP_EXTENDED: Extended device self-test operation in progress. + * @NVME_ST_CURR_OP_VS: Vendor specific. + * @NVME_ST_CURR_OP_RESERVED: Reserved. + * @NVME_ST_CURR_OP_MASK: Mask to get the current operation value from the + * &struct nvme_self_test_log.current_operation field. + * @NVME_ST_CURR_OP_CMPL_MASK: Mask to get the current operation completion value + * from the &struct nvme_self_test_log.completion field. + */ +enum nvme_st_curr_op { + NVME_ST_CURR_OP_NOT_RUNNING = 0x0, + NVME_ST_CURR_OP_SHORT = 0x1, + NVME_ST_CURR_OP_EXTENDED = 0x2, + NVME_ST_CURR_OP_VS = 0xe, + NVME_ST_CURR_OP_RESERVED = 0xf, + NVME_ST_CURR_OP_MASK = 0xf, + NVME_ST_CURR_OP_CMPL_MASK = 0x7f, +}; + +/** + * enum nvme_st_valid_diag_info - Valid Diagnostic Information + * @NVME_ST_VALID_DIAG_INFO_NSID: NSID Valid: if set, then the contents of + * the Namespace Identifier field are valid. + * @NVME_ST_VALID_DIAG_INFO_FLBA: FLBA Valid: if set, then the contents of + * the Failing LBA field are valid. + * @NVME_ST_VALID_DIAG_INFO_SCT: SCT Valid: if set, then the contents of + * the Status Code Type field are valid. + * @NVME_ST_VALID_DIAG_INFO_SC: SC Valid: if set, then the contents of + * the Status Code field are valid. + */ +enum nvme_st_valid_diag_info { + NVME_ST_VALID_DIAG_INFO_NSID = 1 << 0, + NVME_ST_VALID_DIAG_INFO_FLBA = 1 << 1, + NVME_ST_VALID_DIAG_INFO_SCT = 1 << 2, + NVME_ST_VALID_DIAG_INFO_SC = 1 << 3, +}; + +/** + * struct nvme_self_test_log - Device Self-test (Log Identifier 06h) + * @current_operation: Current Device Self-Test Operation: indicates the status + * of the current device self-test operation. If a device + * self-test operation is in process (i.e., this field is set + * to #NVME_ST_CURR_OP_SHORT or #NVME_ST_CURR_OP_EXTENDED), + * then the controller shall not set this field to + * #NVME_ST_CURR_OP_NOT_RUNNING until a new Self-test Result + * Data Structure is created (i.e., if a device self-test + * operation completes or is aborted, then the controller + * shall create a Self-test Result Data Structure prior to + * setting this field to #NVME_ST_CURR_OP_NOT_RUNNING). + * See &enum nvme_st_curr_op. + * @completion: Current Device Self-Test Completion: indicates the percentage + * of the device self-test operation that is complete (e.g., + * a value of 25 indicates that 25% of the device self-test + * operation is complete and 75% remains to be tested). + * If the @current_operation field is cleared to + * #NVME_ST_CURR_OP_NOT_RUNNING (indicating there is no device + * self-test operation in progress), then this field is ignored. + * @rsvd: Reserved + * @result: Self-test Result Data Structures, see &struct nvme_st_result. + */ +struct nvme_self_test_log { + __u8 current_operation; + __u8 completion; + __u8 rsvd[2]; + struct nvme_st_result result[NVME_LOG_ST_MAX_RESULTS]; +} __attribute__((packed)); + +/** + * enum nvme_cmd_get_log_telemetry_host_lsp - Telemetry Host-Initiated log specific field + * @NVME_LOG_TELEM_HOST_LSP_RETAIN: Get Telemetry Data Blocks + * @NVME_LOG_TELEM_HOST_LSP_CREATE: Create Telemetry Data Blocks + */ +enum nvme_cmd_get_log_telemetry_host_lsp { + NVME_LOG_TELEM_HOST_LSP_RETAIN = 0, + NVME_LOG_TELEM_HOST_LSP_CREATE = 1, +}; + +/** + * struct nvme_telemetry_log - Retrieve internal data specific to the + * manufacturer. + * @lpi: Log Identifier, either %NVME_LOG_LID_TELEMETRY_HOST or + * %NVME_LOG_LID_TELEMETRY_CTRL + * @rsvd1: Reserved + * @ieee: IEEE OUI Identifier is the Organization Unique Identifier (OUI) + * for the controller vendor that is able to interpret the data. + * @dalb1: Telemetry Controller-Initiated Data Area 1 Last Block is + * the value of the last block in this area. + * @dalb2: Telemetry Controller-Initiated Data Area 1 Last Block is + * the value of the last block in this area. + * @dalb3: Telemetry Controller-Initiated Data Area 1 Last Block is + * the value of the last block in this area. + * @rsvd14: Reserved + * @dalb4: Telemetry Controller-Initiated Data Area 4 Last Block is + * the value of the last block in this area. + * @rsvd20: Reserved + * @hostdgn: Telemetry Host-Initiated Data Generation Number is a + * value that is incremented each time the host initiates a + * capture of its internal controller state in the controller . + * @ctrlavail: Telemetry Controller-Initiated Data Available, if cleared, + * then the controller telemetry log does not contain saved + * internal controller state. If this field is set to 1h, the + * controller log contains saved internal controller state. If + * this field is set to 1h, the data will be latched until the + * host releases it by reading the log with RAE cleared. + * @ctrldgn: Telemetry Controller-Initiated Data Generation Number is + * a value that is incremented each time the controller initiates a + * capture of its internal controller state in the controller . + * @rsnident: Reason Identifiers a vendor specific identifier that describes + * the operating conditions of the controller at the time of + * capture. + * @data_area: Telemetry data blocks, vendor specific information data. + * + * This log consists of a header describing the log and zero or more Telemetry + * Data Blocks. All Telemetry Data Blocks are %NVME_LOG_TELEM_BLOCK_SIZE, 512 + * bytes, in size. This log captures the controller’s internal state. + */ +struct nvme_telemetry_log { + __u8 lpi; + __u8 rsvd1[4]; + __u8 ieee[3]; + __le16 dalb1; + __le16 dalb2; + __le16 dalb3; + __u8 rsvd14[2]; + __le32 dalb4; + __u8 rsvd20[361]; + __u8 hostdgn; + __u8 ctrlavail; + __u8 ctrldgn; + __u8 rsnident[128]; + __u8 data_area[]; +}; + +/** + * struct nvme_endurance_group_log - Endurance Group Information Log + * @critical_warning: Critical Warning + * @endurance_group_features: Endurance Group Features + * @rsvd2: Reserved + * @avl_spare: Available Spare + * @avl_spare_threshold: Available Spare Threshold + * @percent_used: Percentage Used + * @domain_identifier: Domain Identifier + * @rsvd8: Reserved + * @endurance_estimate: Endurance Estimate + * @data_units_read: Data Units Read + * @data_units_written: Data Units Written + * @media_units_written: Media Units Written + * @host_read_cmds: Host Read Commands + * @host_write_cmds: Host Write Commands + * @media_data_integrity_err: Media and Data Integrity Errors + * @num_err_info_log_entries: Number of Error Information Log Entries + * @total_end_grp_cap: Total Endurance Group Capacity + * @unalloc_end_grp_cap: Unallocated Endurance Group Capacity + * @rsvd192: Reserved + */ +struct nvme_endurance_group_log { + __u8 critical_warning; + __u8 endurance_group_features; + __u8 rsvd2; + __u8 avl_spare; + __u8 avl_spare_threshold; + __u8 percent_used; + __le16 domain_identifier; + __u8 rsvd8[24]; + __u8 endurance_estimate[16]; + __u8 data_units_read[16]; + __u8 data_units_written[16]; + __u8 media_units_written[16]; + __u8 host_read_cmds[16]; + __u8 host_write_cmds[16]; + __u8 media_data_integrity_err[16]; + __u8 num_err_info_log_entries[16]; + __u8 total_end_grp_cap[16]; + __u8 unalloc_end_grp_cap[16]; + __u8 rsvd192[320]; +}; + +/** + * enum nvme_eg_critical_warning_flags - Endurance Group Information Log - Critical Warning + * @NVME_EG_CRITICAL_WARNING_SPARE: Available spare capacity of the Endurance Group + * has fallen below the threshold + * @NVME_EG_CRITICAL_WARNING_DEGRADED: Endurance Group reliability has been degraded + * @NVME_EG_CRITICAL_WARNING_READ_ONLY: Endurance Group have been placed in read only + * mode + */ +enum nvme_eg_critical_warning_flags { + NVME_EG_CRITICAL_WARNING_SPARE = 1 << 0, + NVME_EG_CRITICAL_WARNING_DEGRADED = 1 << 2, + NVME_EG_CRITICAL_WARNING_READ_ONLY = 1 << 3, +}; + +/** + * struct nvme_aggregate_endurance_group_event - Endurance Group Event Aggregate + * @num_entries: Number or entries + * @entries: List of entries + */ +struct nvme_aggregate_endurance_group_event { + __le64 num_entries; + __le16 entries[]; +}; + +/** + * struct nvme_nvmset_predictable_lat_log - Predictable Latency Mode - Deterministic Threshold Configuration Data + * @status: Status + * @rsvd1: Reserved + * @event_type: Event Type + * @rsvd4: Reserved + * @dtwin_rt: DTWIN Reads Typical + * @dtwin_wt: DTWIN Writes Typical + * @dtwin_tmax: DTWIN Time Maximum + * @ndwin_tmin_hi: NDWIN Time Minimum High + * @ndwin_tmin_lo: NDWIN Time Minimum Low + * @rsvd72: Reserved + * @dtwin_re: DTWIN Reads Estimate + * @dtwin_we: DTWIN Writes Estimate + * @dtwin_te: DTWIN Time Estimate + * @rsvd152: Reserved + */ +struct nvme_nvmset_predictable_lat_log { + __u8 status; + __u8 rsvd1; + __le16 event_type; + __u8 rsvd4[28]; + __le64 dtwin_rt; + __le64 dtwin_wt; + __le64 dtwin_tmax; + __le64 ndwin_tmin_hi; + __le64 ndwin_tmin_lo; + __u8 rsvd72[56]; + __le64 dtwin_re; + __le64 dtwin_we; + __le64 dtwin_te; + __u8 rsvd152[360]; +}; + +/** + * enum nvme_nvmeset_pl_status - Predictable Latency Per NVM Set Log - Status + * @NVME_NVMSET_PL_STATUS_DISABLED: Not used (Predictable Latency Mode not enabled) + * @NVME_NVMSET_PL_STATUS_DTWIN: Deterministic Window (DTWIN) + * @NVME_NVMSET_PL_STATUS_NDWIN: Non-Deterministic Window (NDWIN) + */ +enum nvme_nvmeset_pl_status { + NVME_NVMSET_PL_STATUS_DISABLED = 0, + NVME_NVMSET_PL_STATUS_DTWIN = 1, + NVME_NVMSET_PL_STATUS_NDWIN = 2, +}; + +/** + * enum nvme_nvmset_pl_events - Predictable Latency Per NVM Set Log - Event Type + * @NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN: DTWIN Reads Warning + * @NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN: DTWIN Writes Warning + * @NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN: DTWIN Time Warning + * @NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED: Autonomous transition from DTWIN + * to NDWIN due to typical or + * maximum value exceeded + * @NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION: Autonomous transition from DTWIN + * to NDWIN due to Deterministic + * Excursion + */ +enum nvme_nvmset_pl_events { + NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN = 1 << 0, + NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN = 1 << 1, + NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN = 1 << 2, + NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED = 1 << 14, + NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION = 1 << 15, +}; + +/** + * struct nvme_aggregate_predictable_lat_event - Predictable Latency Event Aggregate Log Page + * @num_entries: Number of entries + * @entries: Entry list + */ +struct nvme_aggregate_predictable_lat_event { + __le64 num_entries; + __le16 entries[]; +}; + +/** + * struct nvme_ana_group_desc - ANA Group Descriptor + * @grpid: ANA group id + * @nnsids: Number of namespaces in @nsids + * @chgcnt: Change counter + * @state: ANA state + * @rsvd17: Reserved + * @nsids: List of namespaces + */ +struct nvme_ana_group_desc { + __le32 grpid; + __le32 nnsids; + __le64 chgcnt; + __u8 state; + __u8 rsvd17[15]; + __le32 nsids[]; +}; + +/** + * enum nvme_ana_state - ANA Group Descriptor - Asymmetric Namespace Access State + * @NVME_ANA_STATE_OPTIMIZED: ANA Optimized state + * @NVME_ANA_STATE_NONOPTIMIZED: ANA Non-Optimized state + * @NVME_ANA_STATE_INACCESSIBLE: ANA Inaccessible state + * @NVME_ANA_STATE_PERSISTENT_LOSS: ANA Persistent Loss state + * @NVME_ANA_STATE_CHANGE: ANA Change state + */ +enum nvme_ana_state { + NVME_ANA_STATE_OPTIMIZED = 0x1, + NVME_ANA_STATE_NONOPTIMIZED = 0x2, + NVME_ANA_STATE_INACCESSIBLE = 0x3, + NVME_ANA_STATE_PERSISTENT_LOSS = 0x4, + NVME_ANA_STATE_CHANGE = 0xf, +}; + +/** + * struct nvme_ana_log - Asymmetric Namespace Access Log + * @chgcnt: Change Count + * @ngrps: Number of ANA Group Descriptors + * @rsvd10: Reserved + * @descs: ANA Group Descriptor + */ +struct nvme_ana_log { + __le64 chgcnt; + __le16 ngrps; + __u8 rsvd10[6]; + struct nvme_ana_group_desc descs[]; +}; + +/** + * struct nvme_persistent_event_log - Persistent Event Log + * @lid: Log Identifier + * @rsvd1: Reserved + * @tnev: Total Number of Events + * @tll: Total Log Length + * @rv: Log Revision + * @rsvd17: Reserved + * @lhl: Log Header Length + * @ts: Timestamp + * @poh: Power on Hours + * @pcc: Power Cycle Count + * @vid: PCI Vendor ID + * @ssvid: PCI Subsystem Vendor ID + * @sn: Serial Number + * @mn: Model Number + * @subnqn: NVM Subsystem NVMe Qualified Name + * @gen_number: Generation Number + * @rci: Reporting Context Information + * @rsvd378: Reserved + * @seb: Supported Events Bitmap + */ +struct nvme_persistent_event_log { + __u8 lid; + __u8 rsvd1[3]; + __le32 tnev; + __le64 tll; + __u8 rv; + __u8 rsvd17; + __le16 lhl; + __le64 ts; + __u8 poh[16]; + __le64 pcc; + __le16 vid; + __le16 ssvid; + char sn[20]; + char mn[40]; + char subnqn[NVME_NQN_LENGTH]; + __le16 gen_number; + __le32 rci; + __u8 rsvd378[102]; + __u8 seb[32]; +} __attribute__((packed)); + +/** + * struct nvme_persistent_event_entry - Persistent Event + * @etype: Event Type + * @etype_rev: Event Type Revision + * @ehl: Event Header Length + * @ehai: Event Header Additional Info + * @cntlid: Controller Identifier + * @ets: Event Timestamp + * @pelpid: Port Identifier + * @rsvd16: Reserved + * @vsil: Vendor Specific Information Length + * @el: Event Length + */ +struct nvme_persistent_event_entry { + __u8 etype; + __u8 etype_rev; + __u8 ehl; + __u8 ehai; + __le16 cntlid; + __le64 ets; + __le16 pelpid; + __u8 rsvd16[4]; + __le16 vsil; + __le16 el; +} __attribute__((packed)); + +/** + * enum nvme_persistent_event_types - Persistent event log events + * @NVME_PEL_SMART_HEALTH_EVENT: SMART / Health Log Snapshot Event + * @NVME_PEL_FW_COMMIT_EVENT: Firmware Commit Event + * @NVME_PEL_TIMESTAMP_EVENT: Timestamp Change Event + * @NVME_PEL_POWER_ON_RESET_EVENT: Power-on or Reset Event + * @NVME_PEL_NSS_HW_ERROR_EVENT: NVM Subsystem Hardware Error Event + * @NVME_PEL_CHANGE_NS_EVENT: Change Namespace Event + * @NVME_PEL_FORMAT_START_EVENT: Format NVM Start Event + * @NVME_PEL_FORMAT_COMPLETION_EVENT: Format NVM Completion Event + * @NVME_PEL_SANITIZE_START_EVENT: Sanitize Start Event + * @NVME_PEL_SANITIZE_COMPLETION_EVENT: Sanitize Completion Event + * @NVME_PEL_SET_FEATURE_EVENT: Set Feature Event + * @NVME_PEL_TELEMETRY_CRT: Telemetry Log Create Event + * @NVME_PEL_THERMAL_EXCURSION_EVENT: Thermal Excursion Event + */ +enum nvme_persistent_event_types { + NVME_PEL_SMART_HEALTH_EVENT = 0x01, + NVME_PEL_FW_COMMIT_EVENT = 0x02, + NVME_PEL_TIMESTAMP_EVENT = 0x03, + NVME_PEL_POWER_ON_RESET_EVENT = 0x04, + NVME_PEL_NSS_HW_ERROR_EVENT = 0x05, + NVME_PEL_CHANGE_NS_EVENT = 0x06, + NVME_PEL_FORMAT_START_EVENT = 0x07, + NVME_PEL_FORMAT_COMPLETION_EVENT = 0x08, + NVME_PEL_SANITIZE_START_EVENT = 0x09, + NVME_PEL_SANITIZE_COMPLETION_EVENT = 0x0a, + NVME_PEL_SET_FEATURE_EVENT = 0x0b, + NVME_PEL_TELEMETRY_CRT = 0x0c, + NVME_PEL_THERMAL_EXCURSION_EVENT = 0x0d, +}; + +/** + * struct nvme_fw_commit_event - Firmware Commit Event Data + * @old_fw_rev: Old Firmware Revision + * @new_fw_rev: New Firmware Revision + * @fw_commit_action: Firmware Commit Action + * @fw_slot: Firmware Slot + * @sct_fw: Status Code Type for Firmware Commit Command + * @sc_fw: Status Returned for Firmware Commit Command + * @vndr_assign_fw_commit_rc: Vendor Assigned Firmware Commit Result Code + */ +struct nvme_fw_commit_event { + __le64 old_fw_rev; + __le64 new_fw_rev; + __u8 fw_commit_action; + __u8 fw_slot; + __u8 sct_fw; + __u8 sc_fw; + __le16 vndr_assign_fw_commit_rc; +} __attribute__((packed)); + +/** + * struct nvme_timestamp - Timestamp - Data Structure for Get Features + * @timestamp: Timestamp value based on origin and synch field + * @attr: Attribute + * @rsvd: Reserved + */ +struct nvme_timestamp { + __u8 timestamp[6]; + __u8 attr; + __u8 rsvd; +}; + +/** + * struct nvme_time_stamp_change_event - Timestamp Change Event + * @previous_timestamp: Previous Timestamp + * @ml_secs_since_reset: Milliseconds Since Reset + */ +struct nvme_time_stamp_change_event { + __le64 previous_timestamp; + __le64 ml_secs_since_reset; +}; + +/** + * struct nvme_power_on_reset_info_list - Controller Reset Information + * @cid: Controller ID + * @fw_act: Firmware Activation + * @op_in_prog: Operation in Progress + * @rsvd4: Reserved + * @ctrl_power_cycle: Controller Power Cycle + * @power_on_ml_seconds: Power on milliseconds + * @ctrl_time_stamp: Controller Timestamp + */ +struct nvme_power_on_reset_info_list { + __le16 cid; + __u8 fw_act; + __u8 op_in_prog; + __u8 rsvd4[12]; + __le32 ctrl_power_cycle; + __le64 power_on_ml_seconds; + __le64 ctrl_time_stamp; +} __attribute__((packed)); + +/** + * struct nvme_nss_hw_err_event - NVM Subsystem Hardware Error Event + * @nss_hw_err_event_code: NVM Subsystem Hardware Error Event Code + * @rsvd2: Reserved + * @add_hw_err_info: Additional Hardware Error Information + */ +struct nvme_nss_hw_err_event { + __le16 nss_hw_err_event_code; + __u8 rsvd2[2]; + __u8 *add_hw_err_info; +}; + +/** + * struct nvme_change_ns_event - Change Namespace Event Data + * @nsmgt_cdw10: Namespace Management CDW10 + * @rsvd4: Reserved + * @nsze: Namespace Size + * @rsvd16: Reserved + * @nscap: Namespace Capacity + * @flbas: Formatted LBA Size + * @dps: End-to-end Data Protection Type Settings + * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities + * @rsvd35: Reserved + * @ana_grp_id: ANA Group Identifier + * @nvmset_id: NVM Set Identifier + * @rsvd42: Reserved + * @nsid: Namespace ID + */ +struct nvme_change_ns_event { + __le32 nsmgt_cdw10; + __u8 rsvd4[4]; + __le64 nsze; + __u8 rsvd16[8]; + __le64 nscap; + __u8 flbas; + __u8 dps; + __u8 nmic; + __u8 rsvd35; + __le32 ana_grp_id; + __le16 nvmset_id; + __le16 rsvd42; + __le32 nsid; +}; + +/** + * struct nvme_format_nvm_start_event - Format NVM Start Event Data + * @nsid: Namespace Identifier + * @fna: Format NVM Attributes + * @rsvd5: Reserved + * @format_nvm_cdw10: Format NVM CDW10 + */ +struct nvme_format_nvm_start_event { + __le32 nsid; + __u8 fna; + __u8 rsvd5[3]; + __le32 format_nvm_cdw10; +}; + +/** + * struct nvme_format_nvm_compln_event - Format NVM Completion Event Data + * @nsid: Namespace Identifier + * @smallest_fpi: Smallest Format Progress Indicator + * @format_nvm_status: Format NVM Status + * @compln_info: Completion Information + * @status_field: Status Field + */ +struct nvme_format_nvm_compln_event { + __le32 nsid; + __u8 smallest_fpi; + __u8 format_nvm_status; + __le16 compln_info; + __le32 status_field; +}; + +/** + * struct nvme_sanitize_start_event - Sanitize Start Event Data + * @sani_cap: SANICAP + * @sani_cdw10: Sanitize CDW10 + * @sani_cdw11: Sanitize CDW11 + */ +struct nvme_sanitize_start_event { + __le32 sani_cap; + __le32 sani_cdw10; + __le32 sani_cdw11; +}; + +/** + * struct nvme_sanitize_compln_event - Sanitize Completion Event Data + * @sani_prog: Sanitize Progress + * @sani_status: Sanitize Status + * @cmpln_info: Completion Information + * @rsvd6: Reserved + */ +struct nvme_sanitize_compln_event { + __le16 sani_prog; + __le16 sani_status; + __le16 cmpln_info; + __u8 rsvd6[2]; +}; + +/** + * struct nvme_set_feature_event - Set Feature Event Data + * @layout: Set Feature Event Layout + * @cdw_mem: Command Dwords Memory buffer + */ +struct nvme_set_feature_event { + __le32 layout; + __le32 cdw_mem[0]; +}; + +/** + * struct nvme_thermal_exc_event - Thermal Excursion Event Data + * @over_temp: Over Temperature + * @threshold: temperature threshold + */ +struct nvme_thermal_exc_event { + __u8 over_temp; + __u8 threshold; +}; + +/** + * struct nvme_lba_rd - LBA Range Descriptor + * @rslba: Range Starting LBA + * @rnlb: Range Number of Logical Blocks + * @rsvd12: Reserved + */ +struct nvme_lba_rd { + __le64 rslba; + __le32 rnlb; + __u8 rsvd12[4]; +}; + +/** + * struct nvme_lbas_ns_element - LBA Status Log Namespace Element + * @neid: Namespace Element Identifier + * @nlrd: Number of LBA Range Descriptors + * @ratype: Recommended Action Type. see @enum nvme_lba_status_atype + * @rsvd8: Reserved + * @lba_rd: LBA Range Descriptor + */ +struct nvme_lbas_ns_element { + __le32 neid; + __le32 nlrd; + __u8 ratype; + __u8 rsvd8[7]; + struct nvme_lba_rd lba_rd[]; +}; + +/** + * enum nvme_lba_status_atype - Potentially Unrecoverable LBAs + * @NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED: Potentially Unrecoverable LBAs + * @NVME_LBA_STATUS_ATYPE_SCAN_TRACKED: Potentially Unrecoverable LBAs + * associated with physical storage + */ +enum nvme_lba_status_atype { + NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED = 0x10, + NVME_LBA_STATUS_ATYPE_SCAN_TRACKED = 0x11, +}; + +/** + * struct nvme_lba_status_log - LBA Status Information Log + * @lslplen: LBA Status Log Page Length + * @nlslne: Number of LBA Status Log Namespace Elements + * @estulb: Estimate of Unrecoverable Logical Blocks + * @rsvd12: Reserved + * @lsgc: LBA Status Generation Counter + * @elements: LBA Status Log Namespace Element List + */ +struct nvme_lba_status_log { + __le32 lslplen; + __le32 nlslne; + __le32 estulb; + __u8 rsvd12[2]; + __le16 lsgc; + struct nvme_lbas_ns_element elements[]; +}; + +/** + * struct nvme_eg_event_aggregate_log - Endurance Group Event Aggregate + * @nr_entries: Number of Entries + * @egids: Endurance Group Identifier + */ +struct nvme_eg_event_aggregate_log { + __le64 nr_entries; + __le16 egids[]; +}; + +/** + * enum nvme_fid_supported_effects - FID Supported and Effects Data Structure definitions + * @NVME_FID_SUPPORTED_EFFECTS_FSUPP: FID Supported + * @NVME_FID_SUPPORTED_EFFECTS_UDCC: User Data Content Change + * @NVME_FID_SUPPORTED_EFFECTS_NCC: Namespace Capability Change + * @NVME_FID_SUPPORTED_EFFECTS_NIC: Namespace Inventory Change + * @NVME_FID_SUPPORTED_EFFECTS_CCC: Controller Capability Change + * @NVME_FID_SUPPORTED_EFFECTS_UUID_SEL: UUID Selection Supported + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT: FID Scope Shift + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK: FID Scope Mask + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS: Namespace Scope + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL: Controller Scope + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET: NVM Set Scope + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP: Endurance Group Scope + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN: Domain Scope + * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS: NVM Subsystem Scope + */ +enum nvme_fid_supported_effects { + NVME_FID_SUPPORTED_EFFECTS_FSUPP = 1 << 0, + NVME_FID_SUPPORTED_EFFECTS_UDCC = 1 << 1, + NVME_FID_SUPPORTED_EFFECTS_NCC = 1 << 2, + NVME_FID_SUPPORTED_EFFECTS_NIC = 1 << 3, + NVME_FID_SUPPORTED_EFFECTS_CCC = 1 << 4, + NVME_FID_SUPPORTED_EFFECTS_UUID_SEL = 1 << 19, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT = 20, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK = 0xfff, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS = 1 << 0, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL = 1 << 1, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET= 1 << 2, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP = 1 << 3, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN = 1 << 4, + NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS = 1 << 5, +}; + +/** + * struct nvme_fid_supported_effects_log - Feature Identifiers Supported and Effects + * @fid_support: Feature Identifier Supported + * + */ +struct nvme_fid_supported_effects_log { + __le32 fid_support[NVME_LOG_FID_SUPPORTED_EFFECTS_MAX]; +}; + +/** + * enum nvme_mi_cmd_supported_effects - MI Command Supported and Effects Data Structure + * @NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP: Command Supported + * @NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC: User Data Content Change + * @NVME_MI_CMD_SUPPORTED_EFFECTS_NCC: Namespace Capability Change + * @NVME_MI_CMD_SUPPORTED_EFFECTS_NIC: Namespace Inventory Change + * @NVME_MI_CMD_SUPPORTED_EFFECTS_CCC: Controller Capability Change + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT: 20 bit shift + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK: 12 bit mask - 0xfff + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS: Namespace Scope + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL: Controller Scope + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET: NVM Set Scope + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP: Endurance Group Scope + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN: Domain Scope + * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS: NVM Subsystem Scope + */ +enum nvme_mi_cmd_supported_effects { + NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP = 1 << 0, + NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC = 1 << 1, + NVME_MI_CMD_SUPPORTED_EFFECTS_NCC = 1 << 2, + NVME_MI_CMD_SUPPORTED_EFFECTS_NIC = 1 << 3, + NVME_MI_CMD_SUPPORTED_EFFECTS_CCC = 1 << 4, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT = 20, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK = 0xfff, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS = 1 << 0, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL = 1 << 1, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET = 1 << 2, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP = 1 << 3, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN = 1 << 4, + NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS = 1 << 5, +}; + +/** + * struct nvme_mi_cmd_supported_effects_log - NVMe-MI Commands Supported and Effects Log + * @mi_cmd_support: NVMe-MI Commands Supported + * @reserved1: Reserved + */ +struct nvme_mi_cmd_supported_effects_log { + __le32 mi_cmd_support[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX]; + __le32 reserved1[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED]; +}; + +/** + * struct nvme_boot_partition - Boot Partition Log + * @lid: Boot Partition Identifier + * @rsvd1: Reserved + * @bpinfo: Boot Partition Information + * @rsvd8: Reserved + * @boot_partition_data: Contains the contents of the + * specified Boot Partition + */ +struct nvme_boot_partition { + __u8 lid; + __u8 rsvd1[3]; + __le32 bpinfo; + __u8 rsvd8[8]; + __u8 boot_partition_data[]; +}; + +/** + * struct nvme_eom_lane_desc - EOM Lane Descriptor + * @rsvd0: Reserved + * @mstatus: Measurement Status + * @lane: Lane number + * @eye: Eye number + * @top: Absolute number of rows from center to top edge of eye + * @bottom: Absolute number of rows from center to bottom edge of eye + * @left: Absolute number of rows from center to left edge of eye + * @right: Absolute number of rows from center to right edge of eye + * @nrows: Number of Rows + * @ncols: Number of Columns + * @edlen: Eye Data Length + * @rsvd18: Reserved + * @eye_desc: Printable Eye, Eye Data, and any Padding + */ +struct nvme_eom_lane_desc { + __u8 rsvd0; + __u8 mstatus; + __u8 lane; + __u8 eye; + __le16 top; + __le16 bottom; + __le16 left; + __le16 right; + __le16 nrows; + __le16 ncols; + __le16 edlen; + __u8 rsvd18[14]; + __u8 eye_desc[]; +}; + +/** + * struct nvme_phy_rx_eom_log - Physical Interface Receiver Eye Opening Measurement Log + * @lid: Log Identifier + * @eomip: EOM In Progress + * @hsize: Header Size + * @rsize: Result Size + * @eomdgn: EOM Data Generation Number + * @lr: Log Revision + * @odp: Optional Data Present + * @lanes: Number of lanes configured for this port + * @epl: Eyes Per Lane + * @lspfc: Log Specific Parameter Field Copy + * @li: Link Information + * @rsvd15: Reserved + * @lsic: Log Specific Identifier Copy + * @dsize: Descriptor Size + * @nd: Number of Descriptors + * @maxtb: Maximum Top Bottom + * @maxlr: Maximum Left Right + * @etgood: Estimated Time for Good Quality + * @etbetter: Estimated Time for Better Quality + * @etbest: Estimated Time for Best Quality + * @rsvd36: Reserved + * @descs: EOM Lane Descriptors + */ +struct nvme_phy_rx_eom_log { + __u8 lid; + __u8 eomip; + __le16 hsize; + __le32 rsize; + __u8 eomdgn; + __u8 lr; + __u8 odp; + __u8 lanes; + __u8 epl; + __u8 lspfc; + __u8 li; + __u8 rsvd15[3]; + __le16 lsic; + __le32 dsize; + __le16 nd; + __le16 maxtb; + __le16 maxlr; + __le16 etgood; + __le16 etbetter; + __le16 etbest; + __u8 rsvd36[28]; + struct nvme_eom_lane_desc descs[]; +}; + +/** + * enum nvme_eom_optional_data - EOM Optional Data Present Fields + * @NVME_EOM_EYE_DATA_PRESENT: Eye Data Present + * @NVME_EOM_PRINTABLE_EYE_PRESENT: Printable Eye Present + */ +enum nvme_eom_optional_data { + NVME_EOM_EYE_DATA_PRESENT = 1, + NVME_EOM_PRINTABLE_EYE_PRESENT = 1 << 1, +}; + +/** + * enum nvme_phy_rx_eom_progress - EOM In Progress Values + * @NVME_PHY_RX_EOM_NOT_STARTED: EOM Not Started + * @NVME_PHY_RX_EOM_IN_PROGRESS: EOM In Progress + * @NVME_PHY_RX_EOM_COMPLETED: EOM Completed + */ +enum nvme_phy_rx_eom_progress { + NVME_PHY_RX_EOM_NOT_STARTED = 0, + NVME_PHY_RX_EOM_IN_PROGRESS = 1, + NVME_PHY_RX_EOM_COMPLETED = 2, +}; + +/** + * struct nvme_media_unit_stat_desc - Media Unit Status Descriptor + * @muid: Media Unit Identifier + * @domainid: Domain Identifier + * @endgid: Endurance Group Identifier + * @nvmsetid: NVM Set Identifier + * @cap_adj_fctr: Capacity Adjustment Factor + * @avl_spare: Available Spare + * @percent_used: Percentage Used + * @mucs: Number of Channels attached to media units + * @cio: Channel Identifiers Offset + */ +struct nvme_media_unit_stat_desc { + __le16 muid; + __le16 domainid; + __le16 endgid; + __le16 nvmsetid; + __le16 cap_adj_fctr; + __u8 avl_spare; + __u8 percent_used; + __u8 mucs; + __u8 cio; +}; + +/** + * struct nvme_media_unit_stat_log - Media Unit Status + * @nmu: Number unit status descriptor + * @cchans: Number of Channels + * @sel_config: Selected Configuration + * @rsvd6: Reserved + * @mus_desc: Media unit statistic descriptors + */ +struct nvme_media_unit_stat_log { + __le16 nmu; + __le16 cchans; + __le16 sel_config; + __u8 rsvd6[10]; + struct nvme_media_unit_stat_desc mus_desc[]; +}; + +/** + * struct nvme_media_unit_config_desc - Media Unit Configuration Descriptor + * @muid: Media Unit Identifier + * @rsvd2: Reserved + * @mudl: Media Unit Descriptor Length + */ +struct nvme_media_unit_config_desc { + __le16 muid; + __u8 rsvd2[4]; + __le16 mudl; +}; + +/** + * struct nvme_channel_config_desc - Channel Configuration Descriptor + * @chanid: Channel Identifier + * @chmus: Number Channel Media Units + * @mu_config_desc: Channel Unit config descriptors. + * See @struct nvme_media_unit_config_desc + */ +struct nvme_channel_config_desc { + __le16 chanid; + __le16 chmus; + struct nvme_media_unit_config_desc mu_config_desc[]; +}; + +/** + * struct nvme_end_grp_chan_desc - Endurance Group Channel Configuration Descriptor + * @egchans: Number of Channels + * @chan_config_desc: Channel config descriptors. + * See @struct nvme_channel_config_desc + */ +struct nvme_end_grp_chan_desc { + __le16 egchans; + struct nvme_channel_config_desc chan_config_desc[]; +}; + +/** + * struct nvme_end_grp_config_desc - Endurance Group Configuration Descriptor + * @endgid: Endurance Group Identifier + * @cap_adj_factor: Capacity Adjustment Factor + * @rsvd4: Reserved + * @tegcap: Total Endurance Group Capacity + * @segcap: Spare Endurance Group Capacity + * @end_est: Endurance Estimate + * @egsets: Number of NVM Sets + * @rsvd64: Reserved + * @nvmsetid: NVM Set Identifier + */ +struct nvme_end_grp_config_desc { + __le16 endgid; + __le16 cap_adj_factor; + __u8 rsvd4[12]; + __u8 tegcap[16]; + __u8 segcap[16]; + __u8 end_est[16]; + __u8 rsvd64[16]; + __le16 egsets; + __le16 nvmsetid[]; +}; + +/** + * struct nvme_capacity_config_desc - Capacity Configuration structure definitions + * @cap_config_id: Capacity Configuration Identifier + * @domainid: Domain Identifier + * @egcn: Number Endurance Group Configuration + * Descriptors + * @rsvd6: Reserved + * @egcd: Endurance Group Config descriptors. + * See @struct nvme_end_grp_config_desc + */ +struct nvme_capacity_config_desc { + __le16 cap_config_id; + __le16 domainid; + __le16 egcn; + __u8 rsvd6[26]; + struct nvme_end_grp_config_desc egcd[]; +}; + +/** + * struct nvme_supported_cap_config_list_log - Supported Capacity Configuration list log page + * @sccn: Number of capacity configuration + * @rsvd1: Reserved + * @cap_config_desc: Capacity configuration descriptor. + * See @struct nvme_capacity_config_desc + */ +struct nvme_supported_cap_config_list_log { + __u8 sccn; + __u8 rsvd1[15]; + struct nvme_capacity_config_desc cap_config_desc[]; +}; + +/** + * struct nvme_resv_notification_log - Reservation Notification Log + * @lpc: Log Page Count + * @rnlpt: See &enum nvme_resv_notify_rnlpt. + * @nalp: Number of Available Log Pages + * @rsvd9: Reserved + * @nsid: Namespace ID + * @rsvd16: Reserved + */ +struct nvme_resv_notification_log { + __le64 lpc; + __u8 rnlpt; + __u8 nalp; + __u8 rsvd9[2]; + __le32 nsid; + __u8 rsvd16[48]; +}; + +/** + * enum nvme_resv_notify_rnlpt - Reservation Notification Log - Reservation Notification Log Page Type + * @NVME_RESV_NOTIFY_RNLPT_EMPTY: Empty Log Page + * @NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED: Registration Preempted + * @NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED: Reservation Released + * @NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED: Reservation Preempted + */ +enum nvme_resv_notify_rnlpt { + NVME_RESV_NOTIFY_RNLPT_EMPTY = 0, + NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED = 1, + NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED = 2, + NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED = 3, +}; + +/** + * struct nvme_sanitize_log_page - Sanitize Status (Log Identifier 81h) + * @sprog: Sanitize Progress (SPROG): indicates the fraction complete of the + * sanitize operation. The value is a numerator of the fraction + * complete that has 65,536 (10000h) as its denominator. This value + * shall be set to FFFFh if the @sstat field is not set to + * %NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS. + * @sstat: Sanitize Status (SSTAT): indicates the status associated with + * the most recent sanitize operation. See &enum nvme_sanitize_sstat. + * @scdw10: Sanitize Command Dword 10 Information (SCDW10): contains the value + * of the Command Dword 10 field of the Sanitize command that started + * the sanitize operation. + * @eto: Estimated Time For Overwrite: indicates the number of seconds required + * to complete an Overwrite sanitize operation with 16 passes in + * the background when the No-Deallocate Modifies Media After Sanitize + * field is not set to 10b. A value of 0h indicates that the sanitize + * operation is expected to be completed in the background when the + * Sanitize command that started that operation is completed. A value + * of FFFFFFFFh indicates that no time period is reported. + * @etbe: Estimated Time For Block Erase: indicates the number of seconds + * required to complete a Block Erase sanitize operation in the + * background when the No-Deallocate Modifies Media After Sanitize + * field is not set to 10b. A value of 0h indicates that the sanitize + * operation is expected to be completed in the background when the + * Sanitize command that started that operation is completed. + * A value of FFFFFFFFh indicates that no time period is reported. + * @etce: Estimated Time For Crypto Erase: indicates the number of seconds + * required to complete a Crypto Erase sanitize operation in the + * background when the No-Deallocate Modifies Media After Sanitize + * field is not set to 10b. A value of 0h indicates that the sanitize + * operation is expected to be completed in the background when the + * Sanitize command that started that operation is completed. + * A value of FFFFFFFFh indicates that no time period is reported. + * @etond: Estimated Time For Overwrite With No-Deallocate Media Modification: + * indicates the number of seconds required to complete an Overwrite + * sanitize operation and the associated additional media modification + * after the Overwrite sanitize operation in the background when + * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize + * command that requested the Overwrite sanitize operation; and + * the No-Deallocate Modifies Media After Sanitize field is set to 10b. + * A value of 0h indicates that the sanitize operation is expected + * to be completed in the background when the Sanitize command that + * started that operation is completed. A value of FFFFFFFFh indicates + * that no time period is reported. + * @etbend: Estimated Time For Block Erase With No-Deallocate Media Modification: + * indicates the number of seconds required to complete a Block Erase + * sanitize operation and the associated additional media modification + * after the Block Erase sanitize operation in the background when + * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize + * command that requested the Overwrite sanitize operation; and + * the No-Deallocate Modifies Media After Sanitize field is set to 10b. + * A value of 0h indicates that the sanitize operation is expected + * to be completed in the background when the Sanitize command that + * started that operation is completed. A value of FFFFFFFFh indicates + * that no time period is reported. + * @etcend: Estimated Time For Crypto Erase With No-Deallocate Media Modification: + * indicates the number of seconds required to complete a Crypto Erase + * sanitize operation and the associated additional media modification + * after the Crypto Erase sanitize operation in the background when + * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize + * command that requested the Overwrite sanitize operation; and + * the No-Deallocate Modifies Media After Sanitize field is set to 10b. + * A value of 0h indicates that the sanitize operation is expected + * to be completed in the background when the Sanitize command that + * started that operation is completed. A value of FFFFFFFFh indicates + * that no time period is reported. + * @rsvd32: Reserved + */ +struct nvme_sanitize_log_page { + __le16 sprog; + __le16 sstat; + __le32 scdw10; + __le32 eto; + __le32 etbe; + __le32 etce; + __le32 etond; + __le32 etbend; + __le32 etcend; + __u8 rsvd32[480]; +}; + +/** + * enum nvme_sanitize_sstat - Sanitize Status (SSTAT) + * @NVME_SANITIZE_SSTAT_STATUS_SHIFT: Shift amount to get the status value of + * the most recent sanitize operation from + * the &struct nvme_sanitize_log_page.sstat + * field. + * @NVME_SANITIZE_SSTAT_STATUS_MASK: Mask to get the status value of the most + * recent sanitize operation. + * @NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED: The NVM subsystem has never been + * sanitized. + * @NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS: The most recent sanitize operation + * completed successfully including any + * additional media modification. + * @NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS: A sanitize operation is currently in progress. + * @NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED: The most recent sanitize operation + * failed. + * @NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS: The most recent sanitize operation + * for which No-Deallocate After Sanitize was + * requested has completed successfully with + * deallocation of all user data. + * @NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT: Shift amount to get the number + * of completed passes if the most recent + * sanitize operation was an Overwrite. This + * value shall be cleared to 0h if the most + * recent sanitize operation was not + * an Overwrite. + * @NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK: Mask to get the number of completed + * passes. + * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT: Shift amount to get the Global + * Data Erased value from the + * &struct nvme_sanitize_log_page.sstat field. + * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK: Mask to get the Global Data Erased + * value. + * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED: Global Data Erased: if set, then no + * namespace user data in the NVM subsystem + * has been written to and no Persistent + * Memory Region in the NVM subsystem has + * been enabled since being manufactured and + * the NVM subsystem has never been sanitized; + * or since the most recent successful sanitize + * operation. + */ +enum nvme_sanitize_sstat { + NVME_SANITIZE_SSTAT_STATUS_SHIFT = 0, + NVME_SANITIZE_SSTAT_STATUS_MASK = 0x7, + NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED = 0, + NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS = 1, + NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS = 2, + NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED = 3, + NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS = 4, + NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT = 3, + NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK = 0x1f, + NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT = 8, + NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK = 0x1, + NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED = 1 << NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT, +}; + +/** + * struct nvme_zns_changed_zone_log - ZNS Changed Zone List log + * @nrzid: Number of Zone Identifiers + * @rsvd2: Reserved + * @zid: Zone Identifier + */ +struct nvme_zns_changed_zone_log { + __le16 nrzid; + __u8 rsvd2[6]; + __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX]; +}; + +/** + * enum nvme_zns_zt - Zone Descriptor Data Structure - Zone Type + * @NVME_ZONE_TYPE_SEQWRITE_REQ: Sequential Write Required + */ +enum nvme_zns_zt { + NVME_ZONE_TYPE_SEQWRITE_REQ = 0x2, +}; + +/** + * enum nvme_zns_za - Zone Descriptor Data Structure + * @NVME_ZNS_ZA_ZFC: Zone Finished by Controller + * @NVME_ZNS_ZA_FZR: Finish Zone Recommended + * @NVME_ZNS_ZA_RZR: Reset Zone Recommended + * @NVME_ZNS_ZA_ZRWAV: + * @NVME_ZNS_ZA_ZDEV: Zone Descriptor Extension Valid + */ +enum nvme_zns_za { + NVME_ZNS_ZA_ZFC = 1 << 0, + NVME_ZNS_ZA_FZR = 1 << 1, + NVME_ZNS_ZA_RZR = 1 << 2, + NVME_ZNS_ZA_ZRWAV = 1 << 3, + NVME_ZNS_ZA_ZDEV = 1 << 7, +}; + +/** + * enum nvme_zns_zs - Zone Descriptor Data Structure - Zone State + * @NVME_ZNS_ZS_EMPTY: Empty state + * @NVME_ZNS_ZS_IMPL_OPEN: Implicitly open state + * @NVME_ZNS_ZS_EXPL_OPEN: Explicitly open state + * @NVME_ZNS_ZS_CLOSED: Closed state + * @NVME_ZNS_ZS_READ_ONLY: Read only state + * @NVME_ZNS_ZS_FULL: Full state + * @NVME_ZNS_ZS_OFFLINE: Offline state + */ +enum nvme_zns_zs { + NVME_ZNS_ZS_EMPTY = 0x1, + NVME_ZNS_ZS_IMPL_OPEN = 0x2, + NVME_ZNS_ZS_EXPL_OPEN = 0x3, + NVME_ZNS_ZS_CLOSED = 0x4, + NVME_ZNS_ZS_READ_ONLY = 0xd, + NVME_ZNS_ZS_FULL = 0xe, + NVME_ZNS_ZS_OFFLINE = 0xf, +}; + +/** + * struct nvme_zns_desc - Zone Descriptor Data Structure + * @zt: Zone Type + * @zs: Zone State + * @za: Zone Attributes + * @zai: Zone Attributes Information + * @rsvd4: Reserved + * @zcap: Zone Capacity + * @zslba: Zone Start Logical Block Address + * @wp: Write Pointer + * @rsvd32: Reserved + */ +struct nvme_zns_desc { + __u8 zt; + __u8 zs; + __u8 za; + __u8 zai; + __u8 rsvd4[4]; + __le64 zcap; + __le64 zslba; + __le64 wp; + __u8 rsvd32[32]; +}; + +/** + * struct nvme_zone_report - Report Zones Data Structure + * @nr_zones: Number of descriptors in @entries + * @rsvd8: Reserved + * @entries: Zoned namespace descriptors + */ +struct nvme_zone_report { + __le64 nr_zones; + __u8 rsvd8[56]; + struct nvme_zns_desc entries[]; +}; + +/** + * enum nvme_fdp_ruh_type - Reclaim Unit Handle Type + * @NVME_FDP_RUHT_INITIALLY_ISOLATED: Initially Isolated + * @NVME_FDP_RUHT_PERSISTENTLY_ISOLATED: Persistently Isolated + */ +enum nvme_fdp_ruh_type { + NVME_FDP_RUHT_INITIALLY_ISOLATED = 1, + NVME_FDP_RUHT_PERSISTENTLY_ISOLATED = 2, +}; + +/** + * struct nvme_fdp_ruh_desc - Reclaim Unit Handle Descriptor + * @ruht: Reclaim Unit Handle Type + * @rsvd1: Reserved + */ +struct nvme_fdp_ruh_desc { + __u8 ruht; + __u8 rsvd1[3]; +}; + +/** + * enum nvme_fdp_config_fdpa - FDP Attributes + * @NVME_FDP_CONFIG_FDPA_RGIF_SHIFT: Reclaim Group Identifier Format Shift + * @NVME_FDP_CONFIG_FDPA_RGIF_MASK: Reclaim Group Identifier Format Mask + * @NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT: FDP Volatile Write Cache Shift + * @NVME_FDP_CONFIG_FDPA_FDPVWC_MASK: FDP Volatile Write Cache Mask + * @NVME_FDP_CONFIG_FDPA_VALID_SHIFT: FDP Configuration Valid Shift + * @NVME_FDP_CONFIG_FDPA_VALID_MASK: FDP Configuration Valid Mask + */ +enum nvme_fdp_config_fdpa { + NVME_FDP_CONFIG_FDPA_RGIF_SHIFT = 0, + NVME_FDP_CONFIG_FDPA_RGIF_MASK = 0xf, + NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT = 4, + NVME_FDP_CONFIG_FDPA_FDPVWC_MASK = 0x1, + NVME_FDP_CONFIG_FDPA_VALID_SHIFT = 7, + NVME_FDP_CONFIG_FDPA_VALID_MASK = 0x1, +}; + +/** + * struct nvme_fdp_config_desc - FDP Configuration Descriptor + * @size: Descriptor size + * @fdpa: FDP Attributes (&enum nvme_fdp_config_fdpa) + * @vss: Vendor Specific Size + * @nrg: Number of Reclaim Groups + * @nruh: Number of Reclaim Unit Handles + * @maxpids: Max Placement Identifiers + * @nnss: Number of Namespaces Supported + * @runs: Reclaim Unit Nominal Size + * @erutl: Estimated Reclaim Unit Time Limit + * @rsvd28: Reserved + * @ruhs: Reclaim Unit Handle descriptors (&struct nvme_fdp_ruh_desc) + */ +struct nvme_fdp_config_desc { + __le16 size; + __u8 fdpa; + __u8 vss; + __le32 nrg; + __le16 nruh; + __le16 maxpids; + __le32 nnss; + __le64 runs; + __le32 erutl; + __u8 rsvd28[36]; + struct nvme_fdp_ruh_desc ruhs[]; +}; + +/** + * struct nvme_fdp_config_log - FDP Configurations Log Page + * @n: Number of FDP Configurations + * @version: Log page version + * @rsvd3: Reserved + * @size: Log page size in bytes + * @rsvd8: Reserved + * @configs: FDP Configuration descriptors (&struct nvme_fdp_config_desc) + */ +struct nvme_fdp_config_log { + __le16 n; + __u8 version; + __u8 rsvd3; + __le32 size; + __u8 rsvd8[8]; + struct nvme_fdp_config_desc configs[]; +}; + +/** + * enum nvme_fdp_ruha - Reclaim Unit Handle Attributes + * @NVME_FDP_RUHA_HOST_SHIFT: Host Specified Reclaim Unit Handle Shift + * @NVME_FDP_RUHA_HOST_MASK: Host Specified Reclaim Unit Handle Mask + * @NVME_FDP_RUHA_CTRL_SHIFT: Controller Specified Reclaim Unit Handle Shift + * @NVME_FDP_RUHA_CTRL_MASK: Controller Specified Reclaim Unit Handle Mask + */ +enum nvme_fdp_ruha { + NVME_FDP_RUHA_HOST_SHIFT = 0, + NVME_FDP_RUHA_HOST_MASK = 0x1, + NVME_FDP_RUHA_CTRL_SHIFT = 1, + NVME_FDP_RUHA_CTRL_MASK = 0x1, +}; + +/** + * struct nvme_fdp_ruhu_desc - Reclaim Unit Handle Usage Descriptor + * @ruha: Reclaim Unit Handle Attributes (&enum nvme_fdp_ruha) + * @rsvd1: Reserved + */ +struct nvme_fdp_ruhu_desc { + __u8 ruha; + __u8 rsvd1[7]; +}; + +/** + * struct nvme_fdp_ruhu_log - Reclaim Unit Handle Usage Log Page + * @nruh: Number of Reclaim Unit Handles + * @rsvd2: Reserved + * @ruhus: Reclaim Unit Handle Usage descriptors + */ +struct nvme_fdp_ruhu_log { + __le16 nruh; + __u8 rsvd2[6]; + struct nvme_fdp_ruhu_desc ruhus[]; +}; + +/** + * struct nvme_fdp_stats_log - FDP Statistics Log Page + * @hbmw: Host Bytes with Metadata Written + * @mbmw: Media Bytes with Metadata Written + * @mbe: Media Bytes Erased + * @rsvd48: Reserved + */ +struct nvme_fdp_stats_log { + __u8 hbmw[16]; + __u8 mbmw[16]; + __u8 mbe[16]; + __u8 rsvd48[16]; +}; + +/** + * enum nvme_fdp_event_type - FDP Event Types + * @NVME_FDP_EVENT_RUNFW: Reclaim Unit Not Fully Written + * @NVME_FDP_EVENT_RUTLE: Reclaim Unit Time Limit Exceeded + * @NVME_FDP_EVENT_RESET: Controller Level Reset Modified Reclaim Unit Handles + * @NVME_FDP_EVENT_PID: Invalid Placement Identifier + * @NVME_FDP_EVENT_REALLOC: Media Reallocated + * @NVME_FDP_EVENT_MODIFY: Implicitly Modified Reclaim Unit Handle + */ +enum nvme_fdp_event_type { + /* Host Events */ + NVME_FDP_EVENT_RUNFW = 0x0, + NVME_FDP_EVENT_RUTLE = 0x1, + NVME_FDP_EVENT_RESET = 0x2, + NVME_FDP_EVENT_PID = 0x3, + + /* Controller Events */ + NVME_FDP_EVENT_REALLOC = 0x80, + NVME_FDP_EVENT_MODIFY = 0x81, +}; + +/** + * enum nvme_fdp_event_realloc_flags - Media Reallocated Event Type Specific Flags + * @NVME_FDP_EVENT_REALLOC_F_LBAV: LBA Valid + */ +enum nvme_fdp_event_realloc_flags { + NVME_FDP_EVENT_REALLOC_F_LBAV = 1 << 0, +}; + +/** + * struct nvme_fdp_event_realloc - Media Reallocated Event Type Specific Information + * @flags: Event Type Specific flags (&enum nvme_fdp_event_realloc_flags) + * @rsvd1: Reserved + * @nlbam: Number of LBAs Moved + * @lba: Logical Block Address + * @rsvd12: Reserved + */ +struct nvme_fdp_event_realloc { + __u8 flags; + __u8 rsvd1; + __le16 nlbam; + __le64 lba; + __u8 rsvd12[4]; +}; + +/** + * enum nvme_fdp_event_flags - FDP Event Flags + * @NVME_FDP_EVENT_F_PIV: Placement Identifier Valid + * @NVME_FDP_EVENT_F_NSIDV: Namespace Identifier Valid + * @NVME_FDP_EVENT_F_LV: Location Valid + */ +enum nvme_fdp_event_flags { + NVME_FDP_EVENT_F_PIV = 1 << 0, + NVME_FDP_EVENT_F_NSIDV = 1 << 1, + NVME_FDP_EVENT_F_LV = 1 << 2, +}; + +/** + * struct nvme_fdp_event - FDP Event + * @type: Event Type (&enum nvme_fdp_event_type) + * @flags: Event Flags (&enum nvme_fdp_event_flags) + * @pid: Placement Identifier + * @ts: Timestamp + * @nsid: Namespace Identifier + * @type_specific: Event Type Specific Information + * @rgid: Reclaim Group Identifier + * @ruhid: Reclaim Unit Handle Identifier + * @rsvd35: Reserved + * @vs: Vendor Specific + */ +struct nvme_fdp_event { + __u8 type; + __u8 flags; + __le16 pid; + struct nvme_timestamp ts; + __le32 nsid; + __u8 type_specific[16]; + __le16 rgid; + __u8 ruhid; + __u8 rsvd35[5]; + __u8 vs[24]; +}; + +/** + * struct nvme_fdp_events_log - FDP Events Log Page + * @n: Number of FDP Events + * @rsvd4: Reserved + * @events: FDP Events (&struct nvme_fdp_event) + */ +struct nvme_fdp_events_log { + __le32 n; + __u8 rsvd4[60]; + struct nvme_fdp_event events[63]; +}; + +/** + * struct nvme_feat_fdp_events_cdw11 - FDP Events Feature Command Dword 11 + * @phndl: Placement Handle + * @noet: Number of FDP Event Types + * @rsvd24: Reserved + */ +struct nvme_feat_fdp_events_cdw11 { + __le16 phndl; + __u8 noet; + __u8 rsvd24; +}; + +/** + * enum nvme_fdp_supported_event_attributes - Supported FDP Event Attributes + * @NVME_FDP_SUPP_EVENT_ENABLED_SHIFT: FDP Event Enable Shift + * @NVME_FDP_SUPP_EVENT_ENABLED_MASK: FDP Event Enable Mask + */ +enum nvme_fdp_supported_event_attributes { + NVME_FDP_SUPP_EVENT_ENABLED_SHIFT = 0, + NVME_FDP_SUPP_EVENT_ENABLED_MASK = 0x1, +}; + +/** + * struct nvme_fdp_supported_event_desc - Supported FDP Event Descriptor + * @evt: FDP Event Type + * @evta: FDP Event Type Attributes (&enum nvme_fdp_supported_event_attributes) + */ +struct nvme_fdp_supported_event_desc { + __u8 evt; + __u8 evta; +}; + +/** + * struct nvme_fdp_ruh_status_desc - Reclaim Unit Handle Status Descriptor + * @pid: Placement Identifier + * @ruhid: Reclaim Unit Handle Identifier + * @earutr: Estimated Active Reclaim Unit Time Remaining + * @ruamw: Reclaim Unit Available Media Writes + * @rsvd16: Reserved + */ +struct nvme_fdp_ruh_status_desc { + __le16 pid; + __le16 ruhid; + __le32 earutr; + __le64 ruamw; + __u8 rsvd16[16]; +}; + +/** + * struct nvme_fdp_ruh_status - Reclaim Unit Handle Status + * @rsvd0: Reserved + * @nruhsd: Number of Reclaim Unit Handle Status Descriptors + * @ruhss: Reclaim Unit Handle Status descriptors + */ +struct nvme_fdp_ruh_status { + __u8 rsvd0[14]; + __le16 nruhsd; + struct nvme_fdp_ruh_status_desc ruhss[]; +}; + +/** + * struct nvme_lba_status_desc - LBA Status Descriptor Entry + * @dslba: Descriptor Starting LBA + * @nlb: Number of Logical Blocks + * @rsvd12: Reserved + * @status: Additional status about this LBA range + * @rsvd14: Reserved + */ +struct nvme_lba_status_desc { + __le64 dslba; + __le32 nlb; + __u8 rsvd12; + __u8 status; + __u8 rsvd14[2]; +}; + +/** + * struct nvme_lba_status - LBA Status Descriptor List + * @nlsd: Number of LBA Status Descriptors + * @cmpc: Completion Condition + * @rsvd5: Reserved + * @descs: LBA status descriptor Entry + */ +struct nvme_lba_status { + __le32 nlsd; + __u8 cmpc; + __u8 rsvd5[3]; + struct nvme_lba_status_desc descs[]; +}; + +/** + * struct nvme_feat_auto_pst - Autonomous Power State Transition + * @apst_entry: See &enum nvme_apst_entry + */ +struct nvme_feat_auto_pst { + __le64 apst_entry[32]; +}; + +/** + * enum nvme_apst_entry - Autonomous Power State Transition + * @NVME_APST_ENTRY_ITPS_SHIFT: Idle Transition Power State Shift + * @NVME_APST_ENTRY_ITPT_SHIFT: Idle Time Prior to Transition Shift + * @NVME_APST_ENTRY_ITPS_MASK: Idle Transition Power State Mask + * @NVME_APST_ENTRY_ITPT_MASK: Idle Time Prior to Transition Mask + */ +enum nvme_apst_entry { + NVME_APST_ENTRY_ITPS_SHIFT = 3, + NVME_APST_ENTRY_ITPT_SHIFT = 8, + NVME_APST_ENTRY_ITPS_MASK = 0x1f, + NVME_APST_ENTRY_ITPT_MASK = 0xffffff, +}; + +/** + * struct nvme_metadata_element_desc - Metadata Element Descriptor + * @type: Element Type (ET) + * @rev: Element Revision (ER) + * @len: Element Length (ELEN) + * @val: Element Value (EVAL), UTF-8 string + */ +struct nvme_metadata_element_desc { + __u8 type; + __u8 rev; + __le16 len; + __u8 val[0]; +}; + +/** + * struct nvme_host_metadata - Host Metadata Data Structure + * @ndesc: Number of metadata element descriptors + * @rsvd1: Reserved + * @descs: Metadata element descriptors + * @descs_buf: Metadata element descriptor buffer + */ +struct nvme_host_metadata { + __u8 ndesc; + __u8 rsvd1; + union { + struct nvme_metadata_element_desc descs[0]; + __u8 descs_buf[4094]; + }; +}; + +/** + * enum nvme_ctrl_metadata_type - Controller Metadata Element Types + * @NVME_CTRL_METADATA_OS_CTRL_NAME: Name of the controller in + * the operating system. + * @NVME_CTRL_METADATA_OS_DRIVER_NAME: Name of the driver in the + * operating system. + * @NVME_CTRL_METADATA_OS_DRIVER_VER: Version of the driver in + * the operating system. + * @NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME: Name of the controller in + * the pre-boot environment. + * @NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME: Name of the driver in the + * pre-boot environment. + * @NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER: Version of the driver in the + * pre-boot environment. + * @NVME_CTRL_METADATA_SYS_PROC_MODEL: Model of the processor. + * @NVME_CTRL_METADATA_CHIPSET_DRV_NAME: Chipset driver name. + * @NVME_CTRL_METADATA_CHIPSET_DRV_VERSION: Chipset driver version. + * @NVME_CTRL_METADATA_OS_NAME_AND_BUILD: Operating system name and build. + * @NVME_CTRL_METADATA_SYS_PROD_NAME: System product name. + * @NVME_CTRL_METADATA_FIRMWARE_VERSION: Host firmware (e.g UEFI) version. + * @NVME_CTRL_METADATA_OS_DRIVER_FILENAME: Operating system driver filename. + * @NVME_CTRL_METADATA_DISPLAY_DRV_NAME: Display driver name. + * @NVME_CTRL_METADATA_DISPLAY_DRV_VERSION: Display driver version. + * @NVME_CTRL_METADATA_HOST_DET_FAIL_REC: Failure record. + */ +enum nvme_ctrl_metadata_type { + NVME_CTRL_METADATA_OS_CTRL_NAME = 0x01, + NVME_CTRL_METADATA_OS_DRIVER_NAME = 0x02, + NVME_CTRL_METADATA_OS_DRIVER_VER = 0x03, + NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME = 0x04, + NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME = 0x05, + NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER = 0x06, + NVME_CTRL_METADATA_SYS_PROC_MODEL = 0x07, + NVME_CTRL_METADATA_CHIPSET_DRV_NAME = 0x08, + NVME_CTRL_METADATA_CHIPSET_DRV_VERSION = 0x09, + NVME_CTRL_METADATA_OS_NAME_AND_BUILD = 0x0a, + NVME_CTRL_METADATA_SYS_PROD_NAME = 0x0b, + NVME_CTRL_METADATA_FIRMWARE_VERSION = 0x0c, + NVME_CTRL_METADATA_OS_DRIVER_FILENAME = 0x0d, + NVME_CTRL_METADATA_DISPLAY_DRV_NAME = 0x0e, + NVME_CTRL_METADATA_DISPLAY_DRV_VERSION = 0x0f, + NVME_CTRL_METADATA_HOST_DET_FAIL_REC = 0x10, +}; + +/** + * enum nvme_ns_metadata_type - Namespace Metadata Element Types + * @NVME_NS_METADATA_OS_NS_NAME: Name of the namespace in the + * operating system + * @NVME_NS_METADATA_PRE_BOOT_NS_NAME: Name of the namespace in the pre-boot + * environment. + * @NVME_NS_METADATA_OS_NS_QUAL_1: First qualifier of the Operating System + * Namespace Name. + * @NVME_NS_METADATA_OS_NS_QUAL_2: Second qualifier of the Operating System + * Namespace Name. + */ +enum nvme_ns_metadata_type { + NVME_NS_METADATA_OS_NS_NAME = 0x01, + NVME_NS_METADATA_PRE_BOOT_NS_NAME = 0x02, + NVME_NS_METADATA_OS_NS_QUAL_1 = 0x03, + NVME_NS_METADATA_OS_NS_QUAL_2 = 0x04, +}; + +/** + * struct nvme_lba_range_type_entry - LBA Range Type - Data Structure Entry + * @type: Specifies the Type of the LBA range + * @attributes: Specifies attributes of the LBA range + * @rsvd2: Reserved + * @slba: Starting LBA + * @nlb: Number of Logical Blocks + * @guid: Unique Identifier + * @rsvd48: Reserved + */ +struct nvme_lba_range_type_entry { + __u8 type; + __u8 attributes; + __u8 rsvd2[14]; + __le64 slba; + __le64 nlb; + __u8 guid[16]; + __u8 rsvd48[16]; +}; + +/** + * enum nvme_lbart - LBA Range Type - Data Structure Entry + * @NVME_LBART_TYPE_GP: General Purpose + * @NVME_LBART_TYPE_FS: Filesystem + * @NVME_LBART_TYPE_RAID: RAID + * @NVME_LBART_TYPE_CACHE: Cache + * @NVME_LBART_TYPE_SWAP: Page / swap file + * @NVME_LBART_ATTRIB_TEMP: Temp + * @NVME_LBART_ATTRIB_HIDE: Hidden + */ +enum nvme_lbart { + NVME_LBART_TYPE_GP = 0, + NVME_LBART_TYPE_FS = 1, + NVME_LBART_TYPE_RAID = 2, + NVME_LBART_TYPE_CACHE = 3, + NVME_LBART_TYPE_SWAP = 4, + NVME_LBART_ATTRIB_TEMP = 1 << 0, + NVME_LBART_ATTRIB_HIDE = 1 << 1, +}; + +/** + * struct nvme_lba_range_type - LBA Range Type + * @entry: LBA range type entry. See @struct nvme_lba_range_type_entry + */ +struct nvme_lba_range_type { + struct nvme_lba_range_type_entry entry[NVME_FEAT_LBA_RANGE_MAX]; +}; + +/** + * struct nvme_plm_config - Predictable Latency Mode - Deterministic Threshold Configuration Data Structure + * @ee: Enable Event + * @rsvd2: Reserved + * @dtwinrt: DTWIN Reads Threshold + * @dtwinwt: DTWIN Writes Threshold + * @dtwintt: DTWIN Time Threshold + * @rsvd56: Reserved + */ +struct nvme_plm_config { + __le16 ee; + __u8 rsvd2[30]; + __le64 dtwinrt; + __le64 dtwinwt; + __le64 dtwintt; + __u8 rsvd56[456]; +}; + +/** + * struct nvme_feat_host_behavior - Host Behavior Support - Data Structure + * @acre: Advanced Command Retry Enable + * @etdas: Extended Telemetry Data Area 4 Supported + * @lbafee: LBA Format Extension Enable + * @rsvd3: Reserved + * @cdfe: Copy Descriptor Formats Enable + * @rsvd6: Reserved + */ +struct nvme_feat_host_behavior { + __u8 acre; + __u8 etdas; + __u8 lbafee; + __u8 rsvd3; + __u16 cdfe; + __u8 rsvd6[506]; +}; + +/** + * enum nvme_host_behavior_support - Enable Advanced Command + * @NVME_ENABLE_ACRE: Enable Advanced Command Retry Enable + */ +enum nvme_host_behavior_support { + NVME_ENABLE_ACRE = 1 << 0, +}; + +/** + * struct nvme_dsm_range - Dataset Management - Range Definition + * @cattr: Context Attributes + * @nlb: Length in logical blocks + * @slba: Starting LBA + */ +struct nvme_dsm_range { + __le32 cattr; + __le32 nlb; + __le64 slba; +}; + +/** + * struct nvme_copy_range - Copy - Source Range Entries Descriptor Format + * @rsvd0: Reserved + * @slba: Starting LBA + * @nlb: Number of Logical Blocks + * @rsvd18: Reserved + * @eilbrt: Expected Initial Logical Block Reference Tag / + * Expected Logical Block Storage Tag + * @elbatm: Expected Logical Block Application Tag Mask + * @elbat: Expected Logical Block Application Tag + */ +struct nvme_copy_range { + __u8 rsvd0[8]; + __le64 slba; + __le16 nlb; + __u8 rsvd18[6]; + __le32 eilbrt; + __le16 elbat; + __le16 elbatm; +}; + +/** + * struct nvme_copy_range_f1 - Copy - Source Range Entries Descriptor Format 1h + * @rsvd0: Reserved + * @slba: Starting LBA + * @nlb: Number of Logical Blocks + * @rsvd18: Reserved + * @elbt: Expected Initial Logical Block Reference Tag / + * Expected Logical Block Storage Tag + * @elbatm: Expected Logical Block Application Tag Mask + * @elbat: Expected Logical Block Application Tag + */ +struct nvme_copy_range_f1 { + __u8 rsvd0[8]; + __le64 slba; + __le16 nlb; + __u8 rsvd18[8]; + __u8 elbt[10]; + __le16 elbat; + __le16 elbatm; +}; + +/** + * enum nvme_copy_range_sopt - NVMe Copy Range Source Options + * @NVME_COPY_SOPT_FCO: NVMe Copy Source Option Fast Copy Only + */ +enum nvme_copy_range_sopt { + NVME_COPY_SOPT_FCO = 1 << 15, +}; + +/** + * struct nvme_copy_range_f2 - Copy - Source Range Entries Descriptor Format 2h + * @snsid: Source Namespace Identifier + * @rsvd4: Reserved + * @slba: Starting LBA + * @nlb: Number of Logical Blocks + * @rsvd18: Reserved + * @sopt: Source Options + * @eilbrt: Expected Initial Logical Block Reference Tag / + * Expected Logical Block Storage Tag + * @elbatm: Expected Logical Block Application Tag Mask + * @elbat: Expected Logical Block Application Tag + */ +struct nvme_copy_range_f2 { + __le32 snsid; + __u8 rsvd4[4]; + __le64 slba; + __le16 nlb; + __u8 rsvd18[4]; + __le16 sopt; + __le32 eilbrt; + __le16 elbat; + __le16 elbatm; +}; + +/** + * struct nvme_copy_range_f3 - Copy - Source Range Entries Descriptor Format 3h + * @snsid: Source Namespace Identifier + * @rsvd4: Reserved + * @slba: Starting LBA + * @nlb: Number of Logical Blocks + * @rsvd18: Reserved + * @sopt: Source Options + * @rsvd24: Reserved + * @elbt: Expected Initial Logical Block Reference Tag / + * Expected Logical Block Storage Tag + * @elbatm: Expected Logical Block Application Tag Mask + * @elbat: Expected Logical Block Application Tag + */ +struct nvme_copy_range_f3 { + __le32 snsid; + __u8 rsvd4[4]; + __le64 slba; + __le16 nlb; + __u8 rsvd18[4]; + __le16 sopt; + __u8 rsvd24[2]; + __u8 elbt[10]; + __le16 elbat; + __le16 elbatm; +}; + +/** + * struct nvme_registered_ctrl - Registered Controller Data Structure + * @cntlid: Controller ID + * @rcsts: Reservation Status + * @rsvd3: Reserved + * @hostid: Host Identifier + * @rkey: Reservation Key + */ +struct nvme_registered_ctrl { + __le16 cntlid; + __u8 rcsts; + __u8 rsvd3[5]; + __le64 hostid; + __le64 rkey; +}; + +/** + * struct nvme_registered_ctrl_ext - Registered Controller Extended Data Structure + * @cntlid: Controller ID + * @rcsts: Reservation Status + * @rsvd3: Reserved + * @rkey: Reservation Key + * @hostid: Host Identifier + * @rsvd32: Reserved + */ +struct nvme_registered_ctrl_ext { + __le16 cntlid; + __u8 rcsts; + __u8 rsvd3[5]; + __le64 rkey; + __u8 hostid[16]; + __u8 rsvd32[32]; +}; + +/** + * struct nvme_resv_status - Reservation Status Data Structure + * @gen: Generation + * @rtype: Reservation Type + * @regctl: Number of Registered Controllers + * @rsvd7: Reserved + * @ptpls: Persist Through Power Loss State + * @rsvd10: Reserved + * @rsvd24: Reserved + * @regctl_eds: Registered Controller Extended Data Structure + * @regctl_ds: Registered Controller Data Structure + */ +struct nvme_resv_status { + __le32 gen; + __u8 rtype; + __u8 regctl[2]; + __u8 rsvd7[2]; + __u8 ptpls; + __u8 rsvd10[14]; + union { + struct { + __u8 rsvd24[40]; + struct nvme_registered_ctrl_ext regctl_eds[0]; + }; + struct nvme_registered_ctrl regctl_ds[0]; + }; +}; + +/** + * struct nvme_streams_directive_params - Streams Directive - Return Parameters Data Structure + * @msl: Max Streams Limit + * @nssa: NVM Subsystem Streams Available + * @nsso: NVM Subsystem Streams Open + * @nssc: NVM Subsystem Stream Capability + * @rsvd: Reserved + * @sws: Stream Write Size + * @sgs: Stream Granularity Size + * @nsa: Namespace Streams Allocated + * @nso: Namespace Streams Open + * @rsvd2: Reserved + */ +struct nvme_streams_directive_params { + __le16 msl; + __le16 nssa; + __le16 nsso; + __u8 nssc; + __u8 rsvd[9]; + __le32 sws; + __le16 sgs; + __le16 nsa; + __le16 nso; + __u8 rsvd2[6]; +}; + +/** + * struct nvme_streams_directive_status - Streams Directive - Get Status Data Structure + * @osc: Open Stream Count + * @sid: Stream Identifier + */ +struct nvme_streams_directive_status { + __le16 osc; + __le16 sid[]; +}; + +/** + * struct nvme_id_directives - Identify Directive - Return Parameters Data Structure + * @supported: Identify directive is supported + * @enabled: Identify directive is Enabled + * @rsvd64: Reserved + */ +struct nvme_id_directives { + __u8 supported[32]; + __u8 enabled[32]; + __u8 rsvd64[4032]; +}; + +/** + * enum nvme_directive_types - Directives Supported or Enabled + * @NVME_ID_DIR_ID_BIT: Identify directive is supported + * @NVME_ID_DIR_SD_BIT: Streams directive is supported + * @NVME_ID_DIR_DP_BIT: Direct Placement directive is supported + */ +enum nvme_directive_types { + NVME_ID_DIR_ID_BIT = 0, + NVME_ID_DIR_SD_BIT = 1, + NVME_ID_DIR_DP_BIT = 2, +}; + +/** + * struct nvme_host_mem_buf_attrs - Host Memory Buffer - Attributes Data Structure + * @hsize: Host Memory Buffer Size + * @hmdlal: Host Memory Descriptor List Lower Address + * @hmdlau: Host Memory Descriptor List Upper Address + * @hmdlec: Host Memory Descriptor List Entry Count + * @rsvd16: Reserved + */ +struct nvme_host_mem_buf_attrs { + __le32 hsize; + __le32 hmdlal; + __le32 hmdlau; + __le32 hmdlec; + __u8 rsvd16[4080]; + +}; + +/** + * enum nvme_ae_type - Asynchronous Event Type + * @NVME_AER_ERROR: Error event + * @NVME_AER_SMART: SMART / Health Status event + * @NVME_AER_NOTICE: Notice event + * @NVME_AER_CSS: NVM Command Set Specific events + * @NVME_AER_VS: Vendor Specific event + */ +enum nvme_ae_type { + NVME_AER_ERROR = 0, + NVME_AER_SMART = 1, + NVME_AER_NOTICE = 2, + NVME_AER_CSS = 6, + NVME_AER_VS = 7, +}; + +/** + * enum nvme_ae_info_error - Asynchronous Event Information - Error Status + * @NVME_AER_ERROR_INVALID_DB_REG: Write to Invalid Doorbell Register + * @NVME_AER_ERROR_INVALID_DB_VAL: Invalid Doorbell Write Value + * @NVME_AER_ERROR_DIAG_FAILURE: Diagnostic Failure + * @NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR: Persistent Internal Error + * @NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR: Transient Internal Error + * @NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR: Firmware Image Load Error + */ +enum nvme_ae_info_error { + NVME_AER_ERROR_INVALID_DB_REG = 0x00, + NVME_AER_ERROR_INVALID_DB_VAL = 0x01, + NVME_AER_ERROR_DIAG_FAILURE = 0x02, + NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR = 0x03, + NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR = 0x04, + NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR = 0x05, +}; + +/** + * enum nvme_ae_info_smart - Asynchronous Event Information - SMART / Health Status + * @NVME_AER_SMART_SUBSYSTEM_RELIABILITY: NVM subsystem Reliability + * @NVME_AER_SMART_TEMPERATURE_THRESHOLD: Temperature Threshold + * @NVME_AER_SMART_SPARE_THRESHOLD: Spare Below Threshold + */ +enum nvme_ae_info_smart { + NVME_AER_SMART_SUBSYSTEM_RELIABILITY = 0x00, + NVME_AER_SMART_TEMPERATURE_THRESHOLD = 0x01, + NVME_AER_SMART_SPARE_THRESHOLD = 0x02, +}; + +/** + * enum nvme_ae_info_css_nvm - Asynchronous Event Information - I/O Command Specific Status + * @NVME_AER_CSS_NVM_RESERVATION: Reservation Log Page Available + * @NVME_AER_CSS_NVM_SANITIZE_COMPLETED: Sanitize Operation Completed + * @NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC: Sanitize Operation Completed + * With Unexpected Deallocation + */ +enum nvme_ae_info_css_nvm { + NVME_AER_CSS_NVM_RESERVATION = 0x00, + NVME_AER_CSS_NVM_SANITIZE_COMPLETED = 0x01, + NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC = 0x02, +}; + +/** + * enum nvme_ae_info_notice - Asynchronous Event Information - Notice + * @NVME_AER_NOTICE_NS_CHANGED: Namespace Attribute Changed + * @NVME_AER_NOTICE_FW_ACT_STARTING: Firmware Activation Starting + * @NVME_AER_NOTICE_TELEMETRY: Telemetry Log Changed + * @NVME_AER_NOTICE_ANA: Asymmetric Namespace Access Change + * @NVME_AER_NOTICE_PL_EVENT: Predictable Latency Event Aggregate Log Change + * @NVME_AER_NOTICE_LBA_STATUS_ALERT: LBA Status Information Alert + * @NVME_AER_NOTICE_EG_EVENT: Endurance Group Event Aggregate Log Page Change + * @NVME_AER_NOTICE_DISC_CHANGED: Discovery Log Page Change + */ +enum nvme_ae_info_notice { + NVME_AER_NOTICE_NS_CHANGED = 0x00, + NVME_AER_NOTICE_FW_ACT_STARTING = 0x01, + NVME_AER_NOTICE_TELEMETRY = 0x02, + NVME_AER_NOTICE_ANA = 0x03, + NVME_AER_NOTICE_PL_EVENT = 0x04, + NVME_AER_NOTICE_LBA_STATUS_ALERT = 0x05, + NVME_AER_NOTICE_EG_EVENT = 0x06, + NVME_AER_NOTICE_DISC_CHANGED = 0xf0, +}; + +/** + * enum nvme_subsys_type - Type of the NVM subsystem. + * @NVME_NQN_DISC: Discovery type target subsystem. Describes a referral to another + * Discovery Service composed of Discovery controllers that provide + * additional discovery records. Multiple Referral entries may + * be reported for each Discovery Service (if that Discovery Service + * has multiple NVM subsystem ports or supports multiple protocols). + * @NVME_NQN_NVME: NVME type target subsystem. Describes an NVM subsystem whose + * controllers may have attached namespaces (an NVM subsystem + * that is not composed of Discovery controllers). Multiple NVM + * Subsystem entries may be reported for each NVM subsystem if + * that NVM subsystem has multiple NVM subsystem ports. + * @NVME_NQN_CURR: Current Discovery type target subsystem. Describes this Discovery + * subsystem (the Discovery Service that contains the controller + * processing the Get Log Page command). Multiple Current Discovery + * Subsystem entries may be reported for this Discovery subsystem + * if the current Discovery subsystem has multiple NVM subsystem + * ports. + */ +enum nvme_subsys_type { + NVME_NQN_DISC = 1, + NVME_NQN_NVME = 2, + NVME_NQN_CURR = 3, +}; + +#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" +#define NVME_RDMA_IP_PORT 4420 +#define NVME_DISC_IP_PORT 8009 + +/* However the max length of a qualified name is another size */ +#define NVMF_NQN_SIZE 223 +#define NVMF_TRSVCID_SIZE 32 + +/** + * enum nvmf_disc_eflags - Discovery Log Page entry flags. + * @NVMF_DISC_EFLAGS_NONE: Indicates that none of the DUPRETINFO or EPCSD + * features are supported. + * @NVMF_DISC_EFLAGS_DUPRETINFO: Duplicate Returned Information (DUPRETINFO): + * Indicates that using the content of this entry + * to access this Discovery Service returns the same + * information that is returned by using the content + * of other entries in this log page that also have + * this flag set. + * @NVMF_DISC_EFLAGS_EPCSD: Explicit Persistent Connection Support for Discovery (EPCSD): + * Indicates that Explicit Persistent Connections are + * supported for the Discovery controller. + * @NVMF_DISC_EFLAGS_NCC: No CDC Connectivity (NCC): If set to + * '1', then no DDC that describes this entry + * is currently connected to the CDC. If + * cleared to '0', then at least one DDC that + * describes this entry is currently + * connected to the CDC. If the Discovery + * controller returning this log page is not + * a CDC, then this bit shall be cleared to + * '0' and should be ignored by the host. + */ +enum nvmf_disc_eflags { + NVMF_DISC_EFLAGS_NONE = 0, + NVMF_DISC_EFLAGS_DUPRETINFO = 1 << 0, + NVMF_DISC_EFLAGS_EPCSD = 1 << 1, + NVMF_DISC_EFLAGS_NCC = 1 << 2, +}; + +/* Backwards compatibility. Will be removed with next major release */ +#define NVMF_DISC_EFLAGS_BOTH (NVMF_DISC_EFLAGS_DUPRETINFO | NVMF_DISC_EFLAGS_EPCSD) + +/** + * union nvmf_tsas - Transport Specific Address Subtype + * @common: Common transport specific attributes + * @rdma: RDMA transport specific attribute settings + * @qptype: RDMA QP Service Type (RDMA_QPTYPE): Specifies the type of RDMA + * Queue Pair. See &enum nvmf_rdma_qptype. + * @prtype: RDMA Provider Type (RDMA_PRTYPE): Specifies the type of RDMA + * provider. See &enum nvmf_rdma_prtype. + * @cms: RDMA Connection Management Service (RDMA_CMS): Specifies the type + * of RDMA IP Connection Management Service. See &enum nvmf_rdma_cms. + * @pkey: RDMA_PKEY: Specifies the Partition Key when AF_IB (InfiniBand) + * address family type is used. + * @tcp: TCP transport specific attribute settings + * @sectype: Security Type (SECTYPE): Specifies the type of security used by the + * NVMe/TCP port. If SECTYPE is a value of 0h (No Security), then the + * host shall set up a normal TCP connection. See &enum nvmf_tcp_sectype. + */ +union nvmf_tsas { + char common[NVMF_TSAS_SIZE]; + struct rdma { + __u8 qptype; + __u8 prtype; + __u8 cms; + __u8 rsvd3[5]; + __le16 pkey; + __u8 rsvd10[246]; + } rdma; + struct tcp { + __u8 sectype; + } tcp; +}; + +/** + * struct nvmf_disc_log_entry - Discovery Log Page entry + * @trtype: Transport Type (TRTYPE): Specifies the NVMe Transport type. + * See &enum nvmf_trtype. + * @adrfam: Address Family (ADRFAM): Specifies the address family. + * See &enum nvmf_addr_family. + * @subtype: Subsystem Type (SUBTYPE): Specifies the type of the NVM subsystem + * that is indicated in this entry. See &enum nvme_subsys_type. + * @treq: Transport Requirements (TREQ): Indicates requirements for the NVMe + * Transport. See &enum nvmf_treq. + * @portid: Port ID (PORTID): Specifies a particular NVM subsystem port. + * Different NVMe Transports or address families may utilize the same + * Port ID value (e.g. a Port ID may support both iWARP and RoCE). + * @cntlid: Controller ID (CNTLID): Specifies the controller ID. If the NVM + * subsystem uses a dynamic controller model, then this field shall + * be set to FFFFh. If the NVM subsystem uses a static controller model, + * then this field may be set to a specific controller ID (values 0h + * to FFEFh are valid). If the NVM subsystem uses a static controller + * model and the value indicated is FFFEh, then the host should remember + * the Controller ID returned as part of the Fabrics Connect command + * in order to re-establish an association in the future with the same + * controller. + * @asqsz: Admin Max SQ Size (ASQSZ): Specifies the maximum size of an Admin + * Submission Queue. This applies to all controllers in the NVM + * subsystem. The value shall be a minimum of 32 entries. + * @eflags: Entry Flags (EFLAGS): Indicates additional information related to + * the current entry. See &enum nvmf_disc_eflags. + * @rsvd12: Reserved + * @trsvcid: Transport Service Identifier (TRSVCID): Specifies the NVMe Transport + * service identifier as an ASCII string. The NVMe Transport service + * identifier is specified by the associated NVMe Transport binding + * specification. + * @rsvd64: Reserved + * @subnqn: NVM Subsystem Qualified Name (SUBNQN): NVMe Qualified Name (NQN) + * that uniquely identifies the NVM subsystem. For a subsystem, if that + * Discovery subsystem has a unique NQN (i.e., the NVM Subsystem NVMe + * Qualified Name (SUBNQN) field in that Discovery subsystem's Identify + * Controller data structure contains a unique NQN value), then the + * value returned shall be that unique NQN. If the Discovery subsystem + * does not have a unique NQN, then the value returned shall be the + * well-known Discovery Service NQN (nqn.2014-08.org.nvmexpress.discovery). + * @traddr: Transport Address (TRADDR): Specifies the address of the NVM subsystem + * that may be used for a Connect command as an ASCII string. The + * Address Family field describes the reference for parsing this field. + * @tsas: Transport specific attribute settings + */ +struct nvmf_disc_log_entry { + __u8 trtype; + __u8 adrfam; + __u8 subtype; + __u8 treq; + __le16 portid; + __le16 cntlid; + __le16 asqsz; + __le16 eflags; + __u8 rsvd12[20]; + char trsvcid[NVMF_TRSVCID_SIZE]; + __u8 rsvd64[192]; + char subnqn[NVME_NQN_LENGTH]; + char traddr[NVMF_TRADDR_SIZE]; + union nvmf_tsas tsas; +}; + +/** + * enum nvmf_trtype - Transport Type codes for Discovery Log Page entry TRTYPE field + * @NVMF_TRTYPE_UNSPECIFIED: Not indicated + * @NVMF_TRTYPE_RDMA: RDMA + * @NVMF_TRTYPE_FC: Fibre Channel + * @NVMF_TRTYPE_TCP: TCP + * @NVMF_TRTYPE_LOOP: Intra-host Transport (i.e., loopback), reserved + * for host usage. + * @NVMF_TRTYPE_MAX: Maximum value for &enum nvmf_trtype + */ +enum nvmf_trtype { + NVMF_TRTYPE_UNSPECIFIED = 0, + NVMF_TRTYPE_RDMA = 1, + NVMF_TRTYPE_FC = 2, + NVMF_TRTYPE_TCP = 3, + NVMF_TRTYPE_LOOP = 254, + NVMF_TRTYPE_MAX, +}; + +/** + * enum nvmf_addr_family - Address Family codes for Discovery Log Page entry ADRFAM field + * @NVMF_ADDR_FAMILY_PCI: PCIe + * @NVMF_ADDR_FAMILY_IP4: AF_INET: IPv4 address family. + * @NVMF_ADDR_FAMILY_IP6: AF_INET6: IPv6 address family. + * @NVMF_ADDR_FAMILY_IB: AF_IB: InfiniBand address family. + * @NVMF_ADDR_FAMILY_FC: Fibre Channel address family. + * @NVMF_ADDR_FAMILY_LOOP: Intra-host Transport (i.e., loopback), reserved + * for host usage. + */ +enum nvmf_addr_family { + NVMF_ADDR_FAMILY_PCI = 0, + NVMF_ADDR_FAMILY_IP4 = 1, + NVMF_ADDR_FAMILY_IP6 = 2, + NVMF_ADDR_FAMILY_IB = 3, + NVMF_ADDR_FAMILY_FC = 4, + NVMF_ADDR_FAMILY_LOOP = 254, +}; + +/** + * enum nvmf_treq - Transport Requirements codes for Discovery Log Page entry TREQ field + * @NVMF_TREQ_NOT_SPECIFIED: Not specified + * @NVMF_TREQ_REQUIRED: Required + * @NVMF_TREQ_NOT_REQUIRED: Not Required + * @NVMF_TREQ_DISABLE_SQFLOW: SQ flow control disable supported + */ +enum nvmf_treq { + NVMF_TREQ_NOT_SPECIFIED = 0, + NVMF_TREQ_REQUIRED = 1, + NVMF_TREQ_NOT_REQUIRED = 2, + NVMF_TREQ_DISABLE_SQFLOW = 4, +}; + +/** + * enum nvmf_rdma_qptype - RDMA QP Service Type codes for Discovery Log Page + * entry TSAS RDMA_QPTYPE field + * @NVMF_RDMA_QPTYPE_CONNECTED: Reliable Connected + * @NVMF_RDMA_QPTYPE_DATAGRAM: Reliable Datagram + */ +enum nvmf_rdma_qptype { + NVMF_RDMA_QPTYPE_CONNECTED = 1, + NVMF_RDMA_QPTYPE_DATAGRAM = 2, +}; + +/** + * enum nvmf_rdma_prtype - RDMA Provider Type codes for Discovery Log Page + * entry TSAS RDMA_PRTYPE field + * @NVMF_RDMA_PRTYPE_NOT_SPECIFIED: No Provider Specified + * @NVMF_RDMA_PRTYPE_IB: InfiniBand + * @NVMF_RDMA_PRTYPE_ROCE: InfiniBand RoCE + * @NVMF_RDMA_PRTYPE_ROCEV2: InfiniBand RoCEV2 + * @NVMF_RDMA_PRTYPE_IWARP: iWARP + */ +enum nvmf_rdma_prtype { + NVMF_RDMA_PRTYPE_NOT_SPECIFIED = 1, + NVMF_RDMA_PRTYPE_IB = 2, + NVMF_RDMA_PRTYPE_ROCE = 3, + NVMF_RDMA_PRTYPE_ROCEV2 = 4, + NVMF_RDMA_PRTYPE_IWARP = 5, +}; + +/** + * enum nvmf_rdma_cms - RDMA Connection Management Service Type codes for + * Discovery Log Page entry TSAS RDMA_CMS field + * @NVMF_RDMA_CMS_RDMA_CM: Sockets based endpoint addressing + * + */ +enum nvmf_rdma_cms { + NVMF_RDMA_CMS_RDMA_CM = 1, +}; + +/** + * enum nvmf_tcp_sectype - Transport Specific Address Subtype Definition for + * NVMe/TCP Transport + * @NVMF_TCP_SECTYPE_NONE: No Security + * @NVMF_TCP_SECTYPE_TLS: Transport Layer Security version 1.2 + * @NVMF_TCP_SECTYPE_TLS13: Transport Layer Security version 1.3 or a subsequent + * version. The TLS protocol negotiates the version and + * cipher suite for each TCP connection. + */ +enum nvmf_tcp_sectype { + NVMF_TCP_SECTYPE_NONE = 0, + NVMF_TCP_SECTYPE_TLS = 1, + NVMF_TCP_SECTYPE_TLS13 = 2, +}; + +/** + * enum nvmf_log_discovery_lid_support - Discovery log specific support + * @NVMF_LOG_DISC_LID_NONE: None + * @NVMF_LOG_DISC_LID_EXTDLPES: Extended Discovery Log Page Entries Supported + * @NVMF_LOG_DISC_LID_PLEOS: Port Local Entries Only Supported + * @NVMF_LOG_DISC_LID_ALLSUBES: All NVM Subsystem Entries Supported + */ +enum nvmf_log_discovery_lid_support { + NVMF_LOG_DISC_LID_NONE = 0, + NVMF_LOG_DISC_LID_EXTDLPES = (1 << 0), + NVMF_LOG_DISC_LID_PLEOS = (1 << 1), + NVMF_LOG_DISC_LID_ALLSUBES = (1 << 2), +}; + +/** + * enum nvmf_log_discovery_lsp - Discovery log specific field + * @NVMF_LOG_DISC_LSP_NONE: None + * @NVMF_LOG_DISC_LSP_EXTDLPE: Extended Discovery Log Page Entries + * @NVMF_LOG_DISC_LSP_PLEO: Port Local Entries Only + * @NVMF_LOG_DISC_LSP_ALLSUBE: All NVM Subsystem Entries + */ +enum nvmf_log_discovery_lsp { + NVMF_LOG_DISC_LSP_NONE = 0, + NVMF_LOG_DISC_LSP_EXTDLPE = (1 << 0), + NVMF_LOG_DISC_LSP_PLEO = (1 << 1), + NVMF_LOG_DISC_LSP_ALLSUBE = (1 << 2), +}; + +/** + * struct nvmf_discovery_log - Discovery Log Page (Log Identifier 70h) + * @genctr: Generation Counter (GENCTR): Indicates the version of the discovery + * information, starting at a value of 0h. For each change in the + * Discovery Log Page, this counter is incremented by one. If the value + * of this field is FFFFFFFF_FFFFFFFFh, then the field shall be cleared + * to 0h when incremented (i.e., rolls over to 0h). + * @numrec: Number of Records (NUMREC): Indicates the number of records + * contained in the log. + * @recfmt: Record Format (RECFMT): Specifies the format of the Discovery Log + * Page. If a new format is defined, this value is incremented by one. + * The format of the record specified in this definition shall be 0h. + * @rsvd14: Reserved + * @entries: Discovery Log Page Entries - see &struct nvmf_disc_log_entry. + */ +struct nvmf_discovery_log { + __le64 genctr; + __le64 numrec; + __le16 recfmt; + __u8 rsvd14[1006]; + struct nvmf_disc_log_entry entries[]; +}; + +/* + * Discovery Information Management (DIM) command. This is sent by a + * host to a Discovery Controller (DC) to perform explicit registration. + */ +#define NVMF_ENAME_LEN 256 +#define NVMF_EVER_LEN 64 + +/** + * enum nvmf_dim_tas - Discovery Information Management Task + * @NVMF_DIM_TAS_REGISTER: Register + * @NVMF_DIM_TAS_DEREGISTER: Deregister + * @NVMF_DIM_TAS_UPDATE: Update + */ +enum nvmf_dim_tas { + NVMF_DIM_TAS_REGISTER = 0x00, + NVMF_DIM_TAS_DEREGISTER = 0x01, + NVMF_DIM_TAS_UPDATE = 0x02, +}; + +/** + * enum nvmf_dim_entfmt - Discovery Information Management Entry Format + * @NVMF_DIM_ENTFMT_BASIC: Basic discovery information entry + * @NVMF_DIM_ENTFMT_EXTENDED: Extended discovery information entry + */ +enum nvmf_dim_entfmt { + NVMF_DIM_ENTFMT_BASIC = 0x01, + NVMF_DIM_ENTFMT_EXTENDED = 0x02, +}; + +/** + * enum nvmf_dim_etype -Discovery Information Management Entity Type + * @NVMF_DIM_ETYPE_HOST: Host + * @NVMF_DIM_ETYPE_DDC: Direct Discovery controller + * @NVMF_DIM_ETYPE_CDC: Centralized Discovery controller + */ +enum nvmf_dim_etype { + NVMF_DIM_ETYPE_HOST = 0x01, + NVMF_DIM_ETYPE_DDC = 0x02, + NVMF_DIM_ETYPE_CDC = 0x03, +}; + +/** + * enum nvmf_exattype - Extended Attribute Type + * @NVMF_EXATTYPE_HOSTID: Host Identifier + * @NVMF_EXATTYPE_SYMNAME: Symblic Name + */ +enum nvmf_exattype { + NVMF_EXATTYPE_HOSTID = 0x01, + NVMF_EXATTYPE_SYMNAME = 0x02, +}; + +/** + * struct nvmf_ext_attr - Extended Attribute (EXAT) + * @exattype: Extended Attribute Type (EXATTYPE) - see @enum nvmf_exattype + * @exatlen: Extended Attribute Length (EXATLEN) + * @exatval: Extended Attribute Value (EXATVAL) - size allocated for array + * must be a multiple of 4 bytes + */ +struct nvmf_ext_attr { + __le16 exattype; + __le16 exatlen; + __u8 exatval[]; +}; + +/** + * struct nvmf_ext_die - Extended Discovery Information Entry (DIE) + * @trtype: Transport Type (&enum nvmf_trtype) + * @adrfam: Address Family (&enum nvmf_addr_family) + * @subtype: Subsystem Type (&enum nvme_subsys_type) + * @treq: Transport Requirements (&enum nvmf_treq) + * @portid: Port ID + * @cntlid: Controller ID + * @asqsz: Admin Max SQ Size + * @rsvd10: Reserved + * @trsvcid: Transport Service Identifier + * @resv64: Reserved + * @nqn: NVM Qualified Name + * @traddr: Transport Address + * @tsas: Transport Specific Address Subtype (&union nvmf_tsas) + * @tel: Total Entry Length + * @numexat: Number of Extended Attributes + * @resv1030: Reserved + * @exat: Extended Attributes 0 (&struct nvmf_ext_attr) + */ +struct nvmf_ext_die { + __u8 trtype; + __u8 adrfam; + __u8 subtype; + __u8 treq; + __le16 portid; + __le16 cntlid; + __le16 asqsz; + __u8 rsvd10[22]; + char trsvcid[NVMF_TRSVCID_SIZE]; + __u8 resv64[192]; + char nqn[NVME_NQN_LENGTH]; + char traddr[NVMF_TRADDR_SIZE]; + union nvmf_tsas tsas; + __le32 tel; + __le16 numexat; + __u8 resv1030[2]; + struct nvmf_ext_attr exat[]; +}; + +/** + * union nvmf_die - Discovery Information Entry (DIE) + * @basic: Basic format (&struct nvmf_disc_log_entry) + * @extended: Extended format (&struct nvmf_ext_die) + * + * Depending on the ENTFMT specified in the DIM, DIEs can be entered + * with the Basic or Extended formats. For Basic format, each entry + * has a fixed length. Therefore, the "basic" field defined below can + * be accessed as a C array. For the Extended format, however, each + * entry is of variable length (TEL). Therefore, the "extended" field + * defined below cannot be accessed as a C array. Instead, the + * "extended" field is akin to a linked-list, where one can "walk" + * through the list. To move to the next entry, one simply adds the + * current entry's length (TEL) to the "walk" pointer. The number of + * entries in the list is specified by NUMENT. Although extended + * entries are of a variable lengths (TEL), TEL is always a multiple of + * 4 bytes. + */ +union nvmf_die { + struct nvmf_disc_log_entry basic[0]; + struct nvmf_ext_die extended; +}; + +/** + * struct nvmf_dim_data - Discovery Information Management (DIM) - Data + * @tdl: Total Data Length + * @rsvd4: Reserved + * @nument: Number of entries + * @entfmt: Entry Format (&enum nvmf_dim_entfmt) + * @etype: Entity Type (&enum nvmf_dim_etype) + * @portlcl: Port Local + * @rsvd21: Reserved + * @ektype: Entry Key Type + * @eid: Entity Identifier (e.g. Host NQN) + * @ename: Entity Name (e.g. hostname) + * @ever: Entity Version (e.g. OS Name/Version) + * @rsvd600: Reserved + * @die: Discovery Information Entry (see @nument above) + */ +struct nvmf_dim_data { + __le32 tdl; + __u8 rsvd4[4]; + __le64 nument; + __le16 entfmt; + __le16 etype; + __u8 portlcl; + __u8 rsvd21; + __le16 ektype; + char eid[NVME_NQN_LENGTH]; + char ename[NVMF_ENAME_LEN]; + char ever[NVMF_EVER_LEN]; + __u8 rsvd600[424]; + union nvmf_die die[]; +}; + +/** + * struct nvmf_connect_data - Data payload for the 'connect' command + * @hostid: Host ID of the connecting host + * @cntlid: Requested controller ID + * @rsvd4: Reserved + * @subsysnqn: Subsystem NQN to connect to + * @hostnqn: Host NQN of the connecting host + * @rsvd5: Reserved + */ +struct nvmf_connect_data { + __u8 hostid[16]; + __le16 cntlid; + char rsvd4[238]; + char subsysnqn[NVME_NQN_LENGTH]; + char hostnqn[NVME_NQN_LENGTH]; + char rsvd5[256]; +}; + +/** + * struct nvme_mi_read_nvm_ss_info - NVM Subsystem Information Data Structure + * @nump: Number of Ports + * @mjr: NVMe-MI Major Version Number + * @mnr: NVMe-MI Minor Version Number + * @rsvd3: Reserved + */ +struct nvme_mi_read_nvm_ss_info { + __u8 nump; + __u8 mjr; + __u8 mnr; + __u8 rsvd3[29]; +}; + +/** + * struct nvme_mi_port_pcie - PCIe Port Specific Data + * @mps: PCIe Maximum Payload Size + * @sls: PCIe Supported Link Speeds Vector + * @cls: PCIe Current Link Speed + * @mlw: PCIe Maximum Link Width + * @nlw: PCIe Negotiated Link Width + * @pn: PCIe Port Number + * @rsvd14: Reserved + */ +struct nvme_mi_port_pcie { + __u8 mps; + __u8 sls; + __u8 cls; + __u8 mlw; + __u8 nlw; + __u8 pn; + __u8 rsvd14[18]; +}; + +/** + * struct nvme_mi_port_smb - SMBus Port Specific Data + * @vpd_addr: Current VPD SMBus/I2C Address + * @mvpd_freq: Maximum VPD Access SMBus/I2C Frequency + * @mme_addr: Current Management Endpoint SMBus/I2C Address + * @mme_freq: Maximum Management Endpoint SMBus/I2C Frequency + * @nvmebm: NVMe Basic Management + * @rsvd13: Reserved + */ +struct nvme_mi_port_smb { + __u8 vpd_addr; + __u8 mvpd_freq; + __u8 mme_addr; + __u8 mme_freq; + __u8 nvmebm; + __u8 rsvd13[19]; +}; + +/** + * struct nvme_mi_read_port_info - Port Information Data Structure + * @portt: Port Type + * @rsvd1: Reserved + * @mmctptus: Maximum MCTP Transmission Unit Size + * @meb: Management Endpoint Buffer Size + * @pcie: PCIe Port Specific Data + * @smb: SMBus Port Specific Data + */ +struct nvme_mi_read_port_info { + __u8 portt; + __u8 rsvd1; + __le16 mmctptus; + __le32 meb; + union { + struct nvme_mi_port_pcie pcie; + struct nvme_mi_port_smb smb; + }; +}; + +/** + * struct nvme_mi_read_ctrl_info - Controller Information Data Structure + * @portid: Port Identifier + * @rsvd1: Reserved + * @prii: PCIe Routing ID Information + * @pri: PCIe Routing ID + * @vid: PCI Vendor ID + * @did: PCI Device ID + * @ssvid: PCI Subsystem Vendor ID + * @ssid: PCI Subsystem Device ID + * @rsvd16: Reserved + */ +struct nvme_mi_read_ctrl_info { + __u8 portid; + __u8 rsvd1[4]; + __u8 prii; + __le16 pri; + __le16 vid; + __le16 did; + __le16 ssvid; + __le16 ssid; + __u8 rsvd16[16]; +}; + +/** + * struct nvme_mi_osc - Optionally Supported Command Data Structure + * @type: Command Type + * @opc: Opcode + */ +struct nvme_mi_osc { + __u8 type; + __u8 opc; +}; + +/** + * struct nvme_mi_read_sc_list - Management Endpoint Buffer Supported Command List Data Structure + * @numcmd: Number of Commands + * @cmds: MEB supported Command Data Structure. + * See @struct nvme_mi_osc + */ +struct nvme_mi_read_sc_list { + __le16 numcmd; + struct nvme_mi_osc cmds[]; +}; + +/** + * struct nvme_mi_nvm_ss_health_status - Subsystem Management Data Structure + * @nss: NVM Subsystem Status + * @sw: Smart Warnings + * @ctemp: Composite Temperature + * @pdlu: Percentage Drive Life Used + * @ccs: Composite Controller Status + * @rsvd8: Reserved + */ +struct nvme_mi_nvm_ss_health_status { + __u8 nss; + __u8 sw; + __u8 ctemp; + __u8 pdlu; + __le16 ccs; + __u8 rsvd8[2]; +}; + +/** + * enum nvme_mi_ccs - Get State Control Primitive Success Response Fields - Control Primitive Specific Response + * @NVME_MI_CCS_RDY: Ready + * @NVME_MI_CCS_CFS: Controller Fatal Status + * @NVME_MI_CCS_SHST: Shutdown Status + * @NVME_MI_CCS_NSSRO: NVM Subsystem Reset Occurred + * @NVME_MI_CCS_CECO: Controller Enable Change Occurred + * @NVME_MI_CCS_NAC: Namespace Attribute Changed + * @NVME_MI_CCS_FA: Firmware Activated + * @NVME_MI_CCS_CSTS: Controller Status Change + * @NVME_MI_CCS_CTEMP: Composite Temperature Change + * @NVME_MI_CCS_PDLU: Percentage Used + * @NVME_MI_CCS_SPARE: Available Spare + * @NVME_MI_CCS_CCWARN: Critical Warning + */ +enum nvme_mi_ccs { + NVME_MI_CCS_RDY = 1 << 0, + NVME_MI_CCS_CFS = 1 << 1, + NVME_MI_CCS_SHST = 1 << 2, + NVME_MI_CCS_NSSRO = 1 << 4, + NVME_MI_CCS_CECO = 1 << 5, + NVME_MI_CCS_NAC = 1 << 6, + NVME_MI_CCS_FA = 1 << 7, + NVME_MI_CCS_CSTS = 1 << 8, + NVME_MI_CCS_CTEMP = 1 << 9, + NVME_MI_CCS_PDLU = 1 << 10, + NVME_MI_CCS_SPARE = 1 << 11, + NVME_MI_CCS_CCWARN = 1 << 12, +}; + +/* backwards compat for old "CCS" definitions */ +#define nvme_mi_css nvme_mi_ccs +#define NVME_MI_CSS_CFS NVME_MI_CCS_CFS +#define NVME_MI_CSS_SHST NVME_MI_CCS_SHST +#define NVME_MI_CSS_NSSRO NVME_MI_CCS_NSSRO +#define NVME_MI_CSS_CECO NVME_MI_CCS_CECO +#define NVME_MI_CSS_NAC NVME_MI_CCS_NAC +#define NVME_MI_CSS_FA NVME_MI_CCS_FA +#define NVME_MI_CSS_CSTS NVME_MI_CCS_CSTS +#define NVME_MI_CSS_CTEMP NVME_MI_CCS_CTEMP +#define NVME_MI_CSS_PDLU NVME_MI_CCS_PDLU +#define NVME_MI_CSS_SPARE NVME_MI_CCS_SPARE +#define NVME_MI_CSS_CCWARN NVME_MI_CCS_CCWARN + +/** + * struct nvme_mi_ctrl_health_status - Controller Health Data Structure (CHDS) + * @ctlid: Controller Identifier + * @csts: Controller Status + * @ctemp: Composite Temperature + * @pdlu: Percentage Used + * @spare: Available Spare + * @cwarn: Critical Warning + * @rsvd9: Reserved + */ +struct nvme_mi_ctrl_health_status { + __le16 ctlid; + __le16 csts; + __le16 ctemp; + __u8 pdlu; + __u8 spare; + __u8 cwarn; + __u8 rsvd9[7]; +}; + +/** + * enum nvme_mi_csts - Controller Health Data Structure (CHDS) - Controller Status (CSTS) + * @NVME_MI_CSTS_RDY: Ready + * @NVME_MI_CSTS_CFS: Controller Fatal Status + * @NVME_MI_CSTS_SHST: Shutdown Status + * @NVME_MI_CSTS_NSSRO: NVM Subsystem Reset Occurred + * @NVME_MI_CSTS_CECO: Controller Enable Change Occurred + * @NVME_MI_CSTS_NAC: Namespace Attribute Changed + * @NVME_MI_CSTS_FA: Firmware Activated + */ +enum nvme_mi_csts { + NVME_MI_CSTS_RDY = 1 << 0, + NVME_MI_CSTS_CFS = 1 << 1, + NVME_MI_CSTS_SHST = 1 << 2, + NVME_MI_CSTS_NSSRO = 1 << 4, + NVME_MI_CSTS_CECO = 1 << 5, + NVME_MI_CSTS_NAC = 1 << 6, + NVME_MI_CSTS_FA = 1 << 7, +}; + +/** + * enum nvme_mi_cwarn - Controller Health Data Structure (CHDS) - Critical Warning (CWARN) + * @NVME_MI_CWARN_ST: Spare Threshold + * @NVME_MI_CWARN_TAUT: Temperature Above or Under Threshold + * @NVME_MI_CWARN_RD: Reliability Degraded + * @NVME_MI_CWARN_RO: Read Only + * @NVME_MI_CWARN_VMBF: Volatile Memory Backup Failed + */ +enum nvme_mi_cwarn { + NVME_MI_CWARN_ST = 1 << 0, + NVME_MI_CWARN_TAUT = 1 << 1, + NVME_MI_CWARN_RD = 1 << 2, + NVME_MI_CWARN_RO = 1 << 3, + NVME_MI_CWARN_VMBF = 1 << 4, +}; + +/** + * struct nvme_mi_vpd_mra - NVMe MultiRecord Area + * @nmravn: NVMe MultiRecord Area Version Number + * @ff: Form Factor + * @rsvd7: Reserved + * @i18vpwr: Initial 1.8 V Power Supply Requirements + * @m18vpwr: Maximum 1.8 V Power Supply Requirements + * @i33vpwr: Initial 3.3 V Power Supply Requirements + * @m33vpwr: Maximum 3.3 V Power Supply Requirements + * @rsvd17: Reserved + * @m33vapsr: Maximum 3.3 Vi aux Power Supply Requirements + * @i5vapsr: Initial 5 V Power Supply Requirements + * @m5vapsr: Maximum 5 V Power Supply Requirements + * @i12vapsr: Initial 12 V Power Supply Requirements + * @m12vapsr: Maximum 12 V Power Supply Requirements + * @mtl: Maximum Thermal Load + * @tnvmcap: Total NVM Capacity + * @rsvd37: Reserved + */ +struct nvme_mi_vpd_mra { + __u8 nmravn; + __u8 ff; + __u8 rsvd7[6]; + __u8 i18vpwr; + __u8 m18vpwr; + __u8 i33vpwr; + __u8 m33vpwr; + __u8 rsvd17; + __u8 m33vapsr; + __u8 i5vapsr; + __u8 m5vapsr; + __u8 i12vapsr; + __u8 m12vapsr; + __u8 mtl; + __u8 tnvmcap[16]; + __u8 rsvd37[27]; +}; + +/** + * struct nvme_mi_vpd_ppmra - NVMe PCIe Port MultiRecord Area + * @nppmravn: NVMe PCIe Port MultiRecord Area Version Number + * @pn: PCIe Port Number + * @ppi: Port Information + * @ls: PCIe Link Speed + * @mlw: PCIe Maximum Link Width + * @mctp: MCTP Support + * @refccap: Ref Clk Capability + * @pi: Port Identifier + * @rsvd13: Reserved + */ +struct nvme_mi_vpd_ppmra { + __u8 nppmravn; + __u8 pn; + __u8 ppi; + __u8 ls; + __u8 mlw; + __u8 mctp; + __u8 refccap; + __u8 pi; + __u8 rsvd13[3]; +}; + +/** + * struct nvme_mi_vpd_telem - Vital Product Data Element Descriptor + * @type: Type of the Element Descriptor + * @rev: Revision of the Element Descriptor + * @len: Number of bytes in the Element Descriptor + * @data: Type-specific information associated with + * the Element Descriptor + */ +struct nvme_mi_vpd_telem { + __u8 type; + __u8 rev; + __u8 len; + __u8 data[0]; +}; + +/** + * enum nvme_mi_elem - Element Descriptor Types + * @NVME_MI_ELEM_EED: Extended Element Descriptor + * @NVME_MI_ELEM_USCE: Upstream Connector Element Descriptor + * @NVME_MI_ELEM_ECED: Expansion Connector Element Descriptor + * @NVME_MI_ELEM_LED: Label Element Descriptor + * @NVME_MI_ELEM_SMBMED: SMBus/I2C Mux Element Descriptor + * @NVME_MI_ELEM_PCIESED: PCIe Switch Element Descriptor + * @NVME_MI_ELEM_NVMED: NVM Subsystem Element Descriptor + */ +enum nvme_mi_elem { + NVME_MI_ELEM_EED = 1, + NVME_MI_ELEM_USCE = 2, + NVME_MI_ELEM_ECED = 3, + NVME_MI_ELEM_LED = 4, + NVME_MI_ELEM_SMBMED = 5, + NVME_MI_ELEM_PCIESED = 6, + NVME_MI_ELEM_NVMED = 7, +}; + +/** + * struct nvme_mi_vpd_tra - Vital Product Data Topology MultiRecord + * @vn: Version Number + * @rsvd6: Reserved + * @ec: Element Count + * @elems: Element Descriptor + */ +struct nvme_mi_vpd_tra { + __u8 vn; + __u8 rsvd6; + __u8 ec; + struct nvme_mi_vpd_telem elems[0]; +}; + +/** + * struct nvme_mi_vpd_mr_common - NVMe MultiRecord Area + * @type: NVMe Record Type ID + * @rf: Record Format + * @rlen: Record Length + * @rchksum: Record Checksum + * @hchksum: Header Checksum + * @nmra: NVMe MultiRecord Area + * @ppmra: NVMe PCIe Port MultiRecord Area + * @tmra: Topology MultiRecord Area + */ +struct nvme_mi_vpd_mr_common { + __u8 type; + __u8 rf; + __u8 rlen; + __u8 rchksum; + __u8 hchksum; + + union { + struct nvme_mi_vpd_mra nmra; + struct nvme_mi_vpd_ppmra ppmra; + struct nvme_mi_vpd_tra tmra; + }; +}; + +/** + * struct nvme_mi_vpd_hdr - Vital Product Data Common Header + * @ipmiver: IPMI Format Version Number + * @iuaoff: Internal Use Area Starting Offset + * @ciaoff: Chassis Info Area Starting Offset + * @biaoff: Board Info Area Starting Offset + * @piaoff: Product Info Area Starting Offset + * @mrioff: MultiRecord Info Area Starting Offset + * @rsvd6: Reserved + * @chchk: Common Header Checksum + * @vpd: Vital Product Data + */ +struct nvme_mi_vpd_hdr { + __u8 ipmiver; + __u8 iuaoff; + __u8 ciaoff; + __u8 biaoff; + __u8 piaoff; + __u8 mrioff; + __u8 rsvd6; + __u8 chchk; + __u8 vpd[]; +}; + +/** + * enum nvme_status_field - Defines all parts of the nvme status field: status + * code, status code type, and additional flags. + * @NVME_SCT_GENERIC: Generic errors applicable to multiple opcodes + * @NVME_SCT_CMD_SPECIFIC: Errors associated to a specific opcode + * @NVME_SCT_MEDIA: Errors associated with media and data integrity + * @NVME_SCT_PATH: Errors associated with the paths connection + * @NVME_SCT_VS: Vendor specific errors + * @NVME_SCT_MASK: Mask to get the value of the Status Code Type + * @NVME_SCT_SHIFT: Shift value to get the value of the Status + * Code Type + * @NVME_SC_MASK: Mask to get the value of the status code. + * @NVME_SC_SHIFT: Shift value to get the value of the status + * code. + * @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 being + * received that specified the Submission + * Queue Identifier and Command Identifier + * of this command. + * @NVME_SC_ABORT_QUEUE: Command Aborted due to SQ Deletion: The + * command was aborted due to a Delete I/O + * Submission Queue request received for the + * Submission Queue to which the command was + * submitted. + * @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: Aborted due to Missing Fused Command: The + * fused command was aborted due to the + * adjacent submission queue entry not + * containing a fused command that is the + * other 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: This may occur + * if the length of a Data SGL is too short. + * This may occur if the length of a Data + * SGL is too long and the controller does + * not support SGL transfers longer than the + * amount of data to be transferred as + * indicated in the SGL Support field of the + * Identify Controller data structure. + * @NVME_SC_SGL_INVALID_METADATA: Metadata SGL Length Invalid: This may + * occur if the length of a Metadata SGL is + * too short. This may occur if the length + * of a Metadata SGL is too long and the + * controller does not support SGL transfers + * longer than the amount of data to be + * transferred as indicated in the SGL + * Support field of the Identify Controller + * data structure. + * @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. Refer to + * the appropriate security specification. + * @NVME_SC_SGL_INVALID_OFFSET: SGL Offset Invalid: The offset specified + * in a descriptor is invalid. This may + * occur when using capsules for data + * transfers in NVMe over Fabrics + * implementations and an invalid offset in + * the capsule is specified. + * @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 (e.g., command) 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 implementation does not support + * submission of the command to a Submission + * Queue in the Controller Memory Buffer or + * command completion to a 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 as a result of a change + * in the namespace write protection state + * as defined by the Namespace Write + * Protection State Machine. + * @NVME_SC_CMD_INTERRUPTED: Command Interrupted: Command processing + * was interrupted and the controller is + * unable to successfully complete the + * command. The host should retry the + * command. + * @NVME_SC_TRAN_TPORT_ERROR: Transient Transport Error: A transient + * transport error was detected. If the + * command is retried on the same + * controller, the command is likely to + * succeed. A command that fails with a + * transient transport error four or more + * times should be treated as a persistent + * transport error that is not likely to + * succeed if retried on the same + * controller. + * @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_FDP_DISABLED: Command is not allowed when + * Flexible Data Placement is disabled. + * @NVME_SC_INVALID_PLACEMENT_HANDLE_LIST: The Placement Handle List is invalid + * due to invalid Reclaim Unit Handle Identifier or + * valid Reclaim Unit Handle Identifier but restricted or + * the Placement Handle List number of entries exceeded the + * maximum number allowed. + * @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 as a result of a + * condition other than a condition that is + * reported as an Asymmetric Namespace + * Access condition. + * @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. + * @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. An invalid queue + * identifier is one that is currently in + * use or one that is outside the range + * supported by the controller. + * @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. This + * error is indicated if the firmware slot + * exceeds the number supported. + * @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. This error condition is also + * returned if a reserved log page is + * requested. + * @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. The Feature + * Identifier settings apply across all + * namespaces. + * @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 is trying to modify a locked Boot + * Partition. + * @NVME_SC_INVALID_CTRL_ID: Invalid Controller Identifier: + * @NVME_SC_INVALID_SEC_CTRL_STATE: Invalid Secondary Controller State + * @NVME_SC_INVALID_CTRL_RESOURCES: Invalid Number of Controller Resources + * @NVME_SC_INVALID_RESOURCE_ID: Invalid Resource Identifier + * @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: I/O Command Set Not Supported + * @NVME_SC_IOCS_NOT_ENABLED: I/O Command Set Not Enabled + * @NVME_SC_IOCS_COMBINATION_REJECTED: I/O Command Set Combination Rejected + * @NVME_SC_INVALID_IOCS: Invalid I/O Command Set + * @NVME_SC_ID_UNAVAILABLE: Identifier Unavailable + * @NVME_SC_INVALID_DISCOVERY_INFO: The discovery information provided in + * one or more extended discovery + * information entries is not applicable + * for the type of entity selected in + * the Entity Type (ETYPE) field of the + * Discovery Information Management + * command data portion’s header. + * @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: The number of discover information + * entries provided in the data portion + * of the Discovery Information + * Management command for a registration + * task (i.e., TAS field cleared to 0h) + * exceeds the available capacity for + * new discovery information entries on + * the CDC or DDC. This may be a + * transient condition. + * @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. + * @NVME_SC_BAD_ATTRIBUTES: Conflicting Dataset Management Attributes + * @NVME_SC_INVALID_PI: Invalid Protection Information + * @NVME_SC_READ_ONLY: Attempted Write to Read Only Range + * @NVME_SC_CMD_SIZE_LIMIT_EXCEEDED: Command Size Limit Exceeded + * @NVME_SC_INCOMPATIBLE_NS: Incompatible Namespace or Format: At + * least one source namespace and the + * destination namespace have incompatible + * formats. + * @NVME_SC_FAST_COPY_NOT_POSSIBLE: Fast Copy Not Possible: The Fast Copy + * Only (FCO) bit was set to ‘1’ in a Source + * Range entry and the controller was not + * able to use fast copy operations to copy + * the specified data. + * @NVME_SC_OVERLAPPING_IO_RANGE: Overlapping I/O Range: A source logical + * block range overlaps the destination + * logical block range. + * @NVME_SC_INSUFFICIENT_RESOURCES: Insufficient Resources: A resource + * shortage prevented the controller from + * performing the requested copy. + * @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 either + * not allowed to establish an association + * to any controller in the NVM subsystem or + * the host is not allowed to establish an + * association to 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. + * @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. + * @NVME_SC_ANA_INTERNAL_PATH_ERROR: Internal Path Error: The command was not + * completed as the result of a controller + * internal error that is specific to the + * controller processing the command. + * @NVME_SC_ANA_PERSISTENT_LOSS: Asymmetric Access Persistent Loss: The + * requested function (e.g., command) is not + * able to be performed as a result of the + * relationship between the controller and + * the namespace being in the ANA Persistent + * Loss state. + * @NVME_SC_ANA_INACCESSIBLE: Asymmetric Access Inaccessible: The + * requested function (e.g., command) is not + * able to be performed as a result of the + * relationship between the controller and + * the namespace being in the ANA + * Inaccessible state. + * @NVME_SC_ANA_TRANSITION: Asymmetric Access Transition: The + * requested function (e.g., command) is not + * able to be performed as a result of the + * relationship between the controller and + * the namespace transitioning between + * Asymmetric Namespace Access states. + * @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. + * @NVME_SC_CRD: Mask to get value of Command Retry Delay + * index + * @NVME_SC_MORE: More bit. If set, more status information + * for this command as part of the Error + * Information log that may be retrieved with + * the Get Log Page command. + * @NVME_SC_DNR: Do Not Retry bit. If set, if the same + * command is re-submitted to any controller + * in the NVM subsystem, then that + * re-submitted command is expected to fail. + * @NVME_SC_ZNS_INVALID_OP_REQUEST: Invalid Zone Operation Request: + * The operation requested is invalid. This may be due to + * various conditions, including: attempting to allocate a + * ZRWA when a zone is not in the ZSE:Empty state; or + * invalid Flush Explicit ZRWA Range Send Zone Action + * operation. + * @NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE: ZRWA Resources Unavailable: + * No ZRWAs are available. + * @NVME_SC_ZNS_BOUNDARY_ERROR: Zone Boundary Error: The command specifies + * logical blocks in more than one zone. + * @NVME_SC_ZNS_FULL: Zone Is Full: The accessed zone is in the + * ZSF:Full state. + * @NVME_SC_ZNS_READ_ONLY: Zone Is Read Only: The accessed zone is + * in the ZSRO:Read Only state. + * @NVME_SC_ZNS_OFFLINE: Zone Is Offline: The accessed zone is + * in the ZSO:Offline state. + * @NVME_SC_ZNS_INVALID_WRITE: Zone Invalid Write: The write to a zone + * was not at the write pointer. + * @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. + */ +enum nvme_status_field { + /* + * Status Code Type indicators + */ + NVME_SCT_GENERIC = 0x0, + NVME_SCT_CMD_SPECIFIC = 0x1, + NVME_SCT_MEDIA = 0x2, + NVME_SCT_PATH = 0x3, + NVME_SCT_VS = 0x7, + NVME_SCT_MASK = 0x7, + NVME_SCT_SHIFT = 0x8, + + /* + * Status Code inidicators + */ + NVME_SC_MASK = 0xff, + NVME_SC_SHIFT = 0x0, + + /* + * Generic Command Status Codes: + */ + NVME_SC_SUCCESS = 0x0, + NVME_SC_INVALID_OPCODE = 0x1, + NVME_SC_INVALID_FIELD = 0x2, + NVME_SC_CMDID_CONFLICT = 0x3, + NVME_SC_DATA_XFER_ERROR = 0x4, + NVME_SC_POWER_LOSS = 0x5, + NVME_SC_INTERNAL = 0x6, + NVME_SC_ABORT_REQ = 0x7, + NVME_SC_ABORT_QUEUE = 0x8, + NVME_SC_FUSED_FAIL = 0x9, + NVME_SC_FUSED_MISSING = 0xa, + NVME_SC_INVALID_NS = 0xb, + NVME_SC_CMD_SEQ_ERROR = 0xc, + NVME_SC_SGL_INVALID_LAST = 0xd, + NVME_SC_SGL_INVALID_COUNT = 0xe, + NVME_SC_SGL_INVALID_DATA = 0xf, + NVME_SC_SGL_INVALID_METADATA = 0x10, + NVME_SC_SGL_INVALID_TYPE = 0x11, + NVME_SC_CMB_INVALID_USE = 0x12, + NVME_SC_PRP_INVALID_OFFSET = 0x13, + NVME_SC_AWU_EXCEEDED = 0x14, + NVME_SC_OP_DENIED = 0x15, + NVME_SC_SGL_INVALID_OFFSET = 0x16, + NVME_SC_HOSTID_FORMAT = 0x18, + NVME_SC_KAT_EXPIRED = 0x19, + NVME_SC_KAT_INVALID = 0x1a, + NVME_SC_CMD_ABORTED_PREMEPT = 0x1b, + NVME_SC_SANITIZE_FAILED = 0x1c, + NVME_SC_SANITIZE_IN_PROGRESS = 0x1d, + NVME_SC_SGL_INVALID_GRANULARITY = 0x1e, + NVME_SC_CMD_IN_CMBQ_NOT_SUPP = 0x1f, + NVME_SC_NS_WRITE_PROTECTED = 0x20, + NVME_SC_CMD_INTERRUPTED = 0x21, + NVME_SC_TRAN_TPORT_ERROR = 0x22, + NVME_SC_PROHIBITED_BY_CMD_AND_FEAT = 0x23, + NVME_SC_ADMIN_CMD_MEDIA_NOT_READY = 0x24, + NVME_SC_FDP_DISABLED = 0x29, + NVME_SC_INVALID_PLACEMENT_HANDLE_LIST = 0x2A, + 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 Codes: + */ + NVME_SC_CQ_INVALID = 0x00, + NVME_SC_QID_INVALID = 0x01, + NVME_SC_QUEUE_SIZE = 0x02, + NVME_SC_ABORT_LIMIT = 0x03, + NVME_SC_ABORT_MISSING = 0x04, + NVME_SC_ASYNC_LIMIT = 0x05, + NVME_SC_FIRMWARE_SLOT = 0x06, + NVME_SC_FIRMWARE_IMAGE = 0x07, + NVME_SC_INVALID_VECTOR = 0x08, + NVME_SC_INVALID_LOG_PAGE = 0x09, + NVME_SC_INVALID_FORMAT = 0x0a, + NVME_SC_FW_NEEDS_CONV_RESET = 0x0b, + NVME_SC_INVALID_QUEUE = 0x0c, + NVME_SC_FEATURE_NOT_SAVEABLE = 0x0d, + NVME_SC_FEATURE_NOT_CHANGEABLE = 0x0e, + NVME_SC_FEATURE_NOT_PER_NS = 0x0f, + NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x10, + NVME_SC_FW_NEEDS_RESET = 0x11, + NVME_SC_FW_NEEDS_MAX_TIME = 0x12, + NVME_SC_FW_ACTIVATE_PROHIBITED = 0x13, + NVME_SC_OVERLAPPING_RANGE = 0x14, + NVME_SC_NS_INSUFFICIENT_CAP = 0x15, + NVME_SC_NS_ID_UNAVAILABLE = 0x16, + NVME_SC_NS_ALREADY_ATTACHED = 0x18, + NVME_SC_NS_IS_PRIVATE = 0x19, + NVME_SC_NS_NOT_ATTACHED = 0x1a, + NVME_SC_THIN_PROV_NOT_SUPP = 0x1b, + NVME_SC_CTRL_LIST_INVALID = 0x1c, + NVME_SC_SELF_TEST_IN_PROGRESS = 0x1d, + NVME_SC_BP_WRITE_PROHIBITED = 0x1e, + NVME_SC_INVALID_CTRL_ID = 0x1f, + NVME_SC_INVALID_SEC_CTRL_STATE = 0x20, + NVME_SC_INVALID_CTRL_RESOURCES = 0x21, + NVME_SC_INVALID_RESOURCE_ID = 0x22, + NVME_SC_PMR_SAN_PROHIBITED = 0x23, + NVME_SC_ANA_GROUP_ID_INVALID = 0x24, + NVME_SC_ANA_ATTACH_FAILED = 0x25, + NVME_SC_INSUFFICIENT_CAP = 0x26, + NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED = 0x27, + NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED = 0x28, + + /* + * Command Set Specific - Namespace Types commands: + */ + NVME_SC_IOCS_NOT_SUPPORTED = 0x29, + NVME_SC_IOCS_NOT_ENABLED = 0x2a, + NVME_SC_IOCS_COMBINATION_REJECTED = 0x2b, + NVME_SC_INVALID_IOCS = 0x2c, + NVME_SC_ID_UNAVAILABLE = 0x2d, + + /* + * Discovery Information Management + */ + NVME_SC_INVALID_DISCOVERY_INFO = 0x2f, + NVME_SC_ZONING_DATA_STRUCT_LOCKED = 0x30, + NVME_SC_ZONING_DATA_STRUCT_NOTFND = 0x31, + NVME_SC_INSUFFICIENT_DISC_RES = 0x32, + NVME_SC_REQSTD_FUNCTION_DISABLED = 0x33, + NVME_SC_ZONEGRP_ORIGINATOR_INVLD = 0x34, + + /* + * I/O Command Set Specific - NVM commands: + */ + NVME_SC_BAD_ATTRIBUTES = 0x80, + NVME_SC_INVALID_PI = 0x81, + NVME_SC_READ_ONLY = 0x82, + NVME_SC_CMD_SIZE_LIMIT_EXCEEDED = 0x83, + NVME_SC_INCOMPATIBLE_NS = 0x85, + NVME_SC_FAST_COPY_NOT_POSSIBLE = 0x86, + NVME_SC_OVERLAPPING_IO_RANGE = 0x87, + NVME_SC_INSUFFICIENT_RESOURCES = 0x89, + + /* + * I/O Command Set Specific - Fabrics commands: + */ + NVME_SC_CONNECT_FORMAT = 0x80, + NVME_SC_CONNECT_CTRL_BUSY = 0x81, + NVME_SC_CONNECT_INVALID_PARAM = 0x82, + NVME_SC_CONNECT_RESTART_DISC = 0x83, + NVME_SC_CONNECT_INVALID_HOST = 0x84, + NVME_SC_DISCONNECT_INVALID_QTYPE= 0x85, + NVME_SC_DISCOVERY_RESTART = 0x90, + NVME_SC_AUTH_REQUIRED = 0x91, + + /* + * I/O Command Set Specific - ZNS commands: + */ + NVME_SC_ZNS_INVALID_OP_REQUEST = 0xb6, + NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE = 0xb7, + NVME_SC_ZNS_BOUNDARY_ERROR = 0xb8, + NVME_SC_ZNS_FULL = 0xb9, + NVME_SC_ZNS_READ_ONLY = 0xba, + NVME_SC_ZNS_OFFLINE = 0xbb, + NVME_SC_ZNS_INVALID_WRITE = 0xbc, + NVME_SC_ZNS_TOO_MANY_ACTIVE = 0xbd, + NVME_SC_ZNS_TOO_MANY_OPENS = 0xbe, + NVME_SC_ZNS_INVAL_TRANSITION = 0xbf, + + /* + * Media and Data Integrity Errors: + */ + NVME_SC_WRITE_FAULT = 0x80, + NVME_SC_READ_ERROR = 0x81, + NVME_SC_GUARD_CHECK = 0x82, + NVME_SC_APPTAG_CHECK = 0x83, + NVME_SC_REFTAG_CHECK = 0x84, + NVME_SC_COMPARE_FAILED = 0x85, + NVME_SC_ACCESS_DENIED = 0x86, + NVME_SC_UNWRITTEN_BLOCK = 0x87, + NVME_SC_STORAGE_TAG_CHECK = 0x88, + + /* + * Path-related Errors: + */ + NVME_SC_ANA_INTERNAL_PATH_ERROR = 0x00, + NVME_SC_ANA_PERSISTENT_LOSS = 0x01, + NVME_SC_ANA_INACCESSIBLE = 0x02, + NVME_SC_ANA_TRANSITION = 0x03, + NVME_SC_CTRL_PATH_ERROR = 0x60, + NVME_SC_HOST_PATH_ERROR = 0x70, + NVME_SC_CMD_ABORTED_BY_HOST = 0x71, + + /* + * Additional status field flags + */ + NVME_SC_CRD = 0x1800, + NVME_SC_MORE = 0x2000, + NVME_SC_DNR = 0x4000, +}; + +/** + * nvme_status_code_type() - Returns the NVMe Status Code Type + * @status_field: The NVMe Completion Queue Entry's Status Field + * See &enum nvme_status_field + * + * Returns: status code type + */ +static inline __u16 nvme_status_code_type(__u16 status_field) +{ + return NVME_GET(status_field, SCT); +} + +/** + * nvme_status_code() - Returns the NVMe Status Code + * @status_field: The NVMe Completion Queue Entry's Status Field + * See &enum nvme_status_field + * + * Returns: status code + */ +static inline __u16 nvme_status_code(__u16 status_field) +{ + return NVME_GET(status_field, SC); +} + +/** + * enum nvme_status_type - type encoding for NVMe return values, when + * represented as an int. + * + * The nvme_* api returns an int, with negative values indicating an internal + * or syscall error, zero signifying success, positive values representing + * the NVMe status. + * + * That latter case (the NVMe status) may represent status values from + * different parts of the transport/controller/etc, and are at most 16 bits of + * data. So, we use the most-significant 3 bits of the signed int to indicate + * which type of status this is. + * + * @NVME_STATUS_TYPE_SHIFT: shift value for status bits + * @NVME_STATUS_TYPE_MASK: mask value for status bits + * + * @NVME_STATUS_TYPE_NVME: NVMe command status value, typically from CDW3 + * @NVME_STATUS_TYPE_MI: NVMe-MI header status + */ +enum nvme_status_type { + NVME_STATUS_TYPE_SHIFT = 27, + NVME_STATUS_TYPE_MASK = 0x7, + + NVME_STATUS_TYPE_NVME = 0, + NVME_STATUS_TYPE_MI = 1, +}; + +/** + * nvme_status_get_type() - extract the type from a nvme_* return value + * @status: the (non-negative) return value from the NVMe API + * + * Returns: the type component of the status. + */ +static inline __u32 nvme_status_get_type(int status) +{ + return NVME_GET(status, STATUS_TYPE); +} + +/** + * nvme_status_get_value() - extract the status value from a nvme_* return + * value + * @status: the (non-negative) return value from the NVMe API + * + * Returns: the value component of the status; the set of values will depend + * on the status type. + */ +static inline __u32 nvme_status_get_value(int status) +{ + return status & ~NVME_SET(NVME_STATUS_TYPE_MASK, STATUS_TYPE); +} + +/** + * nvme_status_equals() - helper to check a status against a type and value + * @status: the (non-negative) return value from the NVMe API + * @type: the status type + * @value: the status value + * + * Returns: true if @status is of the specified type and value + */ +static inline __u32 nvme_status_equals(int status, enum nvme_status_type type, + unsigned int value) +{ + if (status < 0) + return false; + + return nvme_status_get_type(status) == type && + nvme_status_get_value(status) == value; +} + +/** + * enum nvme_admin_opcode - Known NVMe admin opcodes + * @nvme_admin_delete_sq: Delete I/O Submission Queue + * @nvme_admin_create_sq: Create I/O Submission Queue + * @nvme_admin_get_log_page: Get Log Page + * @nvme_admin_delete_cq: Delete I/O Completion Queue + * @nvme_admin_create_cq: Create I/O Completion Queue + * @nvme_admin_identify: Identify + * @nvme_admin_abort_cmd: Abort + * @nvme_admin_set_features: Set Features + * @nvme_admin_get_features: Get Features + * @nvme_admin_async_event: Asynchronous Event Request + * @nvme_admin_ns_mgmt: Namespace Management + * @nvme_admin_fw_activate: Firmware Commit + * @nvme_admin_fw_commit: Firmware Commit + * @nvme_admin_fw_download: Firmware Image Download + * @nvme_admin_dev_self_test: Device Self-test + * @nvme_admin_ns_attach: Namespace Attachment + * @nvme_admin_keep_alive: Keep Alive + * @nvme_admin_directive_send: Directive Send + * @nvme_admin_directive_recv: Directive Receive + * @nvme_admin_virtual_mgmt: Virtualization Management + * @nvme_admin_nvme_mi_send: NVMe-MI Send + * @nvme_admin_nvme_mi_recv: NVMe-MI Receive + * @nvme_admin_capacity_mgmt: Capacity Management + * @nvme_admin_discovery_info_mgmt: Discovery Information Management (DIM) + * @nvme_admin_fabric_zoning_recv: Fabric Zoning Receive + * @nvme_admin_lockdown: Lockdown + * @nvme_admin_fabric_zoning_lookup: Fabric Zoning Lookup + * @nvme_admin_fabric_zoning_send: Fabric Zoning Send + * @nvme_admin_dbbuf: Doorbell Buffer Config + * @nvme_admin_fabrics: Fabrics Commands + * @nvme_admin_format_nvm: Format NVM + * @nvme_admin_security_send: Security Send + * @nvme_admin_security_recv: Security Receive + * @nvme_admin_sanitize_nvm: Sanitize + * @nvme_admin_get_lba_status: Get LBA Status + */ +enum nvme_admin_opcode { + nvme_admin_delete_sq = 0x00, + nvme_admin_create_sq = 0x01, + nvme_admin_get_log_page = 0x02, + nvme_admin_delete_cq = 0x04, + nvme_admin_create_cq = 0x05, + nvme_admin_identify = 0x06, + nvme_admin_abort_cmd = 0x08, + nvme_admin_set_features = 0x09, + nvme_admin_get_features = 0x0a, + nvme_admin_async_event = 0x0c, + nvme_admin_ns_mgmt = 0x0d, + nvme_admin_fw_commit = 0x10, + nvme_admin_fw_activate = nvme_admin_fw_commit, + nvme_admin_fw_download = 0x11, + nvme_admin_dev_self_test = 0x14, + nvme_admin_ns_attach = 0x15, + nvme_admin_keep_alive = 0x18, + nvme_admin_directive_send = 0x19, + nvme_admin_directive_recv = 0x1a, + nvme_admin_virtual_mgmt = 0x1c, + nvme_admin_nvme_mi_send = 0x1d, + nvme_admin_nvme_mi_recv = 0x1e, + nvme_admin_capacity_mgmt = 0x20, + nvme_admin_discovery_info_mgmt = 0x21, + nvme_admin_fabric_zoning_recv = 0x22, + nvme_admin_lockdown = 0x24, + nvme_admin_fabric_zoning_lookup = 0x25, + nvme_admin_fabric_zoning_send = 0x29, + nvme_admin_dbbuf = 0x7c, + nvme_admin_fabrics = 0x7f, + nvme_admin_format_nvm = 0x80, + nvme_admin_security_send = 0x81, + nvme_admin_security_recv = 0x82, + nvme_admin_sanitize_nvm = 0x84, + nvme_admin_get_lba_status = 0x86, +}; + +/** + * enum nvme_identify_cns - Identify - CNS Values + * @NVME_IDENTIFY_CNS_NS: Identify Namespace data structure + * @NVME_IDENTIFY_CNS_CTRL: Identify Controller data structure + * @NVME_IDENTIFY_CNS_NS_ACTIVE_LIST: Active Namespace ID list + * @NVME_IDENTIFY_CNS_NS_DESC_LIST: Namespace Identification Descriptor list + * @NVME_IDENTIFY_CNS_NVMSET_LIST: NVM Set List + * @NVME_IDENTIFY_CNS_CSI_NS: I/O Command Set specific Identify + * Namespace data structure + * @NVME_IDENTIFY_CNS_CSI_CTRL: I/O Command Set specific Identify + * Controller data structure + * @NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST: Active Namespace ID list associated + * with the specified I/O Command Set + * @NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS: I/O Command Set Independent Identify + * @NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT: Namespace user data format + * @NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT: I/O Command Set specific user data + * format + * Namespace data structure + * @NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST: Allocated Namespace ID list + * @NVME_IDENTIFY_CNS_ALLOCATED_NS: Identify Namespace data structure for + * the specified allocated NSID + * @NVME_IDENTIFY_CNS_NS_CTRL_LIST: Controller List of controllers attached + * to the specified NSID + * @NVME_IDENTIFY_CNS_CTRL_LIST: Controller List of controllers that exist + * in the NVM subsystem + * @NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP: Primary Controller Capabilities data + * structure for the specified primary controller + * @NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST: Secondary Controller list of controllers + * associated with the primary controller + * processing the command + * @NVME_IDENTIFY_CNS_NS_GRANULARITY: A Namespace Granularity List + * @NVME_IDENTIFY_CNS_UUID_LIST: A UUID List + * @NVME_IDENTIFY_CNS_DOMAIN_LIST: Domain List + * @NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID: Endurance Group List + * @NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST: I/O Command Set specific Allocated Namespace + * ID list + * @NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE: I/O Command Set specific ID Namespace + * Data Structure for Allocated Namespace ID + * @NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE: Base Specification 2.0a section 5.17.2.21 + */ +enum nvme_identify_cns { + NVME_IDENTIFY_CNS_NS = 0x00, + NVME_IDENTIFY_CNS_CTRL = 0x01, + NVME_IDENTIFY_CNS_NS_ACTIVE_LIST = 0x02, + NVME_IDENTIFY_CNS_NS_DESC_LIST = 0x03, + NVME_IDENTIFY_CNS_NVMSET_LIST = 0x04, + NVME_IDENTIFY_CNS_CSI_NS = 0x05, + NVME_IDENTIFY_CNS_CSI_CTRL = 0x06, + NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST = 0x07, + NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS = 0x08, + NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT = 0x09, + NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT = 0x0A, + NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST = 0x10, + NVME_IDENTIFY_CNS_ALLOCATED_NS = 0x11, + NVME_IDENTIFY_CNS_NS_CTRL_LIST = 0x12, + NVME_IDENTIFY_CNS_CTRL_LIST = 0x13, + NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP = 0x14, + NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST = 0x15, + NVME_IDENTIFY_CNS_NS_GRANULARITY = 0x16, + NVME_IDENTIFY_CNS_UUID_LIST = 0x17, + NVME_IDENTIFY_CNS_DOMAIN_LIST = 0x18, + NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID = 0x19, + NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST = 0x1A, + NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE = 0x1B, + NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE = 0x1C, +}; + +/** + * enum nvme_cmd_get_log_lid - Get Log Page -Log Page Identifiers + * @NVME_LOG_LID_SUPPORTED_LOG_PAGES: Supported Log Pages + * @NVME_LOG_LID_ERROR: Error Information + * @NVME_LOG_LID_SMART: SMART / Health Information + * @NVME_LOG_LID_FW_SLOT: Firmware Slot Information + * @NVME_LOG_LID_CHANGED_NS: Changed Namespace List + * @NVME_LOG_LID_CMD_EFFECTS: Commands Supported and Effects + * @NVME_LOG_LID_DEVICE_SELF_TEST: Device Self-test + * @NVME_LOG_LID_TELEMETRY_HOST: Telemetry Host-Initiated + * @NVME_LOG_LID_TELEMETRY_CTRL: Telemetry Controller-Initiated + * @NVME_LOG_LID_ENDURANCE_GROUP: Endurance Group Information + * @NVME_LOG_LID_PREDICTABLE_LAT_NVMSET: Predictable Latency Per NVM Set + * @NVME_LOG_LID_PREDICTABLE_LAT_AGG: Predictable Latency Event Aggregate + * @NVME_LOG_LID_ANA: Asymmetric Namespace Access + * @NVME_LOG_LID_PERSISTENT_EVENT: Persistent Event Log + * @NVME_LOG_LID_LBA_STATUS: LBA Status Information + * @NVME_LOG_LID_ENDURANCE_GRP_EVT: Endurance Group Event Aggregate + * @NVME_LOG_LID_MEDIA_UNIT_STATUS: Media Unit Status + * @NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST: Supported Capacity Configuration Lis + * @NVME_LOG_LID_FID_SUPPORTED_EFFECTS: Feature Identifiers Supported and Effects + * @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects + * @NVME_LOG_LID_BOOT_PARTITION: Boot Partition + * @NVME_LOG_LID_PHY_RX_EOM: Physical Interface Receiver Eye Opening Measurement + * @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations + * @NVME_LOG_LID_FDP_RUH_USAGE: Reclaim Unit Handle Usage + * @NVME_LOG_LID_FDP_STATS: FDP Statistics + * @NVME_LOG_LID_FDP_EVENTS: FDP Events + * @NVME_LOG_LID_DISCOVER: Discovery + * @NVME_LOG_LID_RESERVATION: Reservation Notification + * @NVME_LOG_LID_SANITIZE: Sanitize Status + * @NVME_LOG_LID_ZNS_CHANGED_ZONES: Changed Zone List + */ +enum nvme_cmd_get_log_lid { + NVME_LOG_LID_SUPPORTED_LOG_PAGES = 0x00, + NVME_LOG_LID_ERROR = 0x01, + NVME_LOG_LID_SMART = 0x02, + NVME_LOG_LID_FW_SLOT = 0x03, + NVME_LOG_LID_CHANGED_NS = 0x04, + NVME_LOG_LID_CMD_EFFECTS = 0x05, + NVME_LOG_LID_DEVICE_SELF_TEST = 0x06, + NVME_LOG_LID_TELEMETRY_HOST = 0x07, + NVME_LOG_LID_TELEMETRY_CTRL = 0x08, + NVME_LOG_LID_ENDURANCE_GROUP = 0x09, + NVME_LOG_LID_PREDICTABLE_LAT_NVMSET = 0x0a, + NVME_LOG_LID_PREDICTABLE_LAT_AGG = 0x0b, + NVME_LOG_LID_ANA = 0x0c, + NVME_LOG_LID_PERSISTENT_EVENT = 0x0d, + NVME_LOG_LID_LBA_STATUS = 0x0e, + NVME_LOG_LID_ENDURANCE_GRP_EVT = 0x0f, + NVME_LOG_LID_MEDIA_UNIT_STATUS = 0x10, + NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST = 0x11, + NVME_LOG_LID_FID_SUPPORTED_EFFECTS = 0x12, + NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13, + NVME_LOG_LID_BOOT_PARTITION = 0x15, + NVME_LOG_LID_PHY_RX_EOM = 0x19, + NVME_LOG_LID_FDP_CONFIGS = 0x20, + NVME_LOG_LID_FDP_RUH_USAGE = 0x21, + NVME_LOG_LID_FDP_STATS = 0x22, + NVME_LOG_LID_FDP_EVENTS = 0x23, + NVME_LOG_LID_DISCOVER = 0x70, + NVME_LOG_LID_RESERVATION = 0x80, + NVME_LOG_LID_SANITIZE = 0x81, + NVME_LOG_LID_ZNS_CHANGED_ZONES = 0xbf, +}; + +/** + * enum nvme_features_id - Features - Feature Identifiers + * @NVME_FEAT_FID_ARBITRATION: Arbitration + * @NVME_FEAT_FID_POWER_MGMT: Power Management + * @NVME_FEAT_FID_LBA_RANGE: LBA Range Type + * @NVME_FEAT_FID_TEMP_THRESH: Temperature Threshold + * @NVME_FEAT_FID_ERR_RECOVERY: Error Recovery + * @NVME_FEAT_FID_VOLATILE_WC: Volatile Write Cache + * @NVME_FEAT_FID_NUM_QUEUES: Number of Queues + * @NVME_FEAT_FID_IRQ_COALESCE: Interrupt Coalescing + * @NVME_FEAT_FID_IRQ_CONFIG: Interrupt Vector Configuration + * @NVME_FEAT_FID_WRITE_ATOMIC: Write Atomicity Normal + * @NVME_FEAT_FID_ASYNC_EVENT: Asynchronous Event Configuration + * @NVME_FEAT_FID_AUTO_PST: Autonomous Power State Transition + * @NVME_FEAT_FID_HOST_MEM_BUF: Host Memory Buffer + * @NVME_FEAT_FID_TIMESTAMP: Timestamp + * @NVME_FEAT_FID_KATO: Keep Alive Timer + * @NVME_FEAT_FID_HCTM: Host Controlled Thermal Management + * @NVME_FEAT_FID_NOPSC: Non-Operational Power State Config + * @NVME_FEAT_FID_RRL: Read Recovery Level Config + * @NVME_FEAT_FID_PLM_CONFIG: Predictable Latency Mode Config + * @NVME_FEAT_FID_PLM_WINDOW: Predictable Latency Mode Window + * @NVME_FEAT_FID_LBA_STS_INTERVAL: LBA Status Information Report Interval + * @NVME_FEAT_FID_HOST_BEHAVIOR: Host Behavior Support + * @NVME_FEAT_FID_SANITIZE: Endurance Group Event Configuration + * @NVME_FEAT_FID_ENDURANCE_EVT_CFG: Endurance Group Event Configuration + * @NVME_FEAT_FID_IOCS_PROFILE: I/O Command Set Profile + * @NVME_FEAT_FID_SPINUP_CONTROL: Spinup Control + * @NVME_FEAT_FID_FDP: Flexible Data Placement + * @NVME_FEAT_FID_FDP_EVENTS: FDP Events + * @NVME_FEAT_FID_ENH_CTRL_METADATA: Enhanced Controller Metadata + * @NVME_FEAT_FID_CTRL_METADATA: Controller Metadata + * @NVME_FEAT_FID_NS_METADATA: Namespace Metadata + * @NVME_FEAT_FID_SW_PROGRESS: Software Progress Marker + * @NVME_FEAT_FID_HOST_ID: Host Identifier + * @NVME_FEAT_FID_RESV_MASK: Reservation Notification Mask + * @NVME_FEAT_FID_RESV_PERSIST: Reservation Persistence + * @NVME_FEAT_FID_WRITE_PROTECT: Namespace Write Protection Config + */ +enum nvme_features_id { + NVME_FEAT_FID_ARBITRATION = 0x01, + NVME_FEAT_FID_POWER_MGMT = 0x02, + NVME_FEAT_FID_LBA_RANGE = 0x03, + NVME_FEAT_FID_TEMP_THRESH = 0x04, + NVME_FEAT_FID_ERR_RECOVERY = 0x05, + NVME_FEAT_FID_VOLATILE_WC = 0x06, + NVME_FEAT_FID_NUM_QUEUES = 0x07, + NVME_FEAT_FID_IRQ_COALESCE = 0x08, + NVME_FEAT_FID_IRQ_CONFIG = 0x09, + NVME_FEAT_FID_WRITE_ATOMIC = 0x0a, + NVME_FEAT_FID_ASYNC_EVENT = 0x0b, + NVME_FEAT_FID_AUTO_PST = 0x0c, + NVME_FEAT_FID_HOST_MEM_BUF = 0x0d, + NVME_FEAT_FID_TIMESTAMP = 0x0e, + NVME_FEAT_FID_KATO = 0x0f, + NVME_FEAT_FID_HCTM = 0x10, + NVME_FEAT_FID_NOPSC = 0x11, + NVME_FEAT_FID_RRL = 0x12, + NVME_FEAT_FID_PLM_CONFIG = 0x13, + NVME_FEAT_FID_PLM_WINDOW = 0x14, + NVME_FEAT_FID_LBA_STS_INTERVAL = 0x15, + NVME_FEAT_FID_HOST_BEHAVIOR = 0x16, + NVME_FEAT_FID_SANITIZE = 0x17, + NVME_FEAT_FID_ENDURANCE_EVT_CFG = 0x18, + NVME_FEAT_FID_IOCS_PROFILE = 0x19, /* XXX: Placeholder until assigned */ + NVME_FEAT_FID_SPINUP_CONTROL = 0x1a, + NVME_FEAT_FID_FDP = 0x1d, + NVME_FEAT_FID_FDP_EVENTS = 0x1e, + NVME_FEAT_FID_ENH_CTRL_METADATA = 0x7d, + NVME_FEAT_FID_CTRL_METADATA = 0x7e, + NVME_FEAT_FID_NS_METADATA = 0x7f, + NVME_FEAT_FID_SW_PROGRESS = 0x80, + NVME_FEAT_FID_HOST_ID = 0x81, + NVME_FEAT_FID_RESV_MASK = 0x82, + NVME_FEAT_FID_RESV_PERSIST = 0x83, + NVME_FEAT_FID_WRITE_PROTECT = 0x84, +}; + +/** + * enum nvme_feat - Features Access Shifts/Masks values + * @NVME_FEAT_ARBITRATION_BURST_SHIFT: + * @NVME_FEAT_ARBITRATION_BURST_MASK: + * @NVME_FEAT_ARBITRATION_LPW_SHIFT: + * @NVME_FEAT_ARBITRATION_LPW_MASK: + * @NVME_FEAT_ARBITRATION_MPW_SHIFT: + * @NVME_FEAT_ARBITRATION_MPW_MASK: + * @NVME_FEAT_ARBITRATION_HPW_SHIFT: + * @NVME_FEAT_ARBITRATION_HPW_MASK: + * @NVME_FEAT_PWRMGMT_PS_SHIFT: + * @NVME_FEAT_PWRMGMT_PS_MASK: + * @NVME_FEAT_PWRMGMT_WH_SHIFT: + * @NVME_FEAT_PWRMGMT_WH_MASK: + * @NVME_FEAT_LBAR_NR_SHIFT: + * @NVME_FEAT_LBAR_NR_MASK: + * @NVME_FEAT_TT_TMPTH_SHIFT: + * @NVME_FEAT_TT_TMPTH_MASK: + * @NVME_FEAT_TT_TMPSEL_SHIFT: + * @NVME_FEAT_TT_TMPSEL_MASK: + * @NVME_FEAT_TT_THSEL_SHIFT: + * @NVME_FEAT_TT_THSEL_MASK: + * @NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT: + * @NVME_FEAT_ERROR_RECOVERY_TLER_MASK: + * @NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT: + * @NVME_FEAT_ERROR_RECOVERY_DULBE_MASK: + * @NVME_FEAT_VWC_WCE_SHIFT: + * @NVME_FEAT_VWC_WCE_MASK: + * @NVME_FEAT_NRQS_NSQR_SHIFT: + * @NVME_FEAT_NRQS_NSQR_MASK: + * @NVME_FEAT_NRQS_NCQR_SHIFT: + * @NVME_FEAT_NRQS_NCQR_MASK: + * @NVME_FEAT_IRQC_THR_SHIFT: + * @NVME_FEAT_IRQC_THR_MASK: + * @NVME_FEAT_IRQC_TIME_SHIFT: + * @NVME_FEAT_IRQC_TIME_MASK: + * @NVME_FEAT_ICFG_IV_SHIFT: + * @NVME_FEAT_ICFG_IV_MASK: + * @NVME_FEAT_ICFG_CD_SHIFT: + * @NVME_FEAT_ICFG_CD_MASK: + * @NVME_FEAT_WA_DN_SHIFT: + * @NVME_FEAT_WA_DN_MASK: + * @NVME_FEAT_AE_SMART_SHIFT: + * @NVME_FEAT_AE_SMART_MASK: + * @NVME_FEAT_AE_NAN_SHIFT: + * @NVME_FEAT_AE_NAN_MASK: + * @NVME_FEAT_AE_FW_SHIFT: + * @NVME_FEAT_AE_FW_MASK: + * @NVME_FEAT_AE_TELEM_SHIFT: + * @NVME_FEAT_AE_TELEM_MASK: + * @NVME_FEAT_AE_ANA_SHIFT: + * @NVME_FEAT_AE_ANA_MASK: + * @NVME_FEAT_AE_PLA_SHIFT: + * @NVME_FEAT_AE_PLA_MASK: + * @NVME_FEAT_AE_LBAS_SHIFT: + * @NVME_FEAT_AE_LBAS_MASK: + * @NVME_FEAT_AE_EGA_SHIFT: + * @NVME_FEAT_AE_EGA_MASK: + * @NVME_FEAT_APST_APSTE_SHIFT: + * @NVME_FEAT_APST_APSTE_MASK: + * @NVME_FEAT_HMEM_EHM_SHIFT: + * @NVME_FEAT_HMEM_EHM_MASK: + * @NVME_FEAT_HCTM_TMT2_SHIFT: + * @NVME_FEAT_HCTM_TMT2_MASK: + * @NVME_FEAT_HCTM_TMT1_SHIFT: + * @NVME_FEAT_HCTM_TMT1_MASK: + * @NVME_FEAT_NOPS_NOPPME_SHIFT: + * @NVME_FEAT_NOPS_NOPPME_MASK: + * @NVME_FEAT_RRL_RRL_SHIFT: + * @NVME_FEAT_RRL_RRL_MASK: + * @NVME_FEAT_PLM_PLME_SHIFT: + * @NVME_FEAT_PLM_PLME_MASK: + * @NVME_FEAT_PLMW_WS_SHIFT: + * @NVME_FEAT_PLMW_WS_MASK: + * @NVME_FEAT_LBAS_LSIRI_SHIFT: + * @NVME_FEAT_LBAS_LSIRI_MASK: + * @NVME_FEAT_LBAS_LSIPI_SHIFT: + * @NVME_FEAT_LBAS_LSIPI_MASK: + * @NVME_FEAT_SC_NODRM_SHIFT: + * @NVME_FEAT_SC_NODRM_MASK: + * @NVME_FEAT_EG_ENDGID_SHIFT: + * @NVME_FEAT_EG_ENDGID_MASK: + * @NVME_FEAT_EG_EGCW_SHIFT: + * @NVME_FEAT_EG_EGCW_MASK: + * @NVME_FEAT_SPM_PBSLC_SHIFT: + * @NVME_FEAT_SPM_PBSLC_MASK: + * @NVME_FEAT_HOSTID_EXHID_SHIFT: + * @NVME_FEAT_HOSTID_EXHID_MASK: + * @NVME_FEAT_RM_REGPRE_SHIFT: + * @NVME_FEAT_RM_REGPRE_MASK: + * @NVME_FEAT_RM_RESREL_SHIFT: + * @NVME_FEAT_RM_RESREL_MASK: + * @NVME_FEAT_RM_RESPRE_SHIFT: + * @NVME_FEAT_RM_RESPRE_MASK: + * @NVME_FEAT_RP_PTPL_SHIFT: + * @NVME_FEAT_RP_PTPL_MASK: + * @NVME_FEAT_WP_WPS_SHIFT: + * @NVME_FEAT_WP_WPS_MASK: + * @NVME_FEAT_IOCSP_IOCSCI_SHIFT: + * @NVME_FEAT_IOCSP_IOCSCI_MASK: + * @NVME_FEAT_FDP_ENABLED_SHIFT: + * @NVME_FEAT_FDP_ENABLED_MASK: + * @NVME_FEAT_FDP_INDEX_SHIFT: + * @NVME_FEAT_FDP_INDEX_MASK: + * @NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT: + * @NVME_FEAT_FDP_EVENTS_ENABLE_MASK: + */ +enum nvme_feat { + NVME_FEAT_ARBITRATION_BURST_SHIFT = 0, + NVME_FEAT_ARBITRATION_BURST_MASK = 0x7, + NVME_FEAT_ARBITRATION_LPW_SHIFT = 8, + NVME_FEAT_ARBITRATION_LPW_MASK = 0xff, + NVME_FEAT_ARBITRATION_MPW_SHIFT = 16, + NVME_FEAT_ARBITRATION_MPW_MASK = 0xff, + NVME_FEAT_ARBITRATION_HPW_SHIFT = 24, + NVME_FEAT_ARBITRATION_HPW_MASK = 0xff, + NVME_FEAT_PWRMGMT_PS_SHIFT = 0, + NVME_FEAT_PWRMGMT_PS_MASK = 0x1f, + NVME_FEAT_PWRMGMT_WH_SHIFT = 5, + NVME_FEAT_PWRMGMT_WH_MASK = 0x7, + NVME_FEAT_LBAR_NR_SHIFT = 0, + NVME_FEAT_LBAR_NR_MASK = 0x3f, + NVME_FEAT_TT_TMPTH_SHIFT = 0, + NVME_FEAT_TT_TMPTH_MASK = 0xffff, + NVME_FEAT_TT_TMPSEL_SHIFT = 16, + NVME_FEAT_TT_TMPSEL_MASK = 0xf, + NVME_FEAT_TT_THSEL_SHIFT = 20, + NVME_FEAT_TT_THSEL_MASK = 0x3, + NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT = 0, + NVME_FEAT_ERROR_RECOVERY_TLER_MASK = 0xffff, + NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT = 16, + NVME_FEAT_ERROR_RECOVERY_DULBE_MASK = 0x1, + NVME_FEAT_VWC_WCE_SHIFT = 0, + NVME_FEAT_VWC_WCE_MASK = 0x1, + NVME_FEAT_NRQS_NSQR_SHIFT = 0, + NVME_FEAT_NRQS_NSQR_MASK = 0xffff, + NVME_FEAT_NRQS_NCQR_SHIFT = 16, + NVME_FEAT_NRQS_NCQR_MASK = 0xffff, + NVME_FEAT_IRQC_THR_SHIFT = 0, + NVME_FEAT_IRQC_THR_MASK = 0xff, + NVME_FEAT_IRQC_TIME_SHIFT = 8, + NVME_FEAT_IRQC_TIME_MASK = 0xff, + NVME_FEAT_ICFG_IV_SHIFT = 0, + NVME_FEAT_ICFG_IV_MASK = 0xffff, + NVME_FEAT_ICFG_CD_SHIFT = 16, + NVME_FEAT_ICFG_CD_MASK = 0x1, + NVME_FEAT_WA_DN_SHIFT = 0, + NVME_FEAT_WA_DN_MASK = 0x1, + NVME_FEAT_AE_SMART_SHIFT = 0, + NVME_FEAT_AE_SMART_MASK = 0xff, + NVME_FEAT_AE_NAN_SHIFT = 8, + NVME_FEAT_AE_NAN_MASK = 0x1, + NVME_FEAT_AE_FW_SHIFT = 9, + NVME_FEAT_AE_FW_MASK = 0x1, + NVME_FEAT_AE_TELEM_SHIFT = 10, + NVME_FEAT_AE_TELEM_MASK = 0x1, + NVME_FEAT_AE_ANA_SHIFT = 11, + NVME_FEAT_AE_ANA_MASK = 0x1, + NVME_FEAT_AE_PLA_SHIFT = 12, + NVME_FEAT_AE_PLA_MASK = 0x1, + NVME_FEAT_AE_LBAS_SHIFT = 13, + NVME_FEAT_AE_LBAS_MASK = 0x1, + NVME_FEAT_AE_EGA_SHIFT = 14, + NVME_FEAT_AE_EGA_MASK = 0x1, + NVME_FEAT_APST_APSTE_SHIFT = 0, + NVME_FEAT_APST_APSTE_MASK = 0x1, + NVME_FEAT_HMEM_EHM_SHIFT = 0, + NVME_FEAT_HMEM_EHM_MASK = 0x1, + NVME_FEAT_HCTM_TMT2_SHIFT = 0, + NVME_FEAT_HCTM_TMT2_MASK = 0xffff, + NVME_FEAT_HCTM_TMT1_SHIFT = 16, + NVME_FEAT_HCTM_TMT1_MASK = 0xffff, + NVME_FEAT_NOPS_NOPPME_SHIFT = 0, + NVME_FEAT_NOPS_NOPPME_MASK = 0x1, + NVME_FEAT_RRL_RRL_SHIFT = 0, + NVME_FEAT_RRL_RRL_MASK = 0xff, + NVME_FEAT_PLM_PLME_SHIFT = 0, + NVME_FEAT_PLM_PLME_MASK = 0x1, + NVME_FEAT_PLMW_WS_SHIFT = 0, + NVME_FEAT_PLMW_WS_MASK = 0x7, + NVME_FEAT_LBAS_LSIRI_SHIFT = 0, + NVME_FEAT_LBAS_LSIRI_MASK = 0xffff, + NVME_FEAT_LBAS_LSIPI_SHIFT = 16, + NVME_FEAT_LBAS_LSIPI_MASK = 0xffff, + NVME_FEAT_SC_NODRM_SHIFT = 0, + NVME_FEAT_SC_NODRM_MASK = 0x1, + NVME_FEAT_EG_ENDGID_SHIFT = 0, + NVME_FEAT_EG_ENDGID_MASK = 0xffff, + NVME_FEAT_EG_EGCW_SHIFT = 16, + NVME_FEAT_EG_EGCW_MASK = 0xff, + NVME_FEAT_SPM_PBSLC_SHIFT = 0, + NVME_FEAT_SPM_PBSLC_MASK = 0xff, + NVME_FEAT_HOSTID_EXHID_SHIFT = 0, + NVME_FEAT_HOSTID_EXHID_MASK = 0x1, + NVME_FEAT_RM_REGPRE_SHIFT = 1, + NVME_FEAT_RM_REGPRE_MASK = 0x1, + NVME_FEAT_RM_RESREL_SHIFT = 2, + NVME_FEAT_RM_RESREL_MASK = 0x1, + NVME_FEAT_RM_RESPRE_SHIFT = 0x3, + NVME_FEAT_RM_RESPRE_MASK = 0x1, + NVME_FEAT_RP_PTPL_SHIFT = 0, + NVME_FEAT_RP_PTPL_MASK = 0x1, + NVME_FEAT_WP_WPS_SHIFT = 0, + NVME_FEAT_WP_WPS_MASK = 0x7, + NVME_FEAT_IOCSP_IOCSCI_SHIFT = 0, + NVME_FEAT_IOCSP_IOCSCI_MASK = 0x1ff, + NVME_FEAT_FDP_ENABLED_SHIFT = 0, + NVME_FEAT_FDP_ENABLED_MASK = 0x1, + NVME_FEAT_FDP_INDEX_SHIFT = 8, + NVME_FEAT_FDP_INDEX_MASK = 0xf, + NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT = 0, + NVME_FEAT_FDP_EVENTS_ENABLE_MASK = 0x1, +}; + +/** + * enum nvme_get_features_sel - Get Features - Select + * @NVME_GET_FEATURES_SEL_CURRENT: Current value + * @NVME_GET_FEATURES_SEL_DEFAULT: Default value + * @NVME_GET_FEATURES_SEL_SAVED: Saved value + * @NVME_GET_FEATURES_SEL_SUPPORTED: Supported capabilities + */ +enum nvme_get_features_sel { + NVME_GET_FEATURES_SEL_CURRENT = 0, + NVME_GET_FEATURES_SEL_DEFAULT = 1, + NVME_GET_FEATURES_SEL_SAVED = 2, + NVME_GET_FEATURES_SEL_SUPPORTED = 3, +}; + +/** + * enum nvme_cmd_format_mset - Format NVM - Metadata Settings + * @NVME_FORMAT_MSET_SEPARATE: indicates that the metadata is transferred + * as part of a separate buffer. + * @NVME_FORMAT_MSET_EXTENDED: indicates that the metadata is transferred + * as part of an extended data LBA. + */ +enum nvme_cmd_format_mset { + NVME_FORMAT_MSET_SEPARATE = 0, + NVME_FORMAT_MSET_EXTENDED = 1, +}; + +/** + * enum nvme_cmd_format_pi - Format NVM - Protection Information + * @NVME_FORMAT_PI_DISABLE: Protection information is not enabled. + * @NVME_FORMAT_PI_TYPE1: Protection information is enabled, Type 1. + * @NVME_FORMAT_PI_TYPE2: Protection information is enabled, Type 2. + * @NVME_FORMAT_PI_TYPE3: Protection information is enabled, Type 3. + */ +enum nvme_cmd_format_pi { + NVME_FORMAT_PI_DISABLE = 0, + NVME_FORMAT_PI_TYPE1 = 1, + NVME_FORMAT_PI_TYPE2 = 2, + NVME_FORMAT_PI_TYPE3 = 3, +}; + +/** + * enum nvme_cmd_format_pil - Format NVM - Protection Information Location + * @NVME_FORMAT_PIL_LAST: Protection information is transferred as the last + * bytes of metadata. + * @NVME_FORMAT_PIL_FIRST: Protection information is transferred as the first + * bytes of metadata. + */ +enum nvme_cmd_format_pil { + NVME_FORMAT_PIL_LAST = 0, + NVME_FORMAT_PIL_FIRST = 1, +}; + +/** + * enum nvme_cmd_format_ses - Format NVM - Secure Erase Settings + * @NVME_FORMAT_SES_NONE: No secure erase operation requested. + * @NVME_FORMAT_SES_USER_DATA_ERASE: User Data Erase: All user data shall be erased, + * contents of the user data after the erase is + * indeterminate (e.g. the user data may be zero + * filled, one filled, etc.). If a User Data Erase + * is requested and all affected user data is + * encrypted, then the controller is allowed + * to use a cryptographic erase to perform + * the requested User Data Erase. + * @NVME_FORMAT_SES_CRYPTO_ERASE: Cryptographic Erase: All user data shall + * be erased cryptographically. This is + * accomplished by deleting the encryption key. + */ +enum nvme_cmd_format_ses { + NVME_FORMAT_SES_NONE = 0, + NVME_FORMAT_SES_USER_DATA_ERASE = 1, + NVME_FORMAT_SES_CRYPTO_ERASE = 2, +}; + +/** + * enum nvme_ns_mgmt_sel - Namespace Management - Select + * @NVME_NS_MGMT_SEL_CREATE: Namespace Create selection + * @NVME_NS_MGMT_SEL_DELETE: Namespace Delete selection + */ +enum nvme_ns_mgmt_sel { + NVME_NS_MGMT_SEL_CREATE = 0, + NVME_NS_MGMT_SEL_DELETE = 1, +}; + +/** + * enum nvme_ns_attach_sel - Namespace Attachment - Select + * @NVME_NS_ATTACH_SEL_CTRL_ATTACH: Namespace attach selection + * @NVME_NS_ATTACH_SEL_CTRL_DEATTACH: Namespace detach selection + */ +enum nvme_ns_attach_sel { + NVME_NS_ATTACH_SEL_CTRL_ATTACH = 0, + NVME_NS_ATTACH_SEL_CTRL_DEATTACH = 1, +}; + +/** + * enum nvme_fw_commit_ca - Firmware Commit - Commit Action + * @NVME_FW_COMMIT_CA_REPLACE: Downloaded image replaces the existing + * image, if any, in the specified Firmware + * Slot. The newly placed image is not + * activated. + * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE: Downloaded image replaces the existing + * image, if any, in the specified Firmware + * Slot. The newly placed image is activated + * at the next Controller Level Reset. + * @NVME_FW_COMMIT_CA_SET_ACTIVE: The existing image in the specified + * Firmware Slot is activated at the + * next Controller Level Reset. + * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE: Downloaded image replaces the existing + * image, if any, in the specified Firmware + * Slot and is then activated immediately. + * If there is not a newly downloaded image, + * then the existing image in the specified + * firmware slot is activated immediately. + * @NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION: Downloaded image replaces the Boot + * Partition specified by the Boot + * Partition ID field. + * @NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION: Mark the Boot Partition specified in + * the BPID field as active and update + * BPINFO.ABPID. + */ +enum nvme_fw_commit_ca { + NVME_FW_COMMIT_CA_REPLACE = 0, + NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE = 1, + NVME_FW_COMMIT_CA_SET_ACTIVE = 2, + NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE = 3, + NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION = 6, + NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION = 7, +}; + +/** + * enum nvme_directive_dtype - Directive Types + * @NVME_DIRECTIVE_DTYPE_IDENTIFY: Identify directive type + * @NVME_DIRECTIVE_DTYPE_STREAMS: Streams directive type + */ +enum nvme_directive_dtype { + NVME_DIRECTIVE_DTYPE_IDENTIFY = 0, + NVME_DIRECTIVE_DTYPE_STREAMS = 1, +}; + +/** + * enum nvme_directive_receive_doper - Directive Receive Directive Operation + * @NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM: + * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM: + * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS: + * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE: + */ +enum nvme_directive_receive_doper { + NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM = 0x01, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM = 0x01, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS = 0x02, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE = 0x03, +}; + +/** + * enum nvme_directive_send_doper - Directive Send Directive Operation + * @NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR: + * @NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER: + * @NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE: + */ +enum nvme_directive_send_doper { + NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR = 0x01, + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER = 0x01, + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE = 0x02, +}; + +/** + * enum nvme_directive_send_identify_endir - Enable Directive + * @NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE: + * @NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE: + */ +enum nvme_directive_send_identify_endir { + NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE = 0, + NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE = 1, +}; + +/** + * enum nvme_sanitize_sanact - Sanitize Action + * @NVME_SANITIZE_SANACT_EXIT_FAILURE: Exit Failure Mode. + * @NVME_SANITIZE_SANACT_START_BLOCK_ERASE: Start a Block Erase sanitize operation. + * @NVME_SANITIZE_SANACT_START_OVERWRITE: Start an Overwrite sanitize operation. + * @NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: Start a Crypto Erase sanitize operation. + */ +enum nvme_sanitize_sanact { + NVME_SANITIZE_SANACT_EXIT_FAILURE = 1, + NVME_SANITIZE_SANACT_START_BLOCK_ERASE = 2, + NVME_SANITIZE_SANACT_START_OVERWRITE = 3, + NVME_SANITIZE_SANACT_START_CRYPTO_ERASE = 4, +}; + +/** + * enum nvme_dst_stc - Action taken by the Device Self-test command + * @NVME_DST_STC_SHORT: Start a short device self-test operation + * @NVME_DST_STC_LONG: Start an extended device self-test operation + * @NVME_DST_STC_VS: Start a vendor specific device self-test operation + * @NVME_DST_STC_ABORT: Abort device self-test operation + */ +enum nvme_dst_stc { + NVME_DST_STC_SHORT = 0x1, + NVME_DST_STC_LONG = 0x2, + NVME_DST_STC_VS = 0xe, + NVME_DST_STC_ABORT = 0xf, +}; + +/** + * enum nvme_virt_mgmt_act - Virtualization Management - Action + * @NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC: Primary Controller Flexible + * Allocation + * @NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL: Secondary Controller Offline + * @NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL: Secondary Controller Assign + * @NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL: Secondary Controller Online + */ +enum nvme_virt_mgmt_act { + NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC = 1, + NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL = 7, + NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL = 8, + NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL = 9, +}; + +/** + * enum nvme_virt_mgmt_rt - Virtualization Management - Resource Type + * @NVME_VIRT_MGMT_RT_VQ_RESOURCE: VQ Resources + * @NVME_VIRT_MGMT_RT_VI_RESOURCE: VI Resources + */ +enum nvme_virt_mgmt_rt { + NVME_VIRT_MGMT_RT_VQ_RESOURCE = 0, + NVME_VIRT_MGMT_RT_VI_RESOURCE = 1, +}; + +/** + * enum nvme_ns_write_protect_cfg - Write Protection - Write Protection State + * @NVME_NS_WP_CFG_NONE: No Write Protect + * @NVME_NS_WP_CFG_PROTECT: Write Protect + * @NVME_NS_WP_CFG_PROTECT_POWER_CYCLE: Write Protect Until Power Cycle + * @NVME_NS_WP_CFG_PROTECT_PERMANENT: Permanent Write Protect + */ +enum nvme_ns_write_protect_cfg { + NVME_NS_WP_CFG_NONE = 0, + NVME_NS_WP_CFG_PROTECT = 1, + NVME_NS_WP_CFG_PROTECT_POWER_CYCLE = 2, + NVME_NS_WP_CFG_PROTECT_PERMANENT = 3, +}; + +/** + * enum nvme_log_ana_lsp - Asymmetric Namespace Access - Return Groups Only + * @NVME_LOG_ANA_LSP_RGO_NAMESPACES: + * @NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY: + */ +enum nvme_log_ana_lsp { + NVME_LOG_ANA_LSP_RGO_NAMESPACES = 0, + NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY = 1, +}; + +/** + * enum nvme_log_phy_rx_eom_action - Physical Interface Receiver Eye Opening Measurement Action + * @NVME_LOG_PHY_RX_EOM_READ: Read Log Data + * @NVME_LOG_PHY_RX_EOM_START_READ: Start Measurement and Read Log Data + * @NVME_LOG_PHY_RX_EOM_ABORT_CLEAR: Abort Measurement and Clear Log Data + */ +enum nvme_log_phy_rx_eom_action { + NVME_LOG_PHY_RX_EOM_READ = 0, + NVME_LOG_PHY_RX_EOM_START_READ = 1, + NVME_LOG_PHY_RX_EOM_ABORT_CLEAR = 2, +}; + +/** + * enum nvme_log_phy_rx_eom_quality - Physical Interface Receiver Eye Opening Measurement Quality + * @NVME_LOG_PHY_RX_EOM_GOOD: <= Better Quality + * @NVME_LOG_PHY_RX_EOM_BETTER: <= Best Quality, >= Good Quality + * @NVME_LOG_PHY_RX_EOM_BEST: >= Better Quality + */ +enum nvme_log_phy_rx_eom_quality { + NVME_LOG_PHY_RX_EOM_GOOD = 0, + NVME_LOG_PHY_RX_EOM_BETTER = 1, + NVME_LOG_PHY_RX_EOM_BEST = 2, +}; + +/** + * enum nvme_pevent_log_action - Persistent Event Log - Action + * @NVME_PEVENT_LOG_READ: Read Log Data + * @NVME_PEVENT_LOG_EST_CTX_AND_READ: Establish Context and Read Log Data + * @NVME_PEVENT_LOG_RELEASE_CTX: Release Context + */ +enum nvme_pevent_log_action { + NVME_PEVENT_LOG_READ = 0x0, + NVME_PEVENT_LOG_EST_CTX_AND_READ = 0x1, + NVME_PEVENT_LOG_RELEASE_CTX = 0x2, +}; + +/** + * enum nvme_feat_tmpthresh_thsel - Temperature Threshold - Threshold Type Select + * @NVME_FEATURE_TEMPTHRESH_THSEL_OVER: Over temperature threshold select + * @NVME_FEATURE_TEMPTHRESH_THSEL_UNDER: Under temperature threshold select + */ +enum nvme_feat_tmpthresh_thsel { + NVME_FEATURE_TEMPTHRESH_THSEL_OVER = 0, + NVME_FEATURE_TEMPTHRESH_THSEL_UNDER = 1, +}; + +/** + * enum nvme_features_async_event_config_flags - Asynchronous Event Configuration configuration flags + * @NVME_FEATURE_AENCFG_SMART_CRIT_SPARE: + * @NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE: + * @NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED: + * @NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY: + * @NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP: + * @NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR: + * @NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES: + * @NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION: + * @NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG: + * @NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE: + * @NVME_FEATURE_AENCFG_NOTICE_PL_EVENT: + * @NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS: + * @NVME_FEATURE_AENCFG_NOTICE_EG_EVENT: + * @NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE: + */ +enum nvme_features_async_event_config_flags { + NVME_FEATURE_AENCFG_SMART_CRIT_SPARE = 1 << 0, + NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE = 1 << 1, + NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED = 1 << 2, + NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY = 1 << 3, + NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP = 1 << 4, + NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR = 1 << 5, + NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES = 1 << 8, + NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION = 1 << 9, + NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG = 1 << 10, + NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE = 1 << 11, + NVME_FEATURE_AENCFG_NOTICE_PL_EVENT = 1 << 12, + NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS = 1 << 13, + NVME_FEATURE_AENCFG_NOTICE_EG_EVENT = 1 << 14, + NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE = 1 << 31, +}; + +/** + * enum nvme_feat_plm_window_select - Predictable Latency Per NVM Set Log + * @NVME_FEATURE_PLM_DTWIN: Deterministic Window select + * @NVME_FEATURE_PLM_NDWIN: Non-Deterministic Window select + */ +enum nvme_feat_plm_window_select { + NVME_FEATURE_PLM_DTWIN = 1, + NVME_FEATURE_PLM_NDWIN = 2, +}; + +/** + * enum nvme_feat_resv_notify_flags - Reservation Notification Configuration + * @NVME_FEAT_RESV_NOTIFY_REGPRE: Mask Registration Preempted Notification + * @NVME_FEAT_RESV_NOTIFY_RESREL: Mask Reservation Released Notification + * @NVME_FEAT_RESV_NOTIFY_RESPRE: Mask Reservation Preempted Notification + */ +enum nvme_feat_resv_notify_flags { + NVME_FEAT_RESV_NOTIFY_REGPRE = 1 << 1, + NVME_FEAT_RESV_NOTIFY_RESREL = 1 << 2, + NVME_FEAT_RESV_NOTIFY_RESPRE = 1 << 3, +}; + +/** + * enum nvme_feat_nswpcfg_state - Write Protection - Write Protection State + * @NVME_FEAT_NS_NO_WRITE_PROTECT: No Write Protect + * @NVME_FEAT_NS_WRITE_PROTECT: Write Protect + * @NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE: Write Protect Until Power Cycle + * @NVME_FEAT_NS_WRITE_PROTECT_PERMANENT: Permanent Write Protect + */ +enum nvme_feat_nswpcfg_state { + NVME_FEAT_NS_NO_WRITE_PROTECT = 0, + NVME_FEAT_NS_WRITE_PROTECT = 1, + NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE = 2, + NVME_FEAT_NS_WRITE_PROTECT_PERMANENT = 3, +}; + +/** + * enum nvme_fctype - Fabrics Command Types + * @nvme_fabrics_type_property_set: Property set + * @nvme_fabrics_type_connect: Connect + * @nvme_fabrics_type_property_get: Property Get + * @nvme_fabrics_type_auth_send: Authentication Send + * @nvme_fabrics_type_auth_receive: Authentication Receive + * @nvme_fabrics_type_disconnect: Disconnect + */ +enum nvme_fctype { + nvme_fabrics_type_property_set = 0x00, + nvme_fabrics_type_connect = 0x01, + nvme_fabrics_type_property_get = 0x04, + nvme_fabrics_type_auth_send = 0x05, + nvme_fabrics_type_auth_receive = 0x06, + nvme_fabrics_type_disconnect = 0x08, +}; + +/** + * enum nvme_data_tfr - Data transfer direction of the command + * @NVME_DATA_TFR_NO_DATA_TFR: No data transfer + * @NVME_DATA_TFR_HOST_TO_CTRL: Host to controller + * @NVME_DATA_TFR_CTRL_TO_HOST: Controller to host + * @NVME_DATA_TFR_BIDIRECTIONAL: Bidirectional + */ +enum nvme_data_tfr { + NVME_DATA_TFR_NO_DATA_TFR = 0x0, + NVME_DATA_TFR_HOST_TO_CTRL = 0x1, + NVME_DATA_TFR_CTRL_TO_HOST = 0x2, + NVME_DATA_TFR_BIDIRECTIONAL = 0x3, +}; + +/** + * enum nvme_io_opcode - Opcodes for I/O Commands + * @nvme_cmd_flush: Flush + * @nvme_cmd_write: Write + * @nvme_cmd_read: Read + * @nvme_cmd_write_uncor: Write Uncorrectable + * @nvme_cmd_compare: Compare + * @nvme_cmd_write_zeroes: write Zeros + * @nvme_cmd_dsm: Dataset Management + * @nvme_cmd_verify: Verify + * @nvme_cmd_resv_register: Reservation Register + * @nvme_cmd_resv_report: Reservation Report + * @nvme_cmd_resv_acquire: Reservation Acquire + * @nvme_cmd_io_mgmt_recv: I/O Management Receive + * @nvme_cmd_resv_release: Reservation Release + * @nvme_cmd_copy: Copy + * @nvme_cmd_io_mgmt_send: I/O Management Send + * @nvme_zns_cmd_mgmt_send: Zone Management Send + * @nvme_zns_cmd_mgmt_recv: Zone Management Receive + * @nvme_zns_cmd_append: Zone Append + */ +enum nvme_io_opcode { + nvme_cmd_flush = 0x00, + nvme_cmd_write = 0x01, + nvme_cmd_read = 0x02, + nvme_cmd_write_uncor = 0x04, + nvme_cmd_compare = 0x05, + nvme_cmd_write_zeroes = 0x08, + nvme_cmd_dsm = 0x09, + nvme_cmd_verify = 0x0c, + nvme_cmd_resv_register = 0x0d, + nvme_cmd_resv_report = 0x0e, + nvme_cmd_resv_acquire = 0x11, + nvme_cmd_io_mgmt_recv = 0x12, + nvme_cmd_resv_release = 0x15, + nvme_cmd_copy = 0x19, + nvme_cmd_io_mgmt_send = 0x1d, + nvme_zns_cmd_mgmt_send = 0x79, + nvme_zns_cmd_mgmt_recv = 0x7a, + nvme_zns_cmd_append = 0x7d, +}; + +/** + * enum nvme_io_control_flags - I/O control flags + * @NVME_IO_DTYPE_STREAMS: Directive Type Streams + * @NVME_IO_STC: Storage Tag Check + * @NVME_IO_DEAC: Deallocate + * @NVME_IO_ZNS_APPEND_PIREMAP: Protection Information Remap + * @NVME_IO_PRINFO_PRCHK_REF: Protection Information Check Reference Tag + * @NVME_IO_PRINFO_PRCHK_APP: Protection Information Check Application Tag + * @NVME_IO_PRINFO_PRCHK_GUARD: Protection Information Check Guard field + * @NVME_IO_PRINFO_PRACT: Protection Information Action + * @NVME_IO_FUA: Force Unit Access + * @NVME_IO_LR: Limited Retry + */ +enum nvme_io_control_flags { + NVME_IO_DTYPE_STREAMS = 1 << 4, + NVME_IO_STC = 1 << 8, + NVME_IO_DEAC = 1 << 9, + NVME_IO_ZNS_APPEND_PIREMAP = 1 << 9, + NVME_IO_PRINFO_PRCHK_REF = 1 << 10, + NVME_IO_PRINFO_PRCHK_APP = 1 << 11, + NVME_IO_PRINFO_PRCHK_GUARD = 1 << 12, + NVME_IO_PRINFO_PRACT = 1 << 13, + NVME_IO_FUA = 1 << 14, + NVME_IO_LR = 1 << 15, +}; + +/** + * enum nvme_io_dsm_flags - Dataset Management flags + * @NVME_IO_DSM_FREQ_UNSPEC: No frequency information provided + * @NVME_IO_DSM_FREQ_TYPICAL: Typical number of reads and writes + * expected for this LBA range + * @NVME_IO_DSM_FREQ_RARE: Infrequent writes and infrequent + * reads to the LBA range indicated + * @NVME_IO_DSM_FREQ_READS: Infrequent writes and frequent + * reads to the LBA range indicated + * @NVME_IO_DSM_FREQ_WRITES: Frequent writes and infrequent + * reads to the LBA range indicated + * @NVME_IO_DSM_FREQ_RW: Frequent writes and frequent reads + * to the LBA range indicated + * @NVME_IO_DSM_FREQ_ONCE: + * @NVME_IO_DSM_FREQ_PREFETCH: + * @NVME_IO_DSM_FREQ_TEMP: + * @NVME_IO_DSM_LATENCY_NONE: No latency information provided + * @NVME_IO_DSM_LATENCY_IDLE: Longer latency acceptable + * @NVME_IO_DSM_LATENCY_NORM: Typical latency + * @NVME_IO_DSM_LATENCY_LOW: Smallest possible latency + * @NVME_IO_DSM_SEQ_REQ: + * @NVME_IO_DSM_COMPRESSED: + */ +enum nvme_io_dsm_flags { + NVME_IO_DSM_FREQ_UNSPEC = 0, + NVME_IO_DSM_FREQ_TYPICAL = 1, + NVME_IO_DSM_FREQ_RARE = 2, + NVME_IO_DSM_FREQ_READS = 3, + NVME_IO_DSM_FREQ_WRITES = 4, + NVME_IO_DSM_FREQ_RW = 5, + NVME_IO_DSM_FREQ_ONCE = 6, + NVME_IO_DSM_FREQ_PREFETCH = 7, + NVME_IO_DSM_FREQ_TEMP = 8, + NVME_IO_DSM_LATENCY_NONE = 0 << 4, + NVME_IO_DSM_LATENCY_IDLE = 1 << 4, + NVME_IO_DSM_LATENCY_NORM = 2 << 4, + NVME_IO_DSM_LATENCY_LOW = 3 << 4, + NVME_IO_DSM_SEQ_REQ = 1 << 6, + NVME_IO_DSM_COMPRESSED = 1 << 7, +}; + +/** + * enum nvme_dsm_attributes - Dataset Management attributes + * @NVME_DSMGMT_IDR: Attribute -Integral Dataset for Read + * @NVME_DSMGMT_IDW: Attribute - Integral Dataset for Write + * @NVME_DSMGMT_AD: Attribute - Deallocate + */ +enum nvme_dsm_attributes { + NVME_DSMGMT_IDR = 1 << 0, + NVME_DSMGMT_IDW = 1 << 1, + NVME_DSMGMT_AD = 1 << 2, +}; + +/** + * enum nvme_resv_rtype - Reservation Type Encoding + * @NVME_RESERVATION_RTYPE_WE: Write Exclusive Reservation + * @NVME_RESERVATION_RTYPE_EA: Exclusive Access Reservation + * @NVME_RESERVATION_RTYPE_WERO: Write Exclusive - Registrants Only Reservation + * @NVME_RESERVATION_RTYPE_EARO: Exclusive Access - Registrants Only Reservation + * @NVME_RESERVATION_RTYPE_WEAR: Write Exclusive - All Registrants Reservation + * @NVME_RESERVATION_RTYPE_EAAR: Exclusive Access - All Registrants Reservation + */ +enum nvme_resv_rtype { + NVME_RESERVATION_RTYPE_WE = 1, + NVME_RESERVATION_RTYPE_EA = 2, + NVME_RESERVATION_RTYPE_WERO = 3, + NVME_RESERVATION_RTYPE_EARO = 4, + NVME_RESERVATION_RTYPE_WEAR = 5, + NVME_RESERVATION_RTYPE_EAAR = 6, +}; + +/** + * enum nvme_resv_racqa - Reservation Acquire - Reservation Acquire Action + * @NVME_RESERVATION_RACQA_ACQUIRE: Acquire + * @NVME_RESERVATION_RACQA_PREEMPT: Preempt + * @NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT: Preempt and Abort + */ +enum nvme_resv_racqa { + NVME_RESERVATION_RACQA_ACQUIRE = 0, + NVME_RESERVATION_RACQA_PREEMPT = 1, + NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT = 2, +}; + +/** + * enum nvme_resv_rrega - Reservation Register - Reservation Register Action + * @NVME_RESERVATION_RREGA_REGISTER_KEY: Register Reservation Key + * @NVME_RESERVATION_RREGA_UNREGISTER_KEY: Unregister Reservation Key + * @NVME_RESERVATION_RREGA_REPLACE_KEY: Replace Reservation Key + */ +enum nvme_resv_rrega { + NVME_RESERVATION_RREGA_REGISTER_KEY = 0, + NVME_RESERVATION_RREGA_UNREGISTER_KEY = 1, + NVME_RESERVATION_RREGA_REPLACE_KEY = 2, +}; + +/** + * enum nvme_resv_cptpl - Reservation Register - Change Persist Through Power Loss State + * @NVME_RESERVATION_CPTPL_NO_CHANGE: No change to PTPL state + * @NVME_RESERVATION_CPTPL_CLEAR: Reservations are released and + * registrants are cleared on a power on + * @NVME_RESERVATION_CPTPL_PERSIST: Reservations and registrants persist + * across a power loss + */ +enum nvme_resv_cptpl { + NVME_RESERVATION_CPTPL_NO_CHANGE = 0, + NVME_RESERVATION_CPTPL_CLEAR = 2, + NVME_RESERVATION_CPTPL_PERSIST = 3, +}; + +/** + * enum nvme_resv_rrela - Reservation Release - Reservation Release Action + * @NVME_RESERVATION_RRELA_RELEASE: Release + * @NVME_RESERVATION_RRELA_CLEAR: Clear + */ +enum nvme_resv_rrela { + NVME_RESERVATION_RRELA_RELEASE = 0, + NVME_RESERVATION_RRELA_CLEAR = 1 +}; + +/** + * enum nvme_zns_send_action - Zone Management Send - Zone Send Action + * @NVME_ZNS_ZSA_CLOSE: Close Zone + * @NVME_ZNS_ZSA_FINISH: Finish Zone + * @NVME_ZNS_ZSA_OPEN: Open Zone + * @NVME_ZNS_ZSA_RESET: Reset Zone + * @NVME_ZNS_ZSA_OFFLINE: Offline Zone + * @NVME_ZNS_ZSA_SET_DESC_EXT: Set Zone Descriptor Extension + * @NVME_ZNS_ZSA_ZRWA_FLUSH: Flush + */ +enum nvme_zns_send_action { + NVME_ZNS_ZSA_CLOSE = 0x1, + NVME_ZNS_ZSA_FINISH = 0x2, + NVME_ZNS_ZSA_OPEN = 0x3, + NVME_ZNS_ZSA_RESET = 0x4, + NVME_ZNS_ZSA_OFFLINE = 0x5, + NVME_ZNS_ZSA_SET_DESC_EXT = 0x10, + NVME_ZNS_ZSA_ZRWA_FLUSH = 0x11, +}; + +/** + * enum nvme_zns_recv_action - Zone Management Receive - Zone Receive Action Specific Features + * @NVME_ZNS_ZRA_REPORT_ZONES: Report Zones + * @NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES: Extended Report Zones + */ +enum nvme_zns_recv_action { + NVME_ZNS_ZRA_REPORT_ZONES = 0x0, + NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES = 0x1, +}; + +/** + * enum nvme_zns_report_options - Zone Management Receive - Zone Receive Action Specific Field + * @NVME_ZNS_ZRAS_REPORT_ALL: List all zones + * @NVME_ZNS_ZRAS_REPORT_EMPTY: List the zones in the ZSE:Empty state + * @NVME_ZNS_ZRAS_REPORT_IMPL_OPENED: List the zones in the ZSIO:Implicitly Opened state + * @NVME_ZNS_ZRAS_REPORT_EXPL_OPENED: List the zones in the ZSEO:Explicitly Opened state + * @NVME_ZNS_ZRAS_REPORT_CLOSED: List the zones in the ZSC:Closed state + * @NVME_ZNS_ZRAS_REPORT_FULL: List the zones in the ZSF:Full state + * @NVME_ZNS_ZRAS_REPORT_READ_ONLY: List the zones in the ZSRO:Read Only state + * @NVME_ZNS_ZRAS_REPORT_OFFLINE: List the zones in the ZSO:Offline state + */ +enum nvme_zns_report_options { + NVME_ZNS_ZRAS_REPORT_ALL = 0x0, + NVME_ZNS_ZRAS_REPORT_EMPTY = 0x1, + NVME_ZNS_ZRAS_REPORT_IMPL_OPENED = 0x2, + NVME_ZNS_ZRAS_REPORT_EXPL_OPENED = 0x3, + NVME_ZNS_ZRAS_REPORT_CLOSED = 0x4, + NVME_ZNS_ZRAS_REPORT_FULL = 0x5, + NVME_ZNS_ZRAS_REPORT_READ_ONLY = 0x6, + NVME_ZNS_ZRAS_REPORT_OFFLINE = 0x7, +}; + +/** + * enum nvme_io_mgmt_recv_mo - I/O Management Receive - Management Operation + * @NVME_IO_MGMT_RECV_RUH_STATUS: Reclaim Unit Handle Status + */ +enum nvme_io_mgmt_recv_mo { + NVME_IO_MGMT_RECV_RUH_STATUS = 0x1, +}; + +/** + * enum nvme_io_mgmt_send_mo - I/O Management Send - Management Operation + * @NVME_IO_MGMT_SEND_RUH_UPDATE: Reclaim Unit Handle Update + */ +enum nvme_io_mgmt_send_mo { + NVME_IO_MGMT_SEND_RUH_UPDATE = 0x1, +}; + +#ifndef SWIG +/** + * struct nvme_ns_mgmt_host_sw_specified - Namespace management Host Software + * Specified Fields. + * @nsze: Namespace Size indicates the total size of the namespace in + * logical blocks. The number of logical blocks is based on the + * formatted LBA size. + * @ncap: Namespace Capacity indicates the maximum number of logical blocks + * that may be allocated in the namespace at any point in time. The + * number of logical blocks is based on the formatted LBA size. + * @rsvd16: Reserved + * @flbas: Formatted LBA Size, see &enum nvme_id_ns_flbas. + * @rsvd27: Reserved + * @dps: End-to-end Data Protection Type Settings, see + * &enum nvme_id_ns_dps. + * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities, see + * &enum nvme_id_ns_nmic. + * @rsvd31: Reserved + * @anagrpid: ANA Group Identifier indicates the ANA Group Identifier of the + * ANA group of which the namespace is a member. + * @rsvd96: Reserved + * @nvmsetid: NVM Set Identifier indicates the NVM Set with which this + * namespace is associated. + * @endgid: Endurance Group Identifier indicates the Endurance Group with + * which this namespace is associated. + * @rsvd104: Reserved + * @lbstm: Logical Block Storage Tag Mask Identifies the mask for the + * Storage Tag field for the protection information + * @nphndls: Number of Placement Handles specifies the number of Placement + * Handles included in the Placement Handle List + * @rsvd394: Reserved + * @rsvd499: Reserved for I/O Command Sets that extend this specification. + * @zns: rsvd499( Zoned Namespace Command Set specific field ) + * @znsco: Zoned Namespace Create Options + * Bits 7-1: Reserved. + * Bits 0: Allocate ZRWA Resources (AZR): If set to ‘1’, then the + * namespace is to be created with the number of ZRWA resource specified + * in the RNUMZRWA field of this data structure. If cleared to ‘0’, then + * no ZRWA resources are allocated to the namespace to be created. If + * the ZRWASUP bit is cleared to ‘0’, then this field shall be ignored + * by the controller. + * @rar: Requested Active Resources specifies the number of active + * resources to be allocated to the created namespace. + * @ror: Requested Open Resources specifies the number of open resources + * to be allocated to the created namespace. + * @rnumzrwa: Requested Number of ZRWA Resources specifies the number of ZRWA + * resources to be allocated to the created namespace. + * see &struct nvme_ns_mgmt_host_sw_specified_zns. + * @phndl: Placement Handle Associated RUH : This field specifies the Reclaim + * Unit Handle Identifier to be associated with the Placement Handle + * value. If the Flexible Data Placement capability is not supported or + * not enabled in specified Endurance Group, then the controller shall + * ignore this field. + * @rsvd768: Reserved + */ +struct nvme_ns_mgmt_host_sw_specified { + __le64 nsze; + __le64 ncap; + __u8 rsvd16[10]; + __u8 flbas; + __u8 rsvd27[2]; + __u8 dps; + __u8 nmic; + __u8 rsvd31[61]; + __le32 anagrpid; + __u8 rsvd96[4]; + __le16 nvmsetid; + __le16 endgid; + __u8 rsvd104[280]; + __le64 lbstm; + __le16 nphndls; + __u8 rsvd394[105]; + union { + __u8 rsvd499[13]; + struct { + __u8 znsco; + __le32 rar; + __le32 ror; + __le32 rnumzrwa; + } __attribute__((packed)) zns; + }; + __le16 phndl[128]; + __u8 rsvd768[3328]; +}; +#endif /* SWIG */ + +#endif /* _LIBNVME_TYPES_H */ diff --git a/src/nvme/util.c b/src/nvme/util.c new file mode 100644 index 0000000..f091da7 --- /dev/null +++ b/src/nvme/util.c @@ -0,0 +1,1137 @@ +// 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 <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <fcntl.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 "cleanup.h" +#include "private.h" +#include "util.h" +#include "log.h" + +/* The bionic libc implementation doesn't define LINE_MAX */ +#ifndef LINE_MAX +#define LINE_MAX 2048 +#endif + +/* Source Code Control System, query version of binary with 'what' */ +const char sccsid[] = "@(#)libnvme " GIT_VERSION; + +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_INCOMPATIBLE_NS] = "Incompatible Namespace or Format", + [NVME_SC_FAST_COPY_NOT_POSSIBLE] = "Fast Copy Not Possible", + [NVME_SC_OVERLAPPING_IO_RANGE] = "Overlapping I/O Range", + [NVME_SC_INSUFFICIENT_RESOURCES] = "Insufficient Resources", + [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 command 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; +} + +static inline void nvme_init_copy_range_elbt(__u8 *elbt, __u64 eilbrt) +{ + int i; + + for (i = 0; i < 8; i++) + elbt[9 - i] = (eilbrt >> (8 * i)) & 0xff; + elbt[1] = 0; + elbt[0] = 0; +} + +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_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs, + __u64 *slbas, __u64 *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].elbatm = cpu_to_le16(elbatms[i]); + copy[i].elbat = cpu_to_le16(elbats[i]); + nvme_init_copy_range_elbt(copy[i].elbt, eilbrts[i]); + } +} + +void nvme_init_copy_range_f2(struct nvme_copy_range_f2 *copy, __u32 *snsids, + __u16 *nlbs, __u64 *slbas, __u16 *sopts, + __u32 *eilbrts, __u32 *elbatms, __u32 *elbats, + __u16 nr) +{ + int i; + + for (i = 0; i < nr; i++) { + copy[i].snsid = cpu_to_le32(snsids[i]); + copy[i].nlb = cpu_to_le16(nlbs[i]); + copy[i].slba = cpu_to_le64(slbas[i]); + copy[i].sopt = cpu_to_le16(sopts[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_copy_range_f3(struct nvme_copy_range_f3 *copy, __u32 *snsids, + __u16 *nlbs, __u64 *slbas, __u16 *sopts, + __u64 *eilbrts, __u32 *elbatms, __u32 *elbats, + __u16 nr) +{ + int i; + + for (i = 0; i < nr; i++) { + copy[i].snsid = cpu_to_le32(snsids[i]); + copy[i].nlb = cpu_to_le16(nlbs[i]); + copy[i].slba = cpu_to_le64(slbas[i]); + copy[i].sopt = cpu_to_le16(sopts[i]); + copy[i].elbatm = cpu_to_le16(elbatms[i]); + copy[i].elbat = cpu_to_le16(elbats[i]); + nvme_init_copy_range_elbt(copy[i].elbt, eilbrts[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_HOST_MEM_BUF: + *len = sizeof(struct nvme_host_mem_buf_attrs); + 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_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_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir, + __u32 *len) +{ + switch (fid) { + case NVME_FEAT_FID_HOST_MEM_BUF: + if (dir == NVME_DATA_TFR_HOST_TO_CTRL) { + *len = 0; + break; + } + fallthrough; + default: + return nvme_get_feature_length(fid, cdw11, len); + } + 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", + [ENVME_CONNECT_ALREADY] = "already connected", + [ENVME_CONNECT_INVAL] = "invalid arguments/configuration", + [ENVME_CONNECT_ADDRINUSE] = "hostnqn already in use", + [ENVME_CONNECT_NODEV] = "invalid interface", + [ENVME_CONNECT_OPNOTSUPP] = "not supported", + [ENVME_CONNECT_CONNREFUSED] = "connection refused", + [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address", +}; + +const char *nvme_errno_to_string(int status) +{ + const char *s = ARGSTR(libnvme_status, status); + + return s; +} + +#ifdef HAVE_NETDB +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; +} +#else /* HAVE_NETDB */ +char *hostname2traddr(struct nvme_root *r, const char *traddr) +{ + nvme_msg(NULL, LOG_ERR, "No support for hostname IP address resolution; " \ + "recompile with libnss support.\n"); + + errno = -ENOTSUP; + return NULL; +} +#endif /* HAVE_NETDB */ + +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; + _cleanup_file_ FILE *file = NULL; + size_t len; + + file = fopen(fname, "re"); + if (!file) + return 0; + + p = fgets(buffer, *bufsz, 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) +{ + _cleanup_file_ FILE *file = NULL; + 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); + } + + 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))); +} + +const char *nvme_get_version(enum nvme_version type) +{ + switch(type) { + case NVME_VERSION_PROJECT: + return PROJECT_VERSION; + case NVME_VERSION_GIT: + return GIT_VERSION; + default: + return "n/a"; + } +} + +int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str) +{ + int n; + n = snprintf(str, NVME_UUID_LEN_STRING, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], + uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + return n != NVME_UUID_LEN_STRING - 1 ? -EINVAL : 0; +} + +int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]) +{ + int n; + + n = sscanf(str, + "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" + "%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + &uuid[0], &uuid[1], &uuid[2], &uuid[3], &uuid[4], &uuid[5], + &uuid[6], &uuid[7], &uuid[8], &uuid[9], &uuid[10], &uuid[11], + &uuid[12], &uuid[13], &uuid[14], &uuid[15]); + return n != NVME_UUID_LEN ? -EINVAL : 0; + +} + +int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]) +{ + _cleanup_fd_ int f = -1; + ssize_t n; + + f = open("/dev/urandom", O_RDONLY); + if (f < 0) + return -errno; + n = read(f, uuid, NVME_UUID_LEN); + if (n < 0) + return -errno; + else if (n != NVME_UUID_LEN) + return -EIO; + + /* + * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4 + * Algorithms for Creating a UUID from Truly Random + * or Pseudo-Random Numbers + */ + uuid[6] = (uuid[6] & 0x0f) | 0x40; + uuid[8] = (uuid[8] & 0x3f) | 0x80; + + return 0; +} + +int nvme_uuid_find(struct nvme_id_uuid_list *uuid_list, const unsigned char uuid[NVME_UUID_LEN]) +{ + const unsigned char uuid_end[NVME_UUID_LEN] = {0}; + + if ((!uuid_list) || (!uuid)) { + errno = EINVAL; + return -1; + } + + for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) { + if (memcmp(uuid, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0) + return i + 1; + if (memcmp(uuid_end, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0) + break; + } + errno = ENOENT; + return -1; +} + +#ifdef HAVE_NETDB +static bool _nvme_ipaddrs_eq(struct sockaddr *addr1, struct sockaddr *addr2) +{ + struct sockaddr_in *sockaddr_v4; + struct sockaddr_in6 *sockaddr_v6; + + if (addr1->sa_family == AF_INET && addr2->sa_family == AF_INET) { + struct sockaddr_in *sockaddr1 = (struct sockaddr_in *)addr1; + struct sockaddr_in *sockaddr2 = (struct sockaddr_in *)addr2; + return sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr; + } + + if (addr1->sa_family == AF_INET6 && addr2->sa_family == AF_INET6) { + struct sockaddr_in6 *sockaddr1 = (struct sockaddr_in6 *)addr1; + struct sockaddr_in6 *sockaddr2 = (struct sockaddr_in6 *)addr2; + return !memcmp(&sockaddr1->sin6_addr, &sockaddr2->sin6_addr, sizeof(struct in6_addr)); + } + + switch (addr1->sa_family) { + case AF_INET: + sockaddr_v6 = (struct sockaddr_in6 *)addr2; + if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { + sockaddr_v4 = (struct sockaddr_in *)addr1; + return sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3]; + } + break; + + case AF_INET6: + sockaddr_v6 = (struct sockaddr_in6 *)addr1; + if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) { + sockaddr_v4 = (struct sockaddr_in *)addr2; + return sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3]; + } + break; + + default: ; + } + + return false; +} + +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2) +{ + bool result = false; + struct addrinfo *info1 = NULL, hint1 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC }; + struct addrinfo *info2 = NULL, hint2 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC }; + + if (addr1 == addr2) + return true; + + if (!addr1 || !addr2) + return false; + + if (getaddrinfo(addr1, 0, &hint1, &info1) || !info1) + goto ipaddrs_eq_fail; + + if (getaddrinfo(addr2, 0, &hint2, &info2) || !info2) + goto ipaddrs_eq_fail; + + result = _nvme_ipaddrs_eq(info1->ai_addr, info2->ai_addr); + +ipaddrs_eq_fail: + if (info1) + freeaddrinfo(info1); + if (info2) + freeaddrinfo(info2); + return result; +} +#else /* HAVE_NETDB */ +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2) +{ + nvme_msg(NULL, LOG_ERR, "no support for hostname ip address resolution; " \ + "recompile with libnss support.\n"); + + return false; +} +#endif /* HAVE_NETDB */ + +#ifdef HAVE_NETDB +const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr) +{ + const struct ifaddrs *iface_it; + struct addrinfo *info = NULL, hint = { .ai_flags = AI_NUMERICHOST, .ai_family = AF_UNSPEC }; + const char *iface_name = NULL; + + if (!iface_list || !addr || getaddrinfo(addr, 0, &hint, &info) || !info) + return NULL; + + /* Walk through the linked list */ + for (iface_it = iface_list; iface_it != NULL; iface_it = iface_it->ifa_next) { + struct sockaddr *ifaddr = iface_it->ifa_addr; + + if (ifaddr && (ifaddr->sa_family == AF_INET || ifaddr->sa_family == AF_INET6) && + _nvme_ipaddrs_eq(info->ai_addr, ifaddr)) { + iface_name = iface_it->ifa_name; + break; + } + } + + freeaddrinfo(info); + + return iface_name; +} + +bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr) +{ + const struct ifaddrs *iface_it; + struct addrinfo *info = NULL, hint = { .ai_flags = AI_NUMERICHOST, .ai_family = AF_UNSPEC }; + bool match_found = false; + + if (!iface_list || !addr || getaddrinfo(addr, 0, &hint, &info) || !info) + return false; + + /* Walk through the linked list */ + for (iface_it = iface_list; iface_it != NULL; iface_it = iface_it->ifa_next) { + if (strcmp(iface, iface_it->ifa_name)) + continue; /* Not the interface we're looking for*/ + + /* The interface list is ordered in a way that the primary + * address is listed first. As soon as the parsed address + * matches the family of the address we're looking for, we + * have found the primary address for that family. + */ + if (iface_it->ifa_addr && (iface_it->ifa_addr->sa_family == info->ai_addr->sa_family)) { + match_found = _nvme_ipaddrs_eq(info->ai_addr, iface_it->ifa_addr); + break; + } + } + + freeaddrinfo(info); + + return match_found; +} + +#else /* HAVE_NETDB */ + +const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr) +{ + nvme_msg(NULL, LOG_ERR, "no support for interface lookup; " + "recompile with libnss support.\n"); + + return NULL; +} + +bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr) +{ + nvme_msg(NULL, LOG_ERR, "no support for interface lookup; " + "recompile with libnss support.\n"); + + return false; +} + +#endif /* HAVE_NETDB */ + +void *__nvme_alloc(size_t len) +{ + size_t _len = round_up(len, 0x1000); + void *p; + + if (posix_memalign((void *)&p, getpagesize(), _len)) + return NULL; + + memset(p, 0, _len); + return p; +} diff --git a/src/nvme/util.h b/src/nvme/util.h new file mode 100644 index 0000000..517c696 --- /dev/null +++ b/src/nvme/util.h @@ -0,0 +1,714 @@ +// 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> + */ +#ifndef _LIBNVME_UTIL_H +#define _LIBNVME_UTIL_H + +#include <ifaddrs.h> + +#include "types.h" + +/** + * DOC: util.h + * + * libnvme utility functions + */ + +/** + * enum nvme_connect_err - nvme connect error codes + * @ENVME_CONNECT_RESOLVE: failed to resolve host + * @ENVME_CONNECT_ADDRFAM: unrecognized address family + * @ENVME_CONNECT_TRADDR: failed to get traddr + * @ENVME_CONNECT_TARG: need a transport (-t) argument + * @ENVME_CONNECT_AARG: need a address (-a) argument + * @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 + * @ENVME_CONNECT_ALREADY: the connect attempt failed, already connected + * @ENVME_CONNECT_INVAL: invalid arguments/configuration + * @ENVME_CONNECT_ADDRINUSE: hostnqn already in use + * @ENVME_CONNECT_NODEV: invalid interface + * @ENVME_CONNECT_OPNOTSUPP: not supported + * @ENVME_CONNECT_CONNREFUSED: connection refused + * @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address + * @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration + */ +enum nvme_connect_err { + ENVME_CONNECT_RESOLVE = 1000, + ENVME_CONNECT_ADDRFAM, + ENVME_CONNECT_TRADDR, + ENVME_CONNECT_TARG, + ENVME_CONNECT_AARG, + ENVME_CONNECT_OPEN, + ENVME_CONNECT_WRITE, + ENVME_CONNECT_READ, + ENVME_CONNECT_PARSE, + ENVME_CONNECT_INVAL_TR, + ENVME_CONNECT_LOOKUP_SUBSYS_NAME, + ENVME_CONNECT_LOOKUP_SUBSYS, + ENVME_CONNECT_ALREADY, + ENVME_CONNECT_INVAL, + ENVME_CONNECT_ADDRINUSE, + ENVME_CONNECT_NODEV, + ENVME_CONNECT_OPNOTSUPP, + ENVME_CONNECT_CONNREFUSED, + ENVME_CONNECT_ADDRNOTAVAIL, + ENVME_CONNECT_IGNORED, +}; + +/** + * nvme_status_to_errno() - Converts nvme return status to errno + * @status: Return status from an nvme passthrough command + * @fabrics: Set to true if &status is to a fabrics target. + * + * Return: An errno representing the nvme status if it is an nvme status field, + * or unchanged status is < 0 since errno is already set. + */ +__u8 nvme_status_to_errno(int status, bool fabrics); + +/** + * nvme_status_to_string() - Returns string describing nvme return status. + * @status: Return status from an nvme passthrough command + * @fabrics: Set to true if &status is to a fabrics target. + * + * Return: String representation of the nvme status if it is an nvme status field, + * or a standard errno string if status is < 0. + */ +const char *nvme_status_to_string(int status, bool fabrics); + +/** + * nvme_errno_to_string() - Returns string describing nvme connect failures + * @err: Returned error code from nvme_add_ctrl() + * + * Return: String representation of the nvme connect error codes + */ +const char *nvme_errno_to_string(int err); + +/** + * nvme_init_ctrl_list() - Initialize an nvme_ctrl_list structure from an array. + * @cntlist: The controller list structure to initialize + * @num_ctrls: The number of controllers in the array, &ctrlist. + * @ctrlist: An array of controller identifiers in CPU native endian. + * + * This is intended to be used with any command that takes a controller list + * argument. See nvme_ns_attach_ctrls() and nvme_ns_detach(). + */ +void nvme_init_ctrl_list(struct nvme_ctrl_list *cntlist, __u16 num_ctrls, + __u16 *ctrlist); + +/** + * nvme_init_dsm_range() - Constructs a data set range structure + * @dsm: DSM range array + * @ctx_attrs: Array of context attributes + * @llbas: Array of length in logical blocks + * @slbas: Array of starting logical blocks + * @nr_ranges: The size of the dsm arrays + * + * Each array must be the same size of size 'nr_ranges'. This is intended to be + * used with constructing a payload for nvme_dsm(). + * + * Return: The nvme command status if a response was received or -errno + * otherwise. + */ +void nvme_init_dsm_range(struct nvme_dsm_range *dsm, __u32 *ctx_attrs, + __u32 *llbas, __u64 *slbas, __u16 nr_ranges); + +/** + * nvme_init_copy_range() - Constructs a copy range structure + * @copy: Copy range array + * @nlbs: Number of logical blocks + * @slbas: Starting LBA + * @eilbrts: Expected initial logical block reference tag + * @elbatms: Expected logical block application tag mask + * @elbats: Expected logical block application tag + * @nr: Number of descriptors to construct + */ +void nvme_init_copy_range(struct nvme_copy_range *copy, __u16 *nlbs, + __u64 *slbas, __u32 *eilbrts, __u32 *elbatms, + __u32 *elbats, __u16 nr); + +/** + * nvme_init_copy_range_f1() - Constructs a copy range f1 structure + * @copy: Copy range array + * @nlbs: Number of logical blocks + * @slbas: Starting LBA + * @eilbrts: Expected initial logical block reference tag + * @elbatms: Expected logical block application tag mask + * @elbats: Expected logical block application tag + * @nr: Number of descriptors to construct + */ +void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs, + __u64 *slbas, __u64 *eilbrts, __u32 *elbatms, + __u32 *elbats, __u16 nr); + +/** + * nvme_init_copy_range_f2() - Constructs a copy range f2 structure + * @copy: Copy range array + * @snsids: Source namespace identifier + * @nlbs: Number of logical blocks + * @slbas: Starting LBA + * @sopts: Source options + * @eilbrts: Expected initial logical block reference tag + * @elbatms: Expected logical block application tag mask + * @elbats: Expected logical block application tag + * @nr: Number of descriptors to construct + */ +void nvme_init_copy_range_f2(struct nvme_copy_range_f2 *copy, __u32 *snsids, + __u16 *nlbs, __u64 *slbas, __u16 *sopts, + __u32 *eilbrts, __u32 *elbatms, __u32 *elbats, + __u16 nr); + +/** + * nvme_init_copy_range_f3() - Constructs a copy range f3 structure + * @copy: Copy range array + * @snsids: Source namespace identifier + * @nlbs: Number of logical blocks + * @slbas: Starting LBA + * @sopts: Source options + * @eilbrts: Expected initial logical block reference tag + * @elbatms: Expected logical block application tag mask + * @elbats: Expected logical block application tag + * @nr: Number of descriptors to construct + */ +void nvme_init_copy_range_f3(struct nvme_copy_range_f3 *copy, __u32 *snsids, + __u16 *nlbs, __u64 *slbas, __u16 *sopts, + __u64 *eilbrts, __u32 *elbatms, __u32 *elbats, + __u16 nr); + +/** + * nvme_get_feature_length() - Retreive the command payload length for a + * specific feature identifier + * @fid: Feature identifier, see &enum nvme_features_id. + * @cdw11: The cdw11 value may affect the transfer (only known fid is + * %NVME_FEAT_FID_HOST_ID) + * @len: On success, set to this features payload length in bytes. + * + * Return: 0 on success, -1 with errno set to EINVAL if the function did not + * recognize &fid. + */ +int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len); + +/** + * nvme_get_feature_length2() - Retreive the command payload length for a + * specific feature identifier + * @fid: Feature identifier, see &enum nvme_features_id. + * @cdw11: The cdw11 value may affect the transfer (only known fid is + * %NVME_FEAT_FID_HOST_ID) + * @dir: Data transfer direction: false - host to controller, true - + * controller to host may affect the transfer (only known fid is + * %NVME_FEAT_FID_HOST_MEM_BUF). + * @len: On success, set to this features payload length in bytes. + * + * Return: 0 on success, -1 with errno set to EINVAL if the function did not + * recognize &fid. + */ +int nvme_get_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir, + __u32 *len); + +/** + * nvme_get_directive_receive_length() - Get directive receive length + * @dtype: Directive type, see &enum nvme_directive_dtype + * @doper: Directive receive operation, see &enum nvme_directive_receive_doper + * @len: On success, set to this directives payload length in bytes. + * + * Return: 0 on success, -1 with errno set to EINVAL if the function did not + * recognize &dtype or &doper. + */ +int nvme_get_directive_receive_length(enum nvme_directive_dtype dtype, + enum nvme_directive_receive_doper doper, __u32 *len); + +#define NVME_FEAT_ARB_BURST(v) NVME_GET(v, FEAT_ARBITRATION_BURST) +#define NVME_FEAT_ARB_LPW(v) NVME_GET(v, FEAT_ARBITRATION_LPW) +#define NVME_FEAT_ARB_MPW(v) NVME_GET(v, FEAT_ARBITRATION_MPW) +#define NVME_FEAT_ARB_HPW(v) NVME_GET(v, FEAT_ARBITRATION_HPW) + +static inline void nvme_feature_decode_arbitration(__u32 value, __u8 *ab, + __u8 *lpw, __u8 *mpw, + __u8 *hpw) +{ + *ab = NVME_FEAT_ARB_BURST(value); + *lpw = NVME_FEAT_ARB_LPW(value); + *mpw = NVME_FEAT_ARB_MPW(value); + *hpw = NVME_FEAT_ARB_HPW(value); +}; + +#define NVME_FEAT_PM_PS(v) NVME_GET(v, FEAT_PWRMGMT_PS) +#define NVME_FEAT_PM_WH(v) NVME_GET(v, FEAT_PWRMGMT_WH) + +static inline void nvme_feature_decode_power_mgmt(__u32 value, __u8 *ps, + __u8 *wh) +{ + *ps = NVME_FEAT_PM_PS(value); + *wh = NVME_FEAT_PM_WH(value); +} + +#define NVME_FEAT_LBAR_NR(v) NVME_GET(v, FEAT_LBAR_NR) + +static inline void nvme_feature_decode_lba_range(__u32 value, __u8 *num) +{ + *num = NVME_FEAT_LBAR_NR(value); +} + +#define NVME_FEAT_TT_TMPTH(v) NVME_GET(v, FEAT_TT_TMPTH) +#define NVME_FEAT_TT_TMPSEL(v) NVME_GET(v, FEAT_TT_TMPSEL) +#define NVME_FEAT_TT_THSEL(v) NVME_GET(v, FEAT_TT_THSEL) + +static inline void nvme_feature_decode_temp_threshold(__u32 value, __u16 *tmpth, + __u8 *tmpsel, __u8 *thsel) +{ + *tmpth = NVME_FEAT_TT_TMPTH(value); + *tmpsel = NVME_FEAT_TT_TMPSEL(value); + *thsel = NVME_FEAT_TT_THSEL(value); +} + +#define NVME_FEAT_ER_TLER(v) NVME_GET(v, FEAT_ERROR_RECOVERY_TLER) +#define NVME_FEAT_ER_DULBE(v) NVME_GET(v, FEAT_ERROR_RECOVERY_DULBE) + +static inline void nvme_feature_decode_error_recovery(__u32 value, __u16 *tler, + bool *dulbe) +{ + *tler = NVME_FEAT_ER_TLER(value); + *dulbe = NVME_FEAT_ER_DULBE(value); +} + +#define NVME_FEAT_VWC_WCE(v) NVME_GET(v, FEAT_VWC_WCE) + +static inline void nvme_feature_decode_volatile_write_cache(__u32 value, + bool *wce) +{ + *wce = NVME_FEAT_VWC_WCE(value); +} + +#define NVME_FEAT_NRQS_NSQR(v) NVME_GET(v, FEAT_NRQS_NSQR) +#define NVME_FEAT_NRQS_NCQR(v) NVME_GET(v, FEAT_NRQS_NCQR) + +static inline void nvme_feature_decode_number_of_queues(__u32 value, + __u16 *nsqr, + __u16 *ncqr) +{ + *nsqr = NVME_FEAT_NRQS_NSQR(value); + *ncqr = NVME_FEAT_NRQS_NCQR(value); +} + +#define NVME_FEAT_IRQC_THR(v) NVME_GET(v, FEAT_IRQC_THR) +#define NVME_FEAT_IRQC_TIME(v) NVME_GET(v, FEAT_IRQC_TIME) + +static inline void nvme_feature_decode_interrupt_coalescing(__u32 value, + __u8 *thr, + __u8 *time) +{ + *thr = NVME_FEAT_IRQC_THR(value); + *time = NVME_FEAT_IRQC_TIME(value); +} + +#define NVME_FEAT_ICFG_IV(v) NVME_GET(v, FEAT_ICFG_IV) +#define NVME_FEAT_ICFG_CD(v) NVME_GET(v, FEAT_ICFG_CD) + +static inline void nvme_feature_decode_interrupt_config(__u32 value, __u16 *iv, + bool *cd) +{ + *iv = NVME_FEAT_ICFG_IV(value); + *cd = NVME_FEAT_ICFG_CD(value); +} + +#define NVME_FEAT_WA_DN(v) NVME_GET(v, FEAT_WA_DN) + +static inline void nvme_feature_decode_write_atomicity(__u32 value, bool *dn) +{ + *dn = NVME_FEAT_WA_DN(value); +} + +#define NVME_FEAT_AE_SMART(v) NVME_GET(v, FEAT_AE_SMART) +#define NVME_FEAT_AE_NAN(v) NVME_GET(v, FEAT_AE_NAN) +#define NVME_FEAT_AE_FW(v) NVME_GET(v, FEAT_AE_FW) +#define NVME_FEAT_AE_TELEM(v) NVME_GET(v, FEAT_AE_TELEM) +#define NVME_FEAT_AE_ANA(v) NVME_GET(v, FEAT_AE_ANA) +#define NVME_FEAT_AE_PLA(v) NVME_GET(v, FEAT_AE_PLA) +#define NVME_FEAT_AE_LBAS(v) NVME_GET(v, FEAT_AE_LBAS) +#define NVME_FEAT_AE_EGA(v) NVME_GET(v, FEAT_AE_EGA) + +static inline void nvme_feature_decode_async_event_config(__u32 value, + __u8 *smart, bool *nan, bool *fw, bool *telem, + bool *ana, bool *pla, bool *lbas, bool *ega) +{ + *smart = NVME_FEAT_AE_SMART(value); + *nan = NVME_FEAT_AE_NAN(value); + *fw = NVME_FEAT_AE_FW(value); + *telem = NVME_FEAT_AE_TELEM(value); + *ana = NVME_FEAT_AE_ANA(value); + *pla = NVME_FEAT_AE_PLA(value); + *lbas = NVME_FEAT_AE_LBAS(value); + *ega = NVME_FEAT_AE_EGA(value); +} + +#define NVME_FEAT_APST_APSTE(v) NVME_GET(v, FEAT_APST_APSTE) + +static inline void nvme_feature_decode_auto_power_state(__u32 value, + bool *apste) +{ + *apste = NVME_FEAT_APST_APSTE(value); +} + +#define NVME_FEAT_HMEM_EHM(v) NVME_GET(v, FEAT_HMEM_EHM) + +static inline void nvme_feature_decode_host_memory_buffer(__u32 value, bool *ehm) +{ + *ehm = NVME_FEAT_HMEM_EHM(value); +} + +#define NVME_FEAT_HCTM_TMT2(v) NVME_GET(v, FEAT_HCTM_TMT2) +#define NVME_FEAT_HCTM_TMT1(v) NVME_GET(v, FEAT_HCTM_TMT1) + +static inline void nvme_feature_decode_host_thermal_mgmt(__u32 value, + __u16 *tmt2, + __u16 *tmt1) +{ + *tmt2 = NVME_FEAT_HCTM_TMT2(value); + *tmt1 = NVME_FEAT_HCTM_TMT1(value); +} + +#define NVME_FEAT_NOPS_NOPPME(v) NVME_GET(v, FEAT_NOPS_NOPPME) + +static inline void nvme_feature_decode_non_op_power_config(__u32 value, + bool *noppme) +{ + *noppme = NVME_FEAT_NOPS_NOPPME(value); +} + +#define NVME_FEAT_RRL_RRL(v) NVME_GET(v, FEAT_RRL_RRL) + +static inline void nvme_feature_decode_read_recovery_level_config(__u32 value, + __u8 *rrl) +{ + *rrl = NVME_FEAT_RRL_RRL(value); +} + +#define NVME_FEAT_PLM_PLME(v) NVME_GET(v, FEAT_PLM_PLME) + +static inline void nvme_feature_decode_predictable_latency_mode_config(__u32 value, + bool *plme) +{ + *plme = NVME_FEAT_PLM_PLME(value); +} + +#define NVME_FEAT_PLMW_WS(v) NVME_GET(v, FEAT_PLMW_WS) + +static inline void nvme_feature_decode_predictable_latency_mode_window(__u32 value, + __u8 *ws) +{ + *ws = NVME_FEAT_PLMW_WS(value); +} + +#define NVME_FEAT_LBAS_LSIRI(v) NVME_GET(v, FEAT_LBAS_LSIRI) +#define NVME_FEAT_LBAS_LSIPI(v) NVME_GET(v, FEAT_LBAS_LSIPI) + +static inline void nvme_feature_decode_lba_status_attributes(__u32 value, + __u16 *lsiri, + __u16 *lsipi) +{ + *lsiri = NVME_FEAT_LBAS_LSIRI(value); + *lsipi = NVME_FEAT_LBAS_LSIPI(value); +} + +#define NVME_FEAT_SC_NODRM(v) NVME_GET(v, FEAT_SC_NODRM) + +static inline void nvme_feature_decode_sanitize_config(__u32 value, bool *nodrm) +{ + *nodrm = NVME_FEAT_SC_NODRM(value); +} + +#define NVME_FEAT_EG_ENDGID(v) NVME_GET(v, FEAT_EG_ENDGID) +#define NVME_FEAT_EG_EGCW(v) NVME_GET(v, FEAT_EG_EGCW) + +static inline void nvme_feature_decode_endurance_group_event_config(__u32 value, + __u16 *endgid, __u8 *endgcw) +{ + *endgid = NVME_FEAT_EG_ENDGID(value); + *endgcw = NVME_FEAT_EG_EGCW(value); +} + +#define NVME_FEAT_SPM_PBSLC(v) NVME_GET(v, FEAT_SPM_PBSLC) + +static inline void nvme_feature_decode_software_progress_marker(__u32 value, + __u8 *pbslc) +{ + *pbslc = NVME_FEAT_SPM_PBSLC(value); +} + +#define NVME_FEAT_HOSTID_EXHID(v) NVME_GET(v, FEAT_HOSTID_EXHID) + +static inline void nvme_feature_decode_host_identifier(__u32 value, bool *exhid) +{ + *exhid = NVME_FEAT_HOSTID_EXHID(value); +} + +#define NVME_FEAT_RM_REGPRE(v) NVME_GET(v, FEAT_RM_REGPRE) +#define NVME_FEAT_RM_RESREL(v) NVME_GET(v, FEAT_RM_RESREL) +#define NVME_FEAT_RM_RESPRE(v) NVME_GET(v, FEAT_RM_RESPRE) + +static inline void nvme_feature_decode_reservation_notification(__u32 value, + bool *regpre, + bool *resrel, + bool *respre) +{ + *regpre = NVME_FEAT_RM_REGPRE(value); + *resrel = NVME_FEAT_RM_RESREL(value); + *respre = NVME_FEAT_RM_RESPRE(value); +} + +#define NVME_FEAT_RP_PTPL(v) NVME_GET(v, FEAT_RP_PTPL) + +static inline void nvme_feature_decode_reservation_persistance(__u32 value, + bool *ptpl) +{ + *ptpl = NVME_FEAT_RP_PTPL(value); +} + +#define NVME_FEAT_WP_WPS(v) NVME_GET(v, FEAT_WP_WPS) + +static inline void nvme_feature_decode_namespace_write_protect(__u32 value, + __u8 *wps) +{ + *wps = NVME_FEAT_WP_WPS(value); +} + +static inline void nvme_id_ns_flbas_to_lbaf_inuse(__u8 flbas, __u8 *lbaf_inuse) +{ + *lbaf_inuse = ((NVME_FLBAS_HIGHER(flbas) >> 1) | + NVME_FLBAS_LOWER(flbas)); +} + +struct nvme_root; + +char *hostname2traddr(struct nvme_root *r, const char *traddr); + +/** + * get_entity_name - Get Entity Name (ENAME). + * @buffer: The buffer where the ENAME will be saved as an ASCII string. + * @bufsz: The size of @buffer. + * + * Per TP8010, ENAME is defined as the name associated with the host (i.e. + * hostname). + * + * Return: Number of characters copied to @buffer. + */ +size_t get_entity_name(char *buffer, size_t bufsz); + +/** + * get_entity_version - Get Entity Version (EVER). + * @buffer: The buffer where the EVER will be saved as an ASCII string. + * @bufsz: The size of @buffer. + * + * EVER is defined as the operating system name and version as an ASCII + * string. This function reads different files from the file system and + * builds a string as follows: [os type] [os release] [distro release] + * + * E.g. "Linux 5.17.0-rc1 SLES 15.4" + * + * Return: Number of characters copied to @buffer. + */ +size_t get_entity_version(char *buffer, size_t bufsz); + +/** + * kv_strip - Strip blanks from key value string + * @kv: The key-value string to strip + * + * Strip leading/trailing blanks as well as trailing comments from the + * Key=Value string pointed to by @kv. + * + * Return: A pointer to the stripped string. Note that the original string, + * @kv, gets modified. + */ +char *kv_strip(char *kv); + +/** + * kv_keymatch - Look for key in key value string + * @kv: The key=value string to search for the presence of @key + * @key: The key to look for + * + * Look for @key in the Key=Value pair pointed to by @k and return a + * pointer to the Value if @key is found. + * + * Check if @kv starts with @key. If it does then make sure that we + * have a whole-word match on the @key, and if we do, return a pointer + * to the first character of value (i.e. skip leading spaces, tabs, + * and equal sign) + * + * Return: A pointer to the first character of "value" if a match is found. + * NULL otherwise. + */ +char *kv_keymatch(const char *kv, const char *key); + +/** + * startswith - Checks that a string starts with a given prefix. + * @s: The string to check + * @prefix: A string that @s could be starting with + * + * Return: If @s starts with @prefix, then return a pointer within @s at + * the first character after the matched @prefix. NULL otherwise. + */ +char *startswith(const char *s, const char *prefix); + +#define __round_mask(val, mult) ((__typeof__(val))((mult)-1)) + +/** + * round_up - Round a value @val to the next multiple specified by @mult. + * @val: Value to round + * @mult: Multiple to round to. + * + * usage: int x = round_up(13, sizeof(__u32)); // 13 -> 16 + */ +#define round_up(val, mult) ((((val)-1) | __round_mask((val), (mult)))+1) + +/** + * nvmf_exat_len() - Return length rounded up by 4 + * @val_len: Value length + * + * Return the size in bytes, rounded to a multiple of 4 (e.g., size of + * __u32), of the buffer needed to hold the exat value of size + * @val_len. + * + * Return: Length rounded up by 4 + */ +static inline __u16 nvmf_exat_len(size_t val_len) +{ + return (__u16)round_up(val_len, sizeof(__u32)); +} + +/** + * nvmf_exat_size - Return min aligned size to hold value + * @val_len: This is the length of the data to be copied to the "exatval" + * field of a "struct nvmf_ext_attr". + * + * Return the size of the "struct nvmf_ext_attr" needed to hold + * a value of size @val_len. + * + * Return: The size in bytes, rounded to a multiple of 4 (i.e. size of + * __u32), of the "struct nvmf_ext_attr" required to hold a string of + * length @val_len. + */ +static inline __u16 nvmf_exat_size(size_t val_len) +{ + return (__u16)(sizeof(struct nvmf_ext_attr) + nvmf_exat_len(val_len)); +} + +/** + * nvmf_exat_ptr_next - Increment @p to the next element in the array. + * @p: Pointer to an element of an array of "struct nvmf_ext_attr". + * + * Extended attributes are saved to an array of "struct nvmf_ext_attr" + * where each element of the array is of variable size. In order to + * move to the next element in the array one must increment the + * pointer to the current element (@p) by the size of the current + * element. + * + * Return: Pointer to the next element in the array. + */ +struct nvmf_ext_attr *nvmf_exat_ptr_next(struct nvmf_ext_attr *p); + +/** + * enum nvme_version - Selector for version to be returned by @nvme_get_version + * + * @NVME_VERSION_PROJECT: Project release version + * @NVME_VERSION_GIT: Git reference + */ +enum nvme_version { + NVME_VERSION_PROJECT = 0, + NVME_VERSION_GIT = 1, +}; + +/** + * nvme_get_version - Return version libnvme string + * @type: Selects which version type (see @struct nvme_version) + * + * Return: Returns version string for known types or else "n/a" + */ +const char *nvme_get_version(enum nvme_version type); + +#define NVME_UUID_LEN_STRING 37 /* 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ +#define NVME_UUID_LEN 16 + +/** + * nvme_uuid_to_string - Return string represenation of encoded UUID + * @uuid: Binary encoded input UUID + * @str: Output string represenation of UUID + * + * Return: Returns error code if type conversion fails. + */ +int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str); + +/** + * nvme_uuid_from_string - Return encoded UUID represenation of string UUID + * @uuid: Binary encoded input UUID + * @str: Output string represenation of UUID + * + * Return: Returns error code if type conversion fails. + */ +int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]); + +/** + * nvme_uuid_random - Generate random UUID + * @uuid: Generated random UUID + * + * Generate random number according + * https://www.rfc-editor.org/rfc/rfc4122#section-4.4 + * + * Return: Returns error code if generating of random number fails. + */ +int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]); + +/** + * nvme_uuid_find - Find UUID position on UUID list + * @uuid_list: UUID list returned by identify UUID + * @uuid: Binary encoded input UUID + * + * Return: The array position where given UUID is present, or -1 on failure with errno set. + */ +int nvme_uuid_find(struct nvme_id_uuid_list *uuid_list, const unsigned char uuid[NVME_UUID_LEN]); + +/** + * nvme_ipaddrs_eq - Check if 2 IP addresses are equal. + * @addr1: IP address (can be IPv4 or IPv6) + * @addr2: IP address (can be IPv4 or IPv6) + * + * Return: true if addr1 == addr2. false otherwise. + */ +bool nvme_ipaddrs_eq(const char *addr1, const char *addr2); + +/** + * nvme_iface_matching_addr - Get interface matching @addr + * @iface_list: Interface list returned by getifaddrs() + * @addr: Address to match + * + * Parse the interface list pointed to by @iface_list looking + * for the interface that has @addr as one of its assigned + * addresses. + * + * Return: The name of the interface that owns @addr or NULL. + */ +const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr); + +/** + * nvme_iface_primary_addr_matches - Check that interface's primary address matches + * @iface_list: Interface list returned by getifaddrs() + * @iface: Interface to match + * @addr: Address to match + * + * Parse the interface list pointed to by @iface_list and looking for + * interface @iface. The get its primary address and check if it matches + * @addr. + * + * Return: true if a match is found, false otherwise. + */ +bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr); + +#endif /* _LIBNVME_UTIL_H */ |