From: Keith Busch Date: Thu, 6 Feb 2020 14:14:59 +0000 (-0800) Subject: Initial libnvme commit X-Git-Tag: v1.0-rc0~200 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=240089755db67e58bf4ad0fd21fc3e24396bccc3;p=users%2Fsagi%2Flibnvme.git Initial libnvme commit Most of the planned features are developed, however many of the interfaces have only been lighlty touched as the features they implement are not broadly supported in hardware. The remaining gaps in command support from 1.4 are: Management Interface Send/Recieve OOB support Persistent Event Logs There is no plan to support Abort, AER, IO SQ/CQ Create/Delete, or Doorbell Buffer Config since that has high risk to abuse and interfere with the stability of a running system. Signed-off-by: Keith Busch --- 240089755db67e58bf4ad0fd21fc3e24396bccc3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c2256754 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +a.out +*.o +*.ol +*.os +*~ +*.swp +*.a +*.so.* +test/test + +cscope.* diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..800b9e88 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CFLAGS = -O2 -g -Wall -Werror -std=gnu99 -D_GNU_SOURCE +LDFLAGS = -Ilib -Llib -lnvme + +QUIET_CC = @echo ' ' CC $@; + +test: test/test.c libnvme + $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $< -o test/test $(LDFLAGS) + +libnvme: + @$(MAKE) -C lib/ + +clean: + rm -f test/test + $(MAKE) -C lib clean + + +.PHONY: libnvme test diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..396e8caf --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,91 @@ +NAME=libnvme +SPECFILE=$(NAME).spec +VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECFILE)) + +prefix ?= /usr +includedir ?= $(prefix)/include +libdir ?= $(prefix)/lib + +CFLAGS ?= -g -fomit-frame-pointer -O2 -I/usr/include -Invme/ +override CFLAGS += -Wall -fPIC +SO_CFLAGS=-shared $(CFLAGS) +L_CFLAGS=$(CFLAGS) +LINK_FLAGS= -L /usr/lib64 +LINK_FLAGS+=$(LDFLAGS) +ENABLE_SHARED ?= 1 +SED ?= sed +INSTALL ?= install + +soname=$(NAME).so.1 +minor=0 +micro=1 +libname=$(soname).$(minor).$(micro) +all_targets += $(NAME).a + +SED_PROCESS = \ + $(SED) -e "s%@prefix@%$(prefix)%g" \ + -e "s%@libdir@%$(libdir)%g" \ + -e "s%@includedir@%$(includedir)%g" \ + -e "s%@NAME@%$(NAME)%g" \ + -e "s%@VERSION@%$(VERSION)%g" \ + $< >$@ + +%.pc: %.pc.in Makefile + $(SED_PROCESS) + +ifeq ($(ENABLE_SHARED),1) +all_targets += $(libname) +endif + +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V + QUIET_CC = @echo ' ' CC $@; + QUIET_LINK = @echo ' ' LINK $@; + QUIET_AR = @echo ' ' AR $@; + QUIET_RANLIB = @echo '' RANLIB $@; +endif +endif + +all: $(all_targets) + +libnvme_srcs := nvme/ioctl.c nvme/filters.c nvme/fabrics.c nvme/util.c nvme/tree.c +libnvme_objs := $(patsubst %.c,%.ol,$(libnvme_srcs)) +libnvme_sobjs := $(patsubst %.c,%.os,$(libnvme_srcs)) + +$(libnvme_objs) $(libnvme_sobjs): libnvme.h nvme/types.h nvme/ioctl.h nvme/filters.h nvme/tree.h nvme/util.h nvme/cmd.h + +%.os: %.c + $(QUIET_CC)$(CC) $(SO_CFLAGS) -c -o $@ $< + +%.ol: %.c + $(QUIET_CC)$(CC) $(L_CFLAGS) -c -o $@ $< + +AR ?= ar +RANLIB ?= ranlib +libnvme.a: $(libnvme_objs) + @rm -f libnvme.a + $(QUIET_AR)$(AR) r libnvme.a $^ + $(QUIET_RANLIB)$(RANLIB) libnvme.a + +$(libname): $(libnvme_sobjs) libnvme.map + $(QUIET_CC)$(CC) $(SO_CFLAGS) -Wl,--version-script=libnvme.map -Wl,-soname=$(soname) -o $@ $(libnvme_sobjs) $(LINK_FLAGS) + +install: $(all_targets) $(NAME).pc + $(INSTALL) -D -m 644 nvme/types.h $(includedir)/nvme/types.h + $(INSTALL) -D -m 644 nvme/ioctl.h $(includedir)/nvme/ioctl.h + $(INSTALL) -D -m 644 nvme/fabrics.h $(includedir)/nvme/fabrics.h + $(INSTALL) -D -m 644 nvme/filters.h $(includedir)/nvme/filters.h + $(INSTALL) -D -m 644 libnvme.h $(includedir)/libnvme.h + $(INSTALL) -D -m 644 libnvme.a $(libdir)/libnvme.a + $(INSTALL) -D -m 644 $(NAME).pc $(DESTDIR)$(libdir)/pkgconfig/$(NAME).pc +ifeq ($(ENABLE_SHARED),1) + $(INSTALL) -D -m 755 $(libname) $(libdir)/$(libname) + ln -sf $(libname) $(libdir)/$(soname) + ln -sf $(libname) $(libdir)/libnvme.so +endif + +$(libnvme_objs): libnvme.h + +clean: + rm -f $(all_targets) $(libnvme_objs) $(libnvme_sobjs) $(soname).new $(NAME).pc + rm -f *.so* *.a *.o diff --git a/lib/libnvme.h b/lib/libnvme.h new file mode 100644 index 00000000..c4921cbf --- /dev/null +++ b/lib/libnvme.h @@ -0,0 +1,12 @@ +#ifndef _LIBNVME_H +#define _LIBNVME_H + +#include "nvme/types.h" +#include "nvme/cmd.h" +#include "nvme/ioctl.h" +#include "nvme/fabrics.h" +#include "nvme/filters.h" +#include "nvme/tree.h" +#include "nvme/util.h" + +#endif /* _LIBNVME_H */ diff --git a/lib/libnvme.map b/lib/libnvme.map new file mode 100644 index 00000000..386dddbb --- /dev/null +++ b/lib/libnvme.map @@ -0,0 +1,6 @@ +{ + global: + nvme_submit_passthru; + local: + *; +}; diff --git a/lib/libnvme.pc.in b/lib/libnvme.pc.in new file mode 100644 index 00000000..224dd54f --- /dev/null +++ b/lib/libnvme.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=@libdir@ +includedir=@includedir@ + +Name: @NAME@ +Version: @VERSION@ +Description: Manage "libnvme" subsystem devices (Non-volatile Memory Express) +URL: http://github.com/linux-nvme/nvme-cli/ + +Libs: -L${libdir} -lnvme +Cflags: -I${includedir} diff --git a/lib/libnvme.spec b/lib/libnvme.spec new file mode 100644 index 00000000..d131af9f --- /dev/null +++ b/lib/libnvme.spec @@ -0,0 +1,59 @@ +Name: libnvme +Version: 0.1 +Release: 1 +Summary: Linux-native nvme device management library +License: LGPLv2+ +Group: System Environment/Libraries +Source: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-root +URL: http://github.com/linux-nvme/nvme-cli + +%description +Provides library functions for accessing and managing nvme devices. + +%package devel +Summary: Development files for Linux-native nvme +Group: Development/System +Requires: libnvme +Provides: libnvme.so.1 + +%description devel +This package provides header files to include and libraries to link with +for the Linux-native nvme. + +%prep +%setup + +%build +./configure --prefix=/usr --libdir=/%{_libdir} --mandir=/usr/share/man +make + +%install +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +make install DESTDIR=$RPM_BUILD_ROOT + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root) +%attr(0755,root,root) %{_libdir}/libnvme.so.* +%doc COPYING + +%files devel +%defattr(-,root,root) +%attr(-,root,root) %{_includedir}/libnvme/ +%attr(0644,root,root) %{_includedir}/libnvme.h +%attr(0755,root,root) %{_libdir}/libnvme.so +%attr(0644,root,root) %{_libdir}/libnvme.a +%attr(0644,root,root) %{_libdir}/pkgconfig/* +%attr(0644,root,root) %{_mandir}/man2/* + +%changelog +* Thu Dec 12 2019 Keith Busch - 0.1 +- Initial version diff --git a/lib/nvme/cmd.h b/lib/nvme/cmd.h new file mode 100644 index 00000000..1b34d744 --- /dev/null +++ b/lib/nvme/cmd.h @@ -0,0 +1,2245 @@ +#ifndef _LIBNVME_CMD_H +#define _LIBNVME_CMD_H + +#include "types.h" + +/** + * DOC: NVMe Admin command enums + */ + +/** + * enum nvme_admin_opcode - Known NVMe admin opcodes + * @nvme_admin_delete_sq: + * @nvme_admin_create_sq: + * @nvme_admin_get_log_page: + * @nvme_admin_delete_cq: + * @nvme_admin_create_cq: + * @nvme_admin_identify: + * @nvme_admin_abort_cmd: + * @nvme_admin_set_features: + * @nvme_admin_get_features: + * @nvme_admin_async_event: + * @nvme_admin_ns_mgmt: + * @nvme_admin_fw_commit: + * @nvme_admin_fw_download: + * @nvme_admin_dev_self_test: + * @nvme_admin_ns_attach: + * @nvme_admin_keep_alive: + * @nvme_admin_directive_send: + * @nvme_admin_directive_recv: + * @nvme_admin_virtual_mgmt: + * @nvme_admin_nvme_mi_send: + * @nvme_admin_nvme_mi_recv: + * @nvme_admin_dbbuf: + * @nvme_admin_fabrics: + * @nvme_admin_format_nvm: + * @nvme_admin_security_send: + * @nvme_admin_security_recv: + * @nvme_admin_sanitize_nvm: + * @nvme_admin_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_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_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 - + * @NVME_IDENTIFY_CNS_NS: + * @NVME_IDENTIFY_CNS_CTRL: + * @NVME_IDENTIFY_CNS_NS_ACTIVE_LIST: + * @NVME_IDENTIFY_CNS_NS_DESC_LIST: + * @NVME_IDENTIFY_CNS_NVMSET_LIST: + * @NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST: + * @NVME_IDENTIFY_CNS_ALLOCATED_NS: + * @NVME_IDENTIFY_CNS_NS_CTRL_LIST: + * @NVME_IDENTIFY_CNS_CTRL_LIST: + * @NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP: + * @NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST: + * @NVME_IDENTIFY_CNS_NS_GRANULARITY: + * @NVME_IDENTIFY_CNS_UUID_LIST: + */ +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_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, +}; + +/** + * enum nvme_cmd_get_log_lid - + * NVME_LOG_LID_ERROR: + * NVME_LOG_LID_SMART: + * NVME_LOG_LID_FW_SLOT: + * NVME_LOG_LID_CHANGED_NS: + * NVME_LOG_LID_CMD_EFFECTS: + * NVME_LOG_LID_DEVICE_SELF_TEST: + * NVME_LOG_LID_TELEMETRY_HOST: + * NVME_LOG_LID_TELEMETRY_CTRL: + * NVME_LOG_LID_ENDURANCE_GROUP: + * NVME_LOG_LID_PREDICTABLE_LAT_NVMSET: + * NVME_LOG_LID_PREDICTABLE_LAT_AGG: + * NVME_LOG_LID_ANA: + * NVME_LOG_LID_PERSISTENT_EVENT: + * NVME_LOG_LID_LBA_STATUS: + * NVME_LOG_LID_ENDURANCE_GRP_EVT: + * NVME_LOG_LID_DISC: + * NVME_LOG_LID_RESERVATION: + * NVME_LOG_LID_SANITIZE: + */ +enum nvme_cmd_get_log_lid { + 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_DISC = 0x70, + NVME_LOG_LID_RESERVATION = 0x80, + NVME_LOG_LID_SANITIZE = 0x81, +}; + +/** + * enum nvme_features_id - + * @NVME_FEAT_FID_ARBITRATION: + * @NVME_FEAT_FID_POWER_MGMT: + * @NVME_FEAT_FID_LBA_RANGE: + * @NVME_FEAT_FID_TEMP_THRESH: + * @NVME_FEAT_FID_ERR_RECOVERY: + * @NVME_FEAT_FID_VOLATILE_WC: + * @NVME_FEAT_FID_NUM_QUEUES: + * @NVME_FEAT_FID_IRQ_COALESCE: + * @NVME_FEAT_FID_IRQ_CONFIG: + * @NVME_FEAT_FID_WRITE_ATOMIC: + * @NVME_FEAT_FID_ASYNC_EVENT: + * @NVME_FEAT_FID_AUTO_PST: + * @NVME_FEAT_FID_HOST_MEM_BUF: + * @NVME_FEAT_FID_TIMESTAMP: + * @NVME_FEAT_FID_KATO: + * @NVME_FEAT_FID_HCTM: + * @NVME_FEAT_FID_NOPSC: + * @NVME_FEAT_FID_RRL: + * @NVME_FEAT_FID_PLM_CONFIG: + * @NVME_FEAT_FID_PLM_WINDOW: + * @NVME_FEAT_FID_LBA_STS_INTERVAL: + * @NVME_FEAT_FID_HOST_BEHAVIOR: + * @NVME_FEAT_FID_SANITIZE: + * @NVME_FEAT_FID_ENDURANCE_EVT_CFG: + * @NVME_FEAT_FID_SW_PROGRESS: + * @NVME_FEAT_FID_HOST_ID: + * @NVME_FEAT_FID_RESV_MASK: + * @NVME_FEAT_RESV_PERSIST: + * @NVME_FEAT_FID_WRITE_PROTECT: + */ +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_SW_PROGRESS = 0x80, + NVME_FEAT_FID_HOST_ID = 0x81, + NVME_FEAT_FID_RESV_MASK = 0x82, + NVME_FEAT_RESV_PERSIST = 0x83, + NVME_FEAT_FID_WRITE_PROTECT = 0x84, +}; + +/** + * enum nvme_get_features_sel - + * @NVME_GET_FEATURES_SEL_CURRENT: + * @NVME_GET_FEATURES_SEL_DEFAULT: + * @NVME_GET_FEATURES_SEL_SAVED: + */ +enum nvme_get_features_sel { + NVME_GET_FEATURES_SEL_CURRENT = 0, + NVME_GET_FEATURES_SEL_DEFAULT = 1, + NVME_GET_FEATURES_SEL_SAVED = 2, +}; + +/** + * enum nvme_cmd_format_mset - + * @NVME_FORMAT_MSET_SEPARATE: + * @NVME_FORMAT_MSET_EXTENEDED: + */ +enum nvme_cmd_format_mset { + NVME_FORMAT_MSET_SEPARATE = 0, + NVME_FORMAT_MSET_EXTENEDED = 1, +}; + +/** + * enum nvme_cmd_format_pi - + * @NVME_FORMAT_PI_DISABLE: + * @NVME_FORMAT_PI_TYPE1: + * @NVME_FORMAT_PI_TYPE2: + * @NVME_FORMAT_PI_TYPE3: + */ +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 - + * @NVME_FORMAT_PIL_LAST: + * @NVME_FORMAT_PIL_FIRST: + */ +enum nvme_cmd_format_pil { + NVME_FORMAT_PIL_LAST = 0, + NVME_FORMAT_PIL_FIRST = 1, +}; + +/** + * enum nvme_cmd_format_ses - + * @NVME_FORMAT_SES_NONE: + * @NVME_FORMAT_SES_USER_DATA_ERASE: + * @NVME_FORMAT_SES_CRYPTO_ERASE: + */ +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 - + * @NVME_NAMESPACE_MGMT_SEL_CREATE: + * @NVME_NAMESPACE_MGMT_SEL_DELETE: + */ +enum nvme_ns_mgmt_sel { + NVME_NS_MGMT_SEL_CREATE = 0, + NVME_NS_MGMT_SEL_DELETE = 1, +}; + +/** + * enum nvme_ns_attach_sel - + * NVME_NS_ATTACH_SEL_CTRL_ATTACH: + * NVME_NP_ATTACH_SEL_CTRL_DEATTACH: + */ +enum nvme_ns_attach_sel { + NVME_NS_ATTACH_SEL_CTRL_ATTACH = 0, + NVME_NS_ATTACH_SEL_CTRL_DEATTACH = 1, +}; + +/** + * enum nvme_fw_commit_ca - + * @NVME_FW_COMMIT_CA_REPLACE: + * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE: + * @NVME_FW_COMMIT_CA_SET_ACTIVE: + * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE: + * @NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION: + * @NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION: + */ +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 - + * @NVME_DIRECTIVE_DTYPE_IDENTIFY: + * @NVME_DIRECTIVE_DTYPE_STREAMS: + */ +enum nvme_directive_dtype { + NVME_DIRECTIVE_DTYPE_IDENTIFY = 0, + NVME_DIRECTIVE_DTYPE_STREAMS = 1, +}; + +/** + * enum - + */ +enum nvme_cmd_directive_receive_identify_doper { + NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM = 0x01, +}; + +/** + * enum - + */ +enum nvme_cmd_directive_receive_streams_doper { + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM = 0x01, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS = 0x02, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE = 0x03, +}; + +/** + * enum - + */ +enum nvme_cmd_directive_send_identify_doper { + NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR = 0x01, +}; + +/** + * enum - + */ +enum nvme_cmd_directive_send_identify_endir { + NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE = 0, + NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE = 1, +}; + +/** + * enum - + */ +enum nvme_cmd_directive_send_streams_doper { + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER = 0x01, + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE = 0x02, +}; + +/** + * enum nvme_sanitize_sanact - + * @NVME_SANITIZE_SANACT_EXIT_FAILURE: + * @NVME_SANITIZE_SANACT_START_BLOCK_ERASE: + * @NVME_SANITIZE_SANACT_START_OVERWRITE: + * @NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: + */ +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 - + * @NVME_DST_STC_SHORT: + * @NVME_DST_STC_LONG: + * @NVME_DST_STC_VS: + * @NVME_DST_STC_ABORT: + */ +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 - + * @NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC: + * @NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL: + * @NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL: + * @NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL: + */ +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 - + * @NVME_VIRT_MGMT_RT_VQ_RESOURCE: + * @NVME_VIRT_MGMT_RT_VI_RESOURCE: + */ +enum nvme_virt_mgmt_rt { + NVME_VIRT_MGMT_RT_VQ_RESOURCE = 0, + NVME_VIRT_MGMT_RT_VI_RESOURCE = 1, +}; + +/** + * nvme_identify() - Send the NVMe Identify command + * @fd: File descriptor of nvme device + * @cns: The Controller or Namespace structure, see @enum nvme_identify_cns + * @nsid: Namespace identifier, if applicable + * @cntid: The Controller Identifier, if applicable + * @nvmsetid: The NVMe Set ID if CNS is 04h + * @uuidx: UUID Index if controller supports this id selection method + * @data: User space destination address to transfer the data + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_identify(int fd, enum nvme_identify_cns cns, __u32 nsid, + __u16 cntid, __u16 nvmsetid, __u8 uuidx, void *data); + +/** + * 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 or -1 with errno + * set otherwise. + */ +int nvme_identify_ctrl(int fd, struct nvme_id_ctrl *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 or -1 with errno + * set otherwise. + */ +int nvme_identify_ns(int fd, __u32 nsid, struct nvme_id_ns *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 or -1 with errno + * set otherwise. + */ +int nvme_identify_allocated_ns(int fd, __u32 nsid, struct nvme_id_ns *ns); + +/** + * nvme_identify_active_ns_list() - Retrieves active namespaces id list + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifer + * @ns_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 or -1 with errno + * set otherwise. + */ +int nvme_identify_active_ns_list(int fd, __u32 nsid, struct nvme_ns_list *list); + +/** + * nvme_identify_allocated_ns_list() - Retrieves allocated namespace id list + * @fd: File descriptor of nvme device + * @nsid: Return namespaces greater than this identifer + * @ns_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 or -1 with errno + * set otherwise. + */ +int nvme_identify_allocated_ns_list(int fd, __u32 nsid, struct nvme_ns_list *list); + +/** + * nvme_identify_ctrl_list() - Retrieves identify controller list + * @fd: File descriptor of nvme device + * @cntlid: 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 or -1 with errno + * set otherwise. + */ +int nvme_identify_ctrl_list(int fd, __u16 cntid, + struct nvme_ctrl_list *ctrlist); + +/** + * nvme_identify_nsid_ctrl_list() - + * @fd: File descriptor of nvme device + * @nsid: Return controllers that are attached to this nsid + * @cntlid: 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 or -1 + */ +int nvme_identify_nsid_ctrl_list(int fd, __u32 nsid, __u16 cntid, + struct nvme_ctrl_list *ctrlist); + +/** + * nvme_identify_ns_descs() - Retrieves namespace descriptor list + * @fd: File descriptor of nvme device + * @nsid: The namespace id to retrieve destriptors + * @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 arrray 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 or -1 with errno + * set otherwise. + */ +int nvme_identify_ns_descs(int fd, __u32 nsid, struct nvme_ns_id_desc *descs); + +/** + * nvme_identify_nvmset_list() - Retrieves NVM Set List + * @fd: File descriptor of nvme device + * @nvmeset_id: NVM Set Identifier + * @nvmset: User space destination address to transfer the data + * + * Retrieves an NVM Set List, struct nvme_id_nvmset. 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 defintion of the returned structure. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_identify_nvmset_list(int fd, __u16 nvmsetid, struct nvme_id_nvmset_list *nvmset); + +/** + * nvme_identify_primary_ctrl() - Retrieve NVMe Primary Controller + * identification + * &fd: + * @cntid: + * @cap: + * + * See &struct nvme_primary_ctrl_cap for the defintion of the returned structure, @cap. + * + * Return: The nvme command status if a response was received or -1 + * with errno set otherwise. + */ +int nvme_identify_primary_ctrl(int fd, __u16 cntid, struct nvme_primary_ctrl_cap *cap); + +/** + * 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 defintion of the returned + * structure. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_identify_secondary_ctrl_list(int fd, __u16 cntid, struct nvme_secondary_ctrl_list *list); + +/** + * 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 or -1 with errno + * set otherwise. + */ +int nvme_identify_ns_granularity(int fd, struct nvme_id_ns_granularity_list *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 or -1 with errno + * set otherwise. + */ +int nvme_identify_uuid(int fd, struct nvme_id_uuid_list *list); + +/** + * nvme_get_log() - NVMe Admin Get Log command + * @fd: File descriptor of nvme device + * @lid: Log page identifier, see &enum nvme_cmd_get_log_lid for known values + * @nsid: Namespace identifier, if applicable + * @lpo: Log page offset for partial log transfers + * @lsp: Log specific field + * @lsi: Endurance group information + * @rae: Retain asynchronous events + * @uuidx: UUID selection, if supported + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User space destination address to transfer the data + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_get_log(int fd, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u64 lpo, + __u8 lsp, __u16 lsi, bool rae, __u8 uuidx, __u32 len, void *log); + +/** + * nvme_get_log_error() - Retrieve nvme error log + * @fd: File descriptor of nvme device + * @entries: Number of error log entries allocated + * @rae: Retain asynchronous events + * @err_log: Array of error logs of size 'entries' + * + * This log page is used to describe 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_error(int fd, unsigned nr_entries, bool rae, + struct nvme_error_log_page *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 is used to provide 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_smart(int fd, __u32 nsid, bool rae, struct nvme_smart_log *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 is used to describe 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_fw_slot(int fd, bool rae, struct nvme_firmware_slot *log); + +/** + * nvme_get_log_changed_ns_list() - Retrieve namespace changed list + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @ns_list: User address to store the log page + * + * This log page is used to describe 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_changed_ns_list(int fd, bool rae, struct nvme_ns_list *log); + +/** + * nvme_get_log_cmd_effects() - Retrieve nvme command effects log + * @fd: File descriptor of nvme device + * @effects_log:User address to store the effects log + * + * This log page is used to describe 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_cmd_effects(int fd, struct nvme_cmd_effects_log *log); + +/** + * nvme_get_log_device_self_test() - Retrieve the device self test log + * @fd: File descriptor of nvme device + * @nsid: Namespace ID being tested + * @log: Userspace address of the log payload + * + * The log page is used to indicate 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_device_self_test(int fd, struct nvme_self_test_log *log); + +/** + * nvme_get_log_create_telemetry_host() - + */ +int nvme_get_log_create_telemetry_host(int fd, struct nvme_telemetry_log *log); +/** * nvme_get_log_telemetry_host() - + * @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 + * + * Retreives 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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_telemetry_host(int fd, __u64 offset, __u32 len, void *log); + +/** + * nvme_get_log_telemetry_ctrl() - + * @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 + */ +int nvme_get_log_telemetry_ctrl(int fd, bool rae, __u64 offset, __u32 len, void *log); + +/** + * nvme_get_log_endurance_group() - + * @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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_endurance_group(int fd, __u16 endgid, struct nvme_endurance_group_log *log); + +/** + * nvme_get_log_predictable_lat_nvmset() - + * @fd: + * @nvmsetid: + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_get_log_predictable_lat_nvmset(int fd, __u16 nvmsetid, + struct nvme_nvmset_predictable_lat_log *log); + +/** + * nvme_get_log_predictable_lat_event() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + */ +int nvme_get_log_predictable_lat_event(int fd, bool rae, __u32 offset, + __u32 len, void *log); + +/** + * + */ +enum nvme_log_ana_lsp { + NVME_LOG_ANA_LSP_RGO_NAMESPACES = 0, + NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY = 1, +}; + +/** + * nvme_get_log_ana() - + * @fd: File descriptor of nvme device + * @lsp: Log specific, see &enum nvme_get_log_ana_lsp + * @rae: Retain asynchronous events + * @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 defintion of the returned structure. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_get_log_ana(int fd, enum nvme_log_ana_lsp lsp, bool rae, __u64 offset, + __u32 len, void *log); + +/** + * nvme_get_log_ana_groups() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * + * See &struct nvme_ana_group_desc for the defintion of the returned structure. + */ +int nvme_get_log_ana_groups(int fd, bool rae, __u32 len, + struct nvme_ana_group_desc *log); + +/** + * nvme_get_log_lba_status() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + */ +int nvme_get_log_lba_status(int fd, bool rae, __u64 offset, __u32 len, + void *log); + +/** + * nvme_get_log_endurance_grp_evt() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + */ +int nvme_get_log_endurance_grp_evt(int fd, bool rae, __u32 offset, __u32 len, + void *log); + +/** + * nvme_get_log_discovery() - + * @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 or -1 with errno + * set otherwise. + */ +int nvme_get_log_discovery(int fd, bool rae, __u32 offset, __u32 len, void *log); + +/** + * nvme_get_log_reservation() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + */ +int nvme_get_log_reservation(int fd, bool rae, + struct nvme_resv_notification_log *log); + +/** + * nvme_get_log_sanitize() - + * @fd: File descriptor of nvme device + * @rae: Retain asynchronous events + * @log: User address to store the sanitize log + * + * The Sanitize Status log page is used to report sanitize operation time + * estimates and information about the most recent sanitize operation. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_get_log_sanitize(int fd, bool rae, + struct nvme_sanitize_log_page *log); + +/** + * nvme_set_feature() - Set a feature attribute + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable + * @cdw11: Value to set the feature to + * @cdw12: Feature specific command dword12 field + * @save: Save value across power states + * @uuidx: UUID Index for differentiating vendor specific encoding + * @cdw14: Feature specific command dword15 field + * @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 or -1 with errno + * set otherwise. + */ +int nvme_set_features(int fd, __u8 fid, __u32 nsid, __u32 cdw11, __u32 cdw12, + bool save, __u8 uuidx, __u32 cdw15, __u32 data_len, + void *data, __u32 *result); + +/** + * nvme_set_features_arbitration() - + */ +int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw, + __u8 hpw, bool save, __u32 *result); + +/** + * nvme_set_features_power_mgmt() - + */ +int nvme_set_features_power_mgmt(int fd, __u8 ps, __u8 wh, bool save, + __u32 *result); + +/** + * nvme_set_features_lba_range() - + */ +int nvme_set_features_lba_range(int fd, __u32 nsid, __u32 nr_ranges, bool save, + struct nvme_lba_range_type *data, __u32 *result); + + +/** + * enum nvme_feat_tmpthresh_thsel - + */ +enum nvme_feat_tmpthresh_thsel { + NVME_FEATURE_TEMPTHRESH_THSEL_OVER = 0, + NVME_FEATURETEMPTHRESH__THSEL_UNDER = 1, +}; + +/** + * nvme_set_features_temp_thresh() - + */ +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() - + */ +int nvme_set_features_err_recovery(int fd, __u32 nsid, __u16 tler, + bool dulbe, bool save, __u32 *result); + + +/** + * nvme_set_features_volatile_wc() - + */ +int nvme_set_features_volatile_wc(int fd, bool wce, bool save, + __u32 *result); + +/** + * nvme_set_features_irq_coalesce() - + */ +int nvme_set_features_irq_coalesce(int fd, __u8 thr, __u8 time, + bool save, __u32 *result); + +/** + * nvme_set_features_irq_config() - + */ +int nvme_set_features_irq_config(int fd, __u16 iv, bool cd, bool save, + __u32 *result); + + +/** + * nvme_set_features_write_atomic() - + */ +int nvme_set_features_write_atomic(int fd, bool dn, bool save, + __u32 *result); + +/** + * enum nvme_features_async_event_config_flags - + */ +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, +}; + +/** + * nvme_set_features_async_event() - + */ +int nvme_set_features_async_event(int fd, __u32 events, bool save, + __u32 *result); + + +/** + * nvme_set_features_auto_pst() - + */ +int nvme_set_features_auto_pst(int fd, bool apste, bool save, + struct nvme_feat_auto_pst *apst, + __u32 *result); + +/** + * nvme_set_features_timestamp() - + */ +int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp); + + +/** + * nvme_set_features_hctm() - + */ +int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, bool save, + __u32 *result); + +/** + * nvme_admin_set_features_nopsc() - + */ +int nvme_admin_set_features_nopsc(int fd, bool noppme, bool save, + __u32 *result); + +/** + * nvme_set_features_rrl() - + */ +int nvme_set_features_rrl(int fd, __u8 rrl, __u16 nvmsetid, bool save, + __u32 *result); + +/** + * nvme_set_features_plm_config() - + */ +int nvme_set_features_plm_config(int fd, bool enable, __u16 nvmsetid, + bool save, struct nvme_plm_config *data, + __u32*result); + +/** + * enum nvme_feat_plm_window_select - + */ +enum nvme_feat_plm_window_select { + NVME_FEATURE_PLM_DTWIN = 1, + NVME_FEATURE_PLM_NDWIN = 2, +}; + +/** + * nvme_set_features_plm_window() - + */ +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() - + */ +int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi, + bool save, __u32 *result); + + +/** + * nvme_set_features_host_behavior() - + */ +int nvme_set_features_host_behavior(int fd, bool save, + struct nvme_feat_host_behavior *data); + +/** + * nvme_set_features_sanitize() - + */ +int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result); + +/** + * nvme_set_features_endurance_evt_cfg() - + * @fd: + * @endgid: + * @egwarn: Flags to enable warning, see &enum nvme_eg_critical_warning_flags + */ +int nvme_set_features_endurance_evt_cfg(int fd, __u16 endgid, __u8 egwarn, + bool save, __u32 *result); + +/** + * nvme_set_features_sw_progress() - + */ +int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save, + __u32 *result); + + +/** + * nvme_set_features_host_id() - + */ +int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid); + +/** + * + */ +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, +}; + +/** + * nvme_set_features_resv_mask() - + */ +int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result); + +/** + * nvme_set_features_resv_persist() - + */ +int nvme_set_features_resv_persist(int fd, bool ptpl, bool save, __u32 *result); + +/** + * enum nvme_feat_ns_wp_cfg_state - + * @NVME_FEAT_NS_NO_WRITE_PROTECT: + * @NVME_FEAT_NS_WRITE_PROTECT: + * @NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE: + * @NVME_FEAT_NS_WRITE_PROTECT_PERMANENT: + */ +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, +}; + +/** + * nvme_set_features_write_protect() - + */ +int nvme_set_features_write_protect(int fd, enum nvme_feat_nswpcfg_state state, + bool save, __u32 *result); + +/** + * nvme_get_features() - Retrieve a feature attribute + * @fd: File descriptor of nvme device + * @fid: Feature identifier + * @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 + * @uuidx: UUID Index for differentiating vendor specific encoding + * @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 or -1 with errno + * set otherwise. + */ +int nvme_get_features(int fd, enum nvme_features_id fid, __u32 nsid, + enum nvme_get_features_sel sel, __u32 cdw11, __u8 uuidx, + __u32 data_len, void *data, __u32 *result); + +/** + * nvme_get_features_arbitration() - + */ +int nvme_get_features_arbitration(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_power_mgmt() - + */ +int nvme_get_features_power_mgmt(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_lba_range() - + */ +int nvme_get_features_lba_range(int fd, enum nvme_get_features_sel sel, + struct nvme_lba_range_type *data, + __u32 *result); + +/** + * nvme_get_features_temp_thresh() - + */ +int nvme_get_features_temp_thresh(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_err_recovery() - + */ +int nvme_get_features_err_recovery(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_volatile_wc() - + */ +int nvme_get_features_volatile_wc(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_num_queues() - + */ +int nvme_get_features_num_queues(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_irq_coalesce() - + */ +int nvme_get_features_irq_coalesce(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_irq_config() - + */ +int nvme_get_features_irq_config(int fd, enum nvme_get_features_sel sel, + __u16 iv, __u32 *result); + +/** + * nvme_get_features_write_atomic() - + */ +int nvme_get_features_write_atomic(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_async_event() - + */ +int nvme_get_features_async_event(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_auto_pst() - + */ +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() - + */ +int nvme_get_features_host_mem_buf(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_timestamp() - + */ +int nvme_get_features_timestamp(int fd, enum nvme_get_features_sel sel, + struct nvme_timestamp *ts); + +/** + * nvme_get_features_kato() - + */ +int nvme_get_features_kato(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_hctm() - + */ +int nvme_get_features_hctm(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_nopsc() - + */ +int nvme_get_features_nopsc(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_rrl() - + */ +int nvme_get_features_rrl(int fd, enum nvme_get_features_sel sel, __u32 *result); + +/** + * nvme_get_features_plm_config() - + */ +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() - + */ +int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, __u32 *result); + +/** + * nvme_get_features_lba_sts_interval() - + */ +int nvme_get_features_lba_sts_interval(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_host_behavior() - + */ +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() - + */ +int nvme_get_features_sanitize(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_endurance_event_cfg() - + */ +int nvme_get_features_endurance_event_cfg(int fd, enum nvme_get_features_sel sel, + __u16 endgid, __u32 *result); + +/** + * nvme_get_features_sw_progress() - + */ +int nvme_get_features_sw_progress(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_host_id() - + */ +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() - + */ +int nvme_get_features_resv_mask(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_resv_persist() - + */ +int nvme_get_features_resv_persist(int fd, enum nvme_get_features_sel sel, + __u32 *result); + +/** + * nvme_get_features_write_protect() - + */ +int nvme_get_features_write_protect(int fd, __u32 nsid, + enum nvme_get_features_sel sel, + __u32 *result); + + +/** + * nvme_format_nvm() - Format nvme namespace(s) + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to format + * @lbaf: Logical block address 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 + * @timeout: Set to override default timeout to this value in milliseconds; + * useful for long running formats. 0 will use system default. + * + * The Format NVM command is used to low level format 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 or -1 with errno + * set otherwise. + */ +int nvme_format_nvm(int fd, __u32 nsid, __u8 lbaf, + enum nvme_cmd_format_mset mset, + enum nvme_cmd_format_pi pi, + enum nvme_cmd_format_pil pil, + enum nvme_cmd_format_ses ses, + __u32 timeout); + +/** + * nvme_ns_mgmt() - + * @fd: File descriptor of nvme device + */ +int nvme_ns_mgmt(int fd, __u32 nsid, enum nvme_ns_mgmt_sel sel, + struct nvme_id_ns *ns, __u32 *result, __u32 timeout); + +/** + * nvme_ns_mgmt_create() - + * @fd: File descriptor of nvme device + * @ns: Namespace identifiaction that defines creation parameters + * @nsid: On success, set to the namespace id that was created + * @timeout: Overide the default timeout to this value in milliseconds; + * set to 0 to use the system default. + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, __u32 *nsid, + __u32 timeout); + +/** + * nvme_ns_mgmt_delete() - + * @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 or -1 with errno + * set otherwise. + */ +int nvme_ns_mgmt_delete(int fd, __u32 nsid); + +/** + * nvme_ns_attach() - Attach or detach namespace to controller(s) + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to execute attach selection + * @sel: Attachment selection, see &enum nvme_ns_attach_sel + * @ctrlist: Controller list to modify attachment state of nsid + */ +int nvme_ns_attach(int fd, __u32 nsid, enum nvme_ns_attach_sel sel, + struct nvme_ctrl_list *ctrlist); + +/** + * nvme_ns_attach_ctrls() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to attach + * @ctrlist: Controller list to modify attachment state of nsid + */ +int nvme_ns_attach_ctrls(int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist); + +/** + * nvme_ns_dettach_ctrls() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to dettach + * @ctrlist: Controller list to modify attachment state of nsid + */ +int nvme_ns_dettach_ctrls(int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist); + +/** + * nvme_fw_download() - Download part or all of a firmware image to the + * controller + * @fd: File descriptor of nvme device + * @offset: Offset in the firmware data + * @data_len: Length of data in this command in bytes + * @data: Userspace address of the firmware data + * + * The Firmware Image Download command is used to download 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 or -1 with errno + * set otherwise. + */ +int nvme_fw_download(int fd, __u32 offset, __u32 data_len, void *data); + +/** + * nvme_fw_commit() - Commit firmware using the specified action + * @fd: File descriptor of nvme device + * @slot: Firmware slot to commit the downloaded image + * @action: Action to use for the firmware image, see &enum nvme_fw_commit_ca + * @bpid: Set to true to select the boot partition id + * + * The Firmware Commit command is used to modify the firmware image or Boot + * Partitions. + * + * Return: The nvme command status if a response was received 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(int fd, __u8 slot, enum nvme_fw_commit_ca action, bool bpid); + +/** + * nvme_security_receive() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to issue security command on + * @nssf: NVMe Security Specific field + * @spsp0: Security Protocol Specific field + * @spsp1: Security Protocol Specific field + * @secp: Security Protocol + * @tl: Protocol specific transfer length + * @data_len: Data length of the payload in bytes + * @data: Security data payload to send + * @result: The command completion result from CQE dword0 + * + * The Security Send command is used to transfer 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 or -1 with errno + * set otherwise. + */ +int nvme_security_send(int fd, __u32 nsid, __u8 nssf, __u8 spsp0, __u8 spsp1, + __u8 secp, __u32 tl, __u32 data_len, void *data, + __u32 *result); + +/** + * nvme_security_receive() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to issue security command on + * @nssf: NVMe Security Specific field + * @spsp0: Security Protocol Specific field + * @spsp1: Security Protocol Specific field + * @secp: Security Protocol + * @al: Protocol specific allocation length + * @data_len: Data length of the payload in bytes + * @data: Security data payload to send + * @result: The command completion result from CQE dword0 + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_security_receive(int fd, __u32 nsid, __u8 nssf, __u8 spsp0, + __u8 spsp1, __u8 secp, __u32 al, __u32 data_len, + void *data, __u32 *result); + +/** + * nvme_get_lba_status() - Retrieve information on possibly unrecoverable LBAs + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to retrieve LBA status + * @slba: Starting logical block address to check statuses + * @mndw: Maximum number of dwords to return + * @atype: Action type mechanism to determine LBA status desctriptors to + * return, see &enum nvme_lba_status_atype + * @rl: Range length from slba to perform the action + * @lbas: Data payload to return status descriptors + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_get_lba_status(int fd, __u32 nsid, __u64 slba, __u32 mndw, __u16 rl, + enum nvme_lba_status_atype atype, + struct nvme_lba_status *lbas); + +/** + * nvme_directive_send() - Send directive command + * @fd: File descriptor of nvme device + * @nsid: Namespace ID, if applicable + * @dspec: Directive specific field + * @doper: Directive operation + * @dtype: Directive type, see &enum nvme_directive_dtype + * @dw12: Directive specific command dword12 + * @data_len: Length of data payload in bytes + * @data: Usespace address of data payload + * @result: If successful, the CQE dword0 value + * + * Directives is a mechanism to enable host and NVM subsystem or controller + * information exchange. The Directive Send command is used to transfer 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 or -1 with errno + * set otherwise. + */ +int nvme_directive_send(int fd, __u32 nsid, __u16 dspec, __u8 doper, + enum nvme_directive_dtype dtype, __u32 cdw12, + __u32 data_len, void *data, __u32 *result); + +/** + * nvme_directive_send_id_endir() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received 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() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_send_stream_release_identifier(int fd, __u32 nsid, + __u16 stream_id); + +/** + * nvme_directive_send_stream_release_resource() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_send_stream_release_resource(int fd, __u32 nsid); + +/** + * nvme_directive_recv() - Receive directive specific data + * @fd: File descriptor of nvme device + * @nsid: Namespace ID, if applicable + * @dspec: Directive specific field + * @doper: Directive operation + * @dtype: Directive type, see &enum nvme_directive_dtype + * @dw12: Directive specific command dword12 + * @data_len: Length of data payload + * @data: Usespace address of data payload in bytes + * @result: If successful, the CQE dword0 value + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_recv(int fd, __u32 nsid, __u16 dspec, __u8 doper, + enum nvme_directive_dtype dtype, __u32 cdw12, + __u32 data_len, void *data, __u32 *result); + +/** + * nvme_directive_recv_identify_parameters() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_recv_identify_parameters(int fd, __u32 nsid, + struct nvme_id_directives *id); + +/** + * nvme_directive_recv_stream_parameters() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_recv_stream_parameters(int fd, __u32 nsid, + struct nvme_streams_directive_params *parms); + +/** + * nvme_directive_recv_stream_status() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_recv_stream_status(int fd, __u32 nsid, unsigned nr_entries, + struct nvme_streams_directive_status *id); + +/** + * nvme_directive_recv_stream_allocate() - + * @fd: File descriptor of nvme device + * @nsid: Namespace ID + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_directive_recv_stream_allocate(int fd, __u32 nsid, __u16 nsr, + __u32 *result); + +/** + * enum nvme_fctype - + * @nvme_fabrics_type_property_set: + * @nvme_fabrics_type_connect: + * @nvme_fabrics_type_property_get: + * @nvme_fabrics_type_auth_send: + * @nvme_fabrics_type_auth_receive: + * @nvme_fabrics_type_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, +}; + +/** + * nvme_set_property() - Set controller property + * @fd: File descriptor of nvme device + * @offset: Property offset from the base to set + * @value: The value to set the property + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_set_property(int fd, int offset, __u64 value); + +/** + * nvme_get_property() - Get a controller property + * @fd: File descriptor of nvme device + * @offset: Property offset from the base to retrieve + * @value: Where the property's value will be stored on success + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_get_property(int fd, int offset, __u64 *value); + +/** + * nvme_sanitize() - Start a sanitize operation + * @fd: File descriptor of nvme device + * @sanact: Sanitize action, see &enum nvme_sanitize_sanact + * @ause: Set to allow unrestriced sanitize exit + * @owpass: Overwrite pass count + * @oipbp: Set to overwrite invert pattern between passes + * @nodas: Set to not deallocate blocks after sanitizing + * @ovrpat: Overwrite pattern + * + * 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 is used to start 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 or -1 with errno + * set otherwise. + */ +int nvme_sanitize_nvm(int fd, enum nvme_sanitize_sanact sanact, bool ause, + __u8 owpass, bool oipbp, bool nodas, __u32 ovrpat); + +/** + * nvme_dev_self_test() - Start or abort a self test + * @fd: File descriptor of nvme device + * @nsid: Namespace ID to test + * @stc: Self test code, see &enum nvme_dst_stc + * + * The Device Self-test command is used to start 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 namepsaces 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 or -1 with errno + * set otherwise. + */ +int nvme_dev_self_test(int fd, __u32 nsid, enum nvme_dst_stc stc); + +/** + * nvme_virtual_mgmt() - Virtualization resource management + * @fd: File descriptor of nvme device + * @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 + * @result: If successful, the CQE dword0 + * + * 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 or -1 + * with errno set otherwise. + */ +int nvme_virtual_mgmt(int fd, enum nvme_virt_mgmt_act act, + enum nvme_virt_mgmt_rt rt, __u16 cntlid, __u16 nr, + __u32 *result); + +/** + * DOC: NVMe IO command enums + */ + +/** + * enum nvme_io_opcode - + * @nvme_cmd_flush: + * @nvme_cmd_write: + * @nvme_cmd_read: + * @nvme_cmd_write_uncor: + * @nvme_cmd_compare: + * @nvme_cmd_write_zeroes: + * @nvme_cmd_dsm: + * @nvme_cmd_verify: + * @nvme_cmd_resv_register: + * @nvme_cmd_resv_report: + * @nvme_cmd_resv_acquire: + * @nvme_cmd_resv_release: + */ +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_resv_release = 0x15, +}; + +/** + * nvme_flush() - Send an nvme flush command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * + * The Flush command is used to request that the contents of volatile write + * cache be made non-volatile. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_flush(int fd, __u32 nsid); + +/** + * enum nvme_io_control_flags - + * @NVME_IO_DTYPE_STREAMS: + * @NVME_IO_DEAC: + * @NVME_IO_PRINFO_PRCHK_REF: + * @NVME_IO_PRINFO_PRCHK_APP: + * @NVME_IO_PRINFO_PRCHK_GUARD: + * @NVME_IO_PRINFO_PRACT: + * @NVME_IO_FUA: + * @NVME_IO_LR: + */ +enum nvme_io_control_flags { + NVME_IO_DTYPE_STREAMS = 1 << 4, + NVME_IO_DEAC = 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_flag - + * @NVME_IO_DSM_FREQ_UNSPEC: + * @NVME_IO_DSM_FREQ_TYPICAL: + * @NVME_IO_DSM_FREQ_RARE: + * @NVME_IO_DSM_FREQ_READS: + * @NVME_IO_DSM_FREQ_WRITES: + * @NVME_IO_DSM_FREQ_RW: + * @NVME_IO_DSM_FREQ_ONCE: + * @NVME_IO_DSM_FREQ_PREFETCH: + * @NVME_IO_DSM_FREQ_TEMP: + * @NVME_IO_DSM_LATENCY_NONE: + * @NVME_IO_DSM_LATENCY_IDLE: + * @NVME_IO_DSM_LATENCY_NORM: + * @NVME_IO_DSM_LATENCY_LOW: + * @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, +}; + +/** + * nvme_read() - Submit an nvme user read command + * @fd: File descriptor of nvme device + * @nsid: + * @slba: Starting logical block + * @nblocks: Number of logical blocks to send (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @dsm: Data set management attributes, see &enum nvme_io_dsm_flags + * @reftag: This field specifies the Initial Logical Block Reference Tag + * expected value. Used only if the namespace is formatted to use + * end-to-end protection information. + * @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. + * @data_len: Length of user buffer, @data, in bytes + * @data: Pointer to user address of the data buffer + * metadata_len:Length of user buffer, @metadata, in bytes + * @metadata: Pointer to user address of the metadata buffer + * + * Calls nvme_io() with nvme_cmd_read for the opcode. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_read(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u8 dsm, __u32 reftag, __u16 apptag, __u16 appmask, + __u32 data_len, void *data, __u32 metadata_len, void *metadata); + +/** + * nvme_write() - Submit an nvme user write command + * @fd: File descriptor of nvme device + * @nsid: + * @slba: Starting logical block + * @nblocks: Number of logical blocks to send (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @dsm: Data set management attributes, see &enum nvme_io_dsm_flags + * @dspec: Directive specific command, eg: stream identifier + * @reftag: This field specifies the Initial Logical Block Reference Tag + * expected value. Used only if the namespace is formatted to use + * end-to-end protection information. + * @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. + * @data_len: Length of user buffer, @data, in bytes + * @data: Pointer to user address of the data buffer + * metadata_len:Length of user buffer, @metadata, in bytes + * @metadata: Pointer to user address of the metadata buffer + * + * Calls nvme_io() with nvme_cmd_write for the opcode. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_write(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u8 dsm, __u16 dspec, __u32 reftag, __u16 apptag, + __u16 appmask, __u32 data_len, void *data, __u32 metadata_len, + void *metadata); + +/** + * nvme_compare() - Submit an nvme user compare command + * @fd: File descriptor of nvme device + * @nsid: + * @slba: Starting logical block + * @nblocks: Number of logical blocks to send (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @reftag: This field specifies the Initial Logical Block Reference Tag + * expected value. Used only if the namespace is formatted to use + * end-to-end protection information. + * @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. + * @data_len: Length of user buffer, @data, in bytes + * @data: Pointer to user address of the data buffer + * metadata_len:Length of user buffer, @metadata, in bytes + * @metadata: Pointer to user address of the metadata buffer + * + * Calls nvme_io() with nvme_cmd_compare for the opcode. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_compare(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask, __u32 data_len, + void *data, __u32 metadata_len, void *metadata); + +/** + * nvme_write_zeros() - Submit an nvme write zeroes command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @slba: Starting logical block + * @nlb: Number of logical blocks to clear (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @reftag: This field specifies the Initial Logical Block Reference Tag + * expected value. Used only if the namespace is formatted to use + * end-to-end protection information. + * @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. + * + * The Write Zeroes command is used to set 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 or -1 with errno + * set otherwise. + */ +int nvme_write_zeros(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask); + +/** + * nvme_write_uncorrectable() - Submit an nvme write uncorrectable command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @slba: Starting logical block + * @nlb: Number of logical blocks to invalidate (0's based value) + * + * The Write Uncorrectable command is used to mark 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 or -1 with errno + * set otherwise. + */ +int nvme_write_uncorrectable(int fd, __u32 nsid, __u64 slba, __u16 nlb); + +/** + * nvme_verify() - Send an nvme verify command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @slba: Starting logical block + * @nlb: Number of logical blocks to verify (0's based value) + * @control: Command control flags, see &enum nvme_io_control_flags. + * @reftag: This field specifies the Initial Logical Block Reference Tag + * expected value. Used only if the namespace is formatted to use + * end-to-end protection information. + * @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. + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_verify(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask); + +/** + * enum nvme_dsm_attributes - + * @NVME_DSMGMT_IDR: + * @NVME_DSMGMT_IDW: + * @NVME_DSMGMT_AD: + */ +enum nvme_dsm_attributes { + NVME_DSMGMT_IDR = 1 << 0, + NVME_DSMGMT_IDW = 1 << 1, + NVME_DSMGMT_AD = 1 << 2, +}; + +/** + * nvme_dsm() - Send an nvme data set management command + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @attrs: DSM attributes, see &enum nvme_dsm_attributes + * &nr_ranges: Number of block ranges in the data set management attributes + * @dsm: The data set management attributes + * + * 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 or -1 with errno + * set otherwise. + */ +int nvme_dsm(int fd, __u32 nsid, __u32 attrs, __u16 nr_ranges, + struct nvme_dsm_range *dsm); + +/** + * enum nvme_reservation_rtype - + * @NVME_RESERVATION_RTYPE_WE: + * @NVME_RESERVATION_RTYPE_EA: + * @NVME_RESERVATION_RTYPE_WERO: + * @NVME_RESERVATION_RTYPE_EARO: + * @NVME_RESERVATION_RTYPE_WEAR: + * @NVME_RESERVATION_RTYPE_EAAR: + */ +enum nvme_reservation_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_reservation_racqa - + * @NVME_RESERVATION_RACQA_ACQUIRE: + * @NVME_RESERVATION_RACQA_PREEMPT: + * @NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT: + */ +enum nvme_reservation_racqa { + NVME_RESERVATION_RACQA_ACQUIRE = 0, + NVME_RESERVATION_RACQA_PREEMPT = 1, + NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT = 2, +}; + +/** + * nvme_resv_acquire() - Send an nvme reservation acquire + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @rtype: The type of reservation to be create, see &enum nvme_reservation_rtype + * @racqa: The action that is performed by the command, see &enum nvme_reservation_racqa + * @iekey: Set to ignore the existing key + * @crkey: The current reservation key associated with the host + * @nrkey: The reservation key to be unregistered from the namespace if + * the action is preempt + * + * The Reservation Acquire command is used to acquire 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 or -1 with errno + * set otherwise. + */ +int nvme_resv_acquire(int fd, __u32 nsid, enum nvme_reservation_rtype rtype, + enum nvme_reservation_racqa racqa, bool iekey, + __u64 crkey, __u64 nrkey); + +/** + * enum nvme_reservation_rrega - + * @NVME_RESERVATION_RREGA_REGISTER_KEY: + * @NVME_RESERVATION_RREGA_UNREGISTER_KEY: + * @NVME_RESERVATION_RREGA_REPLACE_KEY: + */ +enum nvme_reservation_rrega { + NVME_RESERVATION_RREGA_REGISTER_KEY = 0, + NVME_RESERVATION_RREGA_UNREGISTER_KEY = 1, + NVME_RESERVATION_RREGA_REPLACE_KEY = 2, +}; + +/** + * enum nvme_reservation_cptpl - + * @NVME_RESERVATION_CPTPL_NO_CHANGE: + * @NVME_RESERVATION_CPTPL_CLEAR: + * @NVME_RESERVATION_CPTPL_PERSIST: + */ +enum nvme_reservation_cptpl { + NVME_RESERVATION_CPTPL_NO_CHANGE = 0, + NVME_RESERVATION_CPTPL_CLEAR = 2, + NVME_RESERVATION_CPTPL_PERSIST = 3, +}; + +/** + * nvme_resv_register() - Send an nvme reservation register + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @rrega: The registration action, see &enum nvme_reservation_rrega + * @cptpl: Change persist through power loss, see &enum nvme_reservation_cptpl + * @iekey: Set to ignore the existing key + * @crkey: The current reservation key associated with the host + * @nrkey: The new reservation key to be register if action is register or + * replace + * + * The Reservation Register command is used to register, unregister, or replace + * a reservation key. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_resv_register(int fd, __u32 nsid, enum nvme_reservation_rrega rrega, + enum nvme_reservation_cptpl cptpl, bool iekey, + __u64 crkey, __u64 nrkey); + +/** + * enum nvme_reservation_rrela - + * @NVME_RESERVATION_RRELA_RELEASE: + * @NVME_RESERVATION_RRELA_CLEAR: + */ +enum nvme_reservation_rrela { + NVME_RESERVATION_RRELA_RELEASE = 0, + NVME_RESERVATION_RRELA_CLEAR = 1 +}; + +/** + * nvme_resv_release() - Send an nvme reservation release + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @rtype: The type of reservation to be create, see &enum nvme_reservation_rtype + * @rrela: Reservation releast action, see &enum nvme_reservation_rrela + * @iekey: Set to ignore the existing key + * @crkey: The current reservation key to release + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_resv_release(int fd, __u32 nsid, enum nvme_reservation_rtype rtype, + enum nvme_reservation_rrela rrela, bool iekey, + __u64 crkey); + +/** + * nvme_resv_report() - Send an nvme reservation report + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @eds: Request extended Data Structure + * @len: Number of bytes to request transfered with this command + * @report: The user space destination address to store the reservation report + * + * Returns a Reservation Status data structure to memory that describes the + * registration and reservation status of a namespace. See the defintion for + * the returned structure, &struct nvme_reservation_status, for more details. + * + * Return: The nvme command status if a response was received or -1 with errno + * set otherwise. + */ +int nvme_resv_report(int fd, __u32 nsid, bool eds, __u32 len, + struct nvme_reservation_status *report); + +#endif /* _LIBNVME_CMD_H */ diff --git a/lib/nvme/fabrics.c b/lib/nvme/fabrics.c new file mode 100644 index 00000000..c1119df0 --- /dev/null +++ b/lib/nvme/fabrics.c @@ -0,0 +1,363 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "fabrics.h" +#include "types.h" +#include "cmd.h" +#include "util.h" + +#define NVMF_HOSTID_SIZE 36 +#define NVME_HOSTNQN_ID SD_ID128_MAKE(c7,f4,61,81,12,be,49,32,8c,83,10,6f,9d,dd,d8,6b) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof (*x)) + +const char *nvmf_dev = "/dev/nvme-fabrics"; +const char *nvmf_hostnqn_file = "/etc/nvme/hostnqn"; +const char *nvmf_hostid_file = "/etc/nvme/hostid"; + +static int add_bool_argument(char **argstr, char *tok, bool arg) +{ + char *nstr; + + if (arg) { + 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 && !allow_zero) || (arg != -1 && allow_zero)) { + 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 && strcmp(arg, "none")) { + if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) { + errno = ENOMEM; + return -1; + } + free(*argstr); + *argstr = nstr; + } + return 0; +} + +static int build_options(char **argstr, struct nvme_fabrics_config *cfg) +{ + /* always specify nqn as first arg - this will init the string */ + if (asprintf(argstr, "nqn=%s", cfg->nqn) < 0) { + errno = ENOMEM; + return -1; + } + + + if (add_argument(argstr, "transport", cfg->transport) || + add_argument(argstr, "traddr", cfg->traddr) || + add_argument(argstr, "host_traddr", cfg->host_traddr) || + add_argument(argstr, "trsvcid", cfg->trsvcid) || + add_argument(argstr, "hostnqn", cfg->hostnqn) || + add_argument(argstr, "hostid", cfg->hostid) || + add_int_argument(argstr, "nr_write_queues", cfg->nr_write_queues, false) || + add_int_argument(argstr, "nr_poll_queues", cfg->nr_poll_queues, false) || + add_int_argument(argstr, "reconnect_delay", cfg->reconnect_delay, false) || + add_int_argument(argstr, "ctrl_loss_tmo", cfg->ctrl_loss_tmo, false) || + add_int_argument(argstr, "tos", cfg->tos, true) || + add_bool_argument(argstr, "duplicate_connect", cfg->duplicate_connect) || + add_bool_argument(argstr, "disable_sqflow", cfg->disable_sqflow) || + add_bool_argument(argstr, "hdr_digest", cfg->hdr_digest) || + add_bool_argument(argstr, "data_digest", cfg->data_digest) || + add_int_argument(argstr, "queue_size", cfg->queue_size, false) || + add_int_argument(argstr, "keep_alive_tmo", cfg->keep_alive_tmo, false) || + add_int_argument(argstr, "nr_io_queues", cfg->nr_io_queues, false)) { + free(*argstr); + return -1; + } + + return 0; +} +static int __nvmf_add_ctrl(const char *argstr) +{ + int ret, fd, len = strlen(argstr); + char buf[0x1000], *options, *p; + + fd = open(nvmf_dev, O_RDWR); + if (fd < 0) + return -1; + + ret = write(fd, argstr, len); + if (ret != len) { + ret = -1; + goto out_close; + } + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + ret = -1; + goto out_close; + } + + buf[len] = '\0'; + options = buf; + while ((p = strsep(&options, ",\n")) != NULL) { + if (!*p) + continue; + if (sscanf(p, "instance=%d", &ret) == 1) + goto out_close; + } + + errno = EINVAL; + ret = -1; +out_close: + close(fd); + return ret; +} + +int nvmf_add_ctrl_opts(struct nvme_fabrics_config *cfg) +{ + char *argstr; + int ret; + + ret = build_options(&argstr, cfg); + if (ret) + return ret; + + ret = __nvmf_add_ctrl(argstr); + printf("ctrl:%s ret:%d\n", argstr, ret); + + free(argstr); + return ret; +} + +nvme_ctrl_t nvmf_add_ctrl(struct nvme_fabrics_config *cfg) +{ + char d[32]; + int ret; + + ret = nvmf_add_ctrl_opts(cfg); + if (ret < 0) + return NULL; + + memset(d, 0, sizeof(d)); + if (snprintf(d, sizeof(d), "nvme%d", ret) < 0) + return NULL; + + return nvme_scan_ctrl(d); +} + +static void chomp(char *s, int l) +{ + while (l && (s[l] == '\0' || s[l] == ' ')) + s[l--] = '\0'; +} + +nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, + const struct nvme_fabrics_config *defcfg, bool *discover) +{ + struct nvme_fabrics_config cfg = { 0 }; + nvme_ctrl_t c; + + memcpy(&cfg, defcfg, sizeof(cfg)); + switch (e->subtype) { + case NVME_NQN_DISC: + if (discover) + *discover = true; + break; + case NVME_NQN_NVME: + break; + default: + errno = EINVAL; + return NULL; + } + + switch (e->trtype) { + case NVMF_TRTYPE_RDMA: + case NVMF_TRTYPE_TCP: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_IP4: + case NVMF_ADDR_FAMILY_IP6: + chomp(e->traddr, NVMF_TRADDR_SIZE); + chomp(e->trsvcid, NVMF_TRSVCID_SIZE); + cfg.traddr = e->traddr; + cfg.trsvcid = e->trsvcid; + break; + default: + errno = EINVAL; + return NULL; + } + break; + case NVMF_TRTYPE_FC: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_FC: + chomp(e->traddr, NVMF_TRADDR_SIZE), + cfg.traddr = e->traddr; + cfg.trsvcid = NULL; + break; + } + default: + errno = EINVAL; + return NULL; + } + cfg.transport = nvmf_trtype_str(e->trtype); + + cfg.nqn = e->subnqn; + if (e->treq & NVMF_TREQ_DISABLE_SQFLOW) + cfg.disable_sqflow = true; + + c = nvmf_add_ctrl(&cfg); + if (!c && errno == EINVAL && cfg.disable_sqflow) { + errno = 0; + /* disable_sqflow is unrecognized option on older kernels */ + cfg.disable_sqflow = false; + c = nvmf_add_ctrl(&cfg); + } + + return c; +} + +static int nvme_discovery_log(int fd, __u32 len, struct nvmf_discovery_log *log) +{ + return nvme_get_log_page(fd, 0, NVME_LOG_LID_DISC, true, len, log); +} + +int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, + int max_retries) +{ + struct nvmf_discovery_log *log; + int hdr, ret, retries = 0; + uint64_t genctr, numrec; + unsigned int size; + + hdr = sizeof(struct nvmf_discovery_log); + log = malloc(hdr); + if (!log) { + errno = ENOMEM; + return -1; + } + memset(log, 0, hdr); + + ret = nvme_discovery_log(nvme_ctrl_get_fd(c), 0x100, log); + if (ret) + goto out_free_log; + + do { + numrec = le64_to_cpu(log->numrec); + genctr = le64_to_cpu(log->genctr); + + free(log); + if (numrec == 0) { + *logp = log; + return 0; + } + + size = sizeof(struct nvmf_discovery_log) + + sizeof(struct nvmf_disc_log_entry) * (numrec); + + log = malloc(size); + if (!log) { + errno = ENOMEM; + return -1; + } + memset(log, 0, size); + + ret = nvme_discovery_log(nvme_ctrl_get_fd(c), size, log); + if (ret) + goto out_free_log; + + genctr = le64_to_cpu(log->genctr); + ret = nvme_discovery_log(nvme_ctrl_get_fd(c), hdr, log); + if (ret) + goto out_free_log; + } while (genctr != le64_to_cpu(log->genctr) && + ++retries < max_retries); + + if (genctr != le64_to_cpu(log->genctr)) { + errno = EAGAIN; + ret = -1; + } else if (numrec != le64_to_cpu(log->numrec)) { + errno = EBADSLT; + ret = -1; + } else { + *logp = log; + return 0; + } + +out_free_log: + free(log); + return ret; +} + +char *nvmf_hostnqn_generate() +{ + sd_id128_t id; + char *ret = NULL; + + if (sd_id128_get_machine_app_specific(NVME_HOSTNQN_ID, &id) < 0) + return NULL; + + if (asprintf(&ret, + "nqn.2014-08.org.nvmexpress:uuid:" SD_ID128_FORMAT_STR "\n", + SD_ID128_FORMAT_VAL(id)) < 0) + ret = NULL; + + return ret; +} + +static char *nvmf_read_file(const char *f, int len) +{ + char buf[len]; + int ret, fd; + + fd = open(f, O_RDONLY); + if (fd < 0) + return false; + + memset(buf, 0, len); + ret = read(fd, buf, sizeof(buf - 1)); + close (fd); + + if (ret < 0) + return NULL; + return strndup(buf, strcspn(buf, "\n")); +} + +char *nvmf_hostnqn_from_file() +{ + return nvmf_read_file(nvmf_hostnqn_file, NVMF_NQN_SIZE); +} + +char *nvmf_hostid_from_file() +{ + return nvmf_read_file(nvmf_hostid_file, NVMF_HOSTID_SIZE); +} diff --git a/lib/nvme/fabrics.h b/lib/nvme/fabrics.h new file mode 100644 index 00000000..0f5e82ec --- /dev/null +++ b/lib/nvme/fabrics.h @@ -0,0 +1,54 @@ +#ifndef _LIBNVME_FABRICS_H +#define _LIBNVME_FABRICS_H + +#include +#include + +#include "tree.h" + +struct nvme_fabrics_config { + const char *transport; + const char *traddr; + const char *trsvcid; + const char *nqn; + const char *hostnqn; + const char *host_traddr; + const char *hostid; + + int queue_size; + int nr_io_queues; + int reconnect_delay; + int ctrl_loss_tmo; + int keep_alive_tmo; + int nr_write_queues; + int nr_poll_queues; + int tos; + + bool duplicate_connect; + bool disable_sqflow; + bool hdr_digest; + bool data_digest; + + uint8_t rsvd[0x200]; +}; + +int nvmf_add_ctrl_opts(struct nvme_fabrics_config *cfg); +nvme_ctrl_t nvmf_add_ctrl(struct nvme_fabrics_config *cfg); +int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, int max_retries); +char *nvmf_hostnqn_generate(); +char *nvmf_hostnqn_from_file(); +char *nvmf_hostid_from_file(); + + +const char *nvmf_trtype_str(__u8 trtype); +const char *nvmf_adrfam_str(__u8 adrfam); +const char *nvmf_subtype_str(__u8 subtype); +const char *nvmf_treq_str(__u8 treq); +const char *nvmf_sectype_str(__u8 sectype); +const char *nvmf_prtype_str(__u8 prtype); +const char *nvmf_qptype_str(__u8 qptype); +const char *nvmf_cms_str(__u8 cm); + +nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, + const struct nvme_fabrics_config *defcfg, bool *discover); +#endif /* _LIBNVME_FABRICS_H */ diff --git a/lib/nvme/filters.c b/lib/nvme/filters.c new file mode 100644 index 00000000..ed0d402f --- /dev/null +++ b/lib/nvme/filters.c @@ -0,0 +1,107 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "filters.h" +#include "types.h" +#include "util.h" + +const char *nvme_ctrl_sysfs_dir = "/sys/class/nvme"; +const char *nvme_subsys_sysfs_dir = "/sys/class/nvme-subsystem"; + +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) +{ + return scandir(nvme_subsys_sysfs_dir, subsys, nvme_subsys_filter, alphasort); +} + +int nvme_scan_subsystem_ctrls(nvme_subsystem_t s, struct dirent ***ctrls) +{ + return scandir(nvme_subsystem_get_sysfs_dir(s), ctrls, nvme_ctrls_filter, alphasort); +} + +int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***namespaces) +{ + return scandir(nvme_subsystem_get_sysfs_dir(s), namespaces, nvme_namespace_filter, alphasort); +} + +int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***namespaces) +{ + return scandir(nvme_ctrl_get_sysfs_dir(c), namespaces, nvme_paths_filter, alphasort); +} + +int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***namespaces) +{ + return scandir(nvme_ctrl_get_sysfs_dir(c), namespaces, nvme_namespace_filter, alphasort); +} diff --git a/lib/nvme/filters.h b/lib/nvme/filters.h new file mode 100644 index 00000000..9021c1bc --- /dev/null +++ b/lib/nvme/filters.h @@ -0,0 +1,19 @@ +#ifndef _LIBNVME_FILTERS_H +#define _LIBNVME_FILTERS_H + +#include +#include "tree.h" + + +int nvme_namespace_filter(const struct dirent *d); +int nvme_paths_filter(const struct dirent *d); +int nvme_ctrls_filter(const struct dirent *d); +int nvme_subsys_filter(const struct dirent *d); + +int nvme_scan_subsystems(struct dirent ***subsys); +int nvme_scan_subsystem_ctrls(nvme_subsystem_t s, struct dirent ***ctrls); +int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***namespaces); +int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***namespaces); +int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***namespaces); + +#endif /* _LIBNVME_FILTERS_H */ diff --git a/lib/nvme/ioctl.c b/lib/nvme/ioctl.c new file mode 100644 index 00000000..ca59e096 --- /dev/null +++ b/lib/nvme/ioctl.c @@ -0,0 +1,1654 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ioctl.h" +#include "cmd.h" +#include "types.h" + +static int nvme_verify_chr(int fd) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) { + perror("fstat"); + 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_reset_controller(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) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) + return -1; + + if (!S_ISBLK(nvme_stat.st_mode)) { + errno = ENOTBLK; + return -1; + } + return ioctl(fd, NVME_IOCTL_ID); +} + +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 int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd *cmd, __u32 *result) +{ + int err = ioctl(fd, ioctl_cmd, cmd); + + 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 = 0x7, + 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_CDW10_LID_MASK = 0xff, + NVME_LOG_CDW10_LSP_MASK = 0xf, + NVME_LOG_CDW10_RAE_MASK = 0x1, + NVME_LOG_CDW10_NUMDL_MASK = 0xff, + NVME_LOG_CDW11_NUMDU_MASK = 0xff, + NVME_LOG_CDW11_LSI_MASK = 0xff, + NVME_LOG_CDW14_UUID_MASK = 0x7f, + NVME_IDENTIFY_CDW10_CNS_SHIFT = 0, + NVME_IDENTIFY_CDW10_CNTID_SHIFT = 16, + NVME_IDENTIFY_CDW11_NVMSETID_SHIFT = 0, + NVME_IDENTIFY_CDW14_UUID_SHIFT = 0, + NVME_IDENTIFY_CDW10_CNS_MASK = 0xff, + NVME_IDENTIFY_CDW10_CNTID_MASK = 0xffff, + NVME_IDENTIFY_CDW11_NVMSETID_MASK = 0xffff, + NVME_IDENTIFY_CDW14_UUID_MASK = 0x7f, + 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_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 = 0, + NVME_VIRT_MGMT_CDW10_RT_MASK = 8, + NVME_VIRT_MGMT_CDW10_CNTLID_MASK = 16, + 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_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_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, +}; + +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, +}; + +#define DW(value, prefix) ((value) & (prefix ## _MASK)) << prefix ## _SHIFT + +int nvme_identify(int fd, enum nvme_identify_cns cns, __u32 nsid, __u16 cntid, + __u16 nvmsetid, __u8 uuidx, void *data) +{ + __u32 cdw10 = DW(cntid, NVME_IDENTIFY_CDW10_CNTID) | + DW(cns, NVME_IDENTIFY_CDW10_CNS); + __u32 cdw11 = DW(nvmsetid, NVME_IDENTIFY_CDW11_NVMSETID); + __u32 cdw14 = DW(uuidx, NVME_IDENTIFY_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_identify, + .nsid = nsid, + .addr = (__u64)(uintptr_t)data, + .data_len = NVME_IDENTIFY_DATA_SIZE, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw14 = cdw14, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +static int __nvme_identify(int fd, __u8 cns, __u32 nsid, void *data) +{ + return nvme_identify(fd, cns, nsid, NVME_CNTLID_NONE, + NVME_NVMSETID_NONE, NVME_UUID_NONE, data); +} + +int nvme_identify_ctrl(int fd, struct nvme_id_ctrl *id) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_CTRL, NVME_NSID_NONE, id); +} + +int nvme_identify_ns(int fd, __u32 nsid, struct nvme_id_ns *ns) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_NS, nsid, ns); +} + +int nvme_identify_allocated_ns(int fd, __u32 nsid, struct nvme_id_ns *ns) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS, nsid, ns); +} + +int nvme_identify_active_ns_list(int fd, __u32 nsid, struct nvme_ns_list *list) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, nsid, + list); +} + +int nvme_identify_allocated_ns_list(int fd, __u32 nsid, + struct nvme_ns_list *list) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, nsid, + list); +} + +int nvme_identify_ctrl_list(int fd, __u16 cntid, + struct nvme_ctrl_list *ctrlist) +{ + return nvme_identify(fd, NVME_IDENTIFY_CNS_CTRL_LIST, + NVME_NSID_NONE, cntid, NVME_NVMSETID_NONE, + NVME_UUID_NONE, ctrlist); +} + +int nvme_identify_nsid_ctrl_list(int fd, __u32 nsid, __u16 cntid, + struct nvme_ctrl_list *ctrlist) +{ + return nvme_identify(fd, NVME_IDENTIFY_CNS_NS_CTRL_LIST, nsid, + cntid, NVME_NVMSETID_NONE, NVME_UUID_NONE, ctrlist); +} + +int nvme_identify_ns_descs(int fd, __u32 nsid, struct nvme_ns_id_desc *descs) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_NS_DESC_LIST, nsid, descs); +} + +int nvme_identify_nvmset_list(int fd, __u16 nvmsetid, + struct nvme_id_nvmset_list *nvmset) +{ + return nvme_identify(fd, NVME_IDENTIFY_CNS_NVMSET_LIST, + NVME_NSID_NONE, NVME_CNTLID_NONE, nvmsetid, NVME_UUID_NONE, + nvmset); +} + +int nvme_identify_primary_ctrl(int fd, __u16 cntid, + struct nvme_primary_ctrl_cap *cap) +{ + return nvme_identify(fd, NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP, + NVME_NSID_NONE, cntid, NVME_NVMSETID_NONE, NVME_UUID_NONE, + cap); +} + +int nvme_identify_secondary_ctrl_list(int fd, __u16 cntid, + struct nvme_secondary_ctrl_list *list) +{ + return nvme_identify(fd, NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST, + NVME_NSID_NONE, cntid, NVME_NVMSETID_NONE, NVME_UUID_NONE, + list); +} + +int nvme_identify_ns_granularity(int fd, + struct nvme_id_ns_granularity_list *list) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_NS_GRANULARITY, + NVME_NSID_NONE, list); +} + +int nvme_identify_uuid(int fd, struct nvme_id_uuid_list *list) +{ + return __nvme_identify(fd, NVME_IDENTIFY_CNS_UUID_LIST, NVME_NSID_NONE, + list); +} + +int nvme_get_log(int fd, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u64 lpo, + __u8 lsp, __u16 lsi, bool rae, __u8 uuidx, __u32 len, void *log) +{ + __u32 numd = (len >> 2) - 1; + __u16 numdu = numd >> 16, numdl = numd & 0xffff; + + __u32 cdw10 = DW(lid, NVME_LOG_CDW10_LID) | + DW(lsp, NVME_LOG_CDW10_LSP) | + DW(!!rae, NVME_LOG_CDW10_RAE) | + DW(numdl, NVME_LOG_CDW10_NUMDL); + __u32 cdw11 = DW(numdu, NVME_LOG_CDW11_NUMDU) | + DW(lsi, NVME_LOG_CDW11_LSI); + __u32 cdw12 = lpo & 0xffffffff; + __u32 cdw13 = lpo >> 32; + __u32 cdw14 = DW(uuidx, NVME_LOG_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_log_page, + .nsid = nsid, + .addr = (__u64)(uintptr_t)log, + .data_len = len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +static int __nvme_get_log(int fd, enum nvme_cmd_get_log_lid lid, bool rae, + __u32 len, void *log) +{ + return nvme_get_log(fd, lid, NVME_NSID_ALL, 0, NVME_LOG_LSP_NONE, + NVME_LOG_LSI_NONE, NVME_UUID_NONE, rae, len, log); +} + +int nvme_get_log_error(int fd, unsigned nr_entries, bool rae, + struct nvme_error_log_page *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_ERROR, rae, + sizeof(*log) * nr_entries, log); +} + +int nvme_get_log_smart(int fd, __u32 nsid, bool rae, struct nvme_smart_log *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_SMART, nsid, 0, + NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, rae, NVME_UUID_NONE, + sizeof(*log), log); +} + +int nvme_get_log_fw_slot(int fd, bool rae, struct nvme_firmware_slot *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_FW_SLOT, rae, sizeof(*log), + log); +} + +int nvme_get_log_changed_ns_list(int fd, bool rae, struct nvme_ns_list *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_CHANGED_NS, rae, + sizeof(*log), log); +} + +int nvme_get_log_cmd_effects(int fd, struct nvme_cmd_effects_log *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_CMD_EFFECTS, false, + sizeof(*log), log); +} + +int nvme_get_log_device_self_test(int fd, struct nvme_self_test_log *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_DEVICE_SELF_TEST, false, + sizeof(*log), log); +} + +enum nvme_cmd_get_log_telemetry_host_lsp { + NVME_LOG_TELEM_HOST_LSP_RETAIN = 0, + NVME_LOG_TELEM_HOST_LSP_CREATE = 1, +}; + +int nvme_get_log_create_telemetry_host(int fd, struct nvme_telemetry_log *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_TELEMETRY_HOST, NVME_NSID_NONE, 0, + NVME_LOG_TELEM_HOST_LSP_CREATE, NVME_LOG_LSI_NONE, false, + NVME_UUID_NONE, sizeof(*log), log); +} + +int nvme_get_log_telemetry_host(int fd, __u64 offset, __u32 len, void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_TELEMETRY_HOST, NVME_NSID_NONE, + offset, NVME_LOG_TELEM_HOST_LSP_RETAIN, NVME_LOG_LSI_NONE, + false, NVME_UUID_NONE, len, log); +} + +int nvme_get_log_telemetry_ctrl(int fd, bool rae, __u64 offset, __u32 len, + void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_TELEMETRY_CTRL, NVME_NSID_NONE, + offset, NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, rae, + NVME_UUID_NONE, len, log); +} + +int nvme_get_log_endurance_group(int fd, __u16 endgid, + struct nvme_endurance_group_log *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_ENDURANCE_GROUP, NVME_NSID_NONE, + 0, NVME_LOG_LSP_NONE, endgid, false, NVME_UUID_NONE, + sizeof(*log), log); +} + +int nvme_get_log_predictable_lat_nvmset(int fd, __u16 nvmsetid, + struct nvme_nvmset_predictable_lat_log *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_PREDICTABLE_LAT_NVMSET, + NVME_NSID_NONE, 0, NVME_LOG_LSP_NONE, nvmsetid, false, + NVME_UUID_NONE, sizeof(*log), log); +} + +int nvme_get_log_predictable_lat_event(int fd, bool rae, __u32 offset, + __u32 len, void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_PREDICTABLE_LAT_AGG, + NVME_NSID_NONE, offset, NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, + rae, NVME_UUID_NONE, len, log); +} + +int nvme_get_log_ana(int fd, enum nvme_log_ana_lsp lsp, bool rae, __u64 offset, + __u32 len, void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_ANA, NVME_NSID_NONE, offset, lsp, + NVME_LOG_LSI_NONE, false, NVME_UUID_NONE, len, log); +} + +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); +} + +int nvme_get_log_lba_status(int fd, bool rae, __u64 offset, __u32 len, + void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_LBA_STATUS, NVME_NSID_NONE, + offset, NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, rae, + NVME_UUID_NONE, len, log); +} + +int nvme_get_log_endurance_grp_evt(int fd, bool rae, __u32 offset, __u32 len, + void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_ENDURANCE_GRP_EVT, + NVME_NSID_NONE, offset, NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, + rae, NVME_UUID_NONE, len, log); +} + +int nvme_get_log_discovery(int fd, bool rae, __u32 offset, __u32 len, void *log) +{ + return nvme_get_log(fd, NVME_LOG_LID_DISC, NVME_NSID_NONE, offset, + NVME_LOG_LSP_NONE, NVME_LOG_LSI_NONE, rae, NVME_UUID_NONE, + len, log); +} + +int nvme_get_log_reservation(int fd, bool rae, + struct nvme_resv_notification_log *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_RESERVATION, rae, + sizeof(*log), log); +} + +int nvme_get_log_sanitize(int fd, bool rae, + struct nvme_sanitize_log_page *log) +{ + return __nvme_get_log(fd, NVME_LOG_LID_SANITIZE, rae, sizeof(*log), + log); +} + +int nvme_set_features(int fd, __u8 fid, __u32 nsid, __u32 cdw11, __u32 cdw12, + bool save, __u8 uuidx, __u32 cdw15, __u32 data_len, + void *data, __u32 *result) +{ + __u32 cdw10 = DW(fid, NVME_FEATURES_CDW10_FID) | + DW(!!save, NVME_SET_FEATURES_CDW10_SAVE); + __u32 cdw14 = DW(uuidx, NVME_FEATURES_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_set_features, + .nsid = nsid, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw14 = cdw14, + .cdw14 = cdw15, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +static int __nvme_set_features(int fd, __u8 fid, __u32 cdw11, bool save, + __u32 *result) +{ + return nvme_set_features(fd, fid, NVME_NSID_NONE, cdw11, 0, save, + NVME_UUID_NONE, 0, 0, NULL, result); +} + +int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw, + __u8 hpw, bool save, __u32 *result) +{ + __u32 value = DW(ab, NVME_FEATURES_ARBITRATION_BURST) | + DW(lpw, NVME_FEATURES_ARBITRATION_LPW) | + DW(mpw, NVME_FEATURES_ARBITRATION_MPW) | + DW(hpw, NVME_FEATURES_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 = DW(ps, NVME_FEATURES_PWRMGMT_PS) | + DW(wh, NVME_FEATURES_PWRMGMT_PS); + + return __nvme_set_features(fd, NVME_FEAT_FID_POWER_MGMT, value, save, + result); +} + +int nvme_set_features_lba_range(int fd, __u32 nsid, __u32 nr_ranges, bool save, + struct nvme_lba_range_type *data, __u32 *result) +{ + return -1; +} + +int nvme_set_features_temp_thresh(int fd, __u16 tmpth, __u8 tmpsel, + enum nvme_feat_tmpthresh_thsel thsel, bool save, __u32 *result) +{ + __u32 value = DW(tmpth, NVME_FEATURES_TMPTH) | + DW(tmpsel, NVME_FEATURES_TMPSEL) | + DW(thsel, NVME_FEATURES_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 = DW(tler, NVME_FEATURES_ERROR_RECOVERY_TLER) | + DW(!!dulbe, NVME_FEATURES_ERROR_RECOVERY_DULBE); + + return __nvme_set_features(fd, NVME_FEAT_FID_ERR_RECOVERY, value, save, + result); +} + +int nvme_set_features_volatile_wc(int fd, bool wce, bool save, __u32 *result) +{ + __u32 value = DW(!!wce, NVME_FEATURES_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 = DW(thr, NVME_FEATURES_IRQC_TIME) | + DW(time, NVME_FEATURES_IRQC_THR); + + 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 = DW(iv, NVME_FEATURES_IVC_IV) | + DW(!!cd, NVME_FEATURES_IVC_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 = DW(!!dn, NVME_FEATURES_WAN_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) +{ + __u32 value = DW(!!apste, NVME_FEATURES_APST_APSTE); + + return __nvme_set_features(fd, NVME_FEAT_FID_AUTO_PST, value, save, + result); +} + +int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp) +{ + __le64 t = cpu_to_le64(timestamp); + struct nvme_timestamp ts; + + memcpy(&t, ts.timestamp, sizeof(ts.timestamp)); + return nvme_set_features(fd, NVME_FEAT_FID_TIMESTAMP, + NVME_NSID_NONE, 0, 0, save, NVME_UUID_NONE, 0, + sizeof(ts), &ts, NULL); +} + +int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, + bool save, __u32 *result) +{ + __u32 value = DW(tmt2, NVME_FEATURES_HCTM_TMT2) | + DW(tmt1, NVME_FEATURES_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 = DW(noppme, NVME_FEATURES_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) +{ + return nvme_set_features(fd, NVME_FEAT_FID_RRL, NVME_NSID_NONE, + nvmsetid, rrl, save, NVME_UUID_NONE, 0, 0, NULL, result); +} + +int nvme_set_features_plm_config(int fd, bool plm, __u16 nvmsetid, bool save, + struct nvme_plm_config *data, __u32 *result) +{ + return nvme_set_features(fd, NVME_FEAT_FID_PLM_CONFIG, + NVME_NSID_NONE, nvmsetid, !!plm, save, NVME_UUID_NONE, 0, 0, + NULL, result); +} + +int nvme_set_features_plm_window(int fd, enum nvme_feat_plm_window_select sel, + __u16 nvmsetid, bool save, __u32 *result) +{ + __u32 cdw12 = DW(sel, NVME_FEATURES_PLM_WINDOW_SELECT); + + return nvme_set_features(fd, NVME_FEAT_FID_PLM_WINDOW, NVME_NSID_NONE, + nvmsetid, cdw12, save, NVME_UUID_NONE, 0, 0, NULL, result); +} + +int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi, + bool save, __u32 *result) +{ + __u32 value = DW(lsiri, NVME_FEATURES_LBAS_LSIRI) | + DW(lsipi, NVME_FEATURES_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) +{ + return nvme_set_features(fd, NVME_FEAT_FID_HOST_BEHAVIOR, + NVME_NSID_NONE, save, 0, 0, NVME_UUID_NONE, 0, sizeof(*data), + data, NULL); +} + +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 save, bool exhid, __u8 *hostid) +{ + __u32 len = exhid ? 16 : 8; + __u32 value = !!exhid; + + return nvme_set_features(fd, NVME_FEAT_FID_HOST_ID, NVME_NSID_NONE, + save, value, 0, NVME_UUID_NONE, 0, len, hostid, NULL); +} + +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_persist(int fd, bool ptpl, bool save, __u32 *result) +{ + return __nvme_set_features(fd, NVME_FEAT_RESV_PERSIST, !!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, + save, result); +} + +int nvme_get_features(int fd, enum nvme_features_id fid, __u32 nsid, + enum nvme_get_features_sel sel, __u32 cdw11, __u8 uuidx, + __u32 data_len, void *data, __u32 *result) +{ + __u32 cdw10 = DW(fid, NVME_FEATURES_CDW10_FID) | + DW(sel, NVME_GET_FEATURES_CDW10_SEL); + __u32 cdw14 = DW(uuidx, NVME_FEATURES_CDW14_UUID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_features, + .nsid = nsid, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw14 = cdw14, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +static int __nvme_get_features(int fd, enum nvme_features_id fid, + enum nvme_get_features_sel sel, __u32 *result) +{ + return nvme_get_features(fd, fid, NVME_NSID_NONE, sel, 0, + NVME_UUID_NONE, 0, NULL, result); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_LBA_RANGE, NVME_NSID_NONE, sel, 0, + NVME_UUID_NONE, 0, NULL, result); +} + +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_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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_IRQ_CONFIG, NVME_NSID_NONE, sel, iv, + NVME_UUID_NONE, 0, NULL, result); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_AUTO_PST, NVME_NSID_NONE, sel, 0, + NVME_UUID_NONE, 0, NULL, result); +} + +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_timestamp(int fd, + enum nvme_get_features_sel sel, struct nvme_timestamp *ts) +{ + return nvme_get_features(fd, NVME_FEAT_FID_TIMESTAMP, NVME_NSID_NONE, sel, 0, + NVME_UUID_NONE, 0, NULL, NULL); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_PLM_CONFIG, NVME_NSID_NONE, + sel, nvmsetid, NVME_UUID_NONE, 0, NULL, result); +} + +int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel, + __u16 nvmsetid, __u32 *result) +{ + return nvme_get_features(fd, NVME_FEAT_FID_PLM_WINDOW, NVME_NSID_NONE, + sel, nvmsetid, NVME_UUID_NONE, 0, NULL, result); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_HOST_BEHAVIOR, + NVME_NSID_NONE, sel, 0, NVME_UUID_NONE, 0, NULL, result); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_ENDURANCE_EVT_CFG, + NVME_NSID_NONE, sel, 0, NVME_UUID_NONE, 0, NULL, result); +} + +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) +{ + return nvme_get_features(fd, NVME_FEAT_FID_HOST_ID, NVME_NSID_NONE, + sel, !!exhid, NVME_UUID_NONE, len, hostid, NULL); +} + +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_persist(int fd, + enum nvme_get_features_sel sel, __u32 *result) +{ + return __nvme_get_features(fd, NVME_FEAT_RESV_PERSIST, sel, result); +} + +int nvme_get_features_write_protect(int fd, __u32 nsid, + enum nvme_get_features_sel sel, __u32 *result) +{ + return nvme_get_features(fd, NVME_FEAT_FID_WRITE_PROTECT, nsid, sel, 0, + NVME_UUID_NONE, 0, NULL, result); +} + +int nvme_format_nvm(int fd, __u32 nsid, __u8 lbaf, + enum nvme_cmd_format_mset mset, enum nvme_cmd_format_pi pi, + enum nvme_cmd_format_pil pil, enum nvme_cmd_format_ses ses, + __u32 timeout) +{ + __u32 cdw10 = DW(lbaf, NVME_FORMAT_CDW10_LBAF) | + DW(mset, NVME_FORMAT_CDW10_MSET) | + DW(pi, NVME_FORMAT_CDW10_PI) | + DW(pil, NVME_FORMAT_CDW10_PIL) | + DW(ses, NVME_FORMAT_CDW10_SES); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_format_nvm, + .nsid = nsid, + .cdw10 = cdw10, + .timeout_ms = timeout, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_ns_mgmt(int fd, __u32 nsid, enum nvme_ns_mgmt_sel sel, + struct nvme_id_ns *ns, __u32 *result, __u32 timeout) +{ + __u32 cdw10 = DW(sel, NVME_NAMESPACE_MGMT_CDW10_SEL); + __u32 data_len = ns ? sizeof(*ns) : 0; + + struct nvme_passthru_cmd cmd = { + .nsid = nsid, + .opcode = nvme_admin_ns_mgmt, + .cdw10 = cdw10, + .timeout_ms = timeout, + .data_len = data_len, + .addr = (__u64)(uintptr_t)ns, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns, __u32 *nsid, + __u32 timeout) +{ + return nvme_ns_mgmt(fd, NVME_NSID_NONE, NVME_NS_MGMT_SEL_CREATE, ns, nsid, + timeout); +} + +int nvme_ns_mgmt_delete(int fd, __u32 nsid) +{ + return nvme_ns_mgmt(fd, nsid, NVME_NS_MGMT_SEL_DELETE, NULL, NULL, 0); +} + +int nvme_ns_attach(int fd, __u32 nsid, enum nvme_ns_attach_sel sel, + struct nvme_ctrl_list *ctrlist) +{ + __u32 cdw10 = DW(sel, NVME_NAMESPACE_ATTACH_CDW10_SEL); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_ns_attach, + .nsid = nsid, + .cdw10 = cdw10, + .data_len = sizeof(*ctrlist), + .addr = (__u64)(uintptr_t)ctrlist, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_ns_attach_ctrls(int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist) +{ + return nvme_ns_attach(fd, nsid, NVME_NS_ATTACH_SEL_CTRL_ATTACH, ctrlist); +} + +int nvme_ns_dettach_ctrls(int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist) +{ + return nvme_ns_attach(fd, nsid, NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + ctrlist); +} + +int nvme_fw_download(int fd, __u32 offset, __u32 data_len, void *data) +{ + __u32 cdw10 = (data_len >> 2) - 1; + __u32 cdw11 = offset >> 2; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_download, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = data_len, + .addr = (__u64)(uintptr_t)data, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_fw_commit(int fd, __u8 slot, enum nvme_fw_commit_ca action, bool bpid) +{ + __u32 cdw10 = DW(slot, NVME_FW_COMMIT_CDW10_FS) | + DW(action, NVME_FW_COMMIT_CDW10_CA) | + DW(bpid, NVME_FW_COMMIT_CDW10_BPID); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_commit, + .cdw10 = cdw10, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_security_send(int fd, __u32 nsid, __u8 nssf, __u8 spsp0, __u8 spsp1, + __u8 secp, __u32 tl, __u32 data_len, void *data, + __u32 *result) +{ + __u32 cdw10 = DW(secp, NVME_SECURITY_SECP) | + DW(spsp0, NVME_SECURITY_SPSP0) | + DW(spsp1, NVME_SECURITY_SPSP1) | + DW(nssf, NVME_SECURITY_NSSF); + __u32 cdw11 = tl; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_security_send, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = data_len, + .addr = (__u64)(uintptr_t)data, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +int nvme_security_receive(int fd, __u32 nsid, __u8 nssf, __u8 spsp0, + __u8 spsp1, __u8 secp, __u32 al, __u32 data_len, + void *data, __u32 *result) +{ + __u32 cdw10 = DW(secp, NVME_SECURITY_SECP) | + DW(spsp0, NVME_SECURITY_SPSP0) | + DW(spsp1, NVME_SECURITY_SPSP1) | + DW(nssf, NVME_SECURITY_NSSF); + __u32 cdw11 = al; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_security_recv, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .data_len = data_len, + .addr = (__u64)(uintptr_t)data, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +int nvme_get_lba_status(int fd, __u32 nsid, __u64 slba, __u32 mndw, __u16 rl, + enum nvme_lba_status_atype atype, + struct nvme_lba_status *lbas) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw12 = mndw; + __u32 cdw13 = DW(rl, NVME_GET_LBA_STATUS_CDW13_RL) | + DW(atype, NVME_GET_LBA_STATUS_CDW13_ATYPE); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_get_lba_status, + .nsid = nsid, + .addr = (__u64)(uintptr_t)lbas, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_directive_send(int fd, __u32 nsid, __u16 dspec, __u8 doper, + enum nvme_directive_dtype dtype, __u32 cdw12, + __u32 data_len, void *data, __u32 *result) +{ + __u32 cdw10 = data_len ? (data_len >> 2) - 1 : 0; + __u32 cdw11 = DW(doper, NVME_DIRECTIVE_CDW11_DOPER) | + DW(dtype, NVME_DIRECTIVE_CDW11_DTYPE) | + DW(dspec, NVME_DIRECTIVE_CDW11_DPSEC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_directive_send, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .data_len = data_len, + .addr = (__u64)(uintptr_t)data, + }; + + return nvme_submit_admin_passthru(fd, &cmd, 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 = DW(dtype, NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE) | + DW(endir, NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR); + + return nvme_directive_send(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_IDENTIFY, + NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR, cdw12, sizeof(*id), + id, NULL); +} + +int nvme_directive_send_stream_release_identifier(int fd, __u32 nsid, + __u16 stream_id) +{ + return nvme_directive_send(fd, nsid, stream_id, + NVME_DIRECTIVE_DTYPE_STREAMS, + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER, 0, 0, + NULL, NULL); +} + +int nvme_directive_send_stream_release_resource(int fd, __u32 nsid) +{ + return nvme_directive_send(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_STREAMS, + NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE, 0, 0, NULL, + NULL); +} + +int nvme_directive_recv(int fd, __u32 nsid, __u16 dspec, __u8 doper, + enum nvme_directive_dtype dtype, __u32 cdw12, + __u32 data_len, void *data, __u32 *result) +{ + __u32 cdw10 = data_len ? (data_len >> 2) - 1 : 0; + __u32 cdw11 = DW(doper, NVME_DIRECTIVE_CDW11_DOPER) | + DW(dtype, NVME_DIRECTIVE_CDW11_DTYPE) | + DW(dspec, NVME_DIRECTIVE_CDW11_DPSEC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_directive_recv, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .data_len = data_len, + .addr = (__u64)(uintptr_t)data, + }; + + return nvme_submit_admin_passthru(fd, &cmd, result); +} + +int nvme_directive_recv_identify_parameters(int fd, __u32 nsid, + struct nvme_id_directives *id) +{ + return nvme_directive_recv(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_IDENTIFY, + NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM, 0, sizeof(*id), + id, NULL); +} + +int nvme_directive_recv_stream_parameters(int fd, __u32 nsid, + struct nvme_streams_directive_params *parms) +{ + return nvme_directive_recv(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_STREAMS, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM, 0, sizeof(*parms), + parms, NULL); +} + +int nvme_directive_recv_stream_status(int fd, __u32 nsid, unsigned nr_entries, + struct nvme_streams_directive_status *id) +{ + return nvme_directive_recv(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_STREAMS, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS, 0, sizeof(*id), + id, NULL); +} + +int nvme_directive_recv_stream_allocate(int fd, __u32 nsid, __u16 nsr, + __u32 *result) +{ + return nvme_directive_recv(fd, nsid, 0, NVME_DIRECTIVE_DTYPE_STREAMS, + NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE, nsr, 0, NULL, + result); +} + +int nvme_set_property(int fd, int offset, __u64 value) +{ + __u32 cdw10 = is_64bit_reg(offset); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_set, + .cdw10 = cdw10, + .cdw11 = offset, + .cdw12 = value & 0xffffffff, + .cdw13 = value >> 32, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_get_property(int fd, int offset, __u64 *value) +{ + __u32 cdw10 = is_64bit_reg(offset); + + struct nvme_passthru_cmd64 cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_get, + .cdw10 = cdw10, + .cdw11 = offset, + }; + + return nvme_submit_admin_passthru64(fd, &cmd, value); +} + +int nvme_sanitize_nvm(int fd, enum nvme_sanitize_sanact sanact, bool ause, + __u8 owpass, bool oipbp, bool nodas, __u32 ovrpat) +{ + __u32 cdw10 = DW(sanact, NVME_SANITIZE_CDW10_SANACT) | + DW(!!ause, NVME_SANITIZE_CDW10_AUSE) | + DW(owpass, NVME_SANITIZE_CDW10_OWPASS) | + DW(!!oipbp, NVME_SANITIZE_CDW10_OIPBP) | + DW(!!nodas, NVME_SANITIZE_CDW10_NODAS); + __u32 cdw11 = ovrpat; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_sanitize_nvm, + .cdw10 = cdw10, + .cdw11 = cdw11, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_dev_self_test(int fd, __u32 nsid, enum nvme_dst_stc stc) +{ + __u32 cdw10 = DW(stc, NVME_DEVICE_SELF_TEST_CDW10_STC); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_dev_self_test, + .nsid = nsid, + .cdw10 = cdw10, + }; + + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +int nvme_virtual_mgmt(int fd, enum nvme_virt_mgmt_act act, + enum nvme_virt_mgmt_rt rt, __u16 cntlid, __u16 nr, + __u32 *result) +{ + __u32 cdw10 = DW(act, NVME_VIRT_MGMT_CDW10_ACT) | + DW(rt, NVME_VIRT_MGMT_CDW10_RT) | + DW(cntlid, NVME_VIRT_MGMT_CDW10_CNTLID); + __u32 cdw11 = DW(nr, NVME_VIRT_MGMT_CDW11_NR); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_virtual_mgmt, + .cdw10 = cdw10, + .cdw11 = cdw11, + }; + + return nvme_submit_admin_passthru(fd, &cmd, 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); +} + +int nvme_flush(int fd, __u32 nsid) +{ + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_flush, + .nsid = nsid, + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +static int nvme_io(int fd, __u8 opcode, __u32 nsid, __u64 slba, __u16 nlb, + __u16 control, __u32 flags, __u32 reftag, __u16 apptag, __u16 appmask, + __u32 data_len, void *data, __u32 metadata_len, void *metadata) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw12 = nlb | (control << 16); + __u32 cdw13 = flags; + __u32 cdw14 = reftag; + __u32 cdw15 = apptag | (appmask << 16); + + struct nvme_passthru_cmd cmd = { + .opcode = opcode, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .cdw15 = cdw15, + .data_len = data_len, + .metadata_len = metadata_len, + .addr = (__u64)(uintptr_t)data, + .metadata = (__u64)(uintptr_t)metadata, + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +int nvme_read(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u8 dsm, __u32 reftag, __u16 apptag, __u16 appmask, + __u32 data_len, void *data, __u32 metadata_len, void *metadata) +{ + return nvme_io(fd, nvme_cmd_read, nsid, slba, nlb, control, dsm, + reftag, apptag, appmask, data_len, data, metadata_len, + metadata); +} + +int nvme_write(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u8 dsm, __u16 dspec, __u32 reftag, __u16 apptag, + __u16 appmask, __u32 data_len, void *data, __u32 metadata_len, + void *metadata) +{ + __u32 flags = dsm | dspec << 16; + + return nvme_io(fd, nvme_cmd_write, nsid, slba, nlb, control, flags, + reftag, apptag, appmask, data_len, data, metadata_len, + metadata); +} + +int nvme_compare(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask, __u32 data_len, + void *data, __u32 metadata_len, void *metadata) +{ + return nvme_io(fd, nvme_cmd_compare, nsid, slba, nlb, control, 0, + reftag, apptag, appmask, data_len, data, metadata_len, + metadata); +} + +int nvme_write_zeros(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask) +{ + return nvme_io(fd, nvme_cmd_write_zeroes, nsid, slba, nlb, control, 0, + reftag, apptag, appmask, 0, NULL, 0, NULL); +} + +int nvme_verify(int fd, __u32 nsid, __u64 slba, __u16 nlb, __u16 control, + __u32 reftag, __u16 apptag, __u16 appmask) +{ + return nvme_io(fd, nvme_cmd_verify, nsid, slba, nlb, control, 0, + reftag, apptag, appmask, 0, NULL, 0, NULL); +} + +int nvme_write_uncorrectable(int fd, __u32 nsid, __u64 slba, __u16 nlb) +{ + return nvme_io(fd, nvme_cmd_write_uncor, nsid, slba, nlb, 0, 0, 0, 0, + 0, 0, NULL, 0, NULL); +} + +int nvme_dsm(int fd, __u32 nsid, __u32 attrs, __u16 nr_ranges, + struct nvme_dsm_range *dsm) +{ + __u32 cdw11 = attrs; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_dsm, + .nsid = nsid, + .addr = (__u64)(uintptr_t)dsm, + .data_len = nr_ranges * sizeof(*dsm), + .cdw10 = nr_ranges - 1, + .cdw11 = cdw11, + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +int nvme_resv_acquire(int fd, __u32 nsid, enum nvme_reservation_rtype rtype, + enum nvme_reservation_racqa racqa, bool iekey, + __u64 crkey, __u64 nrkey) +{ + __le64 payload[2] = { cpu_to_le64(crkey), cpu_to_le64(nrkey) }; + __u32 cdw10 = (racqa & 0x7) | (iekey ? 1 << 3 : 0) | rtype << 8; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_acquire, + .nsid = nsid, + .cdw10 = cdw10, + .data_len = sizeof(payload), + .addr = (__u64)(uintptr_t)(payload), + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +int nvme_resv_register(int fd, __u32 nsid, enum nvme_reservation_rrega rrega, + enum nvme_reservation_cptpl cptpl, bool iekey, + __u64 crkey, __u64 nrkey) +{ + __le64 payload[2] = { cpu_to_le64(crkey), cpu_to_le64(nrkey) }; + __u32 cdw10 = (rrega & 0x7) | (iekey ? 1 << 3 : 0) | cptpl << 30; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_register, + .nsid = nsid, + .cdw10 = cdw10, + .data_len = sizeof(payload), + .addr = (__u64)(uintptr_t)(payload), + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +int nvme_resv_release(int fd, __u32 nsid, enum nvme_reservation_rtype rtype, + enum nvme_reservation_rrela rrela, bool iekey, + __u64 crkey) +{ + __le64 payload[1] = { cpu_to_le64(crkey) }; + __u32 cdw10 = (rrela & 0x7) | (iekey ? 1 << 3 : 0) | rtype << 8; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_release, + .nsid = nsid, + .cdw10 = cdw10, + .addr = (__u64)(uintptr_t)(payload), + .data_len = sizeof(payload), + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} + +int nvme_resv_report(int fd, __u32 nsid, bool eds, __u32 len, + struct nvme_reservation_status *report) +{ + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_resv_report, + .nsid = nsid, + .cdw10 = (len >> 2) - 1, + .cdw11 = eds ? 1 : 0, + .addr = (__u64)(uintptr_t)report, + .data_len = len, + }; + + return nvme_submit_io_passthru(fd, &cmd, NULL); +} diff --git a/lib/nvme/ioctl.h b/lib/nvme/ioctl.h new file mode 100644 index 00000000..e5477f0f --- /dev/null +++ b/lib/nvme/ioctl.h @@ -0,0 +1,355 @@ +#ifndef _LIBNVME_IOCTL_H +#define _LIBNVME_IOCTL_H + +#include +#include + +/* + * 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 + +/** + * struct nvme_passthru_cmd - + * @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 - + * @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 impicit 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; +}; + +#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) + +#endif /* _UAPI_LINUX_NVME_IOCTL_H */ + +/** + * 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 or -1 + * with errno set otherwise. + */ +int nvme_submit_admin_passthru64(int fd, struct nvme_passthru_cmd64 *cmd, + __u64 *result); + +/** + * nvme_admin_passthru64() - 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: Reserevd 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 transfered in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transfered 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 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 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: Reserevd 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 transfered in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transfered 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 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_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 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: Reserevd 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 transfered in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transfered 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 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_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 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: Reserevd 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 transfered in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transfered 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 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_reset_ctrl() - Initiate a controller reset + * @fd: File descriptor of nvme device + * + * This should only be sent to controller handles, not to namespaces. + * + * Return: Zero if a reset was initiated or -1 with errno set otherwise. + */ +int nvme_reset_ctrl(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: Zero 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 + * + * This should only be sent to namespace handles, not to controllers. + * + * Return: The namespace identifier if a succecssful or -1 with errno set + * otherwise. + */ +int nvme_get_nsid(int fd); + +#endif /* _LIBNVME_IOCTL_H */ diff --git a/lib/nvme/private.h b/lib/nvme/private.h new file mode 100644 index 00000000..939aba74 --- /dev/null +++ b/lib/nvme/private.h @@ -0,0 +1,97 @@ +#ifndef _LIBNVME_PRIVATE_H +#define _LIBNVME_PRIVATE_H + +#include +#include +#include +#include + +#include "tree.h" + +struct nvme_path { + TAILQ_ENTRY(nvme_path) entry; + TAILQ_ENTRY(nvme_path) nentry; + struct nvme_ctrl *c; + struct nvme_ns *n; + + char *name; + char *sysfs_dir; + char *ana_state; + int grpid; +}; + +struct nvme_ns { + TAILQ_ENTRY(nvme_ns) entry; + TAILQ_HEAD(, nvme_path) paths; + struct nvme_subsystem *s; + struct nvme_ctrl *c; + + int fd; + char *name; + char *sysfs_dir; + int nsid; + + int lba_size; + int meta_size; + uint64_t lba_count; + uint64_t lba_util; +}; + +struct nvme_ctrl { + TAILQ_ENTRY(nvme_ctrl) entry; + TAILQ_HEAD(, nvme_ns) namespaces; + TAILQ_HEAD(, nvme_path) paths; + 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; +}; + +struct nvme_subsystem { + TAILQ_ENTRY(nvme_subsystem) entry; + TAILQ_HEAD(, nvme_ctrl) ctrls; + TAILQ_HEAD(, nvme_ns) namespaces; + struct nvme_root *r; + + char *name; + char *sysfs_dir; + char *subsysnqn; +}; + +struct nvme_root { + TAILQ_HEAD(, nvme_subsystem) subsystems; +}; + +void nvme_free_ctrl(struct nvme_ctrl *c); +void nvme_ctrl_free_ns(struct nvme_ns *n); +void nvme_subsystem_free_ns(struct nvme_ns *n); +void nvme_free_path(struct nvme_path *p); +void nvme_free_subsystem(struct nvme_subsystem *s); + +int nvme_scan_subsystem(struct nvme_root *t, char *name, nvme_scan_filter_t f); +int nvme_subsystem_scan_namespace(struct nvme_subsystem *s, char *name); +int nvme_subsystem_scan_ctrls(struct nvme_subsystem *s); +int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name); + +int nvme_ctrl_scan_namespace(struct nvme_ctrl *c, char *name); +int nvme_ctrl_scan_path(struct nvme_ctrl *c, char *name); + +static inline void nvme_free_dirents(struct dirent **d, int i) +{ + while (i-- > 0) + free(d[i]); + free(d); +} + +#endif /* _LIBNVME_PRIVATE_H */ diff --git a/lib/nvme/tree.c b/lib/nvme/tree.c new file mode 100644 index 00000000..9de7d493 --- /dev/null +++ b/lib/nvme/tree.c @@ -0,0 +1,808 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "ioctl.h" +#include "filters.h" +#include "tree.h" +#include "private.h" +#include "filters.h" +#include "util.h" +#include "cmd.h" + +static int nvme_scan_topology(struct nvme_root *r, nvme_scan_filter_t f) +{ + struct dirent **subsys; + int i, ret; + + ret = nvme_scan_subsystems(&subsys); + if (ret < 0) + return ret; + + for (i = 0; i < ret; i++) + nvme_scan_subsystem(r, subsys[i]->d_name, f); + + nvme_free_dirents(subsys, i); + return 0; +} + +nvme_root_t nvme_scan_filter(nvme_scan_filter_t f) +{ + struct nvme_root *r = malloc(sizeof(*r)); + + if (!r) { + errno = ENOMEM; + return NULL; + } + + memset(r, 0, sizeof(*r)); + TAILQ_INIT(&r->subsystems); + nvme_scan_topology(r, f); + return r; +} + +nvme_root_t nvme_scan() +{ + return nvme_scan_filter(NULL); +} + +nvme_subsystem_t nvme_first_subsystem(nvme_root_t r) +{ + return TAILQ_FIRST(&r->subsystems); +} + +nvme_subsystem_t nvme_next_subsystem(nvme_root_t r, nvme_subsystem_t s) +{ + if (!s) + return NULL; + return TAILQ_NEXT(s, entry); +} + +void nvme_refresh_topology(nvme_root_t r) +{ + struct nvme_subsystem *s, *_s; + + nvme_for_each_subsystem_safe(r, s, _s) + nvme_free_subsystem(s); + nvme_scan_topology(r, NULL); +} + +void nvme_reset_topology(nvme_root_t r) +{ + struct nvme_subsystem *s, *_s; + + nvme_for_each_subsystem_safe(r, s, _s) + nvme_free_subsystem(s); + nvme_scan_topology(r, NULL); +} + +void nvme_free_tree(nvme_root_t r) +{ + struct nvme_subsystem *s, *_s; + + nvme_for_each_subsystem_safe(r, s, _s) + nvme_free_subsystem(s); + free(r); +} + +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; +} + +nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s) +{ + return TAILQ_FIRST(&s->ctrls); +} + +nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c) +{ + if (!c) + return NULL; + return TAILQ_NEXT(c, entry); +} + +nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s) +{ + return TAILQ_FIRST(&s->namespaces); +} + +nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n) +{ + if (!n) + return NULL; + return TAILQ_NEXT(n, entry); +} + +void nvme_free_subsystem(struct nvme_subsystem *s) +{ + struct nvme_ctrl *c, *_c; + struct nvme_ns *n, *_n; + + if (s->r) + TAILQ_REMOVE(&s->r->subsystems, 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_subsystem_free_ns(n); + + free(s->name); + free(s->sysfs_dir); + free(s->subsysnqn); + free(s); +} + +static int nvme_subsystem_scan_namespaces(struct nvme_subsystem *s) +{ + struct dirent **namespaces; + int i, ret; + + ret = nvme_scan_subsystem_namespaces(s, &namespaces); + if (ret < 0) + return ret; + + for (i = 0; i < ret; i++) + nvme_subsystem_scan_namespace(s, namespaces[i]->d_name); + + nvme_free_dirents(namespaces, i); + return 0; +} + +int nvme_subsystem_scan_ctrls(struct nvme_subsystem *s) +{ + struct dirent **ctrls; + int i, ret; + + ret = nvme_scan_subsystem_ctrls(s, &ctrls); + if (ret < 0) + return ret; + + for (i = 0; i < ret; i++) + nvme_subsystem_scan_ctrl(s, ctrls[i]->d_name); + + nvme_free_dirents(ctrls, i); + return 0; +} + +int nvme_scan_subsystem(struct nvme_root *r, char *name, nvme_scan_filter_t f) +{ + struct nvme_subsystem *s; + char *path; + int ret; + + ret = asprintf(&path, "%s/%s", nvme_subsys_sysfs_dir, name); + if (ret < 0) + return ret; + + s = malloc(sizeof(*s)); + if (!s) { + errno = ENOMEM; + goto free_path; + } + memset(s, 0, sizeof(*s)); + + s->r = r; + s->name = strdup(name);; + s->sysfs_dir = path; + s->subsysnqn = nvme_get_subsys_attr(s, "subsysnqn"); + TAILQ_INIT(&s->ctrls); + TAILQ_INIT(&s->namespaces); + + nvme_subsystem_scan_namespaces(s); + nvme_subsystem_scan_ctrls(s); + TAILQ_INSERT_TAIL(&r->subsystems, s, entry); + + if (f && !f(s)) { + nvme_free_subsystem(s); + return -1; + } + + return 0; + +free_path: + free(path); + return -1; +} +nvme_ctrl_t nvme_path_get_subsystem(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) +{ + TAILQ_REMOVE(&p->c->paths, p, entry); + TAILQ_REMOVE(&p->n->paths, 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] = { 0 }; + 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))) { + TAILQ_INSERT_TAIL(&n->paths, p, nentry); + p->n = n; + } + } +} + +int nvme_ctrl_scan_path(struct nvme_ctrl *c, char *name) +{ + struct nvme_path *p; + char *path, *grpid; + int ret; + + ret = asprintf(&path, "%s/%s", c->sysfs_dir, name); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + p = malloc(sizeof(*p)); + if (!p) { + errno = ENOMEM; + goto free_path; + } + memset(p, 0, sizeof(*p)); + + p->c = c; + p->name = strdup(name); + p->sysfs_dir = path; + p->ana_state = nvme_get_path_attr(p, "ana_state"); + nvme_subsystem_set_path_ns(c->s, p); + + grpid = nvme_get_path_attr(p, "ana_grpid"); + if (grpid) { + sscanf(grpid, "%d", &p->grpid); + free(grpid); + } + + TAILQ_INSERT_TAIL(&c->paths, p, entry); + return 0; + +free_path: + free(path); + return -1; +} + +int nvme_ctrl_get_fd(nvme_ctrl_t c) +{ + return c->fd; +} + +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->subsysnqn; +} + +const char *nvme_ctrl_get_address(nvme_ctrl_t c) +{ + return c->address; +} + +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) +{ + 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; +} + +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 TAILQ_FIRST(&c->namespaces); +} + +nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n) +{ + if (!n) + return NULL; + return TAILQ_NEXT(n, entry); +} + +nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c) +{ + return TAILQ_FIRST(&c->paths); +} + +nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p) +{ + if (!p) + return NULL; + return TAILQ_NEXT(p, entry); +} + +int nvme_ctrl_disconnect(nvme_ctrl_t c) +{ + return nvme_set_attr(nvme_ctrl_get_sysfs_dir(c), "delete_controller", "1"); +} + +void nvme_unlink_ctrl(nvme_ctrl_t c) +{ + if (c->s) + TAILQ_REMOVE(&c->s->ctrls, c, entry); + c->s = NULL; +} + +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_ctrl_free_ns(n); + + close(c->fd); + free(c->name); + free(c->sysfs_dir); + free(c->subsysnqn); + free(c->address); + free(c->firmware); + free(c->model); + free(c->state); + free(c->numa_node); + free(c->queue_count); + free(c->serial); + free(c->sqsize); + free(c->transport); + free(c); +} + +static int nvme_ctrl_scan_paths(struct nvme_ctrl *c) +{ + struct dirent **paths; + int i, ret; + + ret = nvme_scan_ctrl_namespace_paths(c, &paths); + if (ret < 0) + return ret; + + for (i = 0; i < ret; i++) + nvme_ctrl_scan_path(c, paths[i]->d_name); + + nvme_free_dirents(paths, i); + return 0; +} + +static int nvme_ctrl_scan_namespaces(struct nvme_ctrl *c) +{ + struct dirent **namespaces; + int i, ret; + + ret = nvme_scan_ctrl_namespaces(c, &namespaces); + for (i = 0; i < ret; i++) + nvme_ctrl_scan_namespace(c, namespaces[i]->d_name); + + nvme_free_dirents(namespaces, i); + return 0; +} + +static nvme_ctrl_t __nvme_ctrl_alloc(const char *path, const char *name) +{ + DIR *d; + nvme_ctrl_t c; + + d = opendir(path); + if (!d) + return NULL; + closedir(d); + + c = malloc(sizeof(*c)); + if (!c) { + errno = ENOMEM; + return NULL; + } + memset(c, 0, sizeof(*c)); + + c->fd = nvme_open(name); + if (c->fd < 0) + goto free_ctrl; + + TAILQ_INIT(&c->namespaces); + TAILQ_INIT(&c->paths); + c->name = strdup(name); + c->sysfs_dir = (char *)path; + c->subsysnqn = nvme_get_ctrl_attr(c, "subsysnqn"); + c->address = nvme_get_ctrl_attr(c, "address"); + 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"); + c->transport = nvme_get_ctrl_attr(c, "transport"); + + return c; + +free_ctrl: + free(c); + return NULL; +} + +static nvme_ctrl_t nvme_ctrl_alloc(const char *sysfs, const char *name) +{ + nvme_ctrl_t c; + char *path; + int ret; + + ret = asprintf(&path, "%s/%s", sysfs, name); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + + c = __nvme_ctrl_alloc(path, name); + if (!c) + free(path); + return c; +} + +nvme_ctrl_t nvme_scan_ctrl(const char *name) +{ + return nvme_ctrl_alloc(nvme_ctrl_sysfs_dir, name); +} + +int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name) +{ + nvme_ctrl_t c; + + c = nvme_ctrl_alloc(s->sysfs_dir, name); + if (!c) + return -1; + + c->s = s; + nvme_ctrl_scan_namespaces(c); + nvme_ctrl_scan_paths(c); + TAILQ_INSERT_TAIL(&s->ctrls, c, entry); + + return 0; +} + +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 || count & bs) { + errno = EINVAL; + return -1; + } + + *lba = offset / bs; + *nlb = (count / bs) - 1; + return 0; +} + +int nvme_ns_get_fd(nvme_ns_t n) +{ + return n->fd; +} + +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; +} + +int nvme_ns_get_lba_size(nvme_ns_t n) +{ + return n->lba_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; +} + +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_verify(nvme_ns_t n, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_verify(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), slba, nlb, + 0, 0, 0, 0); +} + +int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_write_uncorrectable(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), + slba, nlb); +} + +int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_write_zeros(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), slba, + nlb, 0, 0, 0, 0); +} + +int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_write(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), slba, nlb, 0, + 0, 0, 0, 0, 0, count, buf, 0, NULL); +} + +int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_read(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), slba, nlb, 0, + 0, 0, 0, 0, count, buf, 0, NULL); +} + +int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count) +{ + __u64 slba; + __u16 nlb; + + if (nvme_bytes_to_lba(n, offset, count, &slba, &nlb)) + return -1; + + return nvme_compare(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), slba, nlb, + 0, 0, 0, 0, count, buf, 0, NULL); +} + +int nvme_ns_flush(nvme_ns_t n) +{ + return nvme_flush(nvme_ns_get_fd(n), nvme_ns_get_nsid(n)); +} + +static void nvme_free_ns(struct nvme_ns *n) +{ + close(n->fd); + free(n->name); + free(n->sysfs_dir); + free(n); +} + +void nvme_ctrl_free_ns(struct nvme_ns *n) +{ + TAILQ_REMOVE(&n->s->namespaces, n, entry); + nvme_free_ns(n); +} + +void nvme_subsystem_free_ns(struct nvme_ns *n) +{ + TAILQ_REMOVE(&n->s->namespaces, n, entry); + nvme_free_ns(n); +} + +static void nvme_ns_init(struct nvme_ns *n) +{ + struct nvme_id_ns ns = { 0 }; + + if (nvme_ns_identify(n, &ns) != 0) + return; + + n->lba_size = 1 << ns.lbaf[ns.flbas & NVME_NS_FLBAS_LBA_MASK].ds; + n->lba_count = le64_to_cpu(ns.nsze); + n->lba_util = le64_to_cpu(ns.nuse); +} + +static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, char *name) +{ + struct nvme_ns *n; + char *path; + int ret; + + ret = asprintf(&path, "%s/%s", sysfs_dir, name); + if (ret < 0) { + errno = ENOMEM; + return NULL; + } + + n = malloc(sizeof(*n)); + if (!n) { + errno = ENOMEM; + goto free_path; + } + memset(n, 0, sizeof(*n)); + + n->name = strdup(name); + n->sysfs_dir = path; + n->fd = nvme_open(name); + if (n->fd < 0) + goto free_ns; + + n->nsid = nvme_get_nsid(n->fd); + if (n->nsid < 0) + goto close_fd; + + TAILQ_INIT(&n->paths); + nvme_ns_init(n); + + return n; + +close_fd: + close(n->fd); +free_ns: + free(n); +free_path: + free(path); + return NULL; +} + +int nvme_ctrl_scan_namespace(struct nvme_ctrl *c, char *name) +{ + struct nvme_ns *n; + + n = __nvme_scan_namespace(c->sysfs_dir, name); + if (!n) + return -1; + + n->s = c->s; + n->c = c; + TAILQ_INSERT_TAIL(&c->namespaces, n, entry); + return 0; +} + +int nvme_subsystem_scan_namespace(struct nvme_subsystem *s, char *name) +{ + struct nvme_ns *n; + + n = __nvme_scan_namespace(s->sysfs_dir, name); + if (!n) + return -1; + + n->s = s; + TAILQ_INSERT_TAIL(&s->namespaces, n, entry); + return 0; +} diff --git a/lib/nvme/tree.h b/lib/nvme/tree.h new file mode 100644 index 00000000..b5f53856 --- /dev/null +++ b/lib/nvme/tree.h @@ -0,0 +1,182 @@ +#ifndef _LIBNVME_TREE_H +#define _LIBNVME_TREE_H + +#include +#include + +#include + +#include "ioctl.h" +#include "util.h" + +extern const char *nvme_ctrl_sysfs_dir; +extern const char *nvme_subsys_sysfs_dir; + +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_root *nvme_root_t; + +nvme_subsystem_t nvme_first_subsystem(nvme_root_t r); +nvme_subsystem_t nvme_next_subsystem(nvme_root_t r, nvme_subsystem_t s); + +nvme_ns_t nvme_ctrl_first_ns(nvme_ctrl_t c); +nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n); + +nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c); +nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p); + +nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s); +nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c); + +nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s); +nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n); + +#define nvme_for_each_subsystem_safe(r, s, _s) \ + for (s = nvme_first_subsystem(r), \ + _s = nvme_next_subsystem(r, s); \ + s != NULL; \ + s = _s, _s = nvme_next_subsystem(r, s)) + +#define nvme_for_each_subsystem(r, s) \ + for (s = nvme_first_subsystem(r); s != NULL; \ + s = nvme_next_subsystem(r, s)) + +#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)) + +#define nvme_subsystem_for_each_ctrl(s, c) \ + for (c = nvme_subsystem_first_ctrl(s); c != NULL; \ + c = nvme_subsystem_next_ctrl(s, c)) + +#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)) + +#define nvme_ctrl_for_each_ns(c, n) \ + for (n = nvme_ctrl_first_ns(c); n != NULL; \ + n = nvme_ctrl_next_ns(c, n)) + +#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)) + +#define nvme_ctrl_for_each_path(c, p) \ + for (p = nvme_ctrl_first_path(c); p != NULL; \ + p = nvme_ctrl_next_path(c, p)) + +#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)) + +#define nvme_subsystem_for_each_ns(s, n) \ + for (n = nvme_subsystem_first_ns(s); n != NULL; \ + n = nvme_subsystem_next_ns(s, n)) + +int nvme_ns_get_fd(nvme_ns_t n); +int nvme_ns_get_nsid(nvme_ns_t n); +int nvme_ns_get_lba_size(nvme_ns_t n); +uint64_t nvme_ns_get_lba_count(nvme_ns_t n); +uint64_t nvme_ns_get_lba_util(nvme_ns_t n); +const char *nvme_ns_get_sysfs_dir(nvme_ns_t n); +const char *nvme_ns_get_name(nvme_ns_t n); +nvme_subsystem_t nvme_ns_get_subsystem(nvme_ns_t n); +nvme_ctrl_t nvme_ns_get_ctrl(nvme_ns_t n); + +/** + * nvme_ns_read() - + */ +int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_write() - + */ +int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_verify() - + */ +int nvme_ns_verify(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_compare() - + */ +int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count); + +/** + * nvme_ns_write_zeros() - + */ +int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_write_uncorrectable() - + */ +int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count); + +/** + * nvme_ns_flush() - + */ +int nvme_ns_flush(nvme_ns_t n); + +/** + * nvme_ns_identify() - + */ +int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns); + +const char *nvme_path_get_name(nvme_path_t p); +const char *nvme_path_get_sysfs_dir(nvme_path_t p); +const char *nvme_path_get_ana_state(nvme_path_t p); +nvme_ctrl_t nvme_path_get_subsystem(nvme_path_t p); +nvme_ns_t nvme_path_get_ns(nvme_path_t p); + +int nvme_ctrl_get_fd(nvme_ctrl_t c); +const char *nvme_ctrl_get_name(nvme_ctrl_t c); +const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c); +const char *nvme_ctrl_get_address(nvme_ctrl_t c); +const char *nvme_ctrl_get_firmware(nvme_ctrl_t c); +const char *nvme_ctrl_get_model(nvme_ctrl_t c); +const char *nvme_ctrl_get_state(nvme_ctrl_t c); +const char *nvme_ctrl_get_numa_node(nvme_ctrl_t c); +const char *nvme_ctrl_get_queue_count(nvme_ctrl_t c); +const char *nvme_ctrl_get_serial(nvme_ctrl_t c); +const char *nvme_ctrl_get_sqsize(nvme_ctrl_t c); +const char *nvme_ctrl_get_transport(nvme_ctrl_t c); +const char *nvme_ctrl_get_nqn(nvme_ctrl_t c); +const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c); +nvme_subsystem_t nvme_ctrl_get_subsystem(nvme_ctrl_t c); + +int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id); +int nvme_ctrl_disconnect(nvme_ctrl_t c); +nvme_ctrl_t nvme_scan_ctrl(const char *name); + +void nvme_free_ctrl(struct nvme_ctrl *c); +void nvme_unlink_ctrl(struct nvme_ctrl *c); + +const char *nvme_subsystem_get_nqn(nvme_subsystem_t s); +const char *nvme_subsystem_get_sysfs_dir(nvme_subsystem_t s); +const char *nvme_subsystem_get_name(nvme_subsystem_t s); + +typedef bool (*nvme_scan_filter_t)(nvme_subsystem_t); +nvme_root_t nvme_scan_filter(nvme_scan_filter_t f); + +nvme_root_t nvme_scan(); +void nvme_refresh_topology(nvme_root_t r); +void nvme_reset_topology(nvme_root_t r); +void nvme_free_tree(nvme_root_t r); + +char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr); +char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr); +char *nvme_get_ns_attr(nvme_ns_t n, const char *attr); +char *nvme_get_path_attr(nvme_path_t p, const char *attr); + +#endif /* _LIBNVME_TREE_H */ diff --git a/lib/nvme/types.h b/lib/nvme/types.h new file mode 100644 index 00000000..17d68512 --- /dev/null +++ b/lib/nvme/types.h @@ -0,0 +1,2430 @@ +#ifndef _LIBNVME_TYPES_H +#define _LIBNVME_TYPES_H + +#include +#include +#include + +#include + +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#else +#define __force +#endif + +static inline __le16 cpu_to_le16(uint16_t x) { return (__force __le16)htole16(x); } +static inline __le32 cpu_to_le32(uint32_t x) { return (__force __le32)htole32(x); } +static inline __le64 cpu_to_le64(uint64_t x) { return (__force __le64)htole64(x); } +static inline uint16_t le16_to_cpu(__le16 x) { return le16toh((__force __u16)x); } +static inline uint32_t le32_to_cpu(__le32 x) { return le32toh((__force __u32)x); } +static inline uint64_t le64_to_cpu(__le64 x) { return le64toh((__force __u64)x); } + +/** + * enum nvme_constants - + * @NVME_NSID_ALL: + * @NVME_NSID_NONE: + * @NVME_UUID_NONE: + * @NVME_CNTLID_NONE: + * @NVME_NVMSETID_NONE: + * @NVME_LOG_LSP_NONE: + * @NVME_LOG_LSI_NONE: + * @NVME_IDENTIFY_DATA_SIZE: + * @NVME_ID_NVMSET_LIST_MAX: + * @NVME_ID_UUID_LIST_MAX: + * @NVME_ID_CTRL_LIST_MAX: + * @NVME_ID_NS_LIST_MAX: + * @NVME_ID_SECONDARY_CTRL_MAX: + * @NVME_FEAT_LBA_RANGE_MAX: + * @NVME_LOG_ST_MAX_RESULTS: + * @NVME_DSM_MAX_RANGES: + */ +enum nvme_constants { + NVME_NSID_ALL = 0xffffffff, + NVME_NSID_NONE = 0, + NVME_UUID_NONE = 0, + NVME_CNTLID_NONE = 0, + NVME_NVMSETID_NONE = 0, + NVME_LOG_LSP_NONE = 0, + NVME_LOG_LSI_NONE = 0, + NVME_IDENTIFY_DATA_SIZE = 4096, + 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_FEAT_LBA_RANGE_MAX = 64, + NVME_LOG_ST_MAX_RESULTS = 20, + NVME_DSM_MAX_RANGES = 256, +}; + +/** + * DOC: NVMe controller registers/properties + */ + +/** + * enum nvme_registers - + * @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_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_PMRMSC: Persistent Memory Region Controller Memory Space Control + * @NVME_REG_DBS: SQ 0 Tail Doorbell + */ +enum nvme_registers { + 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_PMRCAP = 0x0e00, + NVME_REG_PMRCTL = 0x0e04, + NVME_REG_PMRSTS = 0x0e08, + NVME_REG_PMREBS = 0x0e0c, + NVME_REG_PMRSWTP= 0x0e10, + NVME_REG_PMRMSC = 0x0e14, + NVME_REG_DBS = 0x1000, +}; + +#define NVME_CAP_MQES(cap) ((cap) & 0xffff) +#define NVME_CAP_CQR(cap) (((cap) >> 16) & 0x1) +#define NVME_CAP_AMS(cap) (((cap) >> 17) & 0x3) +#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) +#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) +#define NVME_CAP_NSSRC(cap) (((cap) >> 36) & 0x1) +#define NVME_CAP_CSS(cap) (((cap) >> 37) & 0xff) +#define NVME_CAP_BPS(cap) (((cap) >> 45) & 0x1) +#define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) +#define NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) +#define NVME_CAP_CMBS(cap) (((cap) >> 57) & 1) +#define NVME_CAP_PMRS(cap) (((cap) >> 56) & 1) + +#define NVME_CMB_BIR(cmbloc) ((cmbloc) & 0x7) +#define NVME_CMB_OFST(cmbloc) (((cmbloc) >> 12) & 0xfffff) +#define NVME_CMB_SZ(cmbsz) (((cmbsz) >> 12) & 0xfffff) +#define NVME_CMB_SZU(cmbsz) (((cmbsz) >> 8) & 0xf) + +#define NVME_CMB_WDS(cmbsz) ((cmbsz) & 0x10) +#define NVME_CMB_RDS(cmbsz) ((cmbsz) & 0x8) +#define NVME_CMB_LISTS(cmbsz) ((cmbsz) & 0x4) +#define NVME_CMB_CQS(cmbsz) ((cmbsz) & 0x2) +#define NVME_CMB_SQS(cmbsz) ((cmbsz) & 0x1) + +/** + * enum - + */ +enum { + NVME_CC_ENABLE = 1 << 0, + NVME_CC_CSS_NVM = 0 << 4, + 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_AMS_RR = 0 << NVME_CC_AMS_SHIFT, + NVME_CC_AMS_WRRU = 1 << NVME_CC_AMS_SHIFT, + NVME_CC_AMS_VS = 7 << NVME_CC_AMS_SHIFT, + NVME_CC_SHN_NONE = 0 << NVME_CC_SHN_SHIFT, + NVME_CC_SHN_NORMAL = 1 << NVME_CC_SHN_SHIFT, + NVME_CC_SHN_ABRUPT = 2 << NVME_CC_SHN_SHIFT, + NVME_CC_SHN_MASK = 3 << NVME_CC_SHN_SHIFT, + NVME_CSTS_RDY = 1 << 0, + NVME_CSTS_CFS = 1 << 1, + NVME_CSTS_NSSRO = 1 << 4, + NVME_CSTS_PP = 1 << 5, + NVME_CSTS_SHST_NORMAL = 0 << 2, + NVME_CSTS_SHST_OCCUR = 1 << 2, + NVME_CSTS_SHST_CMPLT = 2 << 2, + NVME_CSTS_SHST_MASK = 3 << 2, +}; + +/* + * is_64bit_reg() - Checks if offset of the controller register is 64bit or not. + * @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 chcked here. + * + * Returns true if given offset is 64bit register, otherwise it returns false. + */ +static inline bool is_64bit_reg(__u32 offset) +{ + switch (offset) { + case NVME_REG_CAP: + case NVME_REG_ASQ: + case NVME_REG_ACQ: + case NVME_REG_BPMBL: + return true; + default: + return false; + } +} + +/** + * DOC: NVMe Identify + */ + +/** + * struct nvme_id_psd - + * @mp: + * @flags: + * @enlat: + * @exlat: + * @rrt: + * @rrl: + * @rwt: + * @rwl: + * @idlp: + * @ips: + * @actp: + * @apw: + * @aps: + */ +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 apw; + __u8 aps; + __u8 rsvd23[8]; +}; + +/** + * nvme_psd_power_scale() - power scale occupies the upper 3 bits + */ +static inline unsigned nvme_psd_power_scale(__u8 ps) +{ + return ps >> 6; +} + +/** + * enum - + * @NVME_PSD_FLAGS_MAX_POWER_SCALE: + * @NVME_PSD_FLAGS_NON_OP_STATE: + * @NVME_PSD_RELATIVE_MASK: + * @NVME_PSD_APW_MASK: + */ +enum { + NVME_PSD_FLAGS_MAX_POWER_SCALE = 1 << 0, + NVME_PSD_FLAGS_NON_OP_STATE = 1 << 1, + NVME_PSD_RELATIVE_MASK = 0x1f, + NVME_PSD_APW_MASK = 0x7, +}; + +/** + * struct nvme_id_ctrl - Identify Controller data structure + * @vid: Vendor ID + * @ssvid: Subsystem Vendor Id + * @sn: Serial Number + * @mn: Model Number + * @fr: Firmware Revision + * @rab: Recommended Arbitration Burst + * @ieee: IEEE + * @cmic: Controller Mulitpathing Capabilities + * @mdts: Max Data Transfer Size + * @cntlid: Controller Identifier + * @ver: Version + * @rtd3r: Runtime D3 Resume + * @rtd3e: Runtime D3 Exit + * @oaes: Optional Async Events Supported + * @ctratt: Controller Attributes + * @rrls: Read Recovery Levels + * @cntrltype: Controller Type + * @fguid: FRU GUID + * @crdt1: Controller Retry Delay 1 + * @crdt2: Controller Retry Delay 2 + * @crdt3: Controller Retry Delay 3 + * @nvmsr: + * @vwci: + * @mec: + * @oacs: Optional Admin Commands Supported + * @acl: Abort Command Limit + * @aerl: Async Event Request Limit + * @frmw: + * @lpa: Log Page Attributes + * @elpe: + * @npss: Number of Power States Supported + * @avscc: + * @apsta: + * @wctemp: + * @cctemp: + * @mtfa: + * @hmpre: + * @hmmin: + * @tnvmcap: + * @unvmcap: + * @rpmbs: + * @edstt: + * @dsto: + * @fwug: + * @kas: + * @hctma: + * @mntmt: + * @mxtmt: + * @sanicap: + * @hmminds: + * @hmmaxd: + * @nsetidmax: + * @endgidmax: + * @anatt: + * @anacap: + * @anagrpmax: + * @nanagrpid: + * @pels: + * @sqes: + * @cqes: + * @maxcmd: + * @nn: + * @onc: + * @fuses: + * @fna: + * @vwc: + * @awun: + * @awupf: + * @nvscc: + * @nwpc: + * @acwu: + * @sgls: + * @mnan: + * @subnqn: + * @ioccsz: + * @iorcsz: + * @icdoff: + * @fcatt: + * @msdbd: + * @ofcs: + * @psd: + * @vs: + */ +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; + __u8 rsvd356[156]; + __u8 sqes; + __u8 cqes; + __le16 maxcmd; + __le32 nn; + __le16 oncs; + __le16 fuses; + __u8 fna; + __u8 vwc; + __le16 awun; + __le16 awupf; + __u8 nvscc; + __u8 nwpc; + __le16 acwu; + __u8 rsvd534[2]; + __le32 sgls; + __le32 mnan; + __u8 rsvd544[224]; + char subnqn[256]; + __u8 rsvd1024[768]; + + /* Fabrics Only */ + __le32 ioccsz; + __le32 iorcsz; + __le16 icdoff; + __u8 fcatt; + __u8 msdbd; + __le16 ofcs; + __u8 rsvd1806[242]; + + struct nvme_id_psd psd[32]; + __u8 vs[1024]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_CNTRLTYPE_RESERVED = 0, + NVME_CTRL_CNTRLTYPE_IO = 1, + NVME_CTRL_CNTRLTYPE_DISCOVERY = 2, + NVME_CTRL_CNTRLTYPE_ADMIN = 3, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_NVMSR_NVMESD = 1 << 0, + NVME_CTRL_NVMSR_NVMEE = 1 << 1, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_VWCI_VWCR = 0x7f << 0, + NVME_CTRL_VWCI_VWCRV = 1 << 7, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_MEC_SMBUSME = 1 << 0, + NVME_CTRL_MEC_PCIEME = 1 << 1, +}; + +/** + * enum - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_FRMW_1ST_RO = 1 << 0, + NVME_CTRL_FRMW_NR_SLOTS = 3 << 1, + NVME_CTRL_FRMW_FW_ACT_NO_RESET = 1 << 4, +}; + +/** + * enum - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_AVSCC_AVS = 1 << 0, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_APSTA_APST = 1 << 0, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_RPMBS_NR_UNITS = 7 << 0, + NVME_CTRL_RPMBS_AUTH_METHOD = 7 << 3, + NVME_CTRL_RPMBS_TOTAL_SIZE = 255 << 16, + NVME_CTRL_RPMBS_ACCESS_SIZE = 255 << 24, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_DSTO_ONE_DST = 1 << 0, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_HCTMA_HCTM = 1 << 0, +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + 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 - + */ +enum { + NVME_CTRL_SQES_MIN = 15 << 0, + NVME_CTRL_SQES_MAX = 15 << 4, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_CQES_MIN = 15 << 0, + NVME_CTRL_CQES_MAX = 15 << 4, +}; + +/** + * enum - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_FUSES_COMPARE_AND_WRITE = 1 << 0, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_FNA_FMT_ALL_NAMESPACES = 1 << 0, + NVME_CTRL_FNA_SEC_ALL_NAMESPACES = 1 << 1, + NVME_CTRL_FNA_CRYPTO_ERASE = 1 << 2, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_VWC_PRESENT = 1 << 0, + NVME_CTRL_VWC_FLUSH = 3 << 1, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_NVSCC_FMT = 1 << 0, +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + 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 - + */ +enum { + NVME_CTRL_FCATT_DYNAMIC = 1 << 0, +}; + +/** + * enum - + */ +enum { + NVME_CTRL_OFCS_DISCONNECT = 1 << 0, +}; + +/** + * struct nvme_lbaf - + * @ms: + * @ds: + * @rp: + */ +struct nvme_lbaf { + __le16 ms; + __u8 ds; + __u8 rp; +}; + +/** + * enum - + * @NVME_LBAF_RP_BEST: + * @NVME_LBAF_RP_BETTER: + * @NVME_LBAF_RP_GOOD: + * @NVME_LBAF_RP_DEGRADED: + */ +enum { + 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 - + * @nsze: + * @ncap: + * @nuse: + * @nsfeat: + * @nlbaf: + * @flbas: + * @mc: + * @dpc: + * @dps: + * @nmic: + * @rescap: + * @fpi: + * @dlfeat: + * @nawun: + * @nawupf: + * @nacwu: + * @nabsn: + * @nabo: + * @nabspf: + * @noiob: + * @nvmcap: + * @npwg: + * @npwa: + * @npdg: + * @npda: + * @nows: + * @anagrpid: + * @nsattr: + * @nvmsetid: + * @endgid: + * @nguid: + * @eui64: + * @lbaf: + * @vs: + */ +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; + __u8 rsvd74[18]; + __le32 anagrpid; + __u8 rsvd96[3]; + __u8 nsattr; + __le16 nvmsetid; + __le16 endgid; + __u8 nguid[16]; + __u8 eui64[8]; + struct nvme_lbaf lbaf[16]; + __u8 rsvd192[192]; + __u8 vs[3712]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + NVME_NS_FLBAS_LBA_MASK = 15 << 0, + NVME_NS_FLBAS_META_EXT = 1 << 4, +}; + +/** + * enum - + */ +enum { + NVME_NS_MC_EXTENDED = 1 << 0, + NVME_NS_MC_SEPARATE = 1 << 1, +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + 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 - + */ +enum { + NVME_NS_NMIC_SHARED = 1 << 0, +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + NVME_NS_FPI_REMAINING = 127 << 0, + NVME_NS_FPI_SUPPORTED = 1 << 7, +}; + +/** + * enum - + */ +enum { + 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 - + */ +enum { + NVME_NS_NSATTR_WRITE_PROTECTED = 1 << 0 +}; + +/** + * struct nvme_ns_id_desc - + */ +struct nvme_ns_id_desc { + __u8 nidt; + __u8 nidl; + __le16 reserved; + __u8 nid[]; +}; + +/** + * enum - + */ +enum { + NVME_NIDT_EUI64 = 1, + NVME_NIDT_NGUID = 2, + NVME_NIDT_UUID = 3, +}; + +/** + * struct nvme_nvmset_attr - + */ +struct nvme_nvmset_attr { + __le16 id; + __le16 endurance_group_id; + __u8 rsvd4[4]; + __le32 random_4k_read_typical; + __le32 opt_write_size; + __u8 total_nvmset_cap[16]; + __u8 unalloc_nvmset_cap[16]; + __u8 rsvd48[80]; +}; + +/** + * struct nvme_id_nvmset_list - + */ +struct nvme_id_nvmset_list { + __u8 nid; + __u8 rsvd1[127]; + struct nvme_nvmset_attr ent[NVME_ID_NVMSET_LIST_MAX]; +}; + +/** + * struct nvme_id_ns_granularity_list_entry - + */ +struct nvme_id_ns_granularity_list_entry { + __le64 namespace_size_granularity; + __le64 namespace_capacity_granularity; +}; + +/** + * struct nvme_id_ns_granularity_list - + */ +struct nvme_id_ns_granularity_list { + __le32 attributes; + __u8 num_descriptors; + __u8 rsvd[27]; + struct nvme_id_ns_granularity_list_entry entry[16]; +}; + +/** + * struct nvme_id_uuid_list_entry - + */ +struct nvme_id_uuid_list_entry { + __u8 header; + __u8 rsvd1[15]; + __u8 uuid[16]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +struct nvme_id_uuid_list { + __u8 rsvd0[32]; + struct nvme_id_uuid_list_entry entry[NVME_ID_UUID_LIST_MAX]; +}; + +/** + * struct nvme_ctrl_list - + */ +struct nvme_ctrl_list { + __le16 num; + __le16 identifier[NVME_ID_CTRL_LIST_MAX]; +}; + +/** + * struct nvme_ns_list - + */ +struct nvme_ns_list { + __le32 ns[NVME_ID_NS_LIST_MAX]; +}; + +/** + * struct nvme_primary_ctrl_cap - + */ +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 - + */ +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 - + */ +struct nvme_secondary_ctrl_list { + __u8 num; + __u8 rsvd[31]; + struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX]; +}; + +/** + * DOC: NVMe Logs + */ + +/** + * struct nvme_error_log_page - + */ +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 resv[2]; + __le64 cs; + __le16 trtype_spec_info; + __u8 resv2[22]; +}; + +/** + * enum - + */ +enum { + NVME_ERR_PEL_BYTE_MASK = 0xf, + NVME_ERR_PEL_BIT_MASK = 0x70, +}; + +/** + * struct nvme_smart_log - + */ +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 - + */ +enum { + 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 - + */ +enum { + NVME_SMART_EGCW_SPARE = 1 << 0, + NVME_SMART_EGCW_DEGRADED = 1 << 2, + NVME_SMART_EGCW_RO = 1 << 3, +}; + +/** + * struct nvme_frs - + */ +struct nvme_frs { + char frs[8]; +}; + +/** + * struct nvme_firmware_slot - + */ +struct nvme_firmware_slot { + __u8 afi; + __u8 resv[7]; + struct nvme_frs frs[7]; + __u8 resv2[448]; +}; + +/** + * struct nvme_cmd_effects_log - + */ +struct nvme_cmd_effects_log { + __le32 acs[256]; + __le32 iocs[256]; + __u8 resv[2048]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +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 - + */ +enum { + 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, +}; + +/** + * enum - + */ +enum { + NVME_ST_OPERATION_NONE = 0x0, + NVME_ST_OPERATION_SHORT = 0x1, + NVME_ST_OPERATION_EXTENDED = 0x2, + NVME_ST_OPERATION_VS = 0xe, +}; + +/** + * enum - + */ +enum { + 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 - + */ +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)); + +/** + * struct nvme_telemetry_log - + */ +struct nvme_telemetry_log { + __u8 lpi; + __u8 rsvd[4]; + __u8 ieee[3]; + __le16 dalb1; + __le16 dalb2; + __le16 dalb3; + __u8 rsvd1[368]; + __u8 ctrlavail; + __u8 ctrldgn; + __u8 rsnident[128]; + __u8 telemetry_dataarea[]; +}; + +/** + * struct nvme_endurance_group_log - + */ +struct nvme_endurance_group_log { + __u8 critical_warning; + __u8 rsvd1[2]; + __u8 avl_spare; + __u8 avl_spare_threshold; + __u8 percent_used; + __u8 rsvd6[26]; + __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 rsvd160[352]; +}; + +/** + * enum - + */ +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 - + */ +struct nvme_aggregate_endurance_group_event { + __le64 num_entries; + __le16 entries[]; +}; + +/** + * struct nvme_nvmset_predictable_lat_log - + */ +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 dtwin_tmin_hi; + __le64 dtwin_tmin_lo; + __u8 rsvd72[56]; + __le64 dtwin_re; + __le64 dtwin_we; + __le64 dtwin_te; + __u8 rsvd152[360]; +}; + +/** + * enum - + */ +enum { + NVME_NVMSET_PL_STATUS_DISABLED = 0, + NVME_NVMSET_PL_STATUS_DTWIN = 1, + NVME_NVMSET_PL_STATUS_NDWIN = 2, +}; + +/** + * enum - + */ +enum { + 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 - + */ +struct nvme_aggregate_predictable_lat_event { + __le64 num_entries; + __le16 entries[]; +}; + +/** + * struct nvme_ana_group_desc - + */ +struct nvme_ana_group_desc { + __le32 grpid; + __le32 nnsids; + __le64 chgcnt; + __u8 state; + __u8 rsvd17[15]; + __le32 nsids[]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +struct nvme_ana_log { + __le64 chgcnt; + __le16 ngrps; + __u8 rsvd10[6]; + struct nvme_ana_group_desc descs[]; +}; + +/** + * struct nvme_persistent_event_log - + */ +struct nvme_persistent_event_log { + __u8 lid; + __u8 rsvd1[3]; + __le32 ttl; + __u8 rv; + __u8 rsvd17; + __le16 lht; + __le64 ts; + __u8 poh[16]; + __le64 pcc; + __le16 vid; + __le16 ssvid; + char sn[20]; + char mn[40]; + char subnqn[256]; + __u8 rsvd372; + __u8 seb[32]; +}; + +/** + * struct nvme_lba_rd - + */ +struct nvme_lba_rd { + __le64 rslba; + __le32 rnlb; + __u8 rsvd12[4]; +}; + +/** + * struct nvme_lbas_ns_element - + */ +struct nvme_lbas_ns_element { + __le32 neid; + __le32 nrld; + __u8 ratype; + __u8 rsvd8[7]; + struct nvme_lba_rd lba_rd[]; +}; + +/** + * enum nvme_lba_status_atype - + * @NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED: + * @NVME_LBA_STATUS_ATYPE_SCAN_TRACKED: + */ +enum nvme_lba_status_atype { + NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED = 0x10, + NVME_LBA_STATUS_ATYPE_SCAN_TRACKED = 0x11, +}; + +/** + * struct nvme_lba_status_log - + */ +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 - + */ +struct nvme_eg_event_aggregate_log { + __le64 nr_entries; + __le16 egids[]; +}; + +/** + * struct nvme_resv_notification_log - + */ +struct nvme_resv_notification_log { + __le64 lpc; + __u8 rnlpt; + __u8 nalp; + __u8 rsvd9[2]; + __le32 nsid; + __u8 rsvd16[48]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +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]; +}; + +/** + * DOC: NVMe Directives + */ + +/** + * enum - + */ +enum { + NVME_SANITIZE_SSTAT_NEVER_SANITIZED = 0, + NVME_SANITIZE_SSTAT_COMPLETE_SUCCESS = 1, + NVME_SANITIZE_SSTAT_IN_PROGESS = 2, + NVME_SANITIZE_SSTAT_COMPLETED_FAILED = 3, + NVME_SANITIZE_SSTAT_ND_COMPLETE_SUCCESS = 4, +}; + +/** + * struct nvme_lba_status_desc - + */ +struct nvme_lba_status_desc { + __le64 dslba; + __le32 nlb; + __u8 rsvd12; + __u8 status; + __u8 rsvd14[2]; +}; + +/** + * struct nvme_lba_status - + */ +struct nvme_lba_status { + __le32 nlsd; + __u8 cmpc; + __u8 rsvd5[3]; + struct nvme_lba_status_desc descs[]; +}; + + +/** + * DOC: NVMe Management Interface + */ + +/** + * struct nvme_mi_read_nvm_ss_info - + */ +struct nvme_mi_read_nvm_ss_info { + __u8 nump; + __u8 mjr; + __u8 mnr; + __u8 rsvd3[29]; +}; + +/** + * struct nvme_mi_port_pcie - + */ +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 - + */ +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 - + */ +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 - + */ +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 - + */ +struct nvme_mi_osc { + __u8 type; + __u8 opc; +}; + +/** + * struct nvme_mi_read_sc_list - + */ +struct nvme_mi_read_sc_list { + __le16 numcmd; + struct nvme_mi_osc cmds[]; +}; + +/** + * struct nvme_mi_nvm_ss_health_status - + */ +struct nvme_mi_nvm_ss_health_status { + __u8 nss; + __u8 sw; + __u8 ctemp; + __u8 pdlu; + __le16 ccs; + __u8 rsvd8[2]; +}; + +/** + * enum - + */ +enum { + NVME_MI_CCS_RDY = 1 << 0, + NVME_MI_CSS_CFS = 1 << 1, + NVME_MI_CSS_SHST = 1 << 2, + NVME_MI_CSS_NSSRO = 1 << 4, + NVME_MI_CSS_CECO = 1 << 5, + NVME_MI_CSS_NAC = 1 << 6, + NVME_MI_CSS_FA = 1 << 7, + NVME_MI_CSS_CSTS = 1 << 8, + NVME_MI_CSS_CTEMP = 1 << 9, + NVME_MI_CSS_PDLU = 1 << 10, + NVME_MI_CSS_SPARE = 1 << 11, + NVME_MI_CSS_CCWARN = 1 << 12, +}; + +/** + * struct nvme_mi_ctrl_heal_status - + */ +struct nvme_mi_ctrl_heal_status { + __le16 ctlid; + __le16 csts; + __le16 ctemp; + __u8 pdlu; + __u8 spare; + __u8 cwarn; + __u8 rsvd9[7]; +}; + +/** + * enum - + */ +enum { + 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, + 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 - + */ +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 - + */ +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 - + */ +struct nvme_mi_vpd_telem { + __u8 type; + __u8 rev; + __u8 len; + __u8 data[0]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +struct nvme_mi_vpd_tra { + __u8 vn; + __u8 rsvd6; + __u8 ec; + struct nvme_mi_vpd_telem elems[0]; +}; + +/** + * struct nvme_mi_vpd_mr_common - + */ +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 - + */ +struct nvme_mi_vpd_hdr { + __u8 ipmiver; + __u8 iuaoff; + __u8 ciaoff; + __u8 biaoff; + __u8 piaoff; + __u8 mrioff; + __u8 rsvd6; + __u8 chchk; + __u8 vpd[]; +}; + +/** + * DOC: NVMe Features + */ + +/** + * struct nvme_feat_auto_pst - + */ +struct nvme_feat_auto_pst { + __le64 apst_entry[32]; +}; + +/** + * struct nvme_timestamp - + */ +struct nvme_timestamp { + __u8 timestamp[6]; + __u8 attr; + __u8 rsvd; +}; + +/** + * struct nvme_lba_range_type_entry - + */ +struct nvme_lba_range_type_entry { + __u8 type; + __u8 attributes; + __u8 rsvd2[14]; + __u64 slba; + __u64 nlb; + __u8 guid[16]; + __u8 rsvd48[16]; +}; + +/** + * enum - + */ +enum { + 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 - + */ +struct nvme_lba_range_type { + struct nvme_lba_range_type_entry entry[NVME_FEAT_LBA_RANGE_MAX]; +}; + +/** + * struct nvme_plm_config - + */ +struct nvme_plm_config { + __le16 ee; + __u8 rsvd2[30]; + __le64 dtwinrt; + __le64 dtwinwt; + __le64 dtwintt; + __u8 rsvd56[456]; +}; + +/** + * struct nvme_feat_host_behavior - + */ +struct nvme_feat_host_behavior { + __u8 acre; + __u8 resv1[511]; +}; + +/** + * enum - + */ +enum { + NVME_ENABLE_ACRE = 1 << 0, +}; + +/** + * struct nvme_dsm_range - + */ +struct nvme_dsm_range { + __le32 cattr; + __le32 nlb; + __le64 slba; +}; + +/** + * struct nvme_registered_ctrl - + */ +struct nvme_registered_ctrl { + __le16 cntlid; + __u8 rcsts; + __u8 rsvd3[5]; + __le64 hostid; + __le64 rkey; +}; + +/** + * struct nvme_registered_ctrl_ext - + */ +struct nvme_registered_ctrl_ext { + __le16 cntlid; + __u8 rcsts; + __u8 resv3[5]; + __le64 rkey; + __u8 hostid[16]; + __u8 resv32[32]; +}; + +/** + * struct nvme_reservation_status - + */ +struct nvme_reservation_status { + __le32 gen; + __u8 rtype; + __u8 regctl[2]; + __u8 rsvd7[2]; + __u8 ptpls; + __u8 rsvd10[14]; + union { + struct { + __u8 resv24[40]; + struct nvme_registered_ctrl_ext regctl_eds[0]; + }; + struct nvme_registered_ctrl regctl_ds[0]; + }; +}; + +enum { + NVME_FEAT_ARB_BURST_MASK = 0x00000007, + NVME_FEAT_ARB_LPW_MASK = 0x0000ff00, + NVME_FEAT_ARB_MPW_MASK = 0x00ff0000, + NVME_FEAT_ARB_HPW_MASK = 0xff000000, + NVME_FEAT_PM_PS_MASK = 0x0000001f, + NVME_FEAT_PM_WH_MASK = 0x000000e0, + NVME_FEAT_LBAR_NR_MASK = 0x0000003f, + NVME_FEAT_TT_TMPTH_MASK = 0x0000ffff, + NVME_FEAT_TT_TMPSEL_MASK = 0x000f0000, + NVME_FEAT_TT_THSEL_MASK = 0x00300000, + NVME_FEAT_ER_TLER_MASK = 0x0000ffff, + NVME_FEAT_ER_DULBE_MASK = 0x00010000, + NVME_FEAT_VWC_WCE_MASK = 0x00000001, + NVME_FEAT_NRQS_NSQR_MASK = 0x0000ffff, + NVME_FEAT_NRQS_NCQR_MASK = 0xffff0000, + NVME_FEAT_ICOAL_THR_MASK = 0x000000ff, + NVME_FEAT_ICOAL_TIME_MASK = 0x0000ff00, + NVME_FEAT_ICFG_IV_MASK = 0x0000ffff, + NVME_FEAT_ICFG_CD_MASK = 0x00010000, + NVME_FEAT_WA_DN_MASK = 0x00000001, + NVME_FEAT_AE_SMART_MASK = 0x000000ff, + NVME_FEAT_AE_NAN_MASK = 0x00000100, + NVME_FEAT_AE_FW_MASK = 0x00000200, + NVME_FEAT_AE_TELEM_MASK = 0x00000400, + NVME_FEAT_AE_ANA_MASK = 0x00000800, + NVME_FEAT_AE_PLA_MASK = 0x00001000, + NVME_FEAT_AE_LBAS_MASK = 0x00002000, + NVME_FEAT_AE_EGA_MASK = 0x00004000, + NVME_FEAT_APST_APSTE_MASK = 0x00000001, + NVME_FEAT_HMEM_EHM_MASK = 0x00000001, + NVME_FEAT_TS_SYNCH_MASK = 0x00000001, + NVME_FEAT_TS_ORIGIN_MASK = 0x0000000e, + NVME_FEAT_TS_ORIGIN_CLR = 0x00000001, + NVME_FEAT_TS_ORIGIN_SF = 0x00000002, + NVME_FEAT_HCTM_TMT2_MASK = 0x0000ffff, + NVME_FEAT_HCTM_TMT1_MASK = 0xffff0000, + NVME_FEAT_NOPS_NOPPME_MASK = 0x00000001, + NVME_FEAT_RRL_RRL_MASK = 0x000000ff, + NVME_FEAT_PLM_PLME_MASK = 0x00000001, + NVME_FEAT_PLMW_WS_MASK = 0x00000007, + NVME_FEAT_LBAS_LSIRI_MASK = 0x0000ffff, + NVME_FEAT_LBAS_LSIPI_MASK = 0xffff0000, + NVME_FEAT_SC_NODRM_MASK = 0x00000001, + NVME_FEAT_EG_ENDGID_MASK = 0x0000ffff, + NVME_FEAT_EG_EGCW_MASK = 0x00ff0000, + NVME_FEAT_SPM_PBSLC_MASK = 0x000000ff, + NVME_FEAT_HOSTID_EXHID_MASK = 0x00000001, + NVME_FEAT_RM_REGPRE_MASK = 0x00000002, + NVME_FEAT_RM_RESREL_MASK = 0x00000004, + NVME_FEAT_RM_RESPRE_MASK = 0x00000008, + NVME_FEAT_RP_PTPL_MASK = 0x00000001, + NVME_FEAT_WP_WPS_MASK = 0x00000007, +}; + +#define shift(v, s, m) ((v & m) >> s) + +#define NVME_FEAT_ARB_BURST(v) shift(v, 0, NVME_FEAT_ARB_BURST_MASK) +#define NVME_FEAT_ARB_LPW(v) shift(v, 8, NVME_FEAT_ARB_LPW_MASK) +#define NVME_FEAT_ARB_MPW(v) shift(v, 16, NVME_FEAT_ARB_MPW_MASK) +#define NVME_FEAT_ARB_HPW(v) shift(v, 24, NVME_FEAT_ARB_HPW_MASK) +#define NVME_FEAT_PM_PS(v) shift(v, 0, NVME_FEAT_PM_PS_MASK) +#define NVME_FEAT_PM_WH(v) shift(v, 5, NVME_FEAT_PM_WH_MASK) +#define NVME_FEAT_LBAR_NR(v) shift(v, 0, NVME_FEAT_LBAR_NR_MASK) +#define NVME_FEAT_TT_TMPTH(v) shift(v, 0, NVME_FEAT_TT_TMPTH_MASK) +#define NVME_FEAT_TT_TMPSEL(v) shift(v, 16, NVME_FEAT_TT_TMPSEL_MASK) +#define NVME_FEAT_TT_THSEL(v) shift(v, 20, NVME_FEAT_TT_THSEL_MASK) +#define NVME_FEAT_ER_TLER(v) shift(v, 0, NVME_FEAT_ER_TLER_MASK) +#define NVME_FEAT_ER_DULBE(v) shift(v, 16, NVME_FEAT_ER_DULBE_MASK) +#define NVME_FEAT_VWC_WCE(v) shift(v, 0, NVME_FEAT_VWC_WCE_MASK) +#define NVME_FEAT_NRQS_NSQR(v) shift(v, 0, NVME_FEAT_NRQS_NSQR_MASK) +#define NVME_FEAT_NRQS_NCQR(v) shift(v, 16, NVME_FEAT_NRQS_NCQR_MASK) +#define NVME_FEAT_ICOAL_THR(v) shift(v, 0, NVME_FEAT_ICOAL_THR_MASK) +#define NVME_FEAT_ICOAL_TIME(v) shift(v, 8, NVME_FEAT_ICOAL_TIME_MASK) +#define NVME_FEAT_ICFG_IV(v) shift(v, 0, NVME_FEAT_ICFG_IV_MASK) +#define NVME_FEAT_ICFG_CD(v) shift(v, 16, NVME_FEAT_ICFG_CD_MASK) +#define NVME_FEAT_WA_DN(v) shift(v, 0, NVME_FEAT_WA_DN_MASK) +#define NVME_FEAT_AE_SMART(v) shift(v, 0, NVME_FEAT_AE_SMART_MASK) +#define NVME_FEAT_AE_NAN(v) shift(v, 8, NVME_FEAT_AE_NAN_MASK) +#define NVME_FEAT_AE_FW(v) shift(v, 9, NVME_FEAT_AE_FW_MASK) +#define NVME_FEAT_AE_TELEM(v) shift(v, 10, NVME_FEAT_AE_TELEM_MASK) +#define NVME_FEAT_AE_ANA(v) shift(v, 11, NVME_FEAT_AE_ANA_MASK) +#define NVME_FEAT_AE_PLA(v) shift(v, 12, NVME_FEAT_AE_PLA_MASK) +#define NVME_FEAT_AE_LBAS(v) shift(v, 13, NVME_FEAT_AE_LBAS_MASK) +#define NVME_FEAT_AE_EGA(v) shift(v, 14, NVME_FEAT_AE_EGA_MASK) +#define NVME_FEAT_APST_APSTE(v) shift(v, 0, NVME_FEAT_APST_APSTE_MASK) +#define NVME_FEAT_HMEM_EHM(v) shift(v, 0, NVME_FEAT_HMEM_EHM_MASK) +#define NVME_FEAT_TS_SYNC(v) shift(v, 0, NVME_FEAT_TS_SYNCH_MASK) +#define NVME_FEAT_TS_ORIGIN(v) shift(v, 1, NVME_FEAT_TS_ORIGIN_MASK) +#define NVME_FEAT_HCTM_TMT2(v) shift(v, 0, NVME_FEAT_HCTM_TMT2_MASK) +#define NVME_FEAT_HCTM_TMT1(v) shift(v, 16, NVME_FEAT_HCTM_TMT1_MASK) +#define NVME_FEAT_NOPS_NOPPME(v) shift(v, 0, NVME_FEAT_NOPS_NOPPME_MASK) +#define NVME_FEAT_RRL_RRL(v) shift(v, 0, NVME_FEAT_RRL_RRL_MASK) +#define NVME_FEAT_PLM_PLME(v) shift(v, 0, NVME_FEAT_PLM_PLME_MASK) +#define NVME_FEAT_PLMW_WS(v) shift(v, 0, NVME_FEAT_PLMW_WS_MASK) +#define NVME_FEAT_LBAS_LSIRI(v) shift(v, 0, NVME_FEAT_LBAS_LSIRI_MASK) +#define NVME_FEAT_LBAS_LSIPI(v) shift(v, 16, NVME_FEAT_LBAS_LSIPI_MASK) +#define NVME_FEAT_SC_NODRM(v) shift(v, 0, NVME_FEAT_SC_NODRM_MASK) +#define NVME_FEAT_EG_ENDGID(v) shift(v, 0, NVME_FEAT_EG_ENDGID_MASK) +#define NVME_FEAT_EG_EGCW(v) shift(v, 16, NVME_FEAT_EG_EGCW_MASK) +#define NVME_FEAT_SPM_PBSLC(v) shift(v, 0, NVME_FEAT_SPM_PBSLC_MASK) +#define NVME_FEAT_HOSTID_EXHID(v) shift(v, 0, NVME_FEAT_HOSTID_EXHID_MASK) +#define NVME_FEAT_RM_REGPRE(v) shift(v, 1, NVME_FEAT_RM_REGPRE_MASK) +#define NVME_FEAT_RM_RESREL(v) shift(v, 2, NVME_FEAT_RM_RESREL_MASK) +#define NVME_FEAT_RM_RESPRE(v) shift(v, 3, NVME_FEAT_RM_RESPRE_MASK) +#define NVME_FEAT_RP_PTPL(v) shift(v, 0, NVME_FEAT_RP_PTPL_MASK) +#define NVME_FEAT_WP_WPS(v) shift(v, 0, NVME_FEAT_WP_WPS_MASK) + +/** + * struct nvme_streams_directive_params - + */ +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 - + */ +struct nvme_streams_directive_status { + __le16 osc; + __le16 sid[]; +}; + +/** + * struct nvme_id_directives - + */ +struct nvme_id_directives { + __u8 supported[32]; + __u8 enabled[32]; + __u8 rsvd64[4032]; +}; + +/** + * enum - + */ +enum { + NVME_ID_DIR_ID_BIT = 0, + NVME_ID_DIR_SD_BIT = 1, +}; + +/** + * struct nvme_host_mem_buf_desc - + */ +struct nvme_host_mem_buf_desc { + __le64 addr; + __le32 size; + __u32 rsvd; +}; + +/** + * enum - + */ +enum { + NVME_AER_ERROR = 0, + NVME_AER_SMART = 1, + NVME_AER_NOTICE = 2, + NVME_AER_CSS = 6, + NVME_AER_VS = 7, +}; + +/** + * enum - + */ +enum { + NVME_AER_NOTICE_NS_CHANGED = 0x00, + NVME_AER_NOTICE_FW_ACT_STARTING = 0x01, + NVME_AER_NOTICE_ANA = 0x03, + NVME_AER_NOTICE_DISC_CHANGED = 0xf0, +}; + +/** + * enum nvme_subsys_type - + * @NVME_NQN_DISC: Discovery type target subsystem + * @NVME_NQN_NVME: NVME type target subsystem + */ +enum nvme_subsys_type { + NVME_NQN_DISC = 1, + NVME_NQN_NVME = 2, +}; + +#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" +#define NVME_RDMA_IP_PORT 4420 + +/* NQN names in commands fields specified one size */ +#define NVMF_NQN_FIELD_LEN 256 + +/* However the max length of a qualified name is another size */ +#define NVMF_NQN_SIZE 223 +#define NVMF_TRSVCID_SIZE 32 +#define NVMF_TRADDR_SIZE 256 +#define NVMF_TSAS_SIZE 256 + +/** + * struct nvmf_disc_log_entry - + * + * Discovery log page entry + */ +struct nvmf_disc_log_entry { + __u8 trtype; + __u8 adrfam; + __u8 subtype; + __u8 treq; + __le16 portid; + __le16 cntlid; + __le16 asqsz; + __u8 resv10[22]; + char trsvcid[NVMF_TRSVCID_SIZE]; + __u8 resv64[192]; + char subnqn[NVMF_NQN_FIELD_LEN]; + char traddr[NVMF_TRADDR_SIZE]; + union tsas { + char common[NVMF_TSAS_SIZE]; + struct rdma { + __u8 qptype; + __u8 prtype; + __u8 cms; + __u8 resv3[5]; + __u16 pkey; + __u8 resv10[246]; + } rdma; + struct tcp { + __u8 sectype; + } tcp; + } tsas; +}; + +/** + * enum - + * @NVMF_TRTYPE_UNSPECIFIED: Not indicated + * @NVMF_TRTYPE_RDMA: RDMA + * @NVMF_TRTYPE_FC: Fibre Channel + * @NVMF_TRTYPE_TCP: TCP + * @NVMF_TRTYPE_LOOP: Reserved for host usage + * + * Transport Type codes for Discovery Log Page entry TRTYPE field + */ +enum { + 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_PCI: PCIe + * @NVMF_ADDR_FAMILY_IP4: IPv4 + * @NVMF_ADDR_FAMILY_IP6: IPv6 + * @NVMF_ADDR_FAMILY_IB: InfiniBand + * @NVMF_ADDR_FAMILY_FC: Fibre Channel + * + * Address Family codes for Discovery Log Page entry ADRFAM field + */ +enum { + 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, +}; + +/** + * enum - + * @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 + * + * Transport Requirements codes for Discovery Log Page entry TREQ field + */ +enum { + NVMF_TREQ_NOT_SPECIFIED = 0, + NVMF_TREQ_REQUIRED = 1, + NVMF_TREQ_NOT_REQUIRED = 2, + NVMF_TREQ_DISABLE_SQFLOW = 4, +}; + +/** + * enum - + * @NVMF_RDMA_QPTYPE_CONNECTED: Reliable Connected + * @NVMF_RDMA_QPTYPE_DATAGRAM: Reliable Datagram + * + * RDMA QP Service Type codes for Discovery Log Page entry TSAS + * RDMA_QPTYPE field + */ +enum { + NVMF_RDMA_QPTYPE_CONNECTED = 1, + NVMF_RDMA_QPTYPE_DATAGRAM = 2, +}; + +/** + * enum - + * @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 + * + * RDMA Provider Type codes for Discovery Log Page entry TSAS + * RDMA_PRTYPE field + */ +enum { + 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_CM: Sockets based endpoint addressing + * + * RDMA Connection Management Service Type codes for Discovery Log Page + * entry TSAS RDMA_CMS field + */ +enum { + NVMF_RDMA_CMS_RDMA_CM = 1, +}; + +/** + * enum - + * @NVMF_TCP_SECTYPE_NONE: No Security + * @NVMF_TCP_SECTYPE_TLS: Transport Layer Security + */ +enum { + NVMF_TCP_SECTYPE_NONE = 0, + NVMF_TCP_SECTYPE_TLS = 1, +}; + +/** + * struct nvmf_discovery_log - + */ +struct nvmf_discovery_log { + __le64 genctr; + __le64 numrec; + __le16 recfmt; + __u8 resv14[1006]; + struct nvmf_disc_log_entry entries[0]; +}; + +/** + * struct nvmf_connect_data - + */ +struct nvmf_connect_data { + __u8 hostid[16]; + __le16 cntlid; + char resv4[238]; + char subsysnqn[NVMF_NQN_FIELD_LEN]; + char hostnqn[NVMF_NQN_FIELD_LEN]; + char resv5[256]; +}; + +/** + * enum - + */ +enum { + /* + * Status code type + */ + NVME_SCT_GENERIC = 0x000, + NVME_SCT_CMD_SPECIFIC = 0x100, + NVME_SCT_MEDIA = 0x200, + NVME_SCT_PATH = 0x300, + NVME_SCT_VS = 0x700, + NVME_SCT_MASK = 0x700, + + /* + * Generic Command Status: + */ + 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_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: + */ + 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, + + /* + * I/O Command Set Specific - NVM commands: + */ + NVME_SC_BAD_ATTRIBUTES = 0x80, + NVME_SC_INVALID_PI = 0x81, + NVME_SC_READ_ONLY = 0x82, + + /* + * 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, + + /* + * 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, + + /* + * 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, + + /* + * Status code mask + */ + NVME_SC_MASK = 0xff, + + /* + * Additional status info + */ + NVME_SC_CRD = 0x1800, + NVME_SC_MORE = 0x2000, + NVME_SC_DNR = 0x4000, +}; + +#define NVME_MAJOR(ver) ((ver) >> 16) +#define NVME_MINOR(ver) (((ver) >> 8) & 0xff) +#define NVME_TERTIARY(ver) ((ver) & 0xff) + +#endif /* _LIBNVME_TYPES_H */ diff --git a/lib/nvme/util.c b/lib/nvme/util.c new file mode 100644 index 00000000..bd06328a --- /dev/null +++ b/lib/nvme/util.c @@ -0,0 +1,579 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "filters.h" +#include "types.h" +#include "cmd.h" +#include "ioctl.h" +#include "util.h" +#include "tree.h" + +static inline __u8 nvme_generic_status_to_errno(__u16 status) +{ + switch (status) { + case NVME_SC_INVALID_OPCODE: + case NVME_SC_INVALID_FIELD: + case NVME_SC_INVALID_NS: + case NVME_SC_SGL_INVALID_LAST: + case NVME_SC_SGL_INVALID_COUNT: + case NVME_SC_SGL_INVALID_DATA: + case NVME_SC_SGL_INVALID_METADATA: + case NVME_SC_SGL_INVALID_TYPE: + case NVME_SC_SGL_INVALID_OFFSET: + 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: + return EINPROGRESS; + case NVME_SC_NS_WRITE_PROTECTED: + case NVME_SC_NS_NOT_READY: + case NVME_SC_RESERVATION_CONFLICT: + return EACCES; + case NVME_SC_LBA_RANGE: + return EREMOTEIO; + case NVME_SC_CAP_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: + return EINVAL; + case NVME_SC_ABORT_LIMIT: + case NVME_SC_ASYNC_LIMIT: + return EDQUOT; + case NVME_SC_FW_NEEDS_CONV_RESET: + case NVME_SC_FW_NEEDS_SUBSYS_RESET: + case NVME_SC_FW_NEEDS_MAX_TIME: + 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: + return EOPNOTSUPP; + } + + 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: + 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, sct; + + if (!status) + return 0; + if (status < 0) + return errno; + + sc = status & NVME_SC_MASK; + sct = status & NVME_SCT_MASK; + + switch (sct) { + 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: + /* + * Media, integrity related status, and the others will be + * mapped to EIO. + */ + return EIO; + } +} + +static int __nvme_open(const char *name) +{ + char *path; + int fd, ret; + + ret = asprintf(&path, "%s/%s", "/dev", name); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + fd = open(path, O_RDONLY); + free(path); + return fd; +} + +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_split(int fd, __u32 size, __u32 xfer, __u32 offset, + void *buf) +{ + int err = 0; + + while (size > 0) { + xfer = MIN(xfer, size); + err = nvme_fw_download(fd, offset, xfer, buf); + if (err) + break; + + buf += xfer; + size -= xfer; + offset += xfer; + } + + return err; +} + +int nvme_get_log_page(int fd, __u32 nsid, __u8 log_id, bool rae, + __u32 data_len, void *data) +{ + __u64 offset = 0, xfer_len = data_len; + void *ptr = data; + int ret; + + /* + * 4k is the smallest possible transfer unit, so restricting to 4k + * avoids having to check the MDTS value of the controller. + */ + do { + xfer_len = data_len - offset; + if (xfer_len > 4096) + xfer_len = 4096; + + ret = nvme_get_log(fd, log_id, nsid, offset, NVME_LOG_LSP_NONE, + NVME_LOG_LSI_NONE, rae, NVME_UUID_NONE, + xfer_len, ptr); + if (ret) + return ret; + + offset += xfer_len; + ptr += xfer_len; + } while (offset < data_len); + + return 0; +} + +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, int data_area, + void **buf, __u32 *log_size) +{ + struct nvme_telemetry_log *telem; + static const __u32 xfer = 512; + __u32 size, offset = xfer; + void *log; + int err; + + log = malloc(xfer); + if (!log) { + errno = ENOMEM; + return -1; + } + + if (ctrl) + err = nvme_get_log_telemetry_ctrl(fd, true, 0, xfer, log); + else if (create) + err = nvme_get_log_create_telemetry_host(fd, log); + else + err = nvme_get_log_telemetry_host(fd, 0, xfer, log); + + if (err) + goto free; + + telem = log; + if (!telem->ctrlavail) { + size = xfer; + goto done; + } + + switch (data_area) { + case 1: + size = (le16_to_cpu(telem->dalb1) * xfer) + xfer; + break; + case 2: + size = (le16_to_cpu(telem->dalb2) * xfer) + xfer; + break; + case 3: + size = (le16_to_cpu(telem->dalb3) * xfer) + xfer; + break; + default: + errno = EINVAL; + err = -1; + goto free; + } + + log = realloc(log, size); + if (!log) { + errno = ENOMEM; + err = -1; + goto free; + } + + while (offset != size) { + if (ctrl) + err = nvme_get_log_telemetry_ctrl(fd, true, offset, + xfer, log + offset); + else + err = nvme_get_log_telemetry_host(fd, offset, xfer, + log + offset); + if (err) + goto free; + offset += xfer; + } +done: + *log_size = size; + *buf = log; + return 0; +free: + free(log); + return err; +} + +void nvme_setup_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_setup_id_ns(struct nvme_id_ns *ns, __u64 nsze, __u64 ncap, __u8 flbas, + __u8 dps, __u8 nmic, __u32 anagrpid, __u16 nvmsetid) +{ + memset(ns, 0, sizeof(*ns)); + ns->nsze = cpu_to_le64(nsze); + ns->ncap = cpu_to_le64(ncap); + ns->flbas = flbas; + ns->dps = dps; + ns->nmic = nmic; + ns->anagrpid = cpu_to_le32(anagrpid); + ns->nvmsetid = cpu_to_le16(nvmsetid); +} + +void nvme_setup_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]); +} + +static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist, bool attach) +{ + struct nvme_ctrl_list cntlist = { 0 }; + enum nvme_ns_attach_sel sel; + + if (attach) + sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH; + else + sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH; + + nvme_setup_ctrl_list(&cntlist, num_ctrls, ctrlist); + return nvme_ns_attach(fd, nsid, sel, &cntlist); +} + +int nvme_namespace_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist) +{ + return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, true); +} + +int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist) +{ + return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, false); +} + +int nvme_get_ana_log_len(int fd, size_t *analen) +{ + struct nvme_id_ctrl ctrl; + int ret; + + 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_feature_length(int fid, __u32 cdw11, __u32 *len) +{ + switch (fid) { + case NVME_FEAT_FID_LBA_RANGE: + *len = sizeof(struct nvme_lba_range_type); + break; + case NVME_FEAT_FID_AUTO_PST: + *len = sizeof(struct nvme_feat_auto_pst); + break; + case NVME_FEAT_FID_PLM_CONFIG: + *len = sizeof(struct nvme_plm_config); + break; + case NVME_FEAT_FID_TIMESTAMP: + *len = sizeof(struct nvme_timestamp); + break; + case NVME_FEAT_FID_HOST_BEHAVIOR: + *len = sizeof(struct nvme_feat_host_behavior); + break; + case NVME_FEAT_FID_HOST_ID: + *len = (cdw11 & 0x1) ? 16 : 8; + break; + case NVME_FEAT_FID_ARBITRATION: + case NVME_FEAT_FID_POWER_MGMT: + case NVME_FEAT_FID_TEMP_THRESH: + case NVME_FEAT_FID_ERR_RECOVERY: + case NVME_FEAT_FID_VOLATILE_WC: + case NVME_FEAT_FID_NUM_QUEUES: + case NVME_FEAT_FID_IRQ_COALESCE: + case NVME_FEAT_FID_IRQ_CONFIG: + case NVME_FEAT_FID_WRITE_ATOMIC: + case NVME_FEAT_FID_ASYNC_EVENT: + case NVME_FEAT_FID_HOST_MEM_BUF: + case NVME_FEAT_FID_KATO: + case NVME_FEAT_FID_HCTM: + case NVME_FEAT_FID_NOPSC: + case NVME_FEAT_FID_RRL: + case NVME_FEAT_FID_PLM_WINDOW: + case NVME_FEAT_FID_LBA_STS_INTERVAL: + case NVME_FEAT_FID_SANITIZE: + case NVME_FEAT_FID_ENDURANCE_EVT_CFG: + case NVME_FEAT_FID_SW_PROGRESS: + case NVME_FEAT_FID_RESV_MASK: + case NVME_FEAT_RESV_PERSIST: + case NVME_FEAT_FID_WRITE_PROTECT: + *len = 0; + break; + default: + return EINVAL; + } + return 0; +} + +int nvme_get_directive_receive_length(__u8 dtype, __u8 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); + break; + default: + return -EINVAL; + } + break; + case NVME_DIRECTIVE_DTYPE_STREAMS: + switch (doper) { + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM: + *len = sizeof(struct nvme_streams_directive_params); + break; + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS: + *len = (128 * 1024) * sizeof(__le16); + break; + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE: + *len = 0; + break; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + return 0; +} + +static int __nvme_set_attr(const char *path, const char *value) +{ + int ret, fd; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + ret = write(fd, value, strlen(value)); + close(fd); + return ret; +} + +int nvme_set_attr(const char *dir, const char *attr, const char *value) +{ + char *path; + int ret; + + ret = asprintf(&path, "%s/%s", dir, attr); + if (ret < 0) + return -1; + + ret = __nvme_set_attr(path, value); + free(path); + return ret; +} + +static char *__nvme_get_attr(const char *path) +{ + char value[4096] = { 0 }; + int ret, fd; + + fd = open(path, O_RDONLY); + if (fd < 0) + return NULL; + + memset(value, 0, sizeof(value)); + ret = read(fd, value, sizeof(value) - 1); + if (ret < 0) { + close(fd); + 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'; + + close(fd); + return strdup(value); +} + +static char *nvme_get_attr(const char *dir, const char *attr) +{ + char *path, *value; + int ret; + + ret = asprintf(&path, "%s/%s", dir, attr); + if (ret < 0) + return NULL; + + value = __nvme_get_attr(path); + free(path); + return value; +} + +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); +} diff --git a/lib/nvme/util.h b/lib/nvme/util.h new file mode 100644 index 00000000..96f94218 --- /dev/null +++ b/lib/nvme/util.h @@ -0,0 +1,133 @@ +#ifndef _LIBNVME_UTIL_H +#define _LIBNVME_UTIL_H + +#include +#include + +#include "types.h" + +/** + * nvme_status_type() - Returns SCT(Status Code Type) in status field of + * the completion queue entry. + * @status: return value from nvme passthrough commands, which is the nvme + * status field, located at DW3 in completion queue entry + */ +static inline __u8 nvme_status_type(__u16 status) +{ + return (status & NVME_SCT_MASK) >> 8; +} + +/** + * nvme_status_to_string() - + */ +const char *nvme_status_to_string(int status, bool fabrics); + +/* + * nvme_status_to_errno() - Converts nvme return status to errno + * @status: >= 0 for nvme status field in completion queue entry, + * < 0 for linux internal errors + * @fabrics: true if given status is for fabrics + * + * Notes: This function will convert a given status to an errno + */ +__u8 nvme_status_to_errno(int status, bool fabrics); + +/** + * nvme_fw_download_split() - + */ +int nvme_fw_download_split(int fd, __u32 size, __u32 xfer, __u32 offset, + void *buf); + +/** + * nvme_get_telemetry_log() - + */ +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, int data_area, + void **buf, __u32 *log_size); + +/** + * nvme_setup_id_ns() - + */ +void nvme_setup_id_ns(struct nvme_id_ns *ns, __u64 nsze, __u64 ncap, __u8 flbas, + __u8 dps, __u8 nmic, __u32 anagrpid, __u16 nvmsetid); + +/** + * nvme_setup_ctrl_list() - + */ +void nvme_setup_ctrl_list(struct nvme_ctrl_list *cntlist, __u16 num_ctrls, + __u16 *ctrlist); + +/** + * nvme_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'. + * + * Return: The nvme command status if a response was received or -errno + * otherwise. + */ +void nvme_setup_dsm_range(struct nvme_dsm_range *dsm, __u32 *ctx_attrs, + __u32 *llbas, __u64 *slbas, __u16 nr_ranges); + +/** + * nvme_get_log_page() - + */ +int nvme_get_log_page(int fd, __u32 nsid, __u8 log_id, bool rae, + __u32 data_len, void *data); + +/** + * nvme_get_ana_log_len() - + */ +int nvme_get_ana_log_len(int fd, size_t *analen); + +/** + * 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 or -errno + * 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 or -errno + * otherwise. + */ +int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist); + +/** + * nvme_get_feature_length() - + */ +int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len); + +/** + * nvme_get_directive_receive_length() - + */ +int nvme_get_directive_receive_length(__u8 dtype, __u8 doper, __u32 *len); + +/** + * 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); + +int nvme_set_attr(const char *dir, const char *attr, const char *value); +#endif /* _LIBNVME_UTIL_H */ diff --git a/test/test.c b/test/test.c new file mode 100644 index 00000000..0f1bf29c --- /dev/null +++ b/test/test.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include + +static char *nqn_match; + +static bool nvme_match_subsysnqn_filter(nvme_subsystem_t s) +{ + return strcmp(nvme_subsystem_get_nqn(s), nqn_match) == 0; +} + +static int test_ctrl(nvme_ctrl_t c) +{ + static __u8 buf[0x1000]; + + enum nvme_get_features_sel sel = NVME_GET_FEATURES_SEL_CURRENT; + int ret, temp, fd = nvme_ctrl_get_fd(c); + struct nvme_error_log_page error[64]; + struct nvme_smart_log smart = { 0 }; + struct nvme_firmware_slot fw = { 0 }; + struct nvme_ns_list ns_list = { 0 }; + struct nvme_cmd_effects_log cfx = { 0 }; + struct nvme_self_test_log st = { 0 }; + struct nvme_telemetry_log *telem = (void *)buf; + struct nvme_endurance_group_log eglog = { 0 }; + struct nvme_ana_group_desc *analog = (void *)buf; + struct nvme_resv_notification_log resvnotify = { 0 }; + struct nvme_sanitize_log_page sanlog = { 0 }; + + struct nvme_id_uuid_list uuid = { 0 }; + struct nvme_id_ns_granularity_list gran = { 0 }; + struct nvme_secondary_ctrl_list sec = { 0 }; + struct nvme_primary_ctrl_cap prim = { 0 }; + struct nvme_ctrl_list ctrlist = { 0 }; + struct nvme_id_ctrl id = { 0 }; + + __u32 result; + + ret = nvme_ctrl_identify(c, &id); + if (ret) { + printf("ERROR: no identify for:%s\n", nvme_ctrl_get_name(c)); + return ret; + } + + ret = nvme_get_log_smart(fd, NVME_NSID_ALL, true, &smart); + if (ret) { + printf("ERROR: no smart log for:%s %x\n", nvme_ctrl_get_name(c), ret); + return ret; + } + + temp = ((smart.temperature[1] << 8) | smart.temperature[0]) - 273; + printf("Controller:%s\n", nvme_ctrl_get_name(c)); + printf("\nIdentify:\n"); + printf(" vid:%04x ssvid:%04x oacs:%x lpa%x\n", + le16_to_cpu(id.vid), le16_to_cpu(id.ssvid), + id.oacs, id.lpa); + printf(" sn:%-.20s model:%-.40s\n", id.sn, id.mn); + + ret = nvme_identify_allocated_ns_list(fd, 0, &ns_list); + if (!ret) + printf(" Allocated NS List:\n"); + else + printf(" ERROR: Allocated NS List:%x\n", ret); + ret = nvme_identify_active_ns_list(fd, 0, &ns_list); + if (!ret) + printf(" Active NS List:\n"); + else + printf(" ERROR: Active NS List:%x\n", ret); + ret = nvme_identify_ctrl_list(fd, 0, &ctrlist); + if (!ret) + printf(" Ctrl List:\n"); + else + printf(" ERROR: CtrlList:%x\n", ret); + ret = nvme_identify_nsid_ctrl_list(fd, 1, 0, &ctrlist); + if (!ret) + printf(" NSID Ctrl List:\n"); + else + printf(" ERROR: NSID CtrlList:%x\n", ret); + ret = nvme_identify_primary_ctrl(fd, 0, &prim); + if (!ret) + printf(" Identify Primary:\n"); + else + printf(" ERROR: Identify Primary:%x\n", ret); + ret = nvme_identify_secondary_ctrl_list(fd, 0, &sec); + if (!ret) + printf(" Identify Secondary:\n"); + else + printf(" ERROR: Identify Secondary:%x\n", ret); + ret = nvme_identify_ns_granularity(fd, &gran); + if (!ret) + printf(" Identify NS granularity:\n"); + else + printf(" ERROR: Identify NS granularity:%x\n", ret); + ret = nvme_identify_uuid(fd, &uuid); + if (!ret) + printf(" Identify UUID List:\n"); + else + printf(" ERROR: Identify UUID List:%x\n", ret); + + printf("\nLogs\n"); + printf(" SMART: Current temperature:%d percent used:%d%%\n", temp, + smart.percent_used); + ret = nvme_get_log_sanitize(fd, true, &sanlog); + if (!ret) + printf(" Sanitize Log:\n"); + else + printf(" ERROR: Sanitize Log:%x\n", ret); + ret = nvme_get_log_reservation(fd, true, &resvnotify); + if (!ret) + printf(" Reservation Log\n"); + else + printf(" ERROR: Reservation Log :%x\n", ret); + ret = nvme_get_log_ana_groups(fd, true, sizeof(buf), analog); + if (!ret) + printf(" ANA Groups\n"); + else + printf(" ERROR: ANA Groups :%x\n", ret); + ret = nvme_get_log_endurance_group(fd, 0, &eglog); + if (!ret) + printf(" Endurance Group\n"); + else + printf(" ERROR: Endurance Group :%x\n", ret); + ret = nvme_get_log_telemetry_ctrl(fd, true, 0, sizeof(buf), telem); + if (!ret) + printf(" Telemetry Controller\n"); + else + printf(" ERROR: Telemetry Controller :%x\n", ret); + ret = nvme_get_log_device_self_test(fd, &st); + if (!ret) + printf(" Device Self Test\n"); + else + printf(" ERROR: Device Self Test :%x\n", ret); + ret = nvme_get_log_cmd_effects(fd, &cfx); + if (!ret) + printf(" Command Effects\n"); + else + printf(" ERROR: Command Effects :%x\n", ret); + ret = nvme_get_log_changed_ns_list(fd, true, &ns_list); + if (!ret) + printf(" Change NS List\n"); + else + printf(" ERROR: Change NS List :%x\n", ret); + ret = nvme_get_log_fw_slot(fd, true, &fw); + if (ret) + printf(" FW Slot\n"); + else + printf(" ERROR: FW Slot :%x\n", ret); + ret = nvme_get_log_error(fd, 64, true, error); + if (!ret) + printf(" Error Log\n"); + else + printf(" ERROR: Error Log :%x\n", ret); + printf("\nFeatures\n"); + ret = nvme_get_features_arbitration(fd, sel, &result); + if (!ret) + printf(" Arbitration:%x\n", result); + else if (ret > 0) + printf(" ERROR: Arbitration:%x\n", ret); + ret = nvme_get_features_power_mgmt(fd, sel, &result); + if (!ret) + printf(" Power Management:%x\n", result); + else if (ret > 0) + printf(" ERROR: Power Management:%x\n", ret); + ret = nvme_get_features_temp_thresh(fd, sel, &result); + if (!ret) + printf(" Temperature Threshold:%x\n", result); + else if (ret > 0) + printf(" ERROR: Temperature Threshold:%x\n", ret); + ret = nvme_get_features_err_recovery(fd, sel, &result); + if (!ret) + printf(" Error Recovery:%x\n", result); + else if (ret > 0) + printf(" ERROR: Error Recovery:%x\n", ret); + ret = nvme_get_features_volatile_wc(fd, sel, &result); + if (!ret) + printf(" Volatile Write Cache:%x\n", result); + else if (ret > 0) + printf(" ERROR: Volatile Write Cache:%x\n", ret); + ret = nvme_get_features_num_queues(fd, sel, &result); + if (!ret) + printf(" Number of Queues:%x\n", result); + else if (ret > 0) + printf(" ERROR: Number of Queues:%x\n", ret); + ret = nvme_get_features_irq_coalesce(fd, sel, &result); + if (!ret) + printf(" IRQ Coalescing:%x\n", result); + else if (ret > 0) + printf(" ERROR: IRQ Coalescing:%x\n", ret); + ret = nvme_get_features_write_atomic(fd, sel, &result); + if (!ret) + printf(" Write Atomic:%x\n", result); + else if (ret > 0) + printf(" ERROR: Write Atomic:%x\n", ret); + ret = nvme_get_features_async_event(fd, sel, &result); + if (!ret) + printf(" Asycn Event Config:%x\n", result); + else if (ret > 0) + printf(" ERROR: Asycn Event Config:%x\n", ret); + ret = nvme_get_features_hctm(fd, sel, &result); + if (!ret) + printf(" HCTM:%x\n", result); + else if (ret > 0) + printf(" ERROR: HCTM:%x\n", ret); + ret = nvme_get_features_nopsc(fd, sel, &result); + if (!ret) + printf(" NOP Power State Config:%x\n", result); + else if (ret > 0) + printf(" ERROR: NOP Power State Configrbitration:%x\n", ret); + ret = nvme_get_features_rrl(fd, sel, &result); + if (!ret) + printf(" Read Recover Levels:%x\n", result); + else if (ret > 0) + printf(" ERROR: Read Recover Levels:%x\n", ret); + ret = nvme_get_features_lba_sts_interval(fd, sel, &result); + if (!ret) + printf(" LBA Status Interval:%x\n", result); + else if (ret > 0) + printf(" ERROR: LBA Status Interval:%x\n", ret); + ret = nvme_get_features_sanitize(fd, sel, &result); + if (!ret) + printf(" Sanitize:%x\n", result); + else if (ret > 0) + printf(" ERROR: SW Progress Marker:%x\n", ret); + ret = nvme_get_features_sw_progress(fd, sel, &result); + if (!ret) + printf(" SW Progress Marker:%x\n", result); + else if (ret > 0) + printf(" ERROR: Sanitize:%x\n", ret); + ret = nvme_get_features_resv_mask(fd, sel, &result); + if (!ret) + printf(" Reservation Mask:%x\n", result); + else if (ret > 0) + printf(" ERROR: Reservation Mask:%x\n", ret); + ret = nvme_get_features_resv_persist(fd, sel, &result); + if (!ret) + printf(" Reservation Persistence:%x\n", result); + else if (ret > 0) + printf(" ERROR: Reservation Persistence:%x\n", ret); + ret = nvme_get_features_write_protect(fd, 1, sel, &result); + if (!ret) + printf(" Write Protect:%x\n", result); + else if (ret > 0) + printf(" ERROR: Write Protect:%x\n", ret); + return 0; +} + +static int test_namespace(nvme_ns_t n) +{ + struct nvme_id_ns ns = { 0 }, allocated = { 0 }; + int nsid = nvme_ns_get_nsid(n); + int ret, fd = nvme_ns_get_fd(n); + struct nvme_ns_id_desc descs = { 0 }; + + ret = nvme_ns_identify(n, &ns); + if (ret) + return ret; + + printf("%s: nsze:%lx lba size:%d\n", nvme_ns_get_name(n), le64_to_cpu(ns.nsze), + 1 << ns.lbaf[ns.flbas & NVME_NS_FLBAS_LBA_MASK].ds); + + ret = nvme_identify_allocated_ns(fd, nsid, &allocated); + if (!ret) + printf(" Identify allocated ns\n"); + else + printf(" ERROR: Identify allocated ns:%x\n", ret); + ret = nvme_identify_ns_descs(fd, nsid, &descs); + if (!ret) + printf(" Identify NS Descriptorss\n"); + else + printf(" ERROR: Identify NS Descriptors:%x\n", ret); + return 0; +} + +int main() +{ + nvme_root_t r; + nvme_subsystem_t s; + nvme_ctrl_t c; + nvme_path_t p; + nvme_ns_t n; + + r = nvme_scan(); + if (!r) + return -1; + + printf("Test walking the topology\n"); + nvme_for_each_subsystem(r, s) { + printf("%s - NQN=%s\n", nvme_subsystem_get_name(s), + nvme_subsystem_get_nqn(s)); + nvme_subsystem_for_each_ctrl(s, c) { + printf(" +- %s %s %s %s\n", nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_ctrl_get_state(c)); + + nvme_ctrl_for_each_ns(c, n) + printf(" +- %s lba size:%d lba max:%lu\n", + nvme_ns_get_name(n), nvme_ns_get_lba_size(n), + nvme_ns_get_lba_count(n)); + + nvme_ctrl_for_each_path(c, p) + printf(" +- %s %s\n", nvme_path_get_name(p), + nvme_path_get_ana_state(p)); + } + + nvme_subsystem_for_each_ns(s, n) { + printf(" +- %s lba size:%d lba max:%lu\n", + nvme_ns_get_name(n), nvme_ns_get_lba_size(n), + nvme_ns_get_lba_count(n)); + } + } + printf("\n"); + + nvme_for_each_subsystem(r, s) { + bool first = true; + printf("%s %s ", nvme_subsystem_get_name(s), + nvme_subsystem_get_nqn(s)); + + nvme_subsystem_for_each_ctrl(s, c) { + printf("%s%s", first ? "": ", ", nvme_ctrl_get_name(c)); + first = false; + } + printf("\n"); + } + printf("\n"); + + nvme_for_each_subsystem(r, s) { + nvme_subsystem_for_each_ctrl(s, c) { + bool first = true; + + printf("%s %s %s %s %s %s %s ", nvme_ctrl_get_name(c), + nvme_ctrl_get_serial(c), nvme_ctrl_get_model(c), + nvme_ctrl_get_firmware(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_subsystem_get_name(s)); + + nvme_ctrl_for_each_ns(c, n) { + printf("%s%s", first ? "": ", ", + nvme_ns_get_name(n)); + first = false; + } + + nvme_ctrl_for_each_path(c, p) { + printf("%s%s", first ? "": ", ", + nvme_ns_get_name(nvme_path_get_ns(p))); + first = false; + } + printf("\n"); + } + } + printf("\n"); + + nvme_for_each_subsystem(r, s) { + nvme_subsystem_for_each_ctrl(s, c) + nvme_ctrl_for_each_ns(c, n) + printf("%s %d %lu/%lu %d %s\n", + nvme_ns_get_name(n), + nvme_ns_get_nsid(n), + nvme_ns_get_lba_count(n), + nvme_ns_get_lba_util(n), + nvme_ns_get_lba_size(n), + nvme_ctrl_get_name(c)); + + nvme_subsystem_for_each_ns(s, n) { + bool first = true; + + printf("%s %d %lu/%lu %d ", nvme_ns_get_name(n), + nvme_ns_get_nsid(n), + nvme_ns_get_lba_count(n), + nvme_ns_get_lba_util(n), + nvme_ns_get_lba_size(n)); + nvme_subsystem_for_each_ctrl(s, c) { + printf("%s%s", first ? "" : ", ", + nvme_ctrl_get_name(c)); + first = false; + } + printf("\n"); + } + } + printf("\n"); + + printf("Test identification, logs, and features\n"); + nvme_for_each_subsystem(r, s) { + nvme_subsystem_for_each_ctrl(s, c) { + test_ctrl(c); + nvme_ctrl_for_each_ns(c, n) + test_namespace(n); + printf("\n"); + } + nvme_subsystem_for_each_ns(s, n) + test_namespace(n); + } + printf("\n"); + + nvme_free_tree(r); + + printf("Test filter for common loop back target\n"); + nqn_match = "testnqn"; + r = nvme_scan_filter(nvme_match_subsysnqn_filter); + nvme_for_each_subsystem(r, s) { + printf("%s - NQN=%s\n", nvme_subsystem_get_name(s), + nvme_subsystem_get_nqn(s)); + nvme_subsystem_for_each_ctrl(s, c) { + printf(" `- %s %s %s %s\n", nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_ctrl_get_state(c)); + } + } + printf("\n"); + nvme_free_tree(r); + + printf("Test scan specific controller\n"); + c = nvme_scan_ctrl("nvme4"); + if (c) { + printf("%s %s %s %s\n", nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_ctrl_get_state(c)); + nvme_free_ctrl(c); + } + + return 0; +}