diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libnvme-mi.map | 8 | ||||
-rw-r--r-- | src/libnvme.map | 12 | ||||
-rw-r--r-- | src/meson.build | 2 | ||||
-rw-r--r-- | src/nvme/api-types.h | 44 | ||||
-rw-r--r-- | src/nvme/fabrics.c | 93 | ||||
-rw-r--r-- | src/nvme/ioctl.c | 84 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 245 | ||||
-rw-r--r-- | src/nvme/json.c | 9 | ||||
-rw-r--r-- | src/nvme/linux.c | 39 | ||||
-rw-r--r-- | src/nvme/linux.h | 11 | ||||
-rw-r--r-- | src/nvme/mi-mctp.c | 354 | ||||
-rw-r--r-- | src/nvme/mi.c | 351 | ||||
-rw-r--r-- | src/nvme/mi.h | 53 | ||||
-rw-r--r-- | src/nvme/private.h | 20 | ||||
-rw-r--r-- | src/nvme/tree.c | 26 | ||||
-rw-r--r-- | src/nvme/tree.h | 41 | ||||
-rw-r--r-- | src/nvme/types.h | 361 | ||||
-rw-r--r-- | src/nvme/util.c | 26 | ||||
-rw-r--r-- | src/nvme/util.h | 6 |
19 files changed, 1472 insertions, 313 deletions
diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index 53af942..16b7ad4 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -1,3 +1,11 @@ +LIBNVME_MI_1_3 { + global: + nvme_mi_admin_admin_passthru; + nvme_mi_ep_get_timeout; + nvme_mi_ep_set_timeout; + nvme_mi_set_probe_enabled; +}; + LIBNVME_MI_1_2 { global: nvme_mi_admin_get_features; diff --git a/src/libnvme.map b/src/libnvme.map index be9bca3..85ff6f3 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -1,5 +1,17 @@ # SPDX-License-Identifier: LGPL-2.1-or-later +LIBNVME_1_3 { + global: + nvme_ctrl_is_unique_discovery_ctrl; + nvme_ctrl_set_unique_discovery_ctrl; + nvme_fdp_reclaim_unit_handle_status; + nvme_fdp_reclaim_unit_handle_update; + nvme_io_mgmt_recv; + nvme_io_mgmt_send; + nvme_host_is_pdc_enabled; + nvme_host_set_pdc_enabled; +}; + LIBNVME_1_2 { global: nvme_ctrl_get_dhchap_host_key; diff --git a/src/meson.build b/src/meson.build index 9e49a07..1186e81 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,7 +33,7 @@ deps = [ ] mi_deps = [ - libsystemd_dep, + libdbus_dep, ] source_dir = meson.current_source_dir() diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h index 0de41a6..dac50ae 100644 --- a/src/nvme/api-types.h +++ b/src/nvme/api-types.h @@ -793,6 +793,50 @@ struct nvme_resv_report_args { }; /** + * struct nvme_io_mgmt_recv_args - Arguments for the NVMe I/O Management Receive command + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_io_mgmt_recv_args + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of @data + * @timeout: Timeout in ms + * @mos Management Operation Specific + * @mo Management Operation + */ +struct nvme_io_mgmt_recv_args { + void *data; + int args_size; + int fd; + __u32 nsid; + __u32 data_len; + __u32 timeout; + __u16 mos; + __u8 mo; +}; + +/** + * struct nvme_io_mgmt_send_args - Arguments for the NVMe I/O Management Send command + * @data: Userspace address of the data + * @args_size: Size of &struct nvme_io_mgmt_send_args + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of @data + * @timeout: Timeout in ms + * @mos Management Operation Specific + * @mo Management Operation + */ +struct nvme_io_mgmt_send_args { + void *data; + int args_size; + int fd; + __u32 nsid; + __u32 data_len; + __u32 timeout; + __u16 mos; + __u8 mo; +}; + +/** * struct nvme_zns_mgmt_send_args - Arguments for the NVMe ZNS Management Send command * @slba: Starting logical block address * @result: The command completion result from CQE dword0 diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index a501f79..7134dba 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -47,13 +47,19 @@ const char *nvmf_dev = "/dev/nvme-fabrics"; /** * strchomp() - Strip trailing white space - * @s: String to strip - * @l: Maximum length of string + * @str: String to strip + * @max: Maximum length of string */ -static void strchomp(char *s, int l) +static void strchomp(char *str, int max) { - while (l && (s[l] == '\0' || s[l] == ' ')) - s[l--] = '\0'; + int i; + + for (i = max - 1; i >= 0; i--) { + if (str[i] != '\0' && str[i] != ' ') + return; + else + str[i] = '\0'; + } } const char *arg_str(const char * const *strings, @@ -114,7 +120,7 @@ const char *nvmf_treq_str(__u8 treq) } static const char * const eflags_strings[] = { - [NVMF_DISC_EFLAGS_NONE] = "not specified", + [NVMF_DISC_EFLAGS_NONE] = "none", [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections", [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information", [NVMF_DISC_EFLAGS_EPCSD | @@ -459,6 +465,7 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) } if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) { nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, false); discovery_nqn = true; } if (nvme_ctrl_is_discovery_ctrl(c)) @@ -561,6 +568,9 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr) case EOPNOTSUPP: ret = -ENVME_CONNECT_OPNOTSUPP; break; + case ECONNREFUSED : + ret = -ENVME_CONNECT_CONNREFUSED; + break; default: ret = -ENVME_CONNECT_WRITE; break; @@ -658,7 +668,8 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, return -1; } - nvme_msg(h->r, LOG_INFO, "nvme%d: ctrl connected\n", ret); + nvme_msg(h->r, LOG_INFO, "nvme%d: %s connected\n", ret, + nvme_ctrl_get_subsysnqn(c)); return nvme_init_ctrl(h, c, ret); } @@ -678,8 +689,6 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, switch (e->adrfam) { case NVMF_ADDR_FAMILY_IP4: case NVMF_ADDR_FAMILY_IP6: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); - strchomp(e->trsvcid, NVMF_TRSVCID_SIZE - 1); traddr = e->traddr; trsvcid = e->trsvcid; break; @@ -694,7 +703,6 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, case NVMF_TRTYPE_FC: switch (e->adrfam) { case NVMF_ADDR_FAMILY_FC: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); traddr = e->traddr; break; default: @@ -706,7 +714,6 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, } break; case NVMF_TRTYPE_LOOP: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); traddr = strlen(e->traddr) ? e->traddr : NULL; break; default: @@ -734,11 +741,15 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, switch (e->subtype) { case NVME_NQN_CURR: nvme_ctrl_set_discovered(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)); break; case NVME_NQN_DISC: if (discover) *discover = true; nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)); break; default: nvme_msg(h->r, LOG_ERR, "unsupported subtype %d\n", @@ -746,6 +757,7 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, fallthrough; case NVME_NQN_NVME: nvme_ctrl_set_discovery_ctrl(c, false); + nvme_ctrl_set_unique_discovery_ctrl(c, false); break; } @@ -874,9 +886,37 @@ out_free_log: return NULL; } +static void sanitize_discovery_log_entry(struct nvmf_disc_log_entry *e) +{ + switch (e->trtype) { + case NVMF_TRTYPE_RDMA: + case NVMF_TRTYPE_TCP: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_IP4: + case NVMF_ADDR_FAMILY_IP6: + strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); + strchomp(e->trsvcid, NVMF_TRSVCID_SIZE - 1); + break; + } + break; + case NVMF_TRTYPE_FC: + switch (e->adrfam) { + case NVMF_ADDR_FAMILY_FC: + strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); + break; + } + break; + case NVMF_TRTYPE_LOOP: + strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); + break; + } +} + int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, int max_retries) { + struct nvmf_discovery_log *log; + struct nvme_get_log_args args = { .args_size = sizeof(args), .fd = nvme_ctrl_get_fd(c), @@ -893,12 +933,22 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, .rae = false, .ot = false, }; - *logp = nvme_discovery_log(c, &args, max_retries); - return logp ? 0 : -1; + + log = nvme_discovery_log(c, &args, max_retries); + if (!log) + return -1; + + for (int i = 0; i < le64_to_cpu(log->numrec); i++) + sanitize_discovery_log_entry(&log->entries[i]); + + *logp = log; + return 0; } struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args) { + struct nvmf_discovery_log *log; + struct nvme_get_log_args _args = { .args_size = sizeof(_args), .fd = nvme_ctrl_get_fd(args->c), @@ -916,7 +966,14 @@ struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_ar .ot = false, }; - return nvme_discovery_log(args->c, &_args, args->max_retries); + log = nvme_discovery_log(args->c, &_args, args->max_retries); + if (!log) + return NULL; + + for (int i = 0; i < le64_to_cpu(log->numrec); i++) + sanitize_discovery_log_entry(&log->entries[i]); + + return log; } #define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" @@ -946,7 +1003,7 @@ static int uuid_from_dmi_entries(char *system_uuid) int f; DIR *d; struct dirent *de; - char buf[512]; + char buf[512] = {0}; system_uuid[0] = '\0'; d = opendir(PATH_DMI_ENTRIES); @@ -964,7 +1021,7 @@ static int uuid_from_dmi_entries(char *system_uuid) continue; len = read(f, buf, 512); close(f); - if (len < 0) + if (len <= 0) continue; if (sscanf(buf, "%d", &type) != 1) continue; @@ -976,7 +1033,7 @@ static int uuid_from_dmi_entries(char *system_uuid) continue; len = read(f, buf, 512); close(f); - if (len < 0) + if (len <= 0) continue; /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ @@ -1127,7 +1184,7 @@ static __u32 nvmf_get_tel(const char *hostsymname) __u16 len; /* Host ID is mandatory */ - tel += nvmf_exat_size(NVME_UUID_LEN_STRING); + tel += nvmf_exat_size(NVME_UUID_LEN); /* Symbolic name is optional */ len = hostsymname ? strlen(hostsymname) : 0; diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c index 3333993..b2d92ef 100644 --- a/src/nvme/ioctl.c +++ b/src/nvme/ioctl.c @@ -430,6 +430,46 @@ int nvme_get_log(struct nvme_get_log_args *args) return nvme_submit_admin_passthru(args->fd, &cmd, args->result); } +int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) +{ + __u64 offset = 0, xfer, data_len = args->len; + __u64 start = args->lpo; + bool retain = true; + void *ptr = args->log; + 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 = data_len - offset; + if (xfer > xfer_len) + xfer = xfer_len; + + /* + * Always retain regardless of the RAE parameter until the very + * last portion of this log page so the data remains latched + * during the fetch sequence. + */ + if (offset + xfer == data_len) + retain = args->rae; + + args->lpo = start + offset; + args->len = xfer; + args->log = ptr; + args->rae = retain; + ret = nvme_get_log(args); + if (ret) + return ret; + + offset += xfer; + ptr += xfer; + } while (offset < data_len); + + return 0; +} + int nvme_set_features(struct nvme_set_features_args *args) { __u32 cdw10 = NVME_SET(args->fid, FEATURES_CDW10_FID) | @@ -1857,6 +1897,50 @@ int nvme_resv_report(struct nvme_resv_report_args *args) return nvme_submit_io_passthru(args->fd, &cmd, args->result); } +int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args) +{ + __u32 cdw10 = (args->mo & 0xf) | (args->mos & 0xff << 16); + __u32 cdw11 = (args->data_len >> 2) - 1; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_io_mgmt_recv, + .nsid = args->nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + return nvme_submit_io_passthru(args->fd, &cmd, NULL); +} + +int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args) +{ + __u32 cdw10 = (args->mo & 0xf) | ((args->mos & 0xff) << 16); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_io_mgmt_send, + .nsid = args->nsid, + .cdw10 = cdw10, + .addr = (__u64)(uintptr_t)args->data, + .data_len = args->data_len, + .timeout_ms = args->timeout, + }; + + if (args->args_size < sizeof(*args)) { + errno = EINVAL; + return -1; + } + + return nvme_submit_io_passthru(args->fd, &cmd, NULL); +} + int nvme_zns_mgmt_send(struct nvme_zns_mgmt_send_args *args) { __u32 cdw10 = args->slba & 0xffffffff; diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h index af95851..32e722e 100644 --- a/src/nvme/ioctl.h +++ b/src/nvme/ioctl.h @@ -34,6 +34,12 @@ /* '0' is interpreted by the kernel to mean 'apply the default timeout' */ #define NVME_DEFAULT_IOCTL_TIMEOUT 0 +/* + * 4k is the smallest possible transfer unit, so restricting to 4k + * avoids having to check the MDTS value of the controller. + */ +#define NVME_LOG_PAGE_PDU_SIZE 4096 + /** * struct nvme_passthru_cmd - nvme passthrough command structure * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes @@ -1226,6 +1232,17 @@ static inline int nvme_zns_identify_ctrl(int fd, struct nvme_zns_id_ctrl *id) */ int nvme_get_log(struct nvme_get_log_args *args); +/** + * nvme_get_log_page() - Get log page data + * @fd: File descriptor of nvme device + * @xfer_len: Max log transfer size per request to split the total. + * @args: &struct nvme_get_log_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args); + static inline int nvme_get_nsid_log(int fd, bool rae, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u32 len, void *log) @@ -1248,7 +1265,7 @@ static inline int nvme_get_nsid_log(int fd, bool rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } static inline int nvme_get_log_simple(int fd, enum nvme_cmd_get_log_lid lid, @@ -1391,7 +1408,7 @@ static inline int nvme_get_log_cmd_effects(int fd, enum nvme_csi csi, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1441,7 +1458,7 @@ static inline int nvme_get_log_create_telemetry_host(int fd, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1477,7 +1494,7 @@ static inline int nvme_get_log_telemetry_host(int fd, __u64 offset, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1514,7 +1531,7 @@ static inline int nvme_get_log_telemetry_ctrl(int fd, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1553,7 +1570,7 @@ static inline int nvme_get_log_endurance_group(int fd, __u16 endgid, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1585,7 +1602,7 @@ static inline int nvme_get_log_predictable_lat_nvmset(int fd, __u16 nvmsetid, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1619,6 +1636,126 @@ static inline int nvme_get_log_predictable_lat_event(int fd, bool rae, .rae = rae, .ot = false, }; + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); +} + +/** + * nvme_get_log_fdp_configurations() - Get list of Flexible Data Placement configurations + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_configurations(int fd, __u16 egid, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_CONFIGS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_reclaim_unit_handle_usage() - Get reclaim unit handle usage + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_reclaim_unit_handle_usage(int fd, __u16 egid, + __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_RUH_USAGE, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_fdp_stats() - Get Flexible Data Placement statistics + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_stats(int fd, __u16 egid, __u32 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_STATS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_get_log(&args); +} + +/** + * nvme_get_log_fdp_events() - Get Flexible Data Placement events + * @fd: File descriptor of nvme device + * @egid: Endurance group identifier + * @host_events: Whether to report host or controller events + * @offset: Offset into log page + * @len: Length (in bytes) of provided user buffer to hold the log data + * @log: Log page data buffer + */ +static inline int nvme_get_log_fdp_events(int fd, __u16 egid, bool host_events, __u32 offset, + __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_FDP_EVENTS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = egid, + .lsp = (__u8)(host_events ? 0x1 : 0x0), + .uuidx = NVME_UUID_NONE, + }; + return nvme_get_log(&args); } @@ -1660,7 +1797,7 @@ static inline int nvme_get_log_ana(int fd, enum nvme_log_ana_lsp lsp, bool rae, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1713,7 +1850,7 @@ static inline int nvme_get_log_lba_status(int fd, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1747,7 +1884,7 @@ static inline int nvme_get_log_endurance_grp_evt(int fd, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1814,7 +1951,7 @@ static inline int nvme_get_log_boot_partition(int fd, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1851,7 +1988,7 @@ static inline int nvme_get_log_discovery(int fd, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1883,7 +2020,7 @@ static inline int nvme_get_log_media_unit_stat(int fd, __u16 domid, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1915,7 +2052,7 @@ static inline int nvme_get_log_support_cap_config_list(int fd, __u16 domid, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -1985,7 +2122,7 @@ static inline int nvme_get_log_zns_changed_zones(int fd, __u32 nsid, bool rae, .rae = rae, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -2019,7 +2156,7 @@ static inline int nvme_get_log_persistent_event(int fd, .rae = false, .ot = false, }; - return nvme_get_log(&args); + return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args); } /** @@ -2392,7 +2529,7 @@ int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save, __u32 *result); /** - * nvme_set_features_host_id() - Set enable extended host identifers feature + * nvme_set_features_host_id() - Set enable extended host identifiers feature * @fd: File descriptor of nvme device * @exhid: Enable Extended Host Identifier * @save: Save value across power states @@ -3372,7 +3509,7 @@ int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args); * 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 + * Set the 'nsid' field to 0 to not include namespaces in the test. Set to * 0xffffffff to test all namespaces. All other values tests a specific * namespace, if present. * @@ -3588,6 +3725,78 @@ int nvme_resv_release(struct nvme_resv_release_args *args); int nvme_resv_report(struct nvme_resv_report_args *args); /** + * nvme_io_mgmt_recv() - I/O Management Receive command + * @args: &struct nvme_io_mgmt_recv_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args); + +/** + * nvme_fdp_reclaim_unit_handle_status() - Get reclaim unit handle status + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @data_len: Length of response buffer + * @data: Response buffer + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_fdp_reclaim_unit_handle_status(int fd, __u32 nsid, + __u32 data_len, void *data) +{ + struct nvme_io_mgmt_recv_args args = { + .data = data, + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .data_len = data_len, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .mos = 0, + .mo = NVME_IO_MGMT_RECV_RUH_STATUS, + }; + + return nvme_io_mgmt_recv(&args); +} + +/** + * nvme_io_mgmt_send() - I/O Management Send command + * @args: &struct nvme_io_mgmt_send_args argument structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args); + +/** + * nvme_fdp_reclaim_unit_handle_update() - Update a list of reclaim unit handles + * @fd: File descriptor of nvme device + * @nsid: Namespace identifier + * @npids: Number of placement identifiers + * @pids: List of placement identifiers + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_fdp_reclaim_unit_handle_update(int fd, __u32 nsid, + unsigned int npids, __u16 *pids) +{ + struct nvme_io_mgmt_send_args args = { + .data = (void *)pids, + .args_size = sizeof(args), + .fd = fd, + .nsid = nsid, + .data_len = (__u32)(npids * sizeof(__u16)), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .mos = (__u16)(npids - 1), + .mo = NVME_IO_MGMT_SEND_RUH_UPDATE, + }; + + return nvme_io_mgmt_send(&args); +} + +/** * nvme_zns_mgmt_send() - ZNS management send command * @args: &struct nvme_zns_mgmt_send_args argument structure * diff --git a/src/nvme/json.c b/src/nvme/json.c index f0c2ab4..072b622 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -148,6 +148,9 @@ static void json_parse_host(nvme_root_t r, struct json_object *host_obj) attr_obj = json_object_object_get(host_obj, "hostsymname"); if (attr_obj) nvme_host_set_hostsymname(h, json_object_get_string(attr_obj)); + attr_obj = json_object_object_get(host_obj, "persistent_discovery_ctrl"); + if (attr_obj) + nvme_host_set_pdc_enabled(h, json_object_get_boolean(attr_obj)); subsys_array = json_object_object_get(host_obj, "subsystems"); if (!subsys_array) return; @@ -354,6 +357,9 @@ int json_update_config(nvme_root_t r, const char *config_file) if (hostsymname) json_object_object_add(host_obj, "hostsymname", json_object_new_string(hostsymname)); + if (h->pdc_enabled_valid) + json_object_object_add(host_obj, "persistent_discovery_ctrl", + json_object_new_boolean(h->pdc_enabled)); subsys_array = json_object_new_array(); nvme_for_each_subsystem(h, s) { json_update_subsys(subsys_array, s); @@ -492,6 +498,9 @@ int json_dump_tree(nvme_root_t r) if (dhchap_key) json_object_object_add(host_obj, "dhchap_key", json_object_new_string(dhchap_key)); + if (h->pdc_enabled_valid) + json_object_object_add(host_obj, "persistent_discovery_ctrl", + json_object_new_boolean(h->pdc_enabled)); subsys_array = json_object_new_array(); nvme_for_each_subsystem(h, s) { json_dump_subsys(subsys_array, s); diff --git a/src/nvme/linux.c b/src/nvme/linux.c index c9b6bbf..cae4036 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -117,45 +117,6 @@ int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset, return err; } -int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) -{ - __u64 offset = 0, xfer, data_len = args->len; - bool retain = true; - void *ptr = args->log; - 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 = data_len - offset; - if (xfer > xfer_len) - xfer = xfer_len; - - /* - * Always retain regardless of the RAE parameter until the very - * last portion of this log page so the data remains latched - * during the fetch sequence. - */ - if (offset + xfer == data_len) - retain = args->rae; - - args->lpo = offset; - args->len = xfer; - args->log = ptr; - args->rae = retain; - ret = nvme_get_log(args); - if (ret) - return ret; - - offset += xfer; - ptr += xfer; - } while (offset < data_len); - - return 0; -} - static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, struct nvme_telemetry_log **buf, enum nvme_telemetry_da da, size_t *size) diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 8a6d426..aa4c91a 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -98,17 +98,6 @@ int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size); /** - * nvme_get_log_page() - Get log page data - * @fd: File descriptor of nvme device - * @xfer_len: Max log transfer size per request to split the total. - * @args: &struct nvme_get_log_args argument structure - * - * Return: The nvme command status if a response was received (see - * &enum nvme_status_field) or -1 with errno set otherwise. - */ -int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args); - -/** * nvme_get_ana_log_len() - Retrieve size of the current ANA log * @fd: File descriptor of nvme device * @analen: Pointer to where the length will be set on success diff --git a/src/nvme/mi-mctp.c b/src/nvme/mi-mctp.c index 86f5df6..0c5972a 100644 --- a/src/nvme/mi-mctp.c +++ b/src/nvme/mi-mctp.c @@ -23,10 +23,8 @@ #include <ccan/endian/endian.h> -#ifdef CONFIG_LIBSYSTEMD -#include <systemd/sd-event.h> -#include <systemd/sd-bus.h> -#include <systemd/sd-id128.h> +#ifdef CONFIG_DBUS +#include <dbus/dbus.h> #define MCTP_DBUS_PATH "/xyz/openbmc_project/mctp" #define MCTP_DBUS_IFACE "xyz.openbmc_project.MCTP" @@ -510,29 +508,19 @@ nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid) */ ep->timeout = 5000; + nvme_mi_ep_probe(ep); + return ep; err_free_ep: errno_save = errno; - free(ep); + nvme_mi_close(ep); free(mctp); errno = errno_save; return NULL; } -#ifdef CONFIG_LIBSYSTEMD - -/* helper for handling dbus errors: D-Bus API returns a negtive errno on - * failure; set errno and log. - */ -static void _dbus_err(nvme_root_t root, int rc, int line) -{ - nvme_msg(root, LOG_ERR, "MCTP D-Bus failed line %d: %s %d\n", - line, strerror(-rc), rc); - errno = -rc; -} - -#define dbus_err(r, rc) _dbus_err(r, rc, __LINE__) +#ifdef CONFIG_DBUS static int nvme_mi_mctp_add(nvme_root_t root, unsigned int netid, __u8 eid) { @@ -556,84 +544,99 @@ static int nvme_mi_mctp_add(nvme_root_t root, unsigned int netid, __u8 eid) return 0; } -/* We can't rely on sd_bus_message_enter_container() == 0 at the end of - a dictionary (it returns -ENXIO) so we test separately */ -static bool container_end(sd_bus_message *m) +static bool dbus_object_is_type(DBusMessageIter *obj, int type) +{ + return dbus_message_iter_get_arg_type(obj) == type; +} + +static bool dbus_object_is_dict(DBusMessageIter *obj) +{ + return dbus_object_is_type(obj, DBUS_TYPE_ARRAY) && + dbus_message_iter_get_element_type(obj) == DBUS_TYPE_DICT_ENTRY; +} + +static int read_variant_basic(DBusMessageIter *var, int type, void *val) +{ + if (!dbus_object_is_type(var, type)) + return -1; + + dbus_message_iter_get_basic(var, val); + + return 0; +} + +static bool has_message_type(DBusMessageIter *prop, uint8_t type) { - return sd_bus_message_peek_type(m, NULL, NULL) == 0; + DBusMessageIter inner; + uint8_t *types; + int i, n; + + if (!dbus_object_is_type(prop, DBUS_TYPE_ARRAY) || + dbus_message_iter_get_element_type(prop) != DBUS_TYPE_BYTE) + return false; + + dbus_message_iter_recurse(prop, &inner); + + dbus_message_iter_get_fixed_array(&inner, &types, &n); + + for (i = 0; i < n; i++) { + if (types[i] == type) + return true; + } + + return false; } static int handle_mctp_endpoint(nvme_root_t root, const char* objpath, - sd_bus_message *m) + DBusMessageIter *props) { bool have_eid = false, have_net = false, have_nvmemi = false; mctp_eid_t eid; int net; int rc; - /* Iterate properties on this interface */ - while (!container_end(m)) { - /* Enter property dict */ - rc = sd_bus_message_enter_container(m, 'a', "{sv}"); - if (rc < 0) { - dbus_err(root, rc); + /* for each property */ + for (;;) { + DBusMessageIter prop, val; + const char *propname; + + dbus_message_iter_recurse(props, &prop); + + if (!dbus_object_is_type(&prop, DBUS_TYPE_STRING)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (propname)\n"); return -1; } - while (!container_end(m)) { - char *propname = NULL; - size_t sz; - const uint8_t *types = NULL; - /* Enter property item */ - rc = sd_bus_message_enter_container(m, 'e', "sv"); - if (rc < 0) { - dbus_err(root, rc); - return -1; - } - - rc = sd_bus_message_read(m, "s", &propname); - if (rc < 0) { - dbus_err(root, rc); - return -1; - } - - if (strcmp(propname, "EID") == 0) { - rc = sd_bus_message_read(m, "v", "y", &eid); - have_eid = true; - } else if (strcmp(propname, "NetworkId") == 0) { - rc = sd_bus_message_read(m, "v", "i", &net); - have_net = true; - } else if (strcmp(propname, "SupportedMessageTypes") == 0) { - sd_bus_message_enter_container(m, 'v', "ay"); - rc = sd_bus_message_read_array(m, 'y', (const void**)&types, &sz); - if (rc >= 0) - for (size_t s = 0; s < sz; s++) - if (types[s] == MCTP_TYPE_NVME) - have_nvmemi = true; - sd_bus_message_exit_container(m); - } else { - rc = sd_bus_message_skip(m, "v"); - } - - if (rc < 0) { - dbus_err(root, rc); - return -1; - } - - /* Exit prop item */ - rc = sd_bus_message_exit_container(m); - if (rc < 0) { - dbus_err(root, rc); - return -1; - } - } + dbus_message_iter_get_basic(&prop, &propname); - /* Exit property dict */ - rc = sd_bus_message_exit_container(m); - if (rc < 0) { - dbus_err(root, rc); + dbus_message_iter_next(&prop); + + if (!dbus_object_is_type(&prop, DBUS_TYPE_VARIANT)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (propval)\n"); return -1; } + + dbus_message_iter_recurse(&prop, &val); + + if (!strcmp(propname, "EID")) { + rc = read_variant_basic(&val, DBUS_TYPE_BYTE, &eid); + have_eid = true; + + } else if (!strcmp(propname, "NetworkId")) { + rc = read_variant_basic(&val, DBUS_TYPE_INT32, &net); + have_net = true; + + } else if (!strcmp(propname, "SupportedMessageTypes")) { + have_nvmemi = has_message_type(&val, MCTP_TYPE_NVME); + } + + if (rc) + return rc; + + if (!dbus_message_iter_next(props)) + break; } if (have_nvmemi) { @@ -657,70 +660,61 @@ static int handle_mctp_endpoint(nvme_root_t root, const char* objpath, return rc; } -static int handle_mctp_obj(nvme_root_t root, sd_bus_message *m) +/* obj is an array of (object path, interfaces) dict entries - ie., dbus type + * a{oa{sa{sv}}} + */ +static int handle_mctp_obj(nvme_root_t root, DBusMessageIter *obj) { - char *objpath = NULL; - char *ifname = NULL; - int rc; + const char *objpath = NULL; + DBusMessageIter intfs; - rc = sd_bus_message_read(m, "o", &objpath); - if (rc < 0) { - dbus_err(root, rc); + if (!dbus_object_is_type(obj, DBUS_TYPE_OBJECT_PATH)) { + nvme_msg(root, LOG_ERR, "error unmashalling object (path)\n"); return -1; } - /* Enter response object: our array of (string, property dict) - * values */ - rc = sd_bus_message_enter_container(m, 'a', "{sa{sv}}"); - if (rc < 0) { - dbus_err(root, rc); + dbus_message_iter_get_basic(obj, &objpath); + + dbus_message_iter_next(obj); + + if (!dbus_object_is_dict(obj)) { + nvme_msg(root, LOG_ERR, "error unmashalling object (intfs)\n"); return -1; } + dbus_message_iter_recurse(obj, &intfs); /* for each interface */ - while (!container_end(m)) { - /* Enter interface item */ - rc = sd_bus_message_enter_container(m, 'e', "sa{sv}"); - if (rc < 0) { - dbus_err(root, rc); - return -1; - } + for (;;) { + DBusMessageIter props, intf; + const char *intfname; - rc = sd_bus_message_read(m, "s", &ifname); - if (rc < 0) { - dbus_err(root, rc); + dbus_message_iter_recurse(&intfs, &intf); + + if (!dbus_object_is_type(&intf, DBUS_TYPE_STRING)) { + nvme_msg(root, LOG_ERR, + "error unmashalling object (intf)\n"); return -1; } - if (!strcmp(ifname, MCTP_DBUS_IFACE_ENDPOINT)) { - - rc = handle_mctp_endpoint(root, objpath, m); - if (rc < 0) { - /* continue to next object */ - } - } else { - /* skip the interfaces we don't care about */ - rc = sd_bus_message_skip(m, "a{sv}"); - if (rc < 0) { - dbus_err(root, rc); - return -1; - } + dbus_message_iter_get_basic(&intf, &intfname); + + if (strcmp(intfname, MCTP_DBUS_IFACE_ENDPOINT)) { + if (!dbus_message_iter_next(&intfs)) + break; + continue; } - /* Exit interface item */ - rc = sd_bus_message_exit_container(m); - if (rc < 0) { - dbus_err(root, rc); + dbus_message_iter_next(&intf); + + if (!dbus_object_is_dict(&intf)) { + nvme_msg(root, LOG_ERR, + "error unmarshalling object (props)\n"); return -1; } - } - /* Exit response object */ - rc = sd_bus_message_exit_container(m); - if (rc < 0) { - dbus_err(root, rc); - return -1; + dbus_message_iter_recurse(&intf, &props); + return handle_mctp_endpoint(root, objpath, &props); } return 0; @@ -728,85 +722,85 @@ static int handle_mctp_obj(nvme_root_t root, sd_bus_message *m) nvme_root_t nvme_mi_scan_mctp(void) { - sd_bus *bus = NULL; - sd_bus_message *resp = NULL; - sd_bus_error berr = SD_BUS_ERROR_NULL; - int rc, errno_save; + DBusMessage *msg, *resp = NULL; + DBusConnection *bus = NULL; + DBusMessageIter args, objs; + int errno_save, rc = -1; nvme_root_t root; + dbus_bool_t drc; + DBusError berr; root = nvme_mi_create_root(NULL, DEFAULT_LOGLEVEL); if (!root) { errno = ENOMEM; - rc = -1; + return NULL; + } + + dbus_error_init(&berr); + + bus = dbus_bus_get(DBUS_BUS_SYSTEM, &berr); + if (!bus) { + nvme_msg(root, LOG_ERR, "Failed connecting to D-Bus: %s (%s)\n", + berr.message, berr.name); goto out; } - rc = sd_bus_default_system(&bus); - if (rc < 0) { - nvme_msg(root, LOG_ERR, "Failed opening D-Bus: %s\n", - strerror(-rc)); - errno = -rc; - rc = -1; + msg = dbus_message_new_method_call(MCTP_DBUS_IFACE, + MCTP_DBUS_PATH, + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); + if (!msg) { + nvme_msg(root, LOG_ERR, "Failed creating call message\n"); goto out; } - rc = sd_bus_call_method(bus, - MCTP_DBUS_IFACE, - MCTP_DBUS_PATH, - "org.freedesktop.DBus.ObjectManager", - "GetManagedObjects", - &berr, - &resp, - ""); - if (rc < 0) { + resp = dbus_connection_send_with_reply_and_block(bus, msg, + DBUS_TIMEOUT_USE_DEFAULT, + &berr); + dbus_message_unref(msg); + if (!resp) { nvme_msg(root, LOG_ERR, "Failed querying MCTP D-Bus: %s (%s)\n", berr.message, berr.name); - errno = -rc; - rc = -1; goto out; } - rc = sd_bus_message_enter_container(resp, 'a', "{oa{sa{sv}}}"); - if (rc != 1) { - dbus_err(root, rc); - if (rc == 0) - errno = EPROTO; - rc = -1; + /* argument container */ + drc = dbus_message_iter_init(resp, &args); + if (!drc) { + nvme_msg(root, LOG_ERR, "can't read dbus reply args\n"); goto out; } - /* Iterate over all managed objects */ - while (!container_end(resp)) { - rc = sd_bus_message_enter_container(resp, 'e', "oa{sa{sv}}"); - if (rc < 0) { - dbus_err(root, rc); - rc = -1; - goto out; - } + if (!dbus_object_is_dict(&args)) { + nvme_msg(root, LOG_ERR, "error unmashalling args\n"); + goto out; + } - handle_mctp_obj(root, resp); + /* objects container */ + dbus_message_iter_recurse(&args, &objs); - rc = sd_bus_message_exit_container(resp); - if (rc < 0) { - dbus_err(root, rc); - rc = -1; - goto out; - } - } + rc = 0; - rc = sd_bus_message_exit_container(resp); - if (rc < 0) { - dbus_err(root, rc); - rc = -1; - goto out; + for (;;) { + DBusMessageIter ent; + + dbus_message_iter_recurse(&objs, &ent); + + rc = handle_mctp_obj(root, &ent); + if (rc) + break; + + if (!dbus_message_iter_next(&objs)) + break; } - rc = 0; out: errno_save = errno; - sd_bus_error_free(&berr); - sd_bus_message_unref(resp); - sd_bus_unref(bus); + if (resp) + dbus_message_unref(resp); + if (bus) + dbus_connection_unref(bus); + dbus_error_free(&berr); if (rc < 0) { if (root) { @@ -818,11 +812,11 @@ out: return root; } -#else /* CONFIG_LIBSYSTEMD */ +#else /* CONFIG_DBUS */ nvme_root_t nvme_mi_scan_mctp(void) { return NULL; } -#endif /* CONFIG_LIBSYSTEMD */ +#endif /* CONFIG_DBUS */ diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 6ff0a6f..adf1753 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -10,6 +10,7 @@ #include <stdlib.h> #include <stdlib.h> #include <stdio.h> +#include <time.h> #include <ccan/array_size/array_size.h> #include <ccan/endian/endian.h> @@ -21,6 +22,20 @@ static const int default_timeout = 1000; /* milliseconds; endpoints may override */ +static bool nvme_mi_probe_enabled_default(void) +{ + char *val; + + val = getenv("LIBNVME_MI_PROBE_ENABLED"); + if (!val) + return true; + + return strcmp(val, "0") && + strcasecmp(val, "false") && + strncasecmp(val, "disable", 7); + +} + /* MI-equivalent of nvme_create_root, but avoids clashing symbol names * when linking against both libnvme and libnvme-mi. */ @@ -33,6 +48,7 @@ nvme_root_t nvme_mi_create_root(FILE *fp, int log_level) } r->log_level = log_level; r->fp = stderr; + r->mi_probe_enabled = nvme_mi_probe_enabled_default(); if (fp) r->fp = fp; list_head_init(&r->hosts); @@ -50,6 +66,180 @@ void nvme_mi_free_root(nvme_root_t root) free(root); } +void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled) +{ + root->mi_probe_enabled = enabled; +} + +static void nvme_mi_record_resp_time(struct nvme_mi_ep *ep) +{ + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ep->last_resp_time); + ep->last_resp_time_valid = !rc; +} + +static bool nvme_mi_compare_vid_mn(struct nvme_mi_ep *ep, + struct nvme_id_ctrl *id, + __u16 vid, const char *mn) + +{ + int len; + + len = strlen(mn); + if (len >= sizeof(id->mn)) { + nvme_msg(ep->root, LOG_ERR, + "Internal error: invalid model number for %s\n", + __func__); + return false; + } + + return le16_to_cpu(id->vid) == vid && !strncmp(id->mn, mn, len); +} + +static void __nvme_mi_format_mn(struct nvme_id_ctrl *id, + char *mn, size_t mn_len) +{ + const size_t id_mn_size = sizeof(id->mn); + int i; + + /* A BUILD_ASSERT() would be nice here, but we're not const enough for + * that + */ + if (mn_len <= id_mn_size) + abort(); + + memcpy(mn, id->mn, id_mn_size); + mn[id_mn_size] = '\0'; + + for (i = id_mn_size - 1; i >= 0; i--) { + if (mn[i] != '\0' && mn[i] != ' ') + break; + mn[i] = '\0'; + } +} + +#define nvme_mi_format_mn(id, m) __nvme_mi_format_mn(id, m, sizeof(m)) + +void nvme_mi_ep_probe(struct nvme_mi_ep *ep) +{ + struct nvme_identify_args id_args = { 0 }; + struct nvme_id_ctrl id = { 0 }; + struct nvme_mi_ctrl *ctrl; + int rc; + + if (!ep->root->mi_probe_enabled) + return; + + /* start with no quirks, detect as we go */ + ep->quirks = 0; + + ctrl = nvme_mi_init_ctrl(ep, 0); + if (!ctrl) + return; + + /* Do enough of an identify (assuming controller 0) to retrieve + * device and firmware identification information. This gives us the + * following fields in id: + * + * - vid (PCI vendor ID) + * - ssvid (PCI subsystem vendor ID) + * - sn (Serial number) + * - mn (Model number) + * - fr (Firmware revision) + * + * all other fields - rab and onwards - will be zero! + */ + id_args.args_size = sizeof(id_args); + id_args.data = &id; + id_args.cns = NVME_IDENTIFY_CNS_CTRL; + id_args.nsid = NVME_NSID_NONE; + id_args.cntid = 0; + id_args.csi = NVME_CSI_NVM; + + rc = nvme_mi_admin_identify_partial(ctrl, &id_args, 0, + offsetof(struct nvme_id_ctrl, rab)); + if (rc) { + nvme_msg(ep->root, LOG_WARNING, + "Identify Controller failed, no quirks applied\n"); + goto out_close; + } + + /* Samsung MZUL2512: cannot receive commands sent within ~1ms of + * the previous response. Set an inter-command delay of 1.2ms for + * a little extra tolerance. + */ + if (nvme_mi_compare_vid_mn(ep, &id, 0x144d, "MZUL2512HCJQ")) { + ep->quirks |= NVME_QUIRK_MIN_INTER_COMMAND_TIME; + ep->inter_command_us = 1200; + } + + /* If we're quirking for the inter-command time, record the last + * command time now, so we don't conflict with the just-sent identify. + */ + if (ep->quirks & NVME_QUIRK_MIN_INTER_COMMAND_TIME) + nvme_mi_record_resp_time(ep); + + if (ep->quirks) { + char tmp[sizeof(id.mn) + 1]; + + nvme_mi_format_mn(&id, tmp); + nvme_msg(ep->root, LOG_DEBUG, + "device %02x:%s: applying quirks 0x%08lx\n", + id.vid, tmp, ep->quirks); + } + +out_close: + nvme_mi_close_ctrl(ctrl); +} + +static const int nsec_per_sec = 1000 * 1000 * 1000; +/* timercmp and timersub, but for struct timespec */ +#define timespec_cmp(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) \ + ? ((a)->tv_nsec CMP (b)->tv_nsec) \ + : ((a)->tv_sec CMP (b)->tv_sec)) + +#define timespec_sub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += nsec_per_sec; \ + } \ + } while (0) + +static void nvme_mi_insert_delay(struct nvme_mi_ep *ep) +{ + struct timespec now, next, delay; + int rc; + + if (!ep->last_resp_time_valid) + return; + + /* calculate earliest next command time */ + next.tv_nsec = ep->last_resp_time.tv_nsec + ep->inter_command_us * 1000; + next.tv_sec = ep->last_resp_time.tv_sec; + if (next.tv_nsec > nsec_per_sec) { + next.tv_nsec -= nsec_per_sec; + next.tv_sec += 1; + } + + rc = clock_gettime(CLOCK_MONOTONIC, &now); + if (rc) { + /* not much we can do; continue immediately */ + return; + } + + if (timespec_cmp(&now, &next, >=)) + return; + + timespec_sub(&next, &now, &delay); + + nanosleep(&delay, NULL); +} + struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root) { struct nvme_mi_ep *ep; @@ -93,6 +283,11 @@ unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep) return ep->timeout; } +static bool nvme_mi_ep_has_quirk(nvme_mi_ep_t ep, unsigned long quirk) +{ + return ep->quirks & quirk; +} + struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id) { struct nvme_mi_ctrl *ctrl; @@ -139,9 +334,7 @@ int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan) struct nvme_mi_ctrl *ctrl; __u16 id; - id = le32_to_cpu(list.identifier[i]); - if (!id) - continue; + id = le16_to_cpu(list.identifier[i]); ctrl = nvme_mi_init_ctrl(ep, id); if (!ctrl) @@ -223,7 +416,14 @@ int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req, if (ep->transport->mic_enabled) nvme_mi_calc_req_mic(req); + if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME)) + nvme_mi_insert_delay(ep); + rc = ep->transport->submit(ep, req, resp); + + if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME)) + nvme_mi_record_resp_time(ep); + if (rc) { nvme_msg(ep->root, LOG_INFO, "transport failure\n"); return rc; @@ -333,7 +533,12 @@ static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result) admin_hdr = (struct nvme_mi_admin_resp_hdr *)resp->hdr; nvme_result = le32_to_cpu(admin_hdr->cdw0); - nvme_status = le32_to_cpu(admin_hdr->cdw3) >> 16; + + /* Shift down 17 here: the SC starts at bit 17, and the NVME_SC_* + * definitions align to this bit (and up). The CRD, MORE and DNR + * bits are defined accordingly (eg., DNR is 0x4000). + */ + nvme_status = le32_to_cpu(admin_hdr->cdw3) >> 17; /* the result pointer, optionally stored if the caller needs it */ if (result) @@ -419,6 +624,96 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, return 0; } +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result) +{ + /* Input parameters flags, rsvd, metadata, metadata_len are not used */ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + int direction = opcode & 0x3; + bool has_write_data = false; + bool has_read_data = false; + + if (direction == NVME_DATA_TFR_BIDIRECTIONAL) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support bidirectional commands\n"); + errno = EINVAL; + return -1; + } + + if (data_len > 4096) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support data_len over 4096 bytes.\n"); + errno = EINVAL; + return -1; + } + + if (data != NULL && data_len != 0) { + if (direction == NVME_DATA_TFR_HOST_TO_CTRL) + has_write_data = true; + if (direction == NVME_DATA_TFR_CTRL_TO_HOST) + has_read_data = true; + } + + if (timeout_ms > nvme_mi_ep_get_timeout(ctrl->ep)) { + /* Set timeout if user needs a bigger timeout */ + nvme_mi_ep_set_timeout(ctrl->ep, timeout_ms); + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, opcode); + req_hdr.cdw1 = cpu_to_le32(nsid); + req_hdr.cdw2 = cpu_to_le32(cdw2); + req_hdr.cdw3 = cpu_to_le32(cdw3); + req_hdr.cdw10 = cpu_to_le32(cdw10); + req_hdr.cdw11 = cpu_to_le32(cdw11); + req_hdr.cdw12 = cpu_to_le32(cdw12); + req_hdr.cdw13 = cpu_to_le32(cdw13); + req_hdr.cdw14 = cpu_to_le32(cdw14); + req_hdr.cdw15 = cpu_to_le32(cdw15); + req_hdr.doff = 0; + if (data_len != 0) { + req_hdr.dlen = cpu_to_le32(data_len); + /* Bit 0 set to 1 means DLEN contains a value */ + req_hdr.flags = 0x1; + } + + if (has_write_data) { + req.data = data; + req.data_len = data_len; + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + if (has_read_data) { + resp.data = data; + resp.data_len = data_len; + } + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, result); + if (rc) + return rc; + + if (has_read_data && (resp.data_len != data_len)) { + errno = EPROTO; + return -1; + } + + return 0; +} + int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args, off_t offset, size_t size) @@ -477,11 +772,19 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, } /* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are - * specified within the args->data area */ + * specified within the args->data area. The `offset` parameter is a relative + * offset to the args->lpo ! + * + * What's more, we change the LPO of original command to chunk the request + * message into proper size which is allowed by MI interface. One reason is that + * this option seems to be supported better by devices. For more information + * about this option, please check https://github.com/linux-nvme/libnvme/pull/539 + * */ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, const struct nvme_get_log_args *args, off_t offset, size_t *lenp, bool final) { + __u64 log_page_offset = args->lpo + offset; struct nvme_mi_admin_resp_hdr resp_hdr; struct nvme_mi_admin_req_hdr req_hdr; struct nvme_mi_resp resp; @@ -513,17 +816,13 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, (args->lid & 0xff)); req_hdr.cdw11 = cpu_to_le32(args->lsi << 16 | ndw >> 16); - req_hdr.cdw12 = cpu_to_le32(args->lpo & 0xffffffff); - req_hdr.cdw13 = cpu_to_le32(args->lpo >> 32); + req_hdr.cdw12 = cpu_to_le32(log_page_offset & 0xffffffff); + req_hdr.cdw13 = cpu_to_le32(log_page_offset >> 32); req_hdr.cdw14 = cpu_to_le32(args->csi << 24 | (args->ot ? 1 : 0) << 23 | args->uuidx); req_hdr.flags = 0x1; req_hdr.dlen = cpu_to_le32(len & 0xffffffff); - if (offset) { - req_hdr.flags |= 0x2; - req_hdr.doff = cpu_to_le32(offset); - } nvme_mi_calc_req_mic(&req); @@ -544,7 +843,7 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) { - const size_t xfer_size = 4096; + const size_t max_xfer_size = 4096; off_t xfer_offset; int rc = 0; @@ -553,26 +852,32 @@ int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) return -1; } + if (args->ot && (args->len > max_xfer_size)) { + errno = EINVAL; + return -1; + } + for (xfer_offset = 0; xfer_offset < args->len;) { - size_t tmp, cur_xfer_size = xfer_size; + size_t xfered_size, cur_xfer_size = max_xfer_size; bool final; if (xfer_offset + cur_xfer_size > args->len) cur_xfer_size = args->len - xfer_offset; - tmp = cur_xfer_size; + xfered_size = cur_xfer_size; final = xfer_offset + cur_xfer_size >= args->len; + /* xfered_size is used as both input and output parameter */ rc = __nvme_mi_admin_get_log(ctrl, args, xfer_offset, - &tmp, final); + &xfered_size, final); if (rc) break; - xfer_offset += tmp; + xfer_offset += xfered_size; /* if we returned less data than expected, consider that * the end of the log page */ - if (tmp != cur_xfer_size) + if (xfered_size != cur_xfer_size) break; } @@ -606,8 +911,8 @@ int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, nvme_admin_security_send); req_hdr.cdw10 = cpu_to_le32(args->secp << 24 | - args->spsp0 << 16 | - args->spsp1 << 8 | + args->spsp1 << 16 | + args->spsp0 << 8 | args->nssf); req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff); @@ -652,8 +957,8 @@ int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, nvme_admin_security_recv); req_hdr.cdw10 = cpu_to_le32(args->secp << 24 | - args->spsp0 << 16 | - args->spsp1 << 8 | + args->spsp1 << 16 | + args->spsp0 << 8 | args->nssf); req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff); @@ -985,7 +1290,7 @@ static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0, req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3); /* we always use command slot 0 */ req_hdr.opcode = nvme_mi_mi_opcode_mi_data_read; - req_hdr.cdw0 = cdw0; + req_hdr.cdw0 = cpu_to_le32(cdw0); memset(&req, 0, sizeof(req)); req.hdr = &req_hdr.hdr; @@ -1222,7 +1527,7 @@ void nvme_mi_close(nvme_mi_ep_t ep) nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp) nvme_mi_close_ctrl(ctrl); - if (ep->transport->close) + if (ep->transport && ep->transport->close) ep->transport->close(ep); list_del(&ep->root_entry); free(ep); diff --git a/src/nvme/mi.h b/src/nvme/mi.h index ab4216d..5659159 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -403,6 +403,17 @@ nvme_root_t nvme_mi_create_root(FILE *fp, int log_level); */ void nvme_mi_free_root(nvme_root_t root); +/** + * nvme_mi_set_probe_enabled() - enable/disable the probe for new endpoints + * @root: &nvme_root_t object + * @enabled: whether to probe new endpoints + * + * Controls whether newly-created endpoints are probed for quirks on creation. + * Defaults to enabled, which results in some initial messaging with the + * endpoint to determine model-specific details. + */ +void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled); + /* Top level management object: NVMe-MI Management Endpoint */ struct nvme_mi_ep; @@ -956,6 +967,46 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, size_t *resp_data_size); /** + * nvme_mi_admin_admin_passthru() - Submit an nvme admin passthrough command + * @ctrl: Controller to send command to + * @opcode: The nvme admin command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command(not used) + * @metadata: Pointer to user address of the metadata buffer(not used) + * @timeout_ms: How long to wait for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Send a customized NVMe Admin command request message and get the corresponding + * response message. + * + * This interface supports no data, host to controller and controller to + * host but it doesn't support bidirectional data transfer. + * Also this interface only supports data transfer size range [0, 4096] (bytes) + * so the & data_len parameter must be less than 4097. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result); + +/** * nvme_mi_admin_identify_partial() - Perform an Admin identify command, * and retrieve partial response data. * @ctrl: Controller to process identify command @@ -1202,7 +1253,7 @@ static inline int nvme_mi_admin_identify_nsid_ctrl_list(nvme_mi_ctrl_t ctrl, .result = NULL, .data = list, .args_size = sizeof(args), - .cns = NVME_IDENTIFY_CNS_CTRL_LIST, + .cns = NVME_IDENTIFY_CNS_NS_CTRL_LIST, .csi = NVME_CSI_NVM, .nsid = nsid, .cntid = cntid, diff --git a/src/nvme/private.h b/src/nvme/private.h index cdd1bbf..a6ded21 100644 --- a/src/nvme/private.h +++ b/src/nvme/private.h @@ -85,6 +85,7 @@ struct nvme_ctrl { char *cntrltype; char *dctype; bool discovery_ctrl; + bool unique_discovery_ctrl; bool discovered; bool persistent; struct nvme_fabrics_config cfg; @@ -114,6 +115,9 @@ struct nvme_host { char *hostid; char *dhchap_key; char *hostsymname; + bool pdc_enabled; + bool pdc_enabled_valid; /* set if pdc_enabled doesn't have an undefined + * value */ }; struct nvme_root { @@ -125,6 +129,7 @@ struct nvme_root { bool log_pid; bool log_timestamp; bool modified; + bool mi_probe_enabled; }; int nvme_set_attr(const char *dir, const char *attr, const char *value); @@ -186,6 +191,14 @@ struct nvme_mi_transport { int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout); }; +/* quirks */ + +/* Set a minimum time between receiving a response from one command and + * sending the next request. Some devices may ignore new commands sent too soon + * after the previous request, so manually insert a delay + */ +#define NVME_QUIRK_MIN_INTER_COMMAND_TIME (1 << 0) + struct nvme_mi_ep { struct nvme_root *root; const struct nvme_mi_transport *transport; @@ -195,6 +208,12 @@ struct nvme_mi_ep { bool controllers_scanned; unsigned int timeout; unsigned int mprt_max; + unsigned long quirks; + + /* inter-command delay, for NVME_QUIRK_MIN_INTER_COMMAND_TIME */ + unsigned int inter_command_us; + struct timespec last_resp_time; + bool last_resp_time_valid; }; struct nvme_mi_ctrl { @@ -204,6 +223,7 @@ struct nvme_mi_ctrl { }; struct nvme_mi_ep *nvme_mi_init_ep(struct nvme_root *root); +void nvme_mi_ep_probe(struct nvme_mi_ep *ep); /* for tests, we need to calculate the correct MICs */ __u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len); diff --git a/src/nvme/tree.c b/src/nvme/tree.c index b992824..18826bf 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -252,6 +252,19 @@ void nvme_host_set_dhchap_key(nvme_host_t h, const char *key) h->dhchap_key = strdup(key); } +void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled) +{ + h->pdc_enabled_valid = true; + h->pdc_enabled = enabled; +} + +bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback) +{ + if (h->pdc_enabled_valid) + return h->pdc_enabled; + return fallback; +} + nvme_subsystem_t nvme_first_subsystem(nvme_host_t h) { return list_top(&h->subsystems, struct nvme_subsystem, entry); @@ -870,6 +883,16 @@ bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c) return c->discovery_ctrl; } +void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique) +{ + c->unique_discovery_ctrl = unique; +} + +bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c) +{ + return c->unique_discovery_ctrl; +} + int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id) { return nvme_identify_ctrl(nvme_ctrl_get_fd(c), id); @@ -931,7 +954,7 @@ int nvme_disconnect_ctrl(nvme_ctrl_t c) c->name, errno); return ret; } - nvme_msg(r, LOG_INFO, "%s: disconnected\n", c->name); + nvme_msg(r, LOG_INFO, "%s: %s disconnected\n", c->name, c->subsysnqn); nvme_deconfigure_ctrl(c); return 0; } @@ -1833,6 +1856,7 @@ static nvme_ns_t nvme_ns_open(const char *name) close_fd: close(n->fd); free_ns: + free(n->generic_name); free(n->name); free(n); return NULL; diff --git a/src/nvme/tree.h b/src/nvme/tree.h index 156cb79..e4a5126 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -107,6 +107,28 @@ const char *nvme_host_get_dhchap_key(nvme_host_t h); void nvme_host_set_dhchap_key(nvme_host_t h, const char *key); /** + * nvme_host_set_pdc_enabled() - Set Persistent Discovery Controller flag + * @h: Host for which the falg should be set + * @enabled: The bool to set the enabled flag + * + * When nvme_host_set_pdc_enabled() is not used to set the PDC flag, + * nvme_host_is_pdc_enabled() will return the default value which was + * passed into the function and not the undefined flag value. + */ +void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled); + +/** + * nvme_host_is_pdc_enabled() - Is Persistenct Discovery Controller enabled + * @h: Host which to check if PDC is enabled + * @fallback: The fallback default value of the flag when + * @nvme_host_set_pdc_enabled has not be used + * to set the flag. + * + * Return: true if PDC is enabled for @h, else false + */ +bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback); + +/** * nvme_default_host() - Initializes the default host * @r: &nvme_root_t object * @@ -969,6 +991,25 @@ void nvme_ctrl_set_discovery_ctrl(nvme_ctrl_t c, bool discovery); bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c); /** + * nvme_ctrl_set_unique_discovery_ctrl() - Set the 'unique_discovery_ctrl' flag + * @c: Controller to be modified + * @unique: value of the unique_disc_ctrl flag + * + * Sets the 'unique_discovery_ctrl' flag in @c to specify wheter + * @c is a unique discovery controller + * + */ +void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique); + +/** + * nvme_ctrl_is_unique_discovery_ctrl() - Check the 'unique_discovery_ctrl' flag + * @c: Controller to be checked + * + * Return: Value of the 'unique_discovery_ctrl' flag + */ +bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c); + +/** * nvme_ctrl_identify() - Issues an 'identify controller' command * @c: Controller instance * @id: Identify controller data structure diff --git a/src/nvme/types.h b/src/nvme/types.h index 94066fc..929d658 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -1127,6 +1127,7 @@ enum nvme_id_ctrl_oaes { * @NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS: Delete Endurance Groups supported * @NVME_CTRL_CTRATT_DEL_NVM_SETS: Delete NVM Sets supported * @NVME_CTRL_CTRATT_ELBAS: Extended LBA Formats supported + * @NVME_CTRL_CTRATT_FDPS: Flexible Data Placement supported */ enum nvme_id_ctrl_ctratt { NVME_CTRL_CTRATT_128_ID = 1 << 0, @@ -1145,6 +1146,7 @@ enum nvme_id_ctrl_ctratt { NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS = 1 << 13, NVME_CTRL_CTRATT_DEL_NVM_SETS = 1 << 14, NVME_CTRL_CTRATT_ELBAS = 1 << 15, + NVME_CTRL_CTRATT_FDPS = 1 << 19, }; /** @@ -2913,6 +2915,7 @@ enum nvme_status_result { * @NVME_ST_CODE_SHORT: Short device self-test operation. * @NVME_ST_CODE_EXTENDED: Extended device self-test operation. * @NVME_ST_CODE_VS: Vendor specific. + * @NVME_ST_CODE_ABORT: Abort device self-test operation. * @NVME_ST_CODE_SHIFT: Shift amount to get the code value from the * &struct nvme_st_result.dsts field. */ @@ -2921,6 +2924,7 @@ enum nvme_st_code { NVME_ST_CODE_SHORT = 0x1, NVME_ST_CODE_EXTENDED = 0x2, NVME_ST_CODE_VS = 0xe, + NVME_ST_CODE_ABORT = 0xf, NVME_ST_CODE_SHIFT = 4, }; @@ -3367,6 +3371,18 @@ struct nvme_fw_commit_event { } __attribute__((packed)); /** + * struct nvme_timestamp - Timestamp - Data Structure for Get Features + * @timestamp: Timestamp value based on origin and synch field + * @attr: Attribute + * @rsvd: Reserved + */ +struct nvme_timestamp { + __u8 timestamp[6]; + __u8 attr; + __u8 rsvd; +}; + +/** * struct nvme_time_stamp_change_event - Timestamp Change Event * @previous_timestamp: Previous Timestamp * @ml_secs_since_reset: Milliseconds Since Reset @@ -4079,6 +4095,295 @@ struct nvme_zone_report { }; /** + * enum nvme_fdp_ruh_type - Reclaim Unit Handle Type + * @NVME_FDP_RUHT_INITIALLY_ISOLATED: Initially Isolated + * @NVME_FDP_RUHT_PERSISTENTLY_ISOLATED: Persistently Isolated + */ +enum nvme_fdp_ruh_type { + NVME_FDP_RUHT_INITIALLY_ISOLATED = 1, + NVME_FDP_RUHT_PERSISTENTLY_ISOLATED = 2, +}; + +/** + * struct nvme_fdp_ruh_desc - Reclaim Unit Handle Descriptor + * @ruht: Reclaim Unit Handle Type + * @rsvd1: Reserved + */ +struct nvme_fdp_ruh_desc { + __u8 ruht; + __u8 rsvd1[3]; +}; + +/** + * enum nvme_fdp_config_fdpa - FDP Attributes + * @NVME_FDP_CONFIG_FDPA_RGIF_SHIFT: Reclaim Group Identifier Format Shift + * @NVME_FDP_CONFIG_FDPA_RGIF_MASK: Reclaim Group Identifier Format Mask + * @NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT: FDP Volatile Write Cache Shift + * @NVME_FDP_CONFIG_FDPA_FDPVWC_MASK: FDP Volatile Write Cache Mask + * @NVME_FDP_CONFIG_FDPA_VALID_SHIFT: FDP Configuration Valid Shift + * @NVME_FDP_CONFIG_FDPA_VALID_MASK: FDP Configuration Valid Mask + */ +enum nvme_fdp_config_fdpa { + NVME_FDP_CONFIG_FDPA_RGIF_SHIFT = 0, + NVME_FDP_CONFIG_FDPA_RGIF_MASK = 0xf, + NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT = 4, + NVME_FDP_CONFIG_FDPA_FDPVWC_MASK = 0x1, + NVME_FDP_CONFIG_FDPA_VALID_SHIFT = 7, + NVME_FDP_CONFIG_FDPA_VALID_MASK = 0x1, +}; + +/** + * struct nvme_fdp_config_desc - FDP Configuration Descriptor + * @size: Descriptor size + * @fdpa: FDP Attributes (&enum nvme_fdp_config_fdpa) + * @vss: Vendor Specific Size + * @nrg: Number of Reclaim Groups + * @nruh: Number of Reclaim Unit Handles + * @maxpids: Max Placement Identifiers + * @nnss: Number of Namespaces Supported + * @runs: Reclaim Unit Nominal Size + * @erutl: Estimated Reclaim Unit Time Limit + * @rsvd28: Reserved + * @ruhs: Reclaim Unit Handle descriptors (&struct nvme_fdp_ruh_desc) + */ +struct nvme_fdp_config_desc { + __u16 size; + __u8 fdpa; + __u8 vss; + __u32 nrg; + __u16 nruh; + __u16 maxpids; + __u32 nnss; + __u64 runs; + __u32 erutl; + __u8 rsvd28[36]; + struct nvme_fdp_ruh_desc ruhs[]; +}; + +/** + * struct nvme_fdp_config_log - FDP Configurations Log Page + * @n: Number of FDP Configurations + * @version: Log page version + * @rsvd3: Reserved + * @size: Log page size in bytes + * @rsvd8: Reserved + * @configs: FDP Configuration descriptors (&struct nvme_fdp_config_desc) + */ +struct nvme_fdp_config_log { + __u16 n; + __u8 version; + __u8 rsvd3; + __u32 size; + __u8 rsvd8[8]; + struct nvme_fdp_config_desc configs[]; +}; + +/** + * enum nvme_fdp_ruha - Reclaim Unit Handle Attributes + * @NVME_FDP_RUHA_HOST_SHIFT: Host Specified Reclaim Unit Handle Shift + * @NVME_FDP_RUHA_HOST_MASK: Host Specified Reclaim Unit Handle Mask + * @NVME_FDP_RUHA_CTRL_SHIFT: Controller Specified Reclaim Unit Handle Shift + * @NVME_FDP_RUHA_CTRL_MASK: Controller Specified Reclaim Unit Handle Mask + */ +enum nvme_fdp_ruha { + NVME_FDP_RUHA_HOST_SHIFT = 0, + NVME_FDP_RUHA_HOST_MASK = 0x1, + NVME_FDP_RUHA_CTRL_SHIFT = 1, + NVME_FDP_RUHA_CTRL_MASK = 0x1, +}; + +/** + * struct nvme_fdp_ruhu_desc - Reclaim Unit Handle Usage Descriptor + * @ruha: Reclaim Unit Handle Attributes (&enum nvme_fdp_ruha) + * @rsvd1: Reserved + */ +struct nvme_fdp_ruhu_desc { + __u8 ruha; + __u8 rsvd1[7]; +}; + +/** + * struct nvme_fdp_ruhu_log - Reclaim Unit Handle Usage Log Page + * @nruh: Number of Reclaim Unit Handles + * @rsvd2: Reserved + * @ruhus: Reclaim Unit Handle Usage descriptors + */ +struct nvme_fdp_ruhu_log { + __u16 nruh; + __u8 rsvd2[6]; + struct nvme_fdp_ruhu_desc ruhus[]; +}; + +/** + * struct nvme_fdp_stats_log - FDP Statistics Log Page + * @hbmw: Host Bytes with Metadata Written + * @mbmw: Media Bytes with Metadata Written + * @mbe: Media Bytes Erased + * @rsvd48: Reserved + */ +struct nvme_fdp_stats_log { + __u8 hbmw[16]; + __u8 mbmw[16]; + __u8 mbe[16]; + __u8 rsvd48[16]; +}; + +/** + * enum nvme_fdp_event_type - FDP Event Types + * @NVME_FDP_EVENT_RUNFW: Reclaim Unit Not Fully Written + * @NVME_FDP_EVENT_RUTLE: Reclaim Unit Time Limit Exceeded + * @NVME_FDP_EVENT_RESET: Controller Level Reset Modified Reclaim Unit Handles + * @NVME_FDP_EVENT_PID: Invalid Placement Identifier + * @NVME_FDP_EVENT_REALLOC: Media Reallocated + * @NVME_FDP_EVENT_MODIFY: Implicitly Modified Reclaim Unit Handle + */ +enum nvme_fdp_event_type { + /* Host Events */ + NVME_FDP_EVENT_RUNFW = 0x0, + NVME_FDP_EVENT_RUTLE = 0x1, + NVME_FDP_EVENT_RESET = 0x2, + NVME_FDP_EVENT_PID = 0x3, + + /* Controller Events */ + NVME_FDP_EVENT_REALLOC = 0x80, + NVME_FDP_EVENT_MODIFY = 0x81, +}; + +/** + * enum nvme_fdp_event_realloc_flags - Media Reallocated Event Type Specific Flags + * @NVME_FDP_EVENT_REALLOC_F_LBAV: LBA Valid + */ +enum nvme_fdp_event_realloc_flags { + NVME_FDP_EVENT_REALLOC_F_LBAV = 1 << 0, +}; + +/** + * struct nvme_fdp_event_realloc - Media Reallocated Event Type Specific Information + * @flags: Event Type Specific flags (&enum nvme_fdp_event_realloc_flags) + * @rsvd1: Reserved + * @nlbam: Number of LBAs Moved + * @lba: Logical Block Address + * @rsvd12: Reserved + */ +struct nvme_fdp_event_realloc { + __u8 flags; + __u8 rsvd1; + __u16 nlbam; + __u64 lba; + __u8 rsvd12[4]; +}; + +/** + * enum nvme_fdp_event_flags - FDP Event Flags + * @NVME_FDP_EVENT_F_PIV: Placement Identifier Valid + * @NVME_FDP_EVENT_F_NSIDV: Namespace Identifier Valid + * @NVME_FDP_EVENT_F_LV: Location Valid + */ +enum nvme_fdp_event_flags { + NVME_FDP_EVENT_F_PIV = 1 << 0, + NVME_FDP_EVENT_F_NSIDV = 1 << 1, + NVME_FDP_EVENT_F_LV = 1 << 2, +}; + +/** + * struct nvme_fdp_event - FDP Event + * @type: Event Type (&enum nvme_fdp_event_type) + * @flags: Event Flags (&enum nvme_fdp_event_flags) + * @pid: Placement Identifier + * @ts: Timestamp + * @nsid: Namespace Identifier + * @type_specific: Event Type Specific Information + * @rgid: Reclaim Group Identifier + * @ruhid: Reclaim Unit Handle Identifier + * @rsvd35: Reserved + * @vs: Vendor Specific + */ +struct nvme_fdp_event { + __u8 type; + __u8 flags; + __u16 pid; + struct nvme_timestamp ts; + __u32 nsid; + __u8 type_specific[16]; + __u16 rgid; + __u8 ruhid; + __u8 rsvd35[5]; + __u8 vs[24]; +}; + +/** + * struct nvme_fdp_events_log - FDP Events Log Page + * @n: Number of FDP Events + * @rsvd4: Reserved + * @events: FDP Events (&struct nvme_fdp_event) + */ +struct nvme_fdp_events_log { + __u32 n; + __u8 rsvd4[60]; + struct nvme_fdp_event events[63]; +}; + +/** + * struct nvme_feat_fdp_events_cdw11 - FDP Events Feature Command Dword 11 + * @phndl: Placement Handle + * @noet: Number of FDP Event Types + * @rsvd24: Reserved + */ +struct nvme_feat_fdp_events_cdw11 { + __u16 phndl; + __u8 noet; + __u8 rsvd24; +}; + +/** + * enum nvme_fdp_supported_event_attributes - Supported FDP Event Attributes + * @NVME_FDP_SUPP_EVENT_ENABLED_SHIFT: FDP Event Enable Shift + * @NVME_FDP_SUPP_EVENT_ENABLED_MASK: FDP Event Enable Mask + */ +enum nvme_fdp_supported_event_attributes { + NVME_FDP_SUPP_EVENT_ENABLED_SHIFT = 0, + NVME_FDP_SUPP_EVENT_ENABLED_MASK = 0x1, +}; + +/** + * struct nvme_fdp_supported_event_desc - Supported FDP Event Descriptor + * @evt: FDP Event Type + * @evta: FDP Event Type Attributes (&enum nvme_fdp_supported_event_attributes) + */ +struct nvme_fdp_supported_event_desc { + __u8 evt; + __u8 evta; +}; + +/** + * struct nvme_fdp_ruh_status_desc - Reclaim Unit Handle Status Descriptor + * @pid: Placement Identifier + * @ruhid: Reclaim Unit Handle Identifier + * @earutr: Estimated Active Reclaim Unit Time Remaining + * @ruamw: Reclaim Unit Available Media Writes + * @rsvd16: Reserved + */ +struct nvme_fdp_ruh_status_desc { + __u16 pid; + __u16 ruhid; + __u32 earutr; + __u64 ruamw; + __u8 rsvd16[16]; +}; + +/** + * struct nvme_fdp_ruh_status - Reclaim Unit Handle Status + * @rsvd0: Reserved + * @nruhsd: Number of Reclaim Unit Handle Status Descriptors + * @ruhss: Reclaim Unit Handle Status descriptors + */ +struct nvme_fdp_ruh_status { + __u8 rsvd0[14]; + __u16 nruhsd; + struct nvme_fdp_ruh_status_desc ruhss[]; +}; + +/** * struct nvme_lba_status_desc - LBA Status Descriptor Entry * @dslba: Descriptor Starting LBA * @nlb: Number of Logical Blocks @@ -4223,18 +4528,6 @@ enum nvme_ns_metadata_type { }; /** - * struct nvme_timestamp - Timestamp - Data Structure for Get Features - * @timestamp: Timestamp value based on origin and synch field - * @attr: Attribute - * @rsvd: Reserved - */ -struct nvme_timestamp { - __u8 timestamp[6]; - __u8 attr; - __u8 rsvd; -}; - -/** * struct nvme_lba_range_type_entry - LBA Range Type - Data Structure Entry * @type: Specifies the Type of the LBA range * @attributes: Specifies attributes of the LBA range @@ -6376,6 +6669,10 @@ enum nvme_identify_cns { * @NVME_LOG_LID_FID_SUPPORTED_EFFECTS: Feature Identifiers Supported and Effects * @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects * @NVME_LOG_LID_BOOT_PARTITION: Boot Partition + * @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations + * @NVME_LOG_LID_FDP_RUH_USAGE: Reclaim Unit Handle Usage + * @NVME_LOG_LID_FDP_STATS: FDP Statistics + * @NVME_LOG_LID_FDP_EVENTS: FDP Events * @NVME_LOG_LID_DISCOVER: Discovery * @NVME_LOG_LID_RESERVATION: Reservation Notification * @NVME_LOG_LID_SANITIZE: Sanitize Status @@ -6403,6 +6700,10 @@ enum nvme_cmd_get_log_lid { NVME_LOG_LID_FID_SUPPORTED_EFFECTS = 0x12, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13, NVME_LOG_LID_BOOT_PARTITION = 0x15, + NVME_LOG_LID_FDP_CONFIGS = 0x20, + NVME_LOG_LID_FDP_RUH_USAGE = 0x21, + NVME_LOG_LID_FDP_STATS = 0x22, + NVME_LOG_LID_FDP_EVENTS = 0x23, NVME_LOG_LID_DISCOVER = 0x70, NVME_LOG_LID_RESERVATION = 0x80, NVME_LOG_LID_SANITIZE = 0x81, @@ -6437,6 +6738,8 @@ enum nvme_cmd_get_log_lid { * @NVME_FEAT_FID_ENDURANCE_EVT_CFG: Endurance Group Event Configuration * @NVME_FEAT_FID_IOCS_PROFILE: I/O Command Set Profile * @NVME_FEAT_FID_SPINUP_CONTROL: Spinup Control + * @NVME_FEAT_FID_FDP: Flexible Data Placement + * @NVME_FEAT_FID_FDP_EVENTS: FDP Events * @NVME_FEAT_FID_ENH_CTRL_METADATA: Enhanced Controller Metadata * @NVME_FEAT_FID_CTRL_METADATA: Controller Metadata * @NVME_FEAT_FID_NS_METADATA: Namespace Metadata @@ -6473,6 +6776,8 @@ enum nvme_features_id { NVME_FEAT_FID_ENDURANCE_EVT_CFG = 0x18, NVME_FEAT_FID_IOCS_PROFILE = 0x19, /* XXX: Placeholder until assigned */ NVME_FEAT_FID_SPINUP_CONTROL = 0x1a, + NVME_FEAT_FID_FDP = 0x1d, + NVME_FEAT_FID_FDP_EVENTS = 0x1e, NVME_FEAT_FID_ENH_CTRL_METADATA = 0x7d, NVME_FEAT_FID_CTRL_METADATA = 0x7e, NVME_FEAT_FID_NS_METADATA = 0x7f, @@ -6583,6 +6888,12 @@ enum nvme_features_id { * @NVME_FEAT_WP_WPS_MASK: * @NVME_FEAT_IOCSP_IOCSCI_SHIFT: * @NVME_FEAT_IOCSP_IOCSCI_MASK: + * @NVME_FEAT_FDP_ENABLED_SHIFT: + * @NVME_FEAT_FDP_ENABLED_MASK: + * @NVME_FEAT_FDP_INDEX_SHIFT: + * @NVME_FEAT_FDP_INDEX_MASK: + * @NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT: + * @NVME_FEAT_FDP_EVENTS_ENABLE_MASK: */ enum nvme_feat { NVME_FEAT_ARBITRATION_BURST_SHIFT = 0, @@ -6683,6 +6994,12 @@ enum nvme_feat { NVME_FEAT_WP_WPS_MASK = 0x7, NVME_FEAT_IOCSP_IOCSCI_SHIFT = 0, NVME_FEAT_IOCSP_IOCSCI_MASK = 0xff, + NVME_FEAT_FDP_ENABLED_SHIFT = 0, + NVME_FEAT_FDP_ENABLED_MASK = 0x1, + NVME_FEAT_FDP_INDEX_SHIFT = 8, + NVME_FEAT_FDP_INDEX_MASK = 0xf, + NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT = 0, + NVME_FEAT_FDP_EVENTS_ENABLE_MASK = 0x1, }; /** @@ -7073,8 +7390,10 @@ enum nvme_data_tfr { * @nvme_cmd_resv_register: Reservation Register * @nvme_cmd_resv_report: Reservation Report * @nvme_cmd_resv_acquire: Reservation Acquire + * @nvme_cmd_io_mgmt_recv: I/O Management Receive * @nvme_cmd_resv_release: Reservation Release * @nvme_cmd_copy: Copy + * @nvme_cmd_io_mgmt_send: I/O Management Send * @nvme_zns_cmd_mgmt_send: Zone Management Send * @nvme_zns_cmd_mgmt_recv: Zone Management Receive * @nvme_zns_cmd_append: Zone Append @@ -7091,8 +7410,10 @@ enum nvme_io_opcode { nvme_cmd_resv_register = 0x0d, nvme_cmd_resv_report = 0x0e, nvme_cmd_resv_acquire = 0x11, + nvme_cmd_io_mgmt_recv = 0x12, nvme_cmd_resv_release = 0x15, nvme_cmd_copy = 0x19, + nvme_cmd_io_mgmt_send = 0x1d, nvme_zns_cmd_mgmt_send = 0x79, nvme_zns_cmd_mgmt_recv = 0x7a, nvme_zns_cmd_append = 0x7d, @@ -7295,4 +7616,20 @@ enum nvme_zns_report_options { NVME_ZNS_ZRAS_REPORT_OFFLINE = 0x7, }; +/** + * enum nvme_io_mgmt_recv_mo - I/O Management Receive - Management Operation + * @NVME_IO_MGMT_RECV_RUH_STATUS: Reclaim Unit Handle Status + */ +enum nvme_io_mgmt_recv_mo { + NVME_IO_MGMT_RECV_RUH_STATUS = 0x1, +}; + +/** + * enum nvme_io_mgmt_send_mo - I/O Management Send - Management Operation + * @NVME_IO_MGMT_SEND_RUH_UPDATE: Reclaim Unit Handle Update + */ +enum nvme_io_mgmt_send_mo { + NVME_IO_MGMT_SEND_RUH_UPDATE = 0x1, +}; + #endif /* _LIBNVME_TYPES_H */ diff --git a/src/nvme/util.c b/src/nvme/util.c index c61dbe9..0354afe 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -325,7 +325,7 @@ static const char * const media_status[] = { }; static const char * const path_status[] = { - [NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the commmand prevented completion", + [NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the command prevented completion", [NVME_SC_ANA_PERSISTENT_LOSS] = "Asymmetric Access Persistent Loss: The controller is in a persistent loss state with the requested namespace", [NVME_SC_ANA_INACCESSIBLE] = "Asymmetric Access Inaccessible: The controller is in an inaccessible state with the requested namespace", [NVME_SC_ANA_TRANSITION] = "Asymmetric Access Transition: The controller is currently transitioning states with the requested namespace", @@ -559,11 +559,12 @@ static const char * const libnvme_status[] = { [ENVME_CONNECT_INVAL_TR] = "invalid transport type", [ENVME_CONNECT_LOOKUP_SUBSYS_NAME] = "failed to lookup subsystem name", [ENVME_CONNECT_LOOKUP_SUBSYS] = "failed to lookup subsystem", - [ENVME_CONNECT_ALREADY] = "already connnected", + [ENVME_CONNECT_ALREADY] = "already connected", [ENVME_CONNECT_INVAL] = "invalid arguments/configuration", [ENVME_CONNECT_ADDRINUSE] = "hostnqn already in use", [ENVME_CONNECT_NODEV] = "invalid interface", [ENVME_CONNECT_OPNOTSUPP] ="not supported", + [ENVME_CONNECT_CONNREFUSED] = "connection refused", }; const char *nvme_errno_to_string(int status) @@ -573,6 +574,7 @@ const char *nvme_errno_to_string(int status) return s; } +#ifdef HAVE_LIBNSS char *hostname2traddr(struct nvme_root *r, const char *traddr) { struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC}; @@ -617,6 +619,18 @@ free_addrinfo: return ret_traddr; } +#else /* !HAVE_LIBNSS */ + +char *hostname2traddr(struct nvme_root *r, const char *traddr) +{ + nvme_msg(NULL, LOG_ERR, "No support for hostname IP address resolution; " \ + "recompile with libnss support.\n"); + + errno = -ENOTSUP; + return NULL; +} +#endif /* HAVE_LIBNSS */ + char *startswith(const char *s, const char *prefix) { size_t l; @@ -871,13 +885,11 @@ int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]) if (f < 0) return -errno; n = read(f, uuid, NVME_UUID_LEN); - if (n < 0) { - close(f); + close(f); + if (n < 0) return -errno; - } else if (n != NVME_UUID_LEN) { - close(f); + else if (n != NVME_UUID_LEN) return -EIO; - } /* * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4 diff --git a/src/nvme/util.h b/src/nvme/util.h index e72c156..961da18 100644 --- a/src/nvme/util.h +++ b/src/nvme/util.h @@ -36,6 +36,7 @@ * @ENVME_CONNECT_ADDRINUSE: hostnqn already in use * @ENVME_CONNECT_NODEV: invalid interface * @ENVME_CONNECT_OPNOTSUPP: not supported + * @ENVME_CONNECT_CONNREFUSED: connection refused */ enum nvme_connect_err { ENVME_CONNECT_RESOLVE = 1000, @@ -55,6 +56,7 @@ enum nvme_connect_err { ENVME_CONNECT_ADDRINUSE, ENVME_CONNECT_NODEV, ENVME_CONNECT_OPNOTSUPP, + ENVME_CONNECT_CONNREFUSED, }; /** @@ -529,13 +531,13 @@ char *startswith(const char *s, const char *prefix); /** * nvmf_exat_len() - Return length rounded up by 4 - * @val_len: Value lenght + * @val_len: Value length * * Return the size in bytes, rounded to a multiple of 4 (e.g., size of * __u32), of the buffer needed to hold the exat value of size * @val_len. * - * Return: Lenght rounded up by 4 + * Return: Length rounded up by 4 */ static inline __u16 nvmf_exat_len(size_t val_len) { |