diff options
Diffstat (limited to 'src/nvme')
-rw-r--r-- | src/nvme/fabrics.c | 146 | ||||
-rw-r--r-- | src/nvme/fabrics.h | 44 | ||||
-rw-r--r-- | src/nvme/ioctl.h | 2 | ||||
-rw-r--r-- | src/nvme/json.c | 61 | ||||
-rw-r--r-- | src/nvme/mi-mctp.c | 57 | ||||
-rw-r--r-- | src/nvme/mi.c | 406 | ||||
-rw-r--r-- | src/nvme/mi.h | 1458 | ||||
-rw-r--r-- | src/nvme/private.h | 5 | ||||
-rw-r--r-- | src/nvme/tree.c | 42 | ||||
-rw-r--r-- | src/nvme/tree.h | 18 | ||||
-rw-r--r-- | src/nvme/types.h | 111 | ||||
-rw-r--r-- | src/nvme/util.c | 77 | ||||
-rw-r--r-- | src/nvme/util.h | 49 |
13 files changed, 2341 insertions, 135 deletions
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index b68b7b9..a501f79 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -39,7 +39,6 @@ #include "private.h" #define NVMF_HOSTID_SIZE 37 -#define UUID_SIZE 37 /* 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ #define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn" #define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid" @@ -192,13 +191,15 @@ void nvmf_default_config(struct nvme_fabrics_config *cfg) #define MERGE_CFG_OPTION(c, n, o, d) \ if ((c)->o == d) (c)->o = (n)->o +#define MERGE_CFG_OPTION_STR(c, n, o, d) \ + if ((c)->o == d && (n)->o) (c)->o = strdup((n)->o) static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) { struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c); - MERGE_CFG_OPTION(ctrl_cfg, cfg, host_traddr, NULL); - MERGE_CFG_OPTION(ctrl_cfg, cfg, host_iface, NULL); + MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_traddr, NULL); + MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_iface, NULL); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0); @@ -295,7 +296,7 @@ static int add_argument(char **argstr, const char *tok, const char *arg) { char *nstr; - if (!(arg && strcmp(arg, "none"))) + if (!arg || arg[0] == '\0' || !strcmp(arg, "none")) return 0; if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) { errno = ENOMEM; @@ -465,6 +466,8 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) hostnqn = nvme_host_get_hostnqn(h); hostid = nvme_host_get_hostid(h); hostkey = nvme_host_get_dhchap_key(h); + if (!hostkey) + hostkey = nvme_ctrl_get_dhchap_host_key(c); ctrlkey = nvme_ctrl_get_dhchap_key(c); if (add_argument(argstr, "transport", transport) || add_argument(argstr, "traddr", @@ -613,14 +616,20 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, nvme_ctrl_get_trsvcid(c), NULL); if (fc) { + const char *key; + cfg = merge_config(c, nvme_ctrl_get_config(fc)); /* * An authentication key might already been set * in @cfg, so ensure to update @c with the correct * controller key. */ - if (fc->dhchap_key) - nvme_ctrl_set_dhchap_key(c, fc->dhchap_key); + key = nvme_ctrl_get_dhchap_host_key(fc); + if (key) + nvme_ctrl_set_dhchap_host_key(c, key); + key = nvme_ctrl_get_dhchap_key(fc); + if (key) + nvme_ctrl_set_dhchap_key(c, key); } } @@ -734,7 +743,7 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, default: nvme_msg(h->r, LOG_ERR, "unsupported subtype %d\n", e->subtype); - /* fallthrough */ + fallthrough; case NVME_NQN_NVME: nvme_ctrl_set_discovery_ctrl(c, false); break; @@ -772,30 +781,9 @@ nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, return NULL; } -static int nvme_discovery_log(int fd, __u32 len, struct nvmf_discovery_log *log, bool rae) -{ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = fd, - .nsid = NVME_NSID_NONE, - .lsp = NVME_LOG_LSP_NONE, - .lsi = NVME_LOG_LSI_NONE, - .uuidx = NVME_UUID_NONE, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - .lid = NVME_LOG_LID_DISCOVER, - .log = log, - .len = len, - .csi = NVME_CSI_NVM, - .rae = rae, - .ot = false, - }; - - return nvme_get_log_page(fd, 4096, &args); -} - -int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, - int max_retries) +static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c, + struct nvme_get_log_args *args, + int max_retries) { nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL; struct nvmf_discovery_log *log = NULL; @@ -803,6 +791,9 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, const char *name = nvme_ctrl_get_name(c); uint64_t genctr, numrec; unsigned int size; + int fd = nvme_ctrl_get_fd(c); + + args->fd = fd; do { size = sizeof(struct nvmf_discovery_log); @@ -813,12 +804,15 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, nvme_msg(r, LOG_ERR, "could not allocate memory for discovery log header\n"); errno = ENOMEM; - return -1; + return NULL; } nvme_msg(r, LOG_DEBUG, "%s: get header (try %d/%d)\n", name, retries, max_retries); - ret = nvme_discovery_log(nvme_ctrl_get_fd(c), size, log, true); + args->rae = true; + args->len = size; + args->log = log; + ret = nvme_get_log_page(fd, 4096, args); if (ret) { nvme_msg(r, LOG_INFO, "%s: discover try %d/%d failed, error %d\n", @@ -841,14 +835,19 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, nvme_msg(r, LOG_ERR, "could not alloc memory for discovery log page\n"); errno = ENOMEM; - return -1; + return NULL; } nvme_msg(r, LOG_DEBUG, "%s: get header and %" PRIu64 " records (length %d genctr %" PRIu64 ")\n", name, numrec, size, genctr); - ret = nvme_discovery_log(nvme_ctrl_get_fd(c), size, log, false); + + args->rae = false; + args->len = size; + args->log = log; + ret = nvme_get_log_page(fd, 4096, args); + if (ret) { nvme_msg(r, LOG_INFO, "%s: discover try %d/%d failed, error %d\n", @@ -861,21 +860,63 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, if (genctr != le64_to_cpu(log->genctr)) { nvme_msg(r, LOG_INFO, "%s: discover genctr mismatch\n", name); errno = EAGAIN; - ret = -1; } else if (numrec != le64_to_cpu(log->numrec)) { nvme_msg(r, LOG_INFO, "%s: could only fetch %" PRIu64 " of %" PRIu64 " records\n", name, numrec, le64_to_cpu(log->numrec)); errno = EBADSLT; - ret = -1; } else { - *logp = log; - return 0; + return log; } out_free_log: free(log); - return ret; + return NULL; +} + +int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, + int max_retries) +{ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = nvme_ctrl_get_fd(c), + .nsid = NVME_NSID_NONE, + .lsp = NVMF_LOG_DISC_LSP_NONE, + .lsi = NVME_LOG_LSI_NONE, + .uuidx = NVME_UUID_NONE, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + .lid = NVME_LOG_LID_DISCOVER, + .log = NULL, + .len = 0, + .csi = NVME_CSI_NVM, + .rae = false, + .ot = false, + }; + *logp = nvme_discovery_log(c, &args, max_retries); + return logp ? 0 : -1; +} + +struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args) +{ + struct nvme_get_log_args _args = { + .args_size = sizeof(_args), + .fd = nvme_ctrl_get_fd(args->c), + .nsid = NVME_NSID_NONE, + .lsp = args->lsp, + .lsi = NVME_LOG_LSI_NONE, + .uuidx = NVME_UUID_NONE, + .timeout = args->timeout, + .result = args->result, + .lid = NVME_LOG_LID_DISCOVER, + .log = NULL, + .len = 0, + .csi = NVME_CSI_NVM, + .rae = false, + .ot = false, + }; + + return nvme_discovery_log(args->c, &_args, args->max_retries); } #define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" @@ -889,8 +930,8 @@ static int uuid_from_device_tree(char *system_uuid) if (f < 0) return -ENXIO; - memset(system_uuid, 0, UUID_SIZE); - len = read(f, system_uuid, UUID_SIZE - 1); + memset(system_uuid, 0, NVME_UUID_LEN_STRING); + len = read(f, system_uuid, NVME_UUID_LEN_STRING - 1); close(f); if (len < 0) return -ENXIO; @@ -978,7 +1019,7 @@ static int uuid_from_product_uuid(char *system_uuid) system_uuid[0] = '\0'; nread = getline(&line, &len, stream); - if (nread != UUID_SIZE) { + if (nread != NVME_UUID_LEN_STRING) { ret = -ENXIO; goto out; } @@ -986,8 +1027,8 @@ static int uuid_from_product_uuid(char *system_uuid) /* The kernel is handling the byte swapping according DMTF * SMBIOS 3.0 Section 7.2.1 System UUID */ - memcpy(system_uuid, line, UUID_SIZE - 1); - system_uuid[UUID_SIZE - 1] = '\0'; + memcpy(system_uuid, line, NVME_UUID_LEN_STRING - 1); + system_uuid[NVME_UUID_LEN_STRING - 1] = '\0'; ret = 0; @@ -1023,16 +1064,17 @@ char *nvmf_hostnqn_generate() { char *hostnqn; int ret; - char uuid_str[UUID_SIZE]; - uuid_t uuid; + char uuid_str[NVME_UUID_LEN_STRING]; + unsigned char uuid[NVME_UUID_LEN]; ret = uuid_from_dmi(uuid_str); if (ret < 0) { ret = uuid_from_device_tree(uuid_str); } if (ret < 0) { - uuid_generate_random(uuid); - uuid_unparse_lower(uuid, uuid_str); + if (nvme_uuid_random(uuid) < 0) + memset(uuid, 0, NVME_UUID_LEN); + nvme_uuid_to_string(uuid, uuid_str); } if (asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid_str) < 0) @@ -1085,7 +1127,7 @@ static __u32 nvmf_get_tel(const char *hostsymname) __u16 len; /* Host ID is mandatory */ - tel += nvmf_exat_size(sizeof(uuid_t)); + tel += nvmf_exat_size(NVME_UUID_LEN_STRING); /* Symbolic name is optional */ len = hostsymname ? strlen(hostsymname) : 0; @@ -1129,8 +1171,8 @@ static void nvmf_fill_die(struct nvmf_ext_die *die, struct nvme_host *h, numexat++; exat = die->exat; exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID); - exat->exatlen = cpu_to_le16(nvmf_exat_len(sizeof(uuid_t))); - uuid_parse(h->hostid, exat->exatval); + exat->exatlen = cpu_to_le16(nvmf_exat_len(NVME_UUID_LEN)); + nvme_uuid_from_string(h->hostid, exat->exatval); /* Extended Attribute for the Symbolic Name (optional) */ symname_len = h->hostsymname ? strlen(h->hostsymname) : 0; diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h index 9e099fe..272bb40 100644 --- a/src/nvme/fabrics.h +++ b/src/nvme/fabrics.h @@ -195,9 +195,14 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, /** * nvmf_get_discovery_log() - Return the discovery log page - * @c: Discover controller to use + * @c: Discovery controller to use * @logp: Pointer to the log page to be returned - * @max_retries: maximum number of log page entries to be returned + * @max_retries: Number of retries in case of failure + * + * The memory allocated for the log page and returned in @logp + * must be freed by the caller using free(). + * + * Note: Consider using nvmf_get_discovery_wargs() instead. * * Return: 0 on success; on failure -1 is returned and errno is set */ @@ -205,6 +210,41 @@ int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp, int max_retries); /** + * struct nvme_get_discovery_args - Arguments for nvmf_get_discovery_wargs() + * @c: Discovery controller + * @args_size: Length of the structure + * @max_retries: Number of retries in case of failure + * @result: The command completion result from CQE dword0 + * @timeout: Timeout in ms (default: NVME_DEFAULT_IOCTL_TIMEOUT) + * @lsp: Log specific field (See enum nvmf_log_discovery_lsp) + */ +struct nvme_get_discovery_args { + nvme_ctrl_t c; + int args_size; + int max_retries; + __u32 *result; + __u32 timeout; + __u8 lsp; +}; + +/** + * nvmf_get_discovery_wargs() - Get the discovery log page with args + * @args: Argument structure + * + * This function is similar to nvmf_get_discovery_log(), but + * takes an extensible @args parameter. @args provides more + * options than nvmf_get_discovery_log(). + * + * This function performs a get discovery log page (DLP) command + * and returns the DLP. The memory allocated for the returned + * DLP must be freed by the caller using free(). + * + * Return: Pointer to the discovery log page (to be freed). NULL + * on failure and errno is set. + */ +struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args); + +/** * nvmf_hostnqn_generate() - Generate a machine specific host nqn * Returns: An nvm namespace qualified name string based on the machine * identifier, or NULL if not successful. diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h index d559b12..af95851 100644 --- a/src/nvme/ioctl.h +++ b/src/nvme/ioctl.h @@ -1461,7 +1461,7 @@ static inline int nvme_get_log_telemetry_host(int fd, __u64 offset, __u32 len, void *log) { struct nvme_get_log_args args = { - .lpo = 0, + .lpo = offset, .result = NULL, .log = log, .args_size = sizeof(args), diff --git a/src/nvme/json.c b/src/nvme/json.c index b42cd51..f0c2ab4 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -95,6 +95,9 @@ static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj) if (!c) return; json_update_attributes(c, port_obj); + attr_obj = json_object_object_get(port_obj, "dhchap_key"); + if (attr_obj) + nvme_ctrl_set_dhchap_host_key(c, json_object_get_string(attr_obj)); attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key"); if (attr_obj) nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj)); @@ -155,6 +158,43 @@ static void json_parse_host(nvme_root_t r, struct json_object *host_obj) } } +static struct json_object *parse_json(nvme_root_t r, int fd) +{ + char buf[JSON_FILE_BUF_SIZE]; + struct json_object *obj = NULL; + struct printbuf *pb; + json_tokener *tok = NULL; + int ret; + + pb = printbuf_new(); + if (!pb) + return NULL; + + while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) + printbuf_memappend(pb, buf, ret); + + if (ret < 0) + goto out; + + tok = json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); + if (!tok) + goto out; + + /* Enforce correctly formatted JSON */ + tok->flags = JSON_TOKENER_STRICT; + + obj = json_tokener_parse_ex(tok, pb->buf, printbuf_length(pb)); + if (!obj) + nvme_msg(r, LOG_DEBUG, "JSON parsing failed: %s\n", + json_util_get_last_err()); +out: + if (tok) + json_tokener_free(tok); + printbuf_free(pb); + + return obj; +} + int json_read_config(nvme_root_t r, const char *config_file) { struct json_object *json_root, *host_obj; @@ -166,12 +206,16 @@ int json_read_config(nvme_root_t r, const char *config_file) config_file, strerror(errno)); return fd; } - json_root = json_object_from_fd(fd); + json_root = parse_json(r, fd); + close(fd); if (!json_root) { - nvme_msg(r, LOG_DEBUG, "Failed to read %s, %s\n", - config_file, json_util_get_last_err()); errno = EPROTO; - close(fd); + return -1; + } + if (!json_object_is_type(json_root, json_type_array)) { + nvme_msg(r, LOG_DEBUG, "Wrong format, expected array\n"); + json_object_put(json_root); + errno = EPROTO; return -1; } for (h = 0; h < json_object_array_length(json_root); h++) { @@ -180,7 +224,6 @@ int json_read_config(nvme_root_t r, const char *config_file) json_parse_host(r, host_obj); } json_object_put(json_root); - close(fd); return 0; } @@ -222,6 +265,10 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) if (value) json_object_object_add(port_obj, "trsvcid", json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_host_key(c); + if (value) + json_object_object_add(port_obj, "dhchap_key", + json_object_new_string(value)); value = nvme_ctrl_get_dhchap_key(c); if (value) json_object_object_add(port_obj, "dhchap_ctrl_key", @@ -365,6 +412,10 @@ static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c) if (value) json_object_object_add(ctrl_obj, "trsvcid", json_object_new_string(value)); + value = nvme_ctrl_get_dhchap_host_key(c); + if (value) + json_object_object_add(ctrl_obj, "dhchap_key", + json_object_new_string(value)); value = nvme_ctrl_get_dhchap_key(c); if (value) json_object_object_add(ctrl_obj, "dhchap_ctrl_key", diff --git a/src/nvme/mi-mctp.c b/src/nvme/mi-mctp.c index ae604f2..86f5df6 100644 --- a/src/nvme/mi-mctp.c +++ b/src/nvme/mi-mctp.c @@ -179,13 +179,15 @@ struct nvme_mi_msg_resp_mpr { * populate the worst-case expected processing time, given in milliseconds. */ static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len, - unsigned int *mpr_time) + __le32 mic, unsigned int *mpr_time) { + struct nvme_mi_admin_resp_hdr *admin_msg; struct nvme_mi_msg_resp_mpr *msg; - __le32 mic; + size_t clen; __u32 crc; - if (len != sizeof(*msg) + sizeof(mic)) + /* We need at least the minimal header plus checksum */ + if (len < sizeof(*msg) + sizeof(mic)) return false; msg = (struct nvme_mi_msg_resp_mpr *)resp->hdr; @@ -193,22 +195,42 @@ static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len, if (msg->status != NVME_MI_RESP_MPR) return false; - /* We can't use verify_resp_mic here, as the response structure has - * not been laid-out properly in resp yet (this is deferred until - * we have the actual response). + /* Find and verify the MIC from the response, which may not be laid out + * in resp as we expect. We have to preserve resp->hdr_len and + * resp->data_len, as we will need them for the eventual reply message. + * Because of that, we can't use verify_resp_mic here. * - * We know the data is a fixed size, and linear in the hdr buf, so - * calculation is fairly simple. We do need to find the MIC data - * though, which could either be in the header buf (if the original - * header was larger than the minimal header message), or the start of - * the data buf (otherwise). + * If the packet was at the expected response size, then mic will + * be set already; if not, find it within the header/data buffers. + */ + + /* Devices may send a MPR response as a full-sized Admin response, + * rather than the minimal MI-only header. Allow this, but only if the + * type indicates admin, and the allocated response header is the + * correct size for an Admin response. + */ + if (((msg->hdr.nmp >> 3) & 0xf) == NVME_MI_MT_ADMIN && + len == sizeof(*admin_msg) + sizeof(mic) && + resp->hdr_len == sizeof(*admin_msg)) { + if (resp->data_len) + mic = *(__le32 *)resp->data; + } else if (len == sizeof(*msg) + sizeof(mic)) { + if (resp->hdr_len > sizeof(*msg)) + mic = *(__le32 *)(msg + 1); + else if (resp->data_len) + mic = *(__le32 *)(resp->data); + } else { + return false; + } + + /* Since our response is just a header, we're guaranteed to have + * all data in resp->hdr. The response may be shorter than the expected + * header though, so clamp to len. */ - if (resp->hdr_len > sizeof(*msg)) - mic = *(__le32 *)(msg + 1); - else - mic = *(__le32 *)(resp->data); + len -= sizeof(mic); + clen = len < resp->hdr_len ? len : resp->hdr_len; - crc = ~nvme_mi_crc32_update(0xffffffff, msg, sizeof(*msg)); + crc = ~nvme_mi_crc32_update(0xffffffff, resp->hdr, clen); if (le32_to_cpu(mic) != crc) return false; @@ -369,7 +391,7 @@ retry: * header fields. However, we need to do this in the transport in order * to keep the tag allocated and retry the recvmsg */ - if (nvme_mi_mctp_resp_is_mpr(resp, len, &mpr_time)) { + if (nvme_mi_mctp_resp_is_mpr(resp, len, mic, &mpr_time)) { nvme_msg(ep->root, LOG_DEBUG, "Received More Processing Required, waiting for response\n"); @@ -493,6 +515,7 @@ nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid) err_free_ep: errno_save = errno; free(ep); + free(mctp); errno = errno_save; return NULL; } diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 181a16c..6ff0a6f 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -11,6 +11,7 @@ #include <stdlib.h> #include <stdio.h> +#include <ccan/array_size/array_size.h> #include <ccan/endian/endian.h> #include "log.h" @@ -295,6 +296,52 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp, resp->hdr_len = sizeof(*hdr); } +static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result) +{ + struct nvme_mi_admin_resp_hdr *admin_hdr; + struct nvme_mi_msg_resp *resp_hdr; + __u32 nvme_status; + __u32 nvme_result; + + /* we have a few different sources of "result" here: the status header + * in the MI response, the cdw3 status field, and (command specific) + * return values in cdw0. The latter is returned in the result pointer, + * the former two generate return values here + */ + + if (resp->hdr_len < sizeof(*resp_hdr)) { + errno = -EPROTO; + return -1; + } + resp_hdr = (struct nvme_mi_msg_resp *)resp->hdr; + + /* If we have a MI error, we can't be sure there's an admin header + * following; return just the MI status, with the status type + * indicator of MI. + */ + if (resp_hdr->status) + return resp_hdr->status | + (NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT); + + /* We shouldn't hit this, as we'd have an error reported earlier. + * However, for pointer safety, ensure we have a full admin header + */ + if (resp->hdr_len < sizeof(*admin_hdr)) { + errno = EPROTO; + return -1; + } + + admin_hdr = (struct nvme_mi_admin_resp_hdr *)resp->hdr; + nvme_result = le32_to_cpu(admin_hdr->cdw0); + nvme_status = le32_to_cpu(admin_hdr->cdw3) >> 16; + + /* the result pointer, optionally stored if the caller needs it */ + if (result) + *result = nvme_result; + + return nvme_status; +} + int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, size_t req_data_size, @@ -343,6 +390,7 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, admin_req->hdr.type = NVME_MI_MSGTYPE_NVME; admin_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_ADMIN << 3); + admin_req->ctrl_id = cpu_to_le16(ctrl->id); memset(&req, 0, sizeof(req)); req.hdr = &admin_req->hdr; req.hdr_len = sizeof(*admin_req); @@ -414,11 +462,9 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, if (rc) return rc; - if (resp_hdr.status) - return resp_hdr.status; - - if (args->result) - *args->result = le32_to_cpu(resp_hdr.cdw0); + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; /* callers will expect a full response; if the data buffer isn't * fully valid, return an error */ @@ -452,7 +498,7 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, return -1; } - if (offset < 0 || offset >= len) { + if (offset < 0 || offset >= args->len || offset + len > args->len) { errno = EINVAL; return -1; } @@ -489,12 +535,11 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, if (rc) return rc; - if (resp_hdr.status) - return resp_hdr.status; - - *lenp = resp.data_len; + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (!rc) + *lenp = resp.data_len; - return 0; + return rc; } int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) @@ -580,13 +625,7 @@ int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, if (rc) return rc; - if (resp_hdr.status) - return resp_hdr.status; - - if (args->result) - *args->result = le32_to_cpu(resp_hdr.cdw0); - - return 0; + return nvme_mi_admin_parse_status(&resp, args->result); } int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, @@ -632,17 +671,306 @@ int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, if (rc) return rc; - if (resp_hdr.status) - return resp_hdr.status; + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + args->data_len = resp.data_len; + + return 0; +} + +int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl, + struct nvme_get_features_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_get_features); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32((args->sel & 0x7) << 8 | args->fid); + req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f); + req_hdr.cdw11 = cpu_to_le32(args->cdw11); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + resp.data = args->data; + resp.data_len = args->data_len; - if (args->result) - *args->result = resp_hdr.cdw0; + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; + + args->data_len = resp.data_len; + + return 0; +} + +int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl, + struct nvme_set_features_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_set_features); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32((args->save ? 1 : 0) << 31 | + (args->fid & 0xff)); + req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f); + req_hdr.cdw11 = cpu_to_le32(args->cdw11); + req_hdr.cdw12 = cpu_to_le32(args->cdw12); + req_hdr.cdw13 = cpu_to_le32(args->cdw13); + req_hdr.cdw15 = cpu_to_le32(args->cdw15); + + req.data_len = args->data_len; + req.data = args->data; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, args->result); + if (rc) + return rc; args->data_len = resp.data_len; return 0; } +int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, + struct nvme_ns_mgmt_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_ns_mgmt); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf); + req_hdr.cdw11 = cpu_to_le32(args->csi << 24); + if (args->ns) { + req.data = args->ns; + req.data_len = sizeof(*args->ns); + req_hdr.dlen = cpu_to_le32(sizeof(*args->ns)); + req_hdr.flags = 0x1; + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl, + struct nvme_ns_attach_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_ns_attach); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf); + req.data = args->ctrlist; + req.data_len = sizeof(*args->ctrlist); + req_hdr.dlen = cpu_to_le32(sizeof(*args->ctrlist)); + req_hdr.flags = 0x1; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl, + struct nvme_fw_download_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + if (args->data_len & 0x3) + return -EINVAL; + + if (args->offset & 0x3) + return -EINVAL; + + if (!args->data_len) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_fw_download); + + req_hdr.cdw10 = cpu_to_le32((args->data_len >> 2) - 1); + req_hdr.cdw11 = cpu_to_le32(args->offset >> 2); + req.data = args->data; + req.data_len = args->data_len; + req_hdr.dlen = cpu_to_le32(args->data_len); + req_hdr.flags = 0x1; + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, NULL); +} + +int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl, + struct nvme_fw_commit_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_fw_commit); + + req_hdr.cdw10 = cpu_to_le32(((args->bpid & 0x1) << 31) | + ((args->action & 0x7) << 3) | + ((args->slot & 0x7) << 0)); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, NULL); +} + +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_format_nvm); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(((args->lbafu & 0x3) << 12) + | ((args->ses & 0x7) << 9) + | ((args->pil & 0x1) << 8) + | ((args->pi & 0x7) << 5) + | ((args->mset & 0x1) << 4) + | ((args->lbaf & 0xf) << 0)); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + +int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_sanitize_nvm_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_sanitize_nvm); + + req_hdr.cdw10 = cpu_to_le32(((args->nodas ? 1 : 0) << 9) + | ((args->oipbp ? 1 : 0) << 8) + | ((args->owpass & 0xf) << 4) + | ((args->ause ? 1 : 0) << 3) + | ((args->sanact & 0x7) << 0)); + req_hdr.cdw11 = cpu_to_le32(args->ovrpat); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + return nvme_mi_admin_parse_status(&resp, args->result); +} + static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0, void *data, size_t *data_len) { @@ -955,3 +1283,37 @@ nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c) { return c ? list_next(&ep->controllers, c, ep_entry) : NULL; } + + +static const char *const mi_status[] = { + [NVME_MI_RESP_MPR] = "More Processing Required: The command message is in progress and requires more time to complete processing", + [NVME_MI_RESP_INTERNAL_ERR] = "Internal Error: The request message could not be processed due to a vendor-specific error", + [NVME_MI_RESP_INVALID_OPCODE] = "Invalid Command Opcode", + [NVME_MI_RESP_INVALID_PARAM] = "Invalid Parameter", + [NVME_MI_RESP_INVALID_CMD_SIZE] = "Invalid Command Size: The size of the message body of the request was different than expected", + [NVME_MI_RESP_INVALID_INPUT_SIZE] = "Invalid Command Input Data Size: The command requires data and contains too much or too little data", + [NVME_MI_RESP_ACCESS_DENIED] = "Access Denied. Processing prohibited due to a vendor-specific mechanism of the Command and Feature lockdown function", + [NVME_MI_RESP_VPD_UPDATES_EXCEEDED] = "VPD Updates Exceeded", + [NVME_MI_RESP_PCIE_INACCESSIBLE] = "PCIe Inaccessible. The PCIe functionality is not available at this time", + [NVME_MI_RESP_MEB_SANITIZED] = "Management Endpoint Buffer Cleared Due to Sanitize", + [NVME_MI_RESP_ENC_SERV_FAILURE] = "Enclosure Services Failure", + [NVME_MI_RESP_ENC_SERV_XFER_FAILURE] = "Enclosure Services Transfer Failure: Communication with the Enclosure Services Process has failed", + [NVME_MI_RESP_ENC_FAILURE] = "An unrecoverable enclosure failure has been detected by the Enclosuer Services Process", + [NVME_MI_RESP_ENC_XFER_REFUSED] = "Enclosure Services Transfer Refused: The NVM Subsystem or Enclosure Services Process indicated an error or an invalid format in communication", + [NVME_MI_RESP_ENC_FUNC_UNSUP] = "Unsupported Enclosure Function: An SES Send command has been attempted to a simple Subenclosure", + [NVME_MI_RESP_ENC_SERV_UNAVAIL] = "Enclosure Services Unavailable: The NVM Subsystem or Enclosure Services Process has encountered an error but may become available again", + [NVME_MI_RESP_ENC_DEGRADED] = "Enclosure Degraded: A noncritical failure has been detected by the Enclosure Services Process", + [NVME_MI_RESP_SANITIZE_IN_PROGRESS] = "Sanitize In Progress: The requested command is prohibited while a sanitize operation is in progress", +}; + +/* kept in mi.c while we have a split libnvme/libnvme-mi; consider moving + * to utils.c (with nvme_status_to_string) if we ever merge. */ +const char *nvme_mi_status_to_string(int status) +{ + const char *s = "Unknown status"; + + if (status < ARRAY_SIZE(mi_status) && mi_status[status]) + s = mi_status[status]; + + return s; +} diff --git a/src/nvme/mi.h b/src/nvme/mi.h index a7ed240..ab4216d 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -367,6 +367,20 @@ struct nvme_mi_admin_resp_hdr { __le32 cdw0, cdw1, cdw3; } __attribute__((packed)); +/** + * nvme_mi_status_to_string() - return a string representation of the MI + * status. + * @status: MI response status + * + * Gives a string description of @status, as per section 4.1.2 of the NVMe-MI + * spec. The status value should be of type NVME_STATUS_MI, and extracted + * from the return value using nvme_status_get_value(). + * + * Returned string is const, and should not be free()ed. + * + * Returns: A string representing the status value + */ +const char *nvme_mi_status_to_string(int status); /** * nvme_mi_create_root() - Create top-level MI (root) handle. @@ -601,7 +615,8 @@ nvme_root_t nvme_mi_scan_mctp(void); * so, all existing controller objects will be freed - the caller must not * hold a reference to those across this call. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &nvme_mi_for_each_ctrl */ @@ -652,7 +667,8 @@ char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep); * Retrieves the Subsystem information - number of external ports and * NVMe version information. See &struct nvme_mi_read_nvm_ss_info. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep, struct nvme_mi_read_nvm_ss_info *s); @@ -670,7 +686,8 @@ int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep, * * See &struct nvme_mi_read_port_info. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid, struct nvme_mi_read_port_info *p); @@ -687,7 +704,8 @@ int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid, * * See &struct nvme_ctrl_list. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid, struct nvme_ctrl_list *list); @@ -704,7 +722,8 @@ int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid, * * See &struct nvme_mi_read_ctrl_info. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id, struct nvme_mi_read_ctrl_info *ctrl); @@ -722,7 +741,8 @@ int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id, * * See &struct nvme_mi_nvm_ss_health_status. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear, struct nvme_mi_nvm_ss_health_status *nshds); @@ -744,7 +764,8 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear, * * See &enum nvme_mi_config_id for identifiers. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, __u32 *nmresp); @@ -761,7 +782,8 @@ int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, * * See &enum nvme_mi_config_id for identifiers. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1); @@ -775,7 +797,8 @@ int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1); * the port specified in @port. On success, populates @freq with the port * frequency * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port, enum nvme_mi_config_smbus_freq *freq) @@ -787,7 +810,7 @@ static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port, rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp); if (!rc) - *freq = tmp & 0x3; + *freq = (enum nvme_mi_config_smbus_freq)(tmp & 0x3); return rc; } @@ -803,7 +826,8 @@ static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port, * See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency * for the port. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port, enum nvme_mi_config_smbus_freq freq) @@ -828,7 +852,8 @@ static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port, * See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for * values in @mask. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep, __u32 mask) @@ -852,7 +877,8 @@ static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep, * Some controllers may also use this as the maximum receive unit size, and * may not accept MCTP messages larger than the configured MTU. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port, __u16 *mtu) @@ -882,7 +908,8 @@ static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port, * this value, you will likely need to change the MTU of the local MCTP * interface(s) to match. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port, __u16 mtu) @@ -918,7 +945,8 @@ static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port, * * See: &struct nvme_mi_admin_req_hdr and &struct nvme_mi_admin_resp_hdr. * - * Return: 0 on success, non-zero on failure. + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise.. */ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, @@ -949,7 +977,8 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, * of this. If the type of your identify command is standardized but not * yet supported by libnvme-mi, please contact the maintainers. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_identify_args */ @@ -969,7 +998,8 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, * Will return an error if the length of the response data (from the * controller) is not a full &NVME_IDENTIFY_DATA_SIZE. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_identify_args */ @@ -997,7 +1027,8 @@ static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl, * Will return an error if the length of the response data (from the * controller) is not a full &NVME_IDENTIFY_DATA_SIZE. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. */ static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl, enum nvme_identify_cns cns, @@ -1019,6 +1050,69 @@ static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl, } /** + * nvme_mi_admin_identify_ns() - Perform an Admin identify command for a + * namespace + * @ctrl: Controller to process identify command + * @nsid: namespace ID + * @ns: Namespace identification to populate + * + * Perform an Identify (namespace) command, setting the namespace id data + * in @ns. The namespace is expected to active and allocated. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_ns(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_id_ns *ns) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS, + nsid, ns); +} + +/** + * nvme_mi_admin_identify_ns_descs() - Perform an Admin identify Namespace + * Identification Descriptor list command for a namespace + * @ctrl: Controller to process identify command + * @nsid: Namespace ID + * @descs: Namespace Identification Descriptor list to populate + * + * Perform an Identify namespace identification description list command, + * setting the namespace identification description list in @descs + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_ns_descs(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_id_desc *descs) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS_DESC_LIST, + nsid, descs); +} + +/** + * nvme_mi_admin_identify_allocated_ns() - Perform an Admin identify command + * for an allocated namespace + * @ctrl: Controller to process identify command + * @nsid: namespace ID + * @ns: Namespace identification to populate + * + * Perform an Identify (namespace) command, setting the namespace id data + * in @ns. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_identify_allocated_ns(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_id_ns *ns) +{ + return nvme_mi_admin_identify_cns_nsid(ctrl, + NVME_IDENTIFY_CNS_ALLOCATED_NS, + nsid, ns); +} + +/** * nvme_mi_admin_identify_ctrl() - Perform an Admin identify for a controller * @ctrl: Controller to process identify command * @id: Controller identify data to populate @@ -1030,7 +1124,8 @@ static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl, * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be * fully populated on success. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_id_ctrl */ @@ -1055,7 +1150,8 @@ static inline int nvme_mi_admin_identify_ctrl(nvme_mi_ctrl_t ctrl, * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be * fully populated on success. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_ctrl_list */ @@ -1079,6 +1175,198 @@ static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl, } /** + * nvme_mi_admin_identify_nsid_ctrl_list() - Perform an Admin identify for a + * controller list with specific namespace ID + * @ctrl: Controller to process identify command + * @nsid: Namespace identifier + * @cntid: Controller ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the controller list for @nsid, starting + * with IDs greater than or equal to @cntid. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be + * fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ctrl_list + */ +static inline int nvme_mi_admin_identify_nsid_ctrl_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, __u16 cntid, + struct nvme_ctrl_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_allocated_ns_list() - Perform an Admin identify for + * an allocated namespace list + * @ctrl: Controller to process identify command + * @nsid: Namespace ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the allocated namespace list starting with + * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start + * of the list. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ns_list + */ +static inline int nvme_mi_admin_identify_allocated_ns_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_active_ns_list() - Perform an Admin identify for an + * active namespace list + * @ctrl: Controller to process identify command + * @nsid: Namespace ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the active namespace list starting with + * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start + * of the list. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_ns_list + */ +static inline int nvme_mi_admin_identify_active_ns_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, + struct nvme_ns_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_primary_ctrl() - Perform an Admin identify for + * primary controller capabilities data structure. + * @ctrl: Controller to process identify command + * @cntid: Controller ID to specify + * @cap: Primary Controller Capabilities data structure to populate + * + * Perform an Identify command to get the Primary Controller Capabilities data + * for the controller specified by @cntid + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @cap will be + * be fully populated on success. + * + * Return: 0 on success, non-zero on failure + * + * See: &struct nvme_primary_ctrl_cap + */ +static inline int nvme_mi_admin_identify_primary_ctrl(nvme_mi_ctrl_t ctrl, + __u16 cntid, + struct nvme_primary_ctrl_cap *cap) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = cap, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP, + .csi = NVME_CSI_NVM, + .nsid = NVME_NSID_NONE, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** + * nvme_mi_admin_identify_secondary_ctrl_list() - Perform an Admin identify for + * a secondary controller list. + * @ctrl: Controller to process identify command + * @nsid: Namespace ID to specify list start + * @cntid: Controller ID to specify list start + * @list: List data to populate + * + * Perform an Identify command, for the secondary controllers associated with + * the current primary controller. Only entries with IDs greater than or + * equal to @cntid are returned. + * + * Will return an error if the length of the response data (from the + * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be + * be fully populated on success. + * + * Return: 0 on success, non-zero on failure + * + * See: &struct nvme_secondary_ctrl_list + */ +static inline int nvme_mi_admin_identify_secondary_ctrl_list(nvme_mi_ctrl_t ctrl, + __u32 nsid, + __u16 cntid, + struct nvme_secondary_ctrl_list *list) +{ + struct nvme_identify_args args = { + .result = NULL, + .data = list, + .args_size = sizeof(args), + .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST, + .csi = NVME_CSI_NVM, + .nsid = nsid, + .cntid = cntid, + .cns_specific_id = NVME_CNSSPECID_NONE, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_identify(ctrl, &args); +} + +/** * nvme_mi_admin_get_log() - Retrieve log page data from controller * @ctrl: Controller to query * @args: Get Log Page command arguments @@ -1091,13 +1379,851 @@ static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl, * This request may be implemented as multiple log page commands, in order * to fit within MI message-size limits. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_get_log_args */ int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args); /** + * nvme_mi_admin_get_nsid_log() - Helper for Get Log Page functions + * @ctrl: Controller to query + * @rae: Retain Asynchronous Events + * @lid: Log identifier + * @nsid: Namespace ID + * @len: length of log buffer + * @log: pointer for resulting log data + * + * Performs a Get Log Page Admin command for a specific log ID @lid and + * namespace ID @nsid. Log data is expected to be @len bytes, and is stored + * in @log on success. The @rae flag is passed as-is to the Get Log Page + * command, and is specific to the Log Page requested. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_nsid_log(nvme_mi_ctrl_t ctrl, bool rae, + enum nvme_cmd_get_log_lid lid, + __u32 nsid, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = lid, + .len = len, + .nsid = nsid, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_simple() - Helper for Get Log Page functions with no + * NSID or RAE requirements + * @ctrl: Controller to query + * @lid: Log identifier + * @len: length of log buffer + * @log: pointer for resulting log data + * + * Performs a Get Log Page Admin command for a specific log ID @lid, using + * NVME_NSID_ALL for the namespace identifier, and rae set to false. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_simple(nvme_mi_ctrl_t ctrl, + enum nvme_cmd_get_log_lid lid, + __u32 len, void *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, false, lid, NVME_NSID_ALL, + len, log); +} + +/** + * nvme_mi_admin_get_log_supported_log_pages() - Retrieve nmve supported log + * pages + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: Array of LID supported and Effects data structures + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_supported_log_pages(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_supported_log_pages *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, + NVME_LOG_LID_SUPPORTED_LOG_PAGES, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_error() - Retrieve nvme error log + * @ctrl: Controller to query + * @nr_entries: Number of error log entries allocated + * @rae: Retain asynchronous events + * @err_log: Array of error logs of size 'entries' + * + * This log page describes extended error information for a command that + * completed with error, or may report an error that is not specific to a + * particular command. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_error(nvme_mi_ctrl_t ctrl, + unsigned int nr_entries, bool rae, + struct nvme_error_log_page *err_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_ERROR, + NVME_NSID_ALL, sizeof(*err_log) * nr_entries, + err_log); +} + +/** + * nvme_mi_admin_get_log_smart() - Retrieve nvme smart log + * @ctrl: Controller to query + * @nsid: Optional namespace identifier + * @rae: Retain asynchronous events + * @smart_log: User address to store the smart log + * + * This log page provides SMART and general health information. The information + * provided is over the life of the controller and is retained across power + * cycles. To request the controller log page, the namespace identifier + * specified is FFFFFFFFh. The controller may also support requesting the log + * page on a per namespace basis, as indicated by bit 0 of the LPA field in the + * Identify Controller data structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_smart(nvme_mi_ctrl_t ctrl, __u32 nsid, + bool rae, + struct nvme_smart_log *smart_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SMART, + nsid, sizeof(*smart_log), smart_log); +} + +/** + * nvme_mi_admin_get_log_fw_slot() - Retrieves the controller firmware log + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @fw_log: User address to store the log page + * + * This log page describes the firmware revision stored in each firmware slot + * supported. The firmware revision is indicated as an ASCII string. The log + * page also indicates the active slot number. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_fw_slot(nvme_mi_ctrl_t ctrl, bool rae, + struct nvme_firmware_slot *fw_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_FW_SLOT, + NVME_NSID_ALL, sizeof(*fw_log), fw_log); +} + +/** + * nvme_mi_admin_get_log_changed_ns_list() - Retrieve namespace changed list + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @ns_log: User address to store the log page + * + * This log page describes namespaces attached to this controller that have + * changed since the last time the namespace was identified, been added, or + * deleted. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_changed_ns_list(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_ns_list *ns_log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_CHANGED_NS, + NVME_NSID_ALL, sizeof(*ns_log), ns_log); +} + +/** + * nvme_mi_admin_get_log_cmd_effects() - Retrieve nvme command effects log + * @ctrl: Controller to query + * @csi: Command Set Identifier + * @effects_log: User address to store the effects log + * + * This log page describes the commands that the controller supports and the + * effects of those commands on the state of the NVM subsystem. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_cmd_effects(nvme_mi_ctrl_t ctrl, + enum nvme_csi csi, + struct nvme_cmd_effects_log *effects_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = effects_log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_CMD_EFFECTS, + .len = sizeof(*effects_log), + .nsid = NVME_NSID_ALL, + .csi = csi, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_device_self_test() - Retrieve the device self test log + * @ctrl: Controller to query + * @log: Userspace address of the log payload + * + * The log page indicates the status of an in progress self test and the + * percent complete of that operation, and the results of the previous 20 + * self-test operations. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_device_self_test(nvme_mi_ctrl_t ctrl, + struct nvme_self_test_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, false, + NVME_LOG_LID_DEVICE_SELF_TEST, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_create_telemetry_host() - Create host telemetry log + * @ctrl: Controller to query + * @log: Userspace address of the log payload + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_create_telemetry_host(nvme_mi_ctrl_t ctrl, + struct nvme_telemetry_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_telemetry_host() - Get Telemetry Host-Initiated log + * page + * @ctrl: Controller to query + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Host-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_telemetry_host(nvme_mi_ctrl_t ctrl, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_HOST, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_RETAIN, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_telemetry_ctrl() - Get Telemetry Controller-Initiated + * log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset into the telemetry data + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Retrieves the Telemetry Controller-Initiated log page at the requested offset + * using the previously existing capture. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_telemetry_ctrl(nvme_mi_ctrl_t ctrl, + bool rae, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_TELEMETRY_CTRL, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_endurance_group() - Get Endurance Group log + * @ctrl: Controller to query + * @endgid: Starting group identifier to return in the list + * @log: User address to store the endurance log + * + * This log page indicates if an Endurance Group Event has occurred for a + * particular Endurance Group. If an Endurance Group Event has occurred, the + * details of the particular event are included in the Endurance Group + * Information log page for that Endurance Group. An asynchronous event is + * generated when an entry for an Endurance Group is newly added to this log + * page. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_endurance_group(nvme_mi_ctrl_t ctrl, + __u16 endgid, + struct nvme_endurance_group_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ENDURANCE_GROUP, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = endgid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_predictable_lat_nvmset() - Predictable Latency Per NVM + * Set + * @ctrl: Controller to query + * @nvmsetid: NVM set id + * @log: User address to store the predictable latency log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_predictable_lat_nvmset(nvme_mi_ctrl_t ctrl, + __u16 nvmsetid, + struct nvme_nvmset_predictable_lat_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PREDICTABLE_LAT_NVMSET, + .len = sizeof(*log), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = nvmsetid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_predictable_lat_event() - Retrieve Predictable Latency + * Event Aggregate Log Page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset into the predictable latency event + * @len: Length of provided user buffer to hold the log data in bytes + * @log: User address for log page data + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_predictable_lat_event(nvme_mi_ctrl_t ctrl, + bool rae, + __u32 offset, + __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PREDICTABLE_LAT_AGG, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_ana() - Retrieve Asymmetric Namespace Access log page + * @ctrl: Controller to query + * @lsp: Log specific, see &enum nvme_get_log_ana_lsp + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the ana log + * + * This log consists of a header describing the log and descriptors containing + * the asymmetric namespace access information for ANA Groups that contain + * namespaces that are attached to the controller processing the command. + * + * See &struct nvme_ana_rsp_hdr for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_ana(nvme_mi_ctrl_t ctrl, + enum nvme_log_ana_lsp lsp, bool rae, + __u64 offset, __u32 len, void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ANA, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)lsp, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_ana_groups() - Retrieve Asymmetric Namespace Access + * groups only log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @len: The allocated length of the log page + * @log: User address to store the ana group log + * + * See &struct nvme_ana_group_desc for the definition of the returned structure. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_ana_groups(nvme_mi_ctrl_t ctrl, + bool rae, __u32 len, + struct nvme_ana_group_desc *log) +{ + return nvme_mi_admin_get_log_ana(ctrl, NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY, rae, 0, + len, log); +} + +/** + * nvme_mi_admin_get_log_lba_status() - Retrieve LBA Status + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_lba_status(nvme_mi_ctrl_t ctrl, bool rae, + __u64 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_LBA_STATUS, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_endurance_grp_evt() - Retrieve Rotational Media + * Information + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset to the start of the log page + * @len: The allocated length of the log page + * @log: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_endurance_grp_evt(nvme_mi_ctrl_t ctrl, + bool rae, + __u32 offset, + __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ENDURANCE_GRP_EVT, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_fid_supported_effects() - Retrieve Feature Identifiers + * Supported and Effects + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: FID Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_fid_supported_effects(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_fid_supported_effects_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, + NVME_LOG_LID_FID_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_mi_cmd_supported_effects() - displays the MI Commands + * Supported by the controller + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: MI Command Supported and Effects data structure + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_mi_cmd_supported_effects(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_mi_cmd_supported_effects_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, + NVME_NSID_NONE, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_boot_partition() - Retrieve Boot Partition + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @lsp: The log specified field of LID + * @len: The allocated size, minimum + * struct nvme_boot_partition + * @part: User address to store the log page + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_boot_partition(nvme_mi_ctrl_t ctrl, + bool rae, __u8 lsp, + __u32 len, + struct nvme_boot_partition *part) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = part, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_BOOT_PARTITION, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_discovery() - Retrieve Discovery log page + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @offset: Offset of this log to retrieve + * @len: The allocated size for this portion of the log + * @log: User address to store the discovery log + * + * Supported only by fabrics discovery controllers, returning discovery + * records. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_discovery(nvme_mi_ctrl_t ctrl, bool rae, + __u32 offset, __u32 len, + void *log) +{ + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_DISCOVER, + .len = len, + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_media_unit_stat() - Retrieve Media Unit Status + * @ctrl: Controller to query + * @domid: Domain Identifier selection, if supported + * @mus: User address to store the Media Unit statistics log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_media_unit_stat(nvme_mi_ctrl_t ctrl, + __u16 domid, + struct nvme_media_unit_stat_log *mus) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = mus, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_MEDIA_UNIT_STATUS, + .len = sizeof(*mus), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_support_cap_config_list() - Retrieve Supported + * Capacity Configuration List + * @ctrl: Controller to query + * @domid: Domain Identifier selection, if supported + * @cap: User address to store supported capabilities config list + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_support_cap_config_list(nvme_mi_ctrl_t ctrl, + __u16 domid, + struct nvme_supported_cap_config_list_log *cap) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = cap, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST, + .len = sizeof(*cap), + .nsid = NVME_NSID_NONE, + .csi = NVME_CSI_NVM, + .lsi = domid, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_reservation() - Retrieve Reservation Notification + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: User address to store the reservation log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_reservation(nvme_mi_ctrl_t ctrl, + bool rae, + struct nvme_resv_notification_log *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_RESERVATION, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_sanitize() - Retrieve Sanitize Status + * @ctrl: Controller to query + * @rae: Retain asynchronous events + * @log: User address to store the sanitize log + * + * The Sanitize Status log page reports sanitize operation time estimates and + * information about the most recent sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_sanitize(nvme_mi_ctrl_t ctrl, bool rae, + struct nvme_sanitize_log_page *log) +{ + return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SANITIZE, + NVME_NSID_ALL, sizeof(*log), log); +} + +/** + * nvme_mi_admin_get_log_zns_changed_zones() - Retrieve list of zones that have + * changed + * @ctrl: Controller to query + * @nsid: Namespace ID + * @rae: Retain asynchronous events + * @log: User address to store the changed zone log + * + * The list of zones that have changed state due to an exceptional event. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_zns_changed_zones(nvme_mi_ctrl_t ctrl, + __u32 nsid, bool rae, + struct nvme_zns_changed_zone_log *log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_ZNS_CHANGED_ZONES, + .len = sizeof(*log), + .nsid = nsid, + .csi = NVME_CSI_ZNS, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = rae, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** + * nvme_mi_admin_get_log_persistent_event() - Retrieve Persistent Event Log + * @ctrl: Controller to query + * @action: Action the controller should take during processing this command + * @size: Size of @pevent_log + * @pevent_log: User address to store the persistent event log + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_log_persistent_event(nvme_mi_ctrl_t ctrl, + enum nvme_pevent_log_action action, + __u32 size, void *pevent_log) +{ + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = pevent_log, + .args_size = sizeof(args), + .lid = NVME_LOG_LID_PERSISTENT_EVENT, + .len = size, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = (__u8)action, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + return nvme_mi_admin_get_log(ctrl, &args); +} + +/** * nvme_mi_admin_security_send() - Perform a Security Send command on a * controller. * @ctrl: Controller to send command to @@ -1111,7 +2237,8 @@ int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args); * Security Send data length should not be greater than 4096 bytes to * comply with specification limits. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_get_log_args */ @@ -1132,12 +2259,297 @@ int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, * Security Receive data length should not be greater than 4096 bytes to * comply with specification limits. * - * Return: 0 on success, non-zero on failure + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. * * See: &struct nvme_get_log_args */ int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl, struct nvme_security_receive_args *args); +/** + * nvme_mi_admin_get_features - Perform a Get Feature command on a controller + * @ctrl: Controller to send command to + * @args: Get Features command arguments + * + * Performs a Get Features Admin command as specified by @args. Returned + * feature data will be stored in @args->result and @args->data, depending + * on the specification of the feature itself; most features do not return + * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for + * feature-specific information. + * + * On success, @args->data_len will be updated with the actual data length + * received. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl, + struct nvme_get_features_args *args); + +/** + * nvme_mi_admin_get_features_data() - Helper function for &nvme_mi_admin_get_features() + * @ctrl: Controller to send command to + * @fid: Feature identifier + * @nsid: Namespace ID, if applicable for @fid + * @data_len: Length of feature data, if applicable for @fid, in bytes + * @data: User address of feature data, if applicable + * @result: The command completion result from CQE dword0 + * + * Helper for optionally features that optionally return data, using the + * SEL_CURRENT selector value. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_get_features_data(nvme_mi_ctrl_t ctrl, + enum nvme_features_id fid, + __u32 nsid, __u32 data_len, + void *data, __u32 *result) +{ + struct nvme_get_features_args args = { + .result = result, + .data = data, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_GET_FEATURES_SEL_CURRENT, + .cdw11 = 0, + .data_len = data_len, + .fid = (__u8)fid, + .uuidx = NVME_UUID_NONE, + }; + + return nvme_mi_admin_get_features(ctrl, &args); +} + +/** + * nvme_mi_admin_get_features_simple - Get a simple feature value with no data + * @ctrl: Controller to send command to + * @fid: Feature identifier + * @nsid: Namespace id, if required by @fid + * @result: output feature data + */ +static inline int nvme_mi_admin_get_features_simple(nvme_mi_ctrl_t ctrl, + enum nvme_features_id fid, + __u32 nsid, + __u32 *result) +{ + return nvme_mi_admin_get_features_data(ctrl, fid, nsid, + 0, NULL, result); +} + +/** + * nvme_mi_admin_set_features - Perform a Set Features command on a controller + * @ctrl: Controller to send command to + * @args: Set Features command arguments + * + * Performs a Set Features Admin command as specified by @args. Result + * data will be stored in @args->result. + * on the specification of the feature itself; most features do not return + * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for + * feature-specific information. + * + * On success, @args->data_len will be updated with the actual data length + * received. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl, + struct nvme_set_features_args *args); + +/** + * nvme_mi_admin_ns_mgmt - Issue a Namespace Management command + * @ctrl: Controller to send command to + * @args: Namespace management command arguments + * + * Issues a Namespace Management command to @ctrl, with arguments specified + * from @args. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl, + struct nvme_ns_mgmt_args *args); + +/** + * nvme_mi_admin_ns_mgmt_create - Helper for Namespace Management Create command + * @ctrl: Controller to send command to + * @ns: New namespace parameters + * @csi: Command Set Identifier for new NS + * @nsid: Set to new namespace ID on create + * + * Issues a Namespace Management (Create) command to @ctrl, to create a + * new namespace specified by @ns, using command set @csi. On success, + * the new namespace ID will be written to @nsid. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_mgmt_create(nvme_mi_ctrl_t ctrl, + struct nvme_id_ns *ns, + __u8 csi, __u32 *nsid) +{ + struct nvme_ns_mgmt_args args = { + .result = nsid, + .ns = ns, + .args_size = sizeof(args), + .nsid = NVME_NSID_NONE, + .sel = NVME_NS_MGMT_SEL_CREATE, + .csi = csi, + }; + + return nvme_mi_admin_ns_mgmt(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_mgmt_delete - Helper for Namespace Management Delete command + * @ctrl: Controller to send command to + * @nsid: Namespace ID to delete + * + * Issues a Namespace Management (Delete) command to @ctrl, to delete the + * namespace with id @nsid. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_mgmt_delete(nvme_mi_ctrl_t ctrl, __u32 nsid) +{ + struct nvme_ns_mgmt_args args = { + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_MGMT_SEL_DELETE, + }; + + return nvme_mi_admin_ns_mgmt(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_attach() - Attach or detach namespace to controller(s) + * @ctrl: Controller to send command to + * @args: Namespace Attach command arguments + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl, + struct nvme_ns_attach_args *args); + +/** + * nvme_mi_admin_ns_attach_ctrls() - Attach namespace to controllers + * @ctrl: Controller to send command to + * @nsid: Namespace ID to attach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_attach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH, + }; + + return nvme_mi_admin_ns_attach(ctrl, &args); +} + +/** + * nvme_mi_admin_ns_detach_ctrls() - Detach namespace from controllers + * @ctrl: Controller to send command to + * @nsid: Namespace ID to detach + * @ctrlist: Controller list to modify attachment state of nsid + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +static inline int nvme_mi_admin_ns_detach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid, + struct nvme_ctrl_list *ctrlist) +{ + struct nvme_ns_attach_args args = { + .result = NULL, + .ctrlist = ctrlist, + .args_size = sizeof(args), + .nsid = nsid, + .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + }; + + return nvme_mi_admin_ns_attach(ctrl, &args); +} + +/** + * nvme_mi_admin_fw_download() - Download part or all of a firmware image to + * the controller + * @ctrl: Controller to send firmware data to + * @args: &struct nvme_fw_download_args argument structure + * + * The Firmware Image Download command downloads all or a portion of an image + * for a future update to the controller. The Firmware Image Download command + * downloads a new image (in whole or in part) to the controller. + * + * The image may be constructed of multiple pieces that are individually + * downloaded with separate Firmware Image Download commands. Each Firmware + * Image Download command includes a Dword Offset and Number of Dwords that + * specify a dword range. + * + * The new firmware image is not activated as part of the Firmware Image + * Download command. Use the nvme_mi_admin_fw_commit() to activate a newly + * downloaded image. + * + * Return: 0 on success, non-zero on failure + */ +int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl, + struct nvme_fw_download_args *args); + +/** + * nvme_mi_admin_fw_commit() - Commit firmware using the specified action + * @ctrl: Controller to send firmware data to + * @args: &struct nvme_fw_download_args argument structure + * + * The Firmware Commit command modifies the firmware image or Boot Partitions. + * + * Return: 0 on success, non-zero on failure + */ +int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl, + struct nvme_fw_commit_args *args); + +/** + * nvme_mi_admin_format_nvm() - Format NVMe namespace + * @ctrl: Controller to send command to + * @args: Format NVM command arguments + * + * Perform a low-level format to set the LBA data & metadata size. May destroy + * data & metadata on the specified namespaces + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args); + +/** + * nvme_mi_admin_sanitize_nvm() - Start a subsystem Sanitize operation + * @ctrl: Controller to send command to + * @args: Sanitize command arguments + * + * A sanitize operation alters all user data in the NVM subsystem such that + * recovery of any previous user data from any cache, the non-volatile media, + * or any Controller Memory Buffer is not possible. + * + * The Sanitize command starts a sanitize operation or to recover from a + * previously failed sanitize operation. The sanitize operation types that may + * be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize + * operations are processed in the background, i.e., completion of the sanitize + * command does not indicate completion of the sanitize operation. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_sanitize_nvm_args *args); #endif /* _LIBNVME_MI_MI_H */ diff --git a/src/nvme/private.h b/src/nvme/private.h index b5610f5..cdd1bbf 100644 --- a/src/nvme/private.h +++ b/src/nvme/private.h @@ -16,8 +16,6 @@ #include "fabrics.h" #include "mi.h" -#include <uuid.h> - extern const char *nvme_ctrl_sysfs_dir; extern const char *nvme_subsys_sysfs_dir; @@ -57,7 +55,7 @@ struct nvme_ns { uint8_t eui64[8]; uint8_t nguid[16]; - uuid_t uuid; + unsigned char uuid[NVME_UUID_LEN]; enum nvme_csi csi; }; @@ -83,6 +81,7 @@ struct nvme_ctrl { char *traddr; char *trsvcid; char *dhchap_key; + char *dhchap_ctrl_key; char *cntrltype; char *dctype; bool discovery_ctrl; diff --git a/src/nvme/tree.c b/src/nvme/tree.c index e3b41e7..b992824 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -810,12 +810,12 @@ struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c) return &c->cfg; } -const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c) +const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c) { return c->dhchap_key; } -void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key) +void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key) { if (c->dhchap_key) { free(c->dhchap_key); @@ -825,6 +825,21 @@ void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key) c->dhchap_key = strdup(key); } +const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c) +{ + return c->dhchap_ctrl_key; +} + +void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key) +{ + if (c->dhchap_ctrl_key) { + free(c->dhchap_ctrl_key); + c->dhchap_ctrl_key = NULL; + } + if (key) + c->dhchap_ctrl_key = strdup(key); +} + void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered) { c->discovered = discovered; @@ -898,6 +913,7 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c) FREE_CTRL_ATTR(c->serial); FREE_CTRL_ATTR(c->sqsize); FREE_CTRL_ATTR(c->dhchap_key); + FREE_CTRL_ATTR(c->dhchap_ctrl_key); FREE_CTRL_ATTR(c->address); FREE_CTRL_ATTR(c->dctype); FREE_CTRL_ATTR(c->cntrltype); @@ -1146,6 +1162,7 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, const char *name) { DIR *d; + char *host_key; d = opendir(path); if (!d) { @@ -1166,10 +1183,19 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path, c->queue_count = nvme_get_ctrl_attr(c, "queue_count"); c->serial = nvme_get_ctrl_attr(c, "serial"); c->sqsize = nvme_get_ctrl_attr(c, "sqsize"); - c->dhchap_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret"); - if (c->dhchap_key && !strcmp(c->dhchap_key, "none")) { - free(c->dhchap_key); - c->dhchap_key = NULL; + host_key = nvme_get_ctrl_attr(c, "dhchap_secret"); + if (host_key && c->s && c->s->h && c->s->h->dhchap_key && + (!strcmp(c->s->h->dhchap_key, host_key) || + !strcmp("none", host_key))) { + free(host_key); + host_key = NULL; + } + if (host_key) + c->dhchap_key = host_key; + c->dhchap_ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret"); + if (c->dhchap_ctrl_key && !strcmp(c->dhchap_ctrl_key, "none")) { + free(c->dhchap_ctrl_key); + c->dhchap_ctrl_key = NULL; } c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype"); c->dctype = nvme_get_ctrl_attr(c, "dctype"); @@ -1525,9 +1551,9 @@ const uint8_t *nvme_ns_get_nguid(nvme_ns_t n) return n->nguid; } -void nvme_ns_get_uuid(nvme_ns_t n, uuid_t out) +void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN]) { - uuid_copy(out, n->uuid); + memcpy(out, n->uuid, NVME_UUID_LEN); } int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns) diff --git a/src/nvme/tree.h b/src/nvme/tree.h index 3a103c0..156cb79 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -15,7 +15,6 @@ #include <stddef.h> #include <sys/types.h> -#include <uuid.h> #include "ioctl.h" #include "util.h" @@ -521,7 +520,7 @@ const uint8_t *nvme_ns_get_nguid(nvme_ns_t n); * * Copies the namespace's uuid into @out */ -void nvme_ns_get_uuid(nvme_ns_t n, uuid_t out); +void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN]); /** * nvme_ns_get_sysfs_dir() - sysfs directory of a namespace @@ -876,6 +875,21 @@ const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c); const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c); /** + * nvme_ctrl_get_dhchap_host_key() - Return host key + * @c: Controller to be checked + * + * Return: DH-HMAC-CHAP host key or NULL if not set + */ +const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c); + +/** + * nvme_ctrl_set_dhchap_host_key() - Set host key + * @c: Host for which the key should be set + * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key + */ +void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key); + +/** * nvme_ctrl_get_dhchap_key() - Return controller key * @c: Controller for which the key should be set * diff --git a/src/nvme/types.h b/src/nvme/types.h index 3d67bc8..94066fc 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -4857,6 +4857,34 @@ enum nvmf_tcp_sectype { }; /** + * enum nvmf_log_discovery_lid_support - Discovery log specific support + * @NVMF_LOG_DISC_LID_NONE: None + * @NVMF_LOG_DISC_LID_EXTDLPES: Extended Discovery Log Page Entries Supported + * @NVMF_LOG_DISC_LID_PLEOS: Port Local Entries Only Supported + * @NVMF_LOG_DISC_LID_ALLSUBES: All NVM Subsystem Entries Supported + */ +enum nvmf_log_discovery_lid_support { + NVMF_LOG_DISC_LID_NONE = 0, + NVMF_LOG_DISC_LID_EXTDLPES = (1 << 0), + NVMF_LOG_DISC_LID_PLEOS = (1 << 1), + NVMF_LOG_DISC_LID_ALLSUBES = (1 << 2), +}; + +/** + * enum nvmf_log_discovery_lsp - Discovery log specific field + * @NVMF_LOG_DISC_LSP_NONE: None + * @NVMF_LOG_DISC_LSP_EXTDLPE: Extended Discovery Log Page Entries + * @NVMF_LOG_DISC_LSP_PLEO: Port Local Entries Only + * @NVMF_LOG_DISC_LSP_ALLSUBE: All NVM Subsystem Entries + */ +enum nvmf_log_discovery_lsp { + NVMF_LOG_DISC_LSP_NONE = 0, + NVMF_LOG_DISC_LSP_EXTDLPE = (1 << 0), + NVMF_LOG_DISC_LSP_PLEO = (1 << 1), + NVMF_LOG_DISC_LSP_ALLSUBE = (1 << 2), +}; + +/** * struct nvmf_discovery_log - Discovery Log Page (Log Identifier 70h) * @genctr: Generation Counter (GENCTR): Indicates the version of the discovery * information, starting at a value of 0h. For each change in the @@ -6114,6 +6142,75 @@ static inline __u16 nvme_status_code(__u16 status_field) } /** + * enum nvme_status_type - type encoding for NVMe return values, when + * represented as an int. + * + * The nvme_* api returns an int, with negative values indicating an internal + * or syscall error, zero signifying success, positive values representing + * the NVMe status. + * + * That latter case (the NVMe status) may represent status values from + * different parts of the transport/controller/etc, and are at most 16 bits of + * data. So, we use the most-significant 3 bits of the signed int to indicate + * which type of status this is. + * + * @NVME_STATUS_TYPE_SHIFT: shift value for status bits + * @NVME_STATUS_TYPE_MASK: mask value for status bits + * + * @NVME_STATUS_TYPE_NVME: NVMe command status value, typically from CDW3 + * @NVME_STATUS_TYPE_MI: NVMe-MI header status + */ +enum nvme_status_type { + NVME_STATUS_TYPE_SHIFT = 27, + NVME_STATUS_TYPE_MASK = 0x7, + + NVME_STATUS_TYPE_NVME = 0, + NVME_STATUS_TYPE_MI = 1, +}; + +/** + * nvme_status_get_type() - extract the type from a nvme_* return value + * @status: the (non-negative) return value from the NVMe API + * + * Returns: the type component of the status. + */ +static inline __u32 nvme_status_get_type(int status) +{ + return NVME_GET(status, STATUS_TYPE); +} + +/** + * nvme_status_get_value() - extract the status value from a nvme_* return + * value + * @status: the (non-negative) return value from the NVMe API + * + * Returns: the value component of the status; the set of values will depend + * on the status type. + */ +static inline __u32 nvme_status_get_value(int status) +{ + return status & ~(NVME_STATUS_TYPE_MASK << NVME_STATUS_TYPE_SHIFT); +} + +/** + * nvme_status_equals() - helper to check a status against a type and value + * @status: the (non-negative) return value from the NVMe API + * @type: the status type + * @value: the status value + * + * Returns: true if @status is of the specified type and value + */ +static inline __u32 nvme_status_equals(int status, enum nvme_status_type type, + unsigned int value) +{ + if (status < 0) + return false; + + return nvme_status_get_type(status) == type && + nvme_status_get_value(status) == value; +} + +/** * enum nvme_admin_opcode - Known NVMe admin opcodes * @nvme_admin_delete_sq: Delete I/O Submission Queue * @nvme_admin_create_sq: Create I/O Submission Queue @@ -6950,6 +7047,20 @@ enum nvme_fctype { }; /** + * enum nvme_data_tfr - Data transfer direction of the command + * @NVME_DATA_TFR_NO_DATA_TFR: No data transfer + * @NVME_DATA_TFR_HOST_TO_CTRL: Host to controller + * @NVME_DATA_TFR_CTRL_TO_HOST: Controller to host + * @NVME_DATA_TFR_BIDIRECTIONAL: Bidirectional + */ +enum nvme_data_tfr { + NVME_DATA_TFR_NO_DATA_TFR = 0x0, + NVME_DATA_TFR_HOST_TO_CTRL = 0x1, + NVME_DATA_TFR_CTRL_TO_HOST = 0x2, + NVME_DATA_TFR_BIDIRECTIONAL = 0x3, +}; + +/** * enum nvme_io_opcode - Opcodes for I/O Commands * @nvme_cmd_flush: Flush * @nvme_cmd_write: Write diff --git a/src/nvme/util.c b/src/nvme/util.c index ff5e0d8..c61dbe9 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -11,6 +11,8 @@ #include <string.h> #include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> #include <sys/param.h> #include <sys/types.h> #include <arpa/inet.h> @@ -23,6 +25,11 @@ #include "util.h" #include "log.h" +/* The bionic libc implementation doesn't define LINE_MAX */ +#ifndef LINE_MAX +#define LINE_MAX 2048 +#endif + /* Source Code Control System, query version of binary with 'what' */ const char sccsid[] = "@(#)libnvme " GIT_VERSION; @@ -489,6 +496,22 @@ int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len) return 0; } +int nvme_get_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir, + __u32 *len) +{ + switch (fid) { + case NVME_FEAT_FID_HOST_MEM_BUF: + if (dir == NVME_DATA_TFR_HOST_TO_CTRL) { + *len = 0; + break; + } + fallthrough; + default: + return nvme_get_feature_length(fid, cdw11, len); + } + return 0; +} + int nvme_get_directive_receive_length(enum nvme_directive_dtype dtype, enum nvme_directive_receive_doper doper, __u32 *len) { @@ -812,3 +835,57 @@ const char *nvme_get_version(enum nvme_version type) return "n/a"; } } + +int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str) +{ + int n; + n = snprintf(str, NVME_UUID_LEN_STRING, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], + uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + return n != NVME_UUID_LEN_STRING - 1 ? -EINVAL : 0; +} + +int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]) +{ + int n; + + n = sscanf(str, + "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" + "%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + &uuid[0], &uuid[1], &uuid[2], &uuid[3], &uuid[4], &uuid[5], + &uuid[6], &uuid[7], &uuid[8], &uuid[9], &uuid[10], &uuid[11], + &uuid[12], &uuid[13], &uuid[14], &uuid[15]); + return n != NVME_UUID_LEN ? -EINVAL : 0; + +} + +int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]) +{ + int f; + ssize_t n; + + f = open("/dev/urandom", O_RDONLY); + if (f < 0) + return -errno; + n = read(f, uuid, NVME_UUID_LEN); + if (n < 0) { + close(f); + return -errno; + } else if (n != NVME_UUID_LEN) { + close(f); + return -EIO; + } + + /* + * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4 + * Algorithms for Creating a UUID from Truly Random + * or Pseudo-Random Numbers + */ + uuid[6] = (uuid[6] & 0x0f) | 0x40; + uuid[8] = (uuid[8] & 0x3f) | 0x80; + + return 0; +} diff --git a/src/nvme/util.h b/src/nvme/util.h index 6f1d3e9..e72c156 100644 --- a/src/nvme/util.h +++ b/src/nvme/util.h @@ -156,6 +156,23 @@ void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs, int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len); /** + * nvme_get_feature_length2() - Retreive the command payload length for a + * specific feature identifier + * @fid: Feature identifier, see &enum nvme_features_id. + * @cdw11: The cdw11 value may affect the transfer (only known fid is + * %NVME_FEAT_FID_HOST_ID) + * @dir: Data transfer direction: false - host to controller, true - + * controller to host may affect the transfer (only known fid is + * %NVME_FEAT_FID_HOST_MEM_BUF). + * @len: On success, set to this features payload length in bytes. + * + * Return: 0 on success, -1 with errno set to EINVAL if the function did not + * recognize &fid. + */ +int nvme_get_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir, + __u32 *len); + +/** * nvme_get_directive_receive_length() - Get directive receive length * @dtype: Directive type, see &enum nvme_directive_dtype * @doper: Directive receive operation, see &enum nvme_directive_receive_doper @@ -575,4 +592,36 @@ enum nvme_version { */ const char *nvme_get_version(enum nvme_version type); +#define NVME_UUID_LEN_STRING 37 /* 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ +#define NVME_UUID_LEN 16 + +/** + * nvme_uuid_to_string - Return string represenation of encoded UUID + * @uuid: Binary encoded input UUID + * @str: Output string represenation of UUID + * + * Return: Returns error code if type conversion fails. + */ +int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str); + +/** + * nvme_uuid_from_string - Return encoded UUID represenation of string UUID + * @uuid: Binary encoded input UUID + * @str: Output string represenation of UUID + * + * Return: Returns error code if type conversion fails. + */ +int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]); + +/** + * nvme_uuid_random - Generate random UUID + * @uuid: Generated random UUID + * + * Generate random number according + * https://www.rfc-editor.org/rfc/rfc4122#section-4.4 + * + * Return: Returns error code if generating of random number fails. + */ +int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]); + #endif /* _LIBNVME_UTIL_H */ |