summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libnvme-mi.map8
-rw-r--r--src/libnvme.map12
-rw-r--r--src/meson.build2
-rw-r--r--src/nvme/api-types.h44
-rw-r--r--src/nvme/fabrics.c93
-rw-r--r--src/nvme/ioctl.c84
-rw-r--r--src/nvme/ioctl.h245
-rw-r--r--src/nvme/json.c9
-rw-r--r--src/nvme/linux.c39
-rw-r--r--src/nvme/linux.h11
-rw-r--r--src/nvme/mi-mctp.c354
-rw-r--r--src/nvme/mi.c351
-rw-r--r--src/nvme/mi.h53
-rw-r--r--src/nvme/private.h20
-rw-r--r--src/nvme/tree.c26
-rw-r--r--src/nvme/tree.h41
-rw-r--r--src/nvme/types.h361
-rw-r--r--src/nvme/util.c26
-rw-r--r--src/nvme/util.h6
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)
{