From 90cf2208656c513b78a587e3e0f1f64144c0f209 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 3 Apr 2023 09:38:52 +0200 Subject: Merging upstream version 1.4. Signed-off-by: Daniel Baumann --- src/libnvme-mi.map | 5 + src/libnvme.map | 9 ++ src/meson.build | 5 +- src/nvme/api-types.h | 8 +- src/nvme/fabrics.c | 57 +++++++++-- src/nvme/fabrics.h | 16 ++- src/nvme/ioctl.c | 2 + src/nvme/json.c | 44 +++++++++ src/nvme/linux.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/nvme/linux.h | 68 +++++++++++++ src/nvme/mi.c | 10 +- src/nvme/mi.h | 22 +++++ src/nvme/no-json.c | 26 +++++ src/nvme/tree.c | 43 +++++++- src/nvme/types.h | 80 ++++++++------- src/nvme/util.c | 7 +- 16 files changed, 617 insertions(+), 58 deletions(-) create mode 100644 src/nvme/no-json.c (limited to 'src') diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index 16b7ad4..0b8b5b7 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -1,3 +1,8 @@ +LIBNVME_MI_1_4 { + global: + nvme_mi_admin_get_log_page; +}; + LIBNVME_MI_1_3 { global: nvme_mi_admin_admin_passthru; diff --git a/src/libnvme.map b/src/libnvme.map index 85ff6f3..a1294f4 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -1,5 +1,14 @@ # SPDX-License-Identifier: LGPL-2.1-or-later +LIBNVME_1_4 { + global: + nvme_lookup_keyring; + nvme_describe_key_serial; + nvme_lookup_key; + nvme_set_keyring; + nvme_insert_tls_key; +}; + LIBNVME_1_3 { global: nvme_ctrl_is_unique_discovery_ctrl; diff --git a/src/meson.build b/src/meson.build index 1186e81..3732f8c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,13 +23,16 @@ mi_sources = [ 'nvme/mi-mctp.c', ] -if conf.get('CONFIG_JSONC') +if json_c_dep.found() sources += 'nvme/json.c' +else + sources += 'nvme/no-json.c' endif deps = [ json_c_dep, openssl_dep, + keyutils_dep, ] mi_deps = [ diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h index dac50ae..9f3604e 100644 --- a/src/nvme/api-types.h +++ b/src/nvme/api-types.h @@ -800,8 +800,8 @@ struct nvme_resv_report_args { * @nsid: Namespace identifier * @data_len: Length of @data * @timeout: Timeout in ms - * @mos Management Operation Specific - * @mo Management Operation + * @mos: Management Operation Specific + * @mo: Management Operation */ struct nvme_io_mgmt_recv_args { void *data; @@ -822,8 +822,8 @@ struct nvme_io_mgmt_recv_args { * @nsid: Namespace identifier * @data_len: Length of @data * @timeout: Timeout in ms - * @mos Management Operation Specific - * @mo Management Operation + * @mos: Management Operation Specific + * @mo: Management Operation */ struct nvme_io_mgmt_send_args { void *data; diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index 7134dba..3c32e27 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -110,8 +110,15 @@ static const char * const treqs[] = { [NVMF_TREQ_NOT_SPECIFIED] = "not specified", [NVMF_TREQ_REQUIRED] = "required", [NVMF_TREQ_NOT_REQUIRED] = "not required", - [NVMF_TREQ_DISABLE_SQFLOW] = "not specified, " - "sq flow control disable supported", + [NVMF_TREQ_NOT_SPECIFIED | + NVMF_TREQ_DISABLE_SQFLOW] = "not specified, " + "sq flow control disable supported", + [NVMF_TREQ_REQUIRED | + NVMF_TREQ_DISABLE_SQFLOW] = "required, " + "sq flow control disable supported", + [NVMF_TREQ_NOT_REQUIRED | + NVMF_TREQ_DISABLE_SQFLOW] = "not required, " + "sq flow control disable supported", }; const char *nvmf_treq_str(__u8 treq) @@ -216,6 +223,8 @@ static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c, NVMF_DEF_CTRL_LOSS_TMO); MERGE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, tos, -1); + MERGE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0); + MERGE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false); MERGE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false); MERGE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false); @@ -243,6 +252,8 @@ void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) NVMF_DEF_CTRL_LOSS_TMO); UPDATE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0); UPDATE_CFG_OPTION(ctrl_cfg, cfg, tos, -1); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0); + UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0); UPDATE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false); UPDATE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false); UPDATE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false); @@ -517,6 +528,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) cfg->fast_io_fail_tmo, false)) || (strcmp(transport, "loop") && add_int_argument(argstr, "tos", cfg->tos, true)) || + add_int_argument(argstr, "keyring", cfg->keyring, false) || + (!strcmp(transport, "tcp") && + add_int_argument(argstr, "tls_key", cfg->tls_key, false)) || add_bool_argument(argstr, "duplicate_connect", cfg->duplicate_connect) || add_bool_argument(argstr, "disable_sqflow", @@ -894,20 +908,20 @@ static void sanitize_discovery_log_entry(struct nvmf_disc_log_entry *e) switch (e->adrfam) { case NVMF_ADDR_FAMILY_IP4: case NVMF_ADDR_FAMILY_IP6: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); - strchomp(e->trsvcid, NVMF_TRSVCID_SIZE - 1); + strchomp(e->traddr, NVMF_TRADDR_SIZE); + strchomp(e->trsvcid, NVMF_TRSVCID_SIZE); break; } break; case NVMF_TRTYPE_FC: switch (e->adrfam) { case NVMF_ADDR_FAMILY_FC: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); + strchomp(e->traddr, NVMF_TRADDR_SIZE); break; } break; case NVMF_TRTYPE_LOOP: - strchomp(e->traddr, NVMF_TRADDR_SIZE - 1); + strchomp(e->traddr, NVMF_TRADDR_SIZE); break; } } @@ -998,6 +1012,31 @@ static int uuid_from_device_tree(char *system_uuid) #define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" +/* + * See System Management BIOS (SMBIOS) Reference Specification + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf + */ +#define DMI_SYSTEM_INFORMATION 1 + +static bool is_dmi_uuid_valid(const char *buf, size_t len) +{ + int i; + + /* UUID bytes are from byte 8 to 23 */ + if (len < 24) + return false; + + /* Test it's a invalid UUID with all zeros */ + for (i = 8; i < 24; i++) { + if (buf[i]) + break; + } + if (i == 24) + return false; + + return true; +} + static int uuid_from_dmi_entries(char *system_uuid) { int f; @@ -1025,7 +1064,7 @@ static int uuid_from_dmi_entries(char *system_uuid) continue; if (sscanf(buf, "%d", &type) != 1) continue; - if (type != 1) + if (type != DMI_SYSTEM_INFORMATION) continue; sprintf(filename, "%s/%s/raw", PATH_DMI_ENTRIES, de->d_name); f = open(filename, O_RDONLY); @@ -1033,8 +1072,10 @@ static int uuid_from_dmi_entries(char *system_uuid) continue; len = read(f, buf, 512); close(f); - if (len <= 0) + + if (!is_dmi_uuid_valid(buf, len)) continue; + /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ sprintf(system_uuid, diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h index 272bb40..3664a85 100644 --- a/src/nvme/fabrics.h +++ b/src/nvme/fabrics.h @@ -35,6 +35,8 @@ * @nr_write_queues: Number of queues to use for exclusively for writing * @nr_poll_queues: Number of queues to reserve for polling completions * @tos: Type of service + * @keyring: Keyring to store and lookup keys + * @tls_key: TLS PSK for the connection * @duplicate_connect: Allow multiple connections to the same target * @disable_sqflow: Disable controller sq flow control * @hdr_digest: Generate/verify header digest (TCP) @@ -53,6 +55,8 @@ struct nvme_fabrics_config { int nr_write_queues; int nr_poll_queues; int tos; + int keyring; + int tls_key; bool duplicate_connect; bool disable_sqflow; @@ -253,7 +257,11 @@ char *nvmf_hostnqn_generate(); /** * nvmf_hostnqn_from_file() - Reads the host nvm qualified name from the config - * default location in @SYSCONFDIR@/nvme/ + * default location + * + * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme. + * $SYSCONFDIR is usually /etc. + * * Return: The host nqn, or NULL if unsuccessful. If found, the caller * is responsible to free the string. */ @@ -261,7 +269,11 @@ char *nvmf_hostnqn_from_file(); /** * nvmf_hostid_from_file() - Reads the host identifier from the config default - * location in @SYSCONFDIR@/nvme/. + * location + * + * Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/. + * $SYSCONFDIR is usually /etc. + * * Return: The host identifier, or NULL if unsuccessful. If found, the caller * is responsible to free the string. */ diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c index b2d92ef..2b5e09d 100644 --- a/src/nvme/ioctl.c +++ b/src/nvme/ioctl.c @@ -438,6 +438,8 @@ int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args) void *ptr = args->log; int ret; + args->fd = fd; + /* * 4k is the smallest possible transfer unit, so restricting to 4k * avoids having to check the MDTS value of the controller. diff --git a/src/nvme/json.c b/src/nvme/json.c index 072b622..a74b5a4 100644 --- a/src/nvme/json.c +++ b/src/nvme/json.c @@ -17,6 +17,7 @@ #include "fabrics.h" #include "log.h" #include "private.h" +#include "linux.h" #define JSON_UPDATE_INT_OPTION(c, k, a, o) \ if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_int(o); @@ -64,6 +65,27 @@ static void json_update_attributes(nvme_ctrl_t c, if (!strcmp("discovery", key_str) && !nvme_ctrl_is_discovery_ctrl(c)) nvme_ctrl_set_discovery_ctrl(c, true); + /* + * The JSON configuration holds the keyring description + * which needs to be converted into the keyring serial number. + */ + if (!strcmp("keyring", key_str) && cfg->keyring == 0) { + long keyring; + + keyring = nvme_lookup_keyring(json_object_get_string(val_obj)); + if (keyring) { + cfg->keyring = keyring; + nvme_set_keyring(cfg->keyring); + } + } + if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) { + long key; + + key = nvme_lookup_key("psk", + json_object_get_string(val_obj)); + if (key) + cfg->tls_key = key; + } } } @@ -299,6 +321,28 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c) if (nvme_ctrl_is_discovery_ctrl(c)) json_object_object_add(port_obj, "discovery", json_object_new_boolean(true)); + /* + * Store the keyring description in the JSON config file. + */ + if (cfg->keyring) { + char *desc = nvme_describe_key_serial(cfg->keyring); + + if (desc) { + json_object_object_add(port_obj, "keyring", + json_object_new_string(desc)); + free(desc); + } + } + if (cfg->tls_key) { + char *desc = nvme_describe_key_serial(cfg->tls_key); + + if (desc) { + json_object_object_add(port_obj, "tls_key", + json_object_new_string(desc)); + free(desc); + } + } + json_object_array_add(ctrl_array, port_obj); } diff --git a/src/nvme/linux.c b/src/nvme/linux.c index cae4036..c6eedc2 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_OPENSSL_3 #include @@ -28,6 +29,10 @@ #endif #endif +#ifdef CONFIG_KEYUTILS +#include +#endif + #include #include "linux.h" @@ -470,7 +475,143 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, memcpy(key, secret, key_len); return 0; } -#endif /* !CONFIG_OPENSSL */ + +static int derive_nvme_keys(const char *hostnqn, const char *identity, + int hmac, unsigned char *configured, + unsigned char *psk, int key_len) +{ + errno = EOPNOTSUPP; + return -1; +} +#else /* CONFIG_OPENSSL */ +static int derive_retained_key(const EVP_MD *md, const char *hostnqn, + unsigned char *generated, + unsigned char *retained, + size_t key_len) +{ + EVP_PKEY_CTX *ctx; + int ret; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + ret = -ENOMEM; + goto out_free_ctx; + } + ret = -ENOKEY; + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"tls13 ", 6) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"HostNQN", 7) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) + goto out_free_ctx; + + if (EVP_PKEY_derive(ctx, retained, &key_len) > 0) + ret = key_len; + +out_free_ctx: + if (ret < 0) { + errno = -ret; + ret = -1; + } + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int derive_tls_key(const EVP_MD *md, const char *identity, + unsigned char *retained, + unsigned char *psk, size_t key_len) +{ + EVP_PKEY_CTX *ctx; + int ret; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + ret = -ENOMEM; + goto out_free_ctx; + } + ret = -ENOKEY; + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"tls13 ", 6) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"nvme-tls-psk", 12) <= 0) + goto out_free_ctx; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)identity, + strlen(identity)) <= 0) + goto out_free_ctx; + + if (EVP_PKEY_derive(ctx, psk, &key_len) > 0) + ret = key_len; + +out_free_ctx: + EVP_PKEY_CTX_free(ctx); + if (ret < 0) { + errno = -ret; + ret = -1; + } + + return ret; +} + +static int derive_nvme_keys(const char *hostnqn, const char *identity, + int hmac, unsigned char *configured, + unsigned char *psk, int key_len) +{ + const EVP_MD *md; + unsigned char *retained; + int ret = -1; + + if (!hostnqn || !identity) { + errno = EINVAL; + return -1; + } + + switch (hmac) { + case 1: + md = EVP_sha256(); + break; + case 2: + md = EVP_sha384(); + break; + default: + errno = EINVAL; + return -1; + } + + retained = malloc(key_len); + if (!retained) { + errno = ENOMEM; + return -1; + } + ret = derive_retained_key(md, hostnqn, configured, retained, key_len); + if (ret > 0) + ret = derive_tls_key(md, identity, retained, psk, key_len); + free(retained); + return ret; +} +#endif /* CONFIG_OPENSSL */ #ifdef CONFIG_OPENSSL_1 int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, @@ -638,3 +779,133 @@ out: return err; } #endif /* !CONFIG_OPENSSL_3 */ + +#ifdef CONFIG_KEYUTILS +long nvme_lookup_keyring(const char *keyring) +{ + key_serial_t keyring_id; + + keyring_id = find_key_by_type_and_desc("keyring", keyring, 0); + if (keyring_id < 0) + return 0; + return keyring_id; +} + +char *nvme_describe_key_serial(long key_id) +{ + char *desc; + + if (keyctl_describe_alloc(key_id, &desc) < 0) + desc = NULL; + return desc; +} + +long nvme_lookup_key(const char *type, const char *identity) +{ + key_serial_t key; + + key = keyctl_search(KEY_SPEC_SESSION_KEYRING, type, identity, 0); + if (key < 0) + return 0; + return key; +} + +int nvme_set_keyring(long key_id) +{ + long err; + + err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING); + if (err < 0) + return -1; + return 0; +} + +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len) +{ + key_serial_t keyring_id, key = 0; + char *identity; + unsigned char *psk; + int ret = -1; + + keyring_id = nvme_lookup_keyring(keyring); + if (keyring_id == 0) + return -1; + + identity = malloc(strlen(hostnqn) + strlen(subsysnqn) + 12); + if (!identity) { + errno = ENOMEM; + return -1; + } + + sprintf(identity, "NVMe0R%02d %s %s", hmac, hostnqn, subsysnqn); + + psk = malloc(key_len); + if (!psk) { + errno = ENOMEM; + goto out_free_identity; + } + memset(psk, 0, key_len); + ret = derive_nvme_keys(hostnqn, identity, hmac, + configured_key, psk, key_len); + if (ret != key_len) + goto out_free_psk; + + key = keyctl_search(keyring_id, key_type, identity, 0); + if (key > 0) { + if (keyctl_update(key, psk, key_len) < 0) + key = 0; + } else { + key = add_key(key_type, identity, + psk, key_len, keyring_id); + if (key < 0) + key = 0; + } +out_free_psk: + free(psk); +out_free_identity: + free(identity); + return key; +} + +#else +long nvme_lookup_keyring(const char *keyring) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return 0; +} + +char *nvme_describe_key_serial(long key_id) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return NULL; +} + +long nvme_lookup_key(const char *type, const char *identity) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return 0; +} + +int nvme_set_keyring(long key_id) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; "\ + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return -1; +} + +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len) +{ + return derive_nvme_keys(NULL, NULL, 0, NULL, NULL, 0); +} +#endif diff --git a/src/nvme/linux.h b/src/nvme/linux.h index aa4c91a..37ba9d4 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -194,4 +194,72 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, unsigned int key_len, unsigned char *secret, unsigned char *key); +/** + * nvme_lookup_keyring() - Lookup keyring serial number + * @keyring: Keyring name + * + * Looks up the serial number of the keyring @keyring. + * + * Return: The key serial number of the keyring + * or 0 with errno set otherwise. + */ +long nvme_lookup_keyring(const char *keyring); + +/** + * nvme_describe_key_serial() - Return key description + * @key_id: Key serial number + * + * Fetches the description of the key or keyring identified + * by the serial number @key_id. + * + * Return: The description of @key_id or NULL on failure. + * The returned string needs to be freed by the caller. + */ +char *nvme_describe_key_serial(long key_id); + +/** + * nvme_lookup_key() - Lookup key serial number + * @type: Key type + * @identity: Key description + * + * Looks up the serial number of the key @identity + * with type %type in the current session keyring. + * + * Return: The key serial number of the key + * or 0 with errno set otherwise. + */ +long nvme_lookup_key(const char *type, const char *identity); + +/** + * nvme_set_keyring() - Link keyring for lookup + * @keyring_id: Keyring id + * + * Links @keyring_id into the session keyring such that + * its keys are available for further key lookups. + * + * Return: 0 on success, a negative number on error + * with errno set. + */ +int nvme_set_keyring(long keyring_id); + +/** + * nvme_insert_tls_key() - Derive and insert TLS key + * @keyring: Keyring to use + * @key_type: Type of the resulting key + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and + * stores it as type @key_type in the keyring specified by @keyring. + * + * Return: The key serial number if the key could be inserted into + * the keyring or 0 with errno otherwise. + */ +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len); + #endif /* _LIBNVME_LINUX_H */ diff --git a/src/nvme/mi.c b/src/nvme/mi.c index adf1753..391ba1a 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -841,9 +841,10 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, return rc; } -int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) +int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_size, + struct nvme_get_log_args *args) { - const size_t max_xfer_size = 4096; + const size_t max_xfer_size = xfer_size; off_t xfer_offset; int rc = 0; @@ -887,6 +888,11 @@ int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) return rc; } +int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args) +{ + return nvme_mi_admin_get_log_page(ctrl, 4096, args); +} + int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl, struct nvme_security_send_args *args) { diff --git a/src/nvme/mi.h b/src/nvme/mi.h index 5659159..12dbf6f 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -1417,6 +1417,28 @@ static inline int nvme_mi_admin_identify_secondary_ctrl_list(nvme_mi_ctrl_t ctrl return nvme_mi_admin_identify(ctrl, &args); } +/** + * nvme_mi_admin_get_log_page() - Retrieve log page data from controller + * @ctrl: Controller to query + * @xfer_len: The chunk size of the read + * @args: Get Log Page command arguments + * + * Performs a Get Log Page Admin command as specified by @args. Response data + * is stored in @args->data, which should be a buffer of @args->data_len bytes. + * Resulting data length is stored in @args->data_len on successful + * command completion. + * + * This request may be implemented as multiple log page commands, in order + * to fit within MI message-size limits. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + * + * See: &struct nvme_get_log_args + */ +int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_len, + struct nvme_get_log_args *args); + /** * nvme_mi_admin_get_log() - Retrieve log page data from controller * @ctrl: Controller to query diff --git a/src/nvme/no-json.c b/src/nvme/no-json.c new file mode 100644 index 0000000..171144e --- /dev/null +++ b/src/nvme/no-json.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2023 SUSE Software Solutions + * + * Authors: Daniel Wagner + */ + +#include "tree.h" + +#include + +int json_read_config(nvme_root_t r, const char *config_file) +{ + return -ENOTSUP; +} + +int json_update_config(nvme_root_t r, const char *config_file) +{ + return -ENOTSUP; +} + +int json_dump_tree(nvme_root_t r) +{ + return -ENOTSUP; +} diff --git a/src/nvme/tree.c b/src/nvme/tree.c index 18826bf..3484463 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -363,6 +363,7 @@ static void __nvme_free_ns(struct nvme_ns *n) /* Stub for SWIG */ void nvme_free_ns(struct nvme_ns *n) { + __nvme_free_ns(n); } static void __nvme_free_subsystem(struct nvme_subsystem *s) @@ -459,6 +460,7 @@ static void __nvme_free_host(struct nvme_host *h) /* Stub for SWIG */ void nvme_free_host(struct nvme_host *h) { + __nvme_free_host(h); } struct nvme_host *nvme_lookup_host(nvme_root_t r, const char *hostnqn, @@ -1862,27 +1864,62 @@ free_ns: return NULL; } +static inline bool nvme_ns_is_generic(const char *name) +{ + int instance, head_instance; + + if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2) + return false; + return true; +} + +static char *nvme_ns_generic_to_blkdev(const char *generic) +{ + + int instance, head_instance; + char blkdev[PATH_MAX]; + + if (!nvme_ns_is_generic(generic)) + return strdup(generic); + + sscanf(generic, "ng%dn%d", &instance, &head_instance); + sprintf(blkdev, "nvme%dn%d", instance, head_instance); + + return strdup(blkdev); +} + static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char *name) { struct nvme_ns *n; char *path; int ret; + char *blkdev; - ret = asprintf(&path, "%s/%s", sysfs_dir, name); - if (ret < 0) { + blkdev = nvme_ns_generic_to_blkdev(name); + if (!blkdev) { errno = ENOMEM; return NULL; } - n = nvme_ns_open(name); + ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev); + if (ret < 0) { + errno = ENOMEM; + goto free_blkdev; + } + + n = nvme_ns_open(blkdev); if (!n) goto free_path; n->sysfs_dir = path; + + free(blkdev); return n; free_path: free(path); +free_blkdev: + free(blkdev); return NULL; } diff --git a/src/nvme/types.h b/src/nvme/types.h index 929d658..eed50ef 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -2239,8 +2239,8 @@ struct nvme_id_ctrl_nvm { __u8 wzsl; __u8 wusl; __u8 dmrl; - __u32 dmrsl; - __u64 dmsl; + __le32 dmrsl; + __le64 dmsl; __u8 rsvd16[4080]; }; @@ -2407,7 +2407,7 @@ struct nvme_secondary_ctrl_list { * @iocsc: List of supported IO Command Set Combination vectors */ struct nvme_id_iocs { - __u64 iocsc[512]; + __le64 iocsc[512]; }; /** @@ -2515,12 +2515,18 @@ struct nvme_supported_log_pages { * related, this field shall be cleared to %0h. If the error * is transport related, this field shall be set to the type * of the transport - see &enum nvme_trtype. - * @rsvd: Reserved + * @csi: Command Set Indicator: This field contains command set + * indicator for the command that the error is associated + * with. + * @opcode: Opcode: This field contains opcode for the command that + * the error is associated with. * @cs: Command Specific Information: This field contains command * specific information. If used, the command definition * specifies the information returned. * @trtype_spec_info: Transport Type Specific Information - * @rsvd2: Reserved + * @rsvd: Reserved: [62:42] + * @log_page_version: This field shall be set to 1h. If set, @csi and @opcode + * will have valid values. */ struct nvme_error_log_page { __le64 error_count; @@ -2532,10 +2538,12 @@ struct nvme_error_log_page { __le32 nsid; __u8 vs; __u8 trtype; - __u8 rsvd[2]; + __u8 csi; + __u8 opcode; __le64 cs; __le16 trtype_spec_info; - __u8 rsvd2[22]; + __u8 rsvd[21]; + __u8 log_page_version; }; enum nvme_err_pel { @@ -4147,15 +4155,15 @@ enum nvme_fdp_config_fdpa { * @ruhs: Reclaim Unit Handle descriptors (&struct nvme_fdp_ruh_desc) */ struct nvme_fdp_config_desc { - __u16 size; + __le16 size; __u8 fdpa; __u8 vss; - __u32 nrg; - __u16 nruh; - __u16 maxpids; - __u32 nnss; - __u64 runs; - __u32 erutl; + __le32 nrg; + __le16 nruh; + __le16 maxpids; + __le32 nnss; + __le64 runs; + __le32 erutl; __u8 rsvd28[36]; struct nvme_fdp_ruh_desc ruhs[]; }; @@ -4170,10 +4178,10 @@ struct nvme_fdp_config_desc { * @configs: FDP Configuration descriptors (&struct nvme_fdp_config_desc) */ struct nvme_fdp_config_log { - __u16 n; + __le16 n; __u8 version; __u8 rsvd3; - __u32 size; + __le32 size; __u8 rsvd8[8]; struct nvme_fdp_config_desc configs[]; }; @@ -4209,7 +4217,7 @@ struct nvme_fdp_ruhu_desc { * @ruhus: Reclaim Unit Handle Usage descriptors */ struct nvme_fdp_ruhu_log { - __u16 nruh; + __le16 nruh; __u8 rsvd2[6]; struct nvme_fdp_ruhu_desc ruhus[]; }; @@ -4268,8 +4276,8 @@ enum nvme_fdp_event_realloc_flags { struct nvme_fdp_event_realloc { __u8 flags; __u8 rsvd1; - __u16 nlbam; - __u64 lba; + __le16 nlbam; + __le64 lba; __u8 rsvd12[4]; }; @@ -4301,11 +4309,11 @@ enum nvme_fdp_event_flags { struct nvme_fdp_event { __u8 type; __u8 flags; - __u16 pid; + __le16 pid; struct nvme_timestamp ts; - __u32 nsid; + __le32 nsid; __u8 type_specific[16]; - __u16 rgid; + __le16 rgid; __u8 ruhid; __u8 rsvd35[5]; __u8 vs[24]; @@ -4318,7 +4326,7 @@ struct nvme_fdp_event { * @events: FDP Events (&struct nvme_fdp_event) */ struct nvme_fdp_events_log { - __u32 n; + __le32 n; __u8 rsvd4[60]; struct nvme_fdp_event events[63]; }; @@ -4330,7 +4338,7 @@ struct nvme_fdp_events_log { * @rsvd24: Reserved */ struct nvme_feat_fdp_events_cdw11 { - __u16 phndl; + __le16 phndl; __u8 noet; __u8 rsvd24; }; @@ -4364,10 +4372,10 @@ struct nvme_fdp_supported_event_desc { * @rsvd16: Reserved */ struct nvme_fdp_ruh_status_desc { - __u16 pid; - __u16 ruhid; - __u32 earutr; - __u64 ruamw; + __le16 pid; + __le16 ruhid; + __le32 earutr; + __le64 ruamw; __u8 rsvd16[16]; }; @@ -4379,7 +4387,7 @@ struct nvme_fdp_ruh_status_desc { */ struct nvme_fdp_ruh_status { __u8 rsvd0[14]; - __u16 nruhsd; + __le16 nruhsd; struct nvme_fdp_ruh_status_desc ruhss[]; }; @@ -4445,7 +4453,7 @@ enum nvme_apst_entry { struct nvme_metadata_element_desc { __u8 type; __u8 rev; - __u16 len; + __le16 len; __u8 val[0]; }; @@ -4541,8 +4549,8 @@ struct nvme_lba_range_type_entry { __u8 type; __u8 attributes; __u8 rsvd2[14]; - __u64 slba; - __u64 nlb; + __le64 slba; + __le64 nlb; __u8 guid[16]; __u8 rsvd48[16]; }; @@ -4640,8 +4648,8 @@ struct nvme_copy_range { __le16 nlb; __u8 rsvd18[6]; __le32 eilbrt; - __le16 elbatm; __le16 elbat; + __le16 elbatm; }; /** @@ -4661,8 +4669,8 @@ struct nvme_copy_range_f1 { __le16 nlb; __u8 rsvd18[8]; __u8 elbt[10]; - __le16 elbatm; __le16 elbat; + __le16 elbatm; }; /** @@ -4779,10 +4787,12 @@ struct nvme_id_directives { * enum nvme_directive_types - Directives Supported or Enabled * @NVME_ID_DIR_ID_BIT: Identify directive is supported * @NVME_ID_DIR_SD_BIT: Streams directive is supported + * @NVME_ID_DIR_DP_BIT: Direct Placement directive is supported */ enum nvme_directive_types { NVME_ID_DIR_ID_BIT = 0, NVME_ID_DIR_SD_BIT = 1, + NVME_ID_DIR_DP_BIT = 2, }; /** @@ -4973,7 +4983,7 @@ union nvmf_tsas { __u8 prtype; __u8 cms; __u8 rsvd3[5]; - __u16 pkey; + __le16 pkey; __u8 rsvd10[246]; } rdma; struct tcp { diff --git a/src/nvme/util.c b/src/nvme/util.c index 0354afe..e7cbc8a 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -403,14 +403,17 @@ void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs, __u64 *slbas, __u64 *eilbrts, __u32 *elbatms, __u32 *elbats, __u16 nr) { - int i; + int i, j; for (i = 0; i < nr; i++) { copy[i].nlb = cpu_to_le16(nlbs[i]); copy[i].slba = cpu_to_le64(slbas[i]); - copy[i].elbt[2] = cpu_to_le64(eilbrts[i]); copy[i].elbatm = cpu_to_le16(elbatms[i]); copy[i].elbat = cpu_to_le16(elbats[i]); + for (j = 0; j < 8; j++) + copy[i].elbt[9 - j] = (eilbrts[i] >> (8 * j)) & 0xff; + copy[i].elbt[1] = 0; + copy[i].elbt[0] = 0; } } -- cgit v1.2.3