From: Klaus Jensen Date: Tue, 13 Dec 2022 20:05:12 +0000 (+0100) Subject: nvme: add flexible data placement management commands X-Git-Tag: v2.3~21^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=83e5084040856a7192b24614a927cf8ce3ac8f62;p=users%2Fsagi%2Fnvme-cli.git nvme: add flexible data placement management commands Add relevant TP4146 ("Flexible Data Placement") management commands. Signed-off-by: Klaus Jensen --- diff --git a/Documentation/meson.build b/Documentation/meson.build index c6489148..a526b992 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -31,6 +31,13 @@ adoc_sources = [ 'nvme-error-log', 'nvme-fid-support-effects-log', 'nvme-mi-cmd-support-effects-log', + 'nvme-fdp-configs', + 'nvme-fdp-usage', + 'nvme-fdp-stats', + 'nvme-fdp-events', + 'nvme-fdp-status', + 'nvme-fdp-update', + 'nvme-fdp-set-events', 'nvme-flush', 'nvme-format', 'nvme-fw-commit', diff --git a/Documentation/nvme-fdp-configs.txt b/Documentation/nvme-fdp-configs.txt new file mode 100644 index 00000000..c4311fb3 --- /dev/null +++ b/Documentation/nvme-fdp-configs.txt @@ -0,0 +1,42 @@ +nvme-fdp-configs(1) +=================== + +NAME +---- +nvme-fdp-configs - Get Flexible Data Placement Configurations + +SYNOPSIS +-------- +[verse] +'nvme fdp configs' [--endgrp-id= | -e ] + [--human-readable | -H] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, and the endurance group identifier specified, list +the possible configurations for Flexible Data Placement. + +OPTIONS +------- +-e :: +--endgrp-id=:: + The endurance group identifier to use when requesting the log page. + +-H:: +--human-readable:: + Parse, print and describe individual parts of bitfields. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-events.txt b/Documentation/nvme-fdp-events.txt new file mode 100644 index 00000000..606a1636 --- /dev/null +++ b/Documentation/nvme-fdp-events.txt @@ -0,0 +1,42 @@ +nvme-fdp-events(1) +================== + +NAME +---- +nvme-fdp-events - Get Flexible Data Placement Events + +SYNOPSIS +-------- +[verse] +'nvme fdp events' [--endgrp-id= | -e ] + [--host-events | -E] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, provide information about events affecting Reclaim +Units and media usage in an Endurance Group. + +OPTIONS +------- +-e :: +--endgrp-id=:: + The endurance group identifier to use when requesting the log page. + +-E:: +--host-events:: + Request the controller to report host events. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-set-events.txt b/Documentation/nvme-fdp-set-events.txt new file mode 100644 index 00000000..b45819be --- /dev/null +++ b/Documentation/nvme-fdp-set-events.txt @@ -0,0 +1,39 @@ +nvme-fdp-set-events(1) +====================== + +NAME +---- +nvme-fdp-set-events - Enable or disable FDP events + +SYNOPSIS +-------- +[verse] +'nvme fdp set-events' [--namespace-id= | -n ] + [--placement-handle= | -p ] + [--enable | -e] + [--event-types= | -t ] + +DESCRIPTION +----------- +For the NVMe device given, enable or disable a list of event types from being +generated for the Reclaim Unit Handle reference by the specified Placement +Handle. + +OPTIONS +------- +-n :: +--namespace-id=:: + Namespace identifier. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-stats.txt b/Documentation/nvme-fdp-stats.txt new file mode 100644 index 00000000..7f96065a --- /dev/null +++ b/Documentation/nvme-fdp-stats.txt @@ -0,0 +1,37 @@ +nvme-fdp-stats(1) +================= + +NAME +---- +nvme-fdp-stats - Get Flexible Data Placement Statistics + +SYNOPSIS +-------- +[verse] +'nvme fdp stats' [--endgrp-id= | -e ] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, provide information about the FDP configuration over +the life of the FDP configuration in an Endurance Group. + +OPTIONS +------- +-e :: +--endgrp-id=:: + The endurance group identifier to use when requesting the log page. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-status.txt b/Documentation/nvme-fdp-status.txt new file mode 100644 index 00000000..263cb4c9 --- /dev/null +++ b/Documentation/nvme-fdp-status.txt @@ -0,0 +1,37 @@ +nvme-fdp-status(1) +================== + +NAME +---- +nvme-fdp-status - Get Reclaim Unit Handle Status + +SYNOPSIS +-------- +[verse] +'nvme fdp status' [--namespace-id= | -n ] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, provide information about Reclaim Unit Handles that +are accessible by the specified namespace. + +OPTIONS +------- +-n :: +--namespace-id=:: + Namespace identifier. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-update.txt b/Documentation/nvme-fdp-update.txt new file mode 100644 index 00000000..4b70c247 --- /dev/null +++ b/Documentation/nvme-fdp-update.txt @@ -0,0 +1,31 @@ +nvme-fdp-update(1) +================== + +NAME +---- +nvme-fdp-update - Reclaim Unit Handle Update + +SYNOPSIS +-------- +[verse] +'nvme fdp update' [--namespace-id= | -n ] + [--pids= | -p ] + +DESCRIPTION +----------- +For the NVMe device given, update the given Placement Identifiers to reference +a different Reclaim Unit accessible by the specified namespace. + +OPTIONS +------- +-n :: +--namespace-id=:: + Namespace identifier. + +-p :: +--pids=:: + Comma-separated list of placement identifiers to update. + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-fdp-usage.txt b/Documentation/nvme-fdp-usage.txt new file mode 100644 index 00000000..ad9d1eba --- /dev/null +++ b/Documentation/nvme-fdp-usage.txt @@ -0,0 +1,38 @@ +nvme-fdp-usage(1) +================= + +NAME +---- +nvme-fdp-usage - Get Reclaim Unit Handle Usage + +SYNOPSIS +-------- +[verse] +'nvme fdp usage' [--endgrp-id= | -e ] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, provide information about the Reclaim Unit Handles +associated with the Placement Handles of the namespaces in the specified +Endurance Group. + +OPTIONS +------- +-e :: +--endgrp-id=:: + The endurance group identifier to use when requesting the log page. + +-b:: +--raw-binary:: + Print the raw buffer to the standard output stream. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. Only one + output format can be used at a time. + +NVME +---- +Part of nvme-cli diff --git a/nvme-print.c b/nvme-print.c index 14a90a31..55423416 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -2292,6 +2292,331 @@ static void json_supported_cap_config_log( json_free_object(root); } +static void json_nvme_fdp_configs(struct nvme_fdp_config_log *log, size_t len) +{ + struct json_object *root, *obj_configs; + uint16_t n; + + void *p = log->configs; + + root = json_create_object(); + obj_configs = json_create_array(); + + n = le16_to_cpu(log->n); + + json_object_add_value_uint(root, "n", n); + + for (int i = 0; i < n + 1; i++) { + struct nvme_fdp_config_desc *config = p; + + struct json_object *obj_config = json_create_object(); + struct json_object *obj_ruhs = json_create_array(); + + json_object_add_value_uint(obj_config, "fdpa", config->fdpa); + json_object_add_value_uint(obj_config, "vss", config->vss); + json_object_add_value_uint(obj_config, "nrg", le32_to_cpu(config->nrg)); + json_object_add_value_uint(obj_config, "nruh", le16_to_cpu(config->nruh)); + json_object_add_value_uint(obj_config, "nnss", le32_to_cpu(config->nnss)); + json_object_add_value_uint(obj_config, "runs", le64_to_cpu(config->runs)); + json_object_add_value_uint(obj_config, "erutl", le32_to_cpu(config->erutl)); + + for (int j = 0; j < le16_to_cpu(config->nruh); j++) { + struct nvme_fdp_ruh_desc *ruh = &config->ruhs[j]; + + struct json_object *obj_ruh = json_create_object(); + + json_object_add_value_uint(obj_ruh, "ruht", ruh->ruht); + + json_array_add_value_object(obj_ruhs, obj_ruh); + } + + json_array_add_value_object(obj_configs, obj_config); + + p += config->size; + } + + json_object_add_value_array(root, "configs", obj_configs); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} + +void nvme_show_fdp_config_fdpa(uint8_t fdpa) +{ + __u8 valid = (fdpa >> 7) & 0x1; + __u8 rsvd = (fdpa >> 5) >> 0x3; + __u8 fdpvwc = (fdpa >> 4) & 0x1; + __u8 rgif = fdpa & 0xf; + + printf(" [7:7] : %#x\tFDP Configuration %sValid\n", + valid, valid ? "" : "Not "); + if (rsvd) + printf(" [6:5] : %#x\tReserved\n", rsvd); + printf(" [4:4] : %#x\tFDP Volatile Write Cache %sPresent\n", + fdpvwc, fdpvwc ? "" : "Not "); + printf(" [3:0] : %#x\tReclaim Group Identifier Format\n", rgif); +} + +void nvme_show_fdp_configs(struct nvme_fdp_config_log *log, size_t len, + enum nvme_print_flags flags) +{ + void *p = log->configs; + int human = flags & VERBOSE; + uint16_t n; + + if (flags & BINARY) + return d_raw((unsigned char *)log, len); + if (flags & JSON) + return json_nvme_fdp_configs(log, len); + + n = le16_to_cpu(log->n) + 1; + + for (int i = 0; i < n; i++) { + struct nvme_fdp_config_desc *config = p; + + printf("FDP Attributes: %#x\n", config->fdpa); + if (human) + nvme_show_fdp_config_fdpa(config->fdpa); + + printf("Vendor Specific Size: %u\n", config->vss); + printf("Number of Reclaim Groups: %"PRIu32"\n", le32_to_cpu(config->nrg)); + printf("Number of Reclaim Unit Handles: %"PRIu16"\n", le16_to_cpu(config->nruh)); + printf("Number of Namespaces Supported: %"PRIu32"\n", le32_to_cpu(config->nnss)); + printf("Reclaim Unit Nominal Size: %"PRIu64"\n", le64_to_cpu(config->runs)); + printf("Estimated Reclaim Unit Time Limit: %"PRIu32"\n", le32_to_cpu(config->erutl)); + + printf("Reclaim Unit Handle List:\n"); + for (int j = 0; j < le16_to_cpu(config->nruh); j++) { + struct nvme_fdp_ruh_desc *ruh = &config->ruhs[j]; + + printf(" [%d]: %s\n", j, ruh->ruht == NVME_FDP_RUHT_INITIALLY_ISOLATED ? "Initially Isolated" : "Persistently Isolated"); + } + + p += config->size; + } +} + +static void json_nvme_fdp_usage(struct nvme_fdp_ruhu_log *log, size_t len) +{ + struct json_object *root, *obj_ruhus; + uint16_t nruh; + + root = json_create_object(); + obj_ruhus = json_create_array(); + + nruh = le16_to_cpu(log->nruh); + + json_object_add_value_uint(root, "nruh", nruh); + + for (int i = 0; i < nruh; i++) { + struct nvme_fdp_ruhu_desc *ruhu = &log->ruhus[i]; + + struct json_object *obj_ruhu = json_create_object(); + + json_object_add_value_uint(obj_ruhu, "ruha", ruhu->ruha); + + json_array_add_value_object(obj_ruhus, obj_ruhu); + } + + json_object_add_value_array(root, "ruhus", obj_ruhus); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} + +void nvme_show_fdp_usage(struct nvme_fdp_ruhu_log *log, size_t len, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char *)log, len); + if (flags & JSON) + return json_nvme_fdp_usage(log, len); + + uint16_t nruh = le16_to_cpu(log->nruh); + + for (int i = 0; i < nruh; i++) { + struct nvme_fdp_ruhu_desc *ruhu = &log->ruhus[i]; + + printf("Reclaim Unit Handle %d Attributes: 0x%"PRIx8" (%s)\n", i, ruhu->ruha, + ruhu->ruha == 0x1 ? "Host Specified" : ( + ruhu->ruha == 0x2 ? "Controller Specified" : + "Unknown")); + } +} + +static void json_nvme_fdp_stats(struct nvme_fdp_stats_log *log) +{ + struct json_object *root = json_create_object(); + + json_object_add_value_uint128(root, "hbmw", le128_to_cpu(log->hbmw)); + json_object_add_value_uint128(root, "mbmw", le128_to_cpu(log->mbmw)); + json_object_add_value_uint128(root, "mbe", le128_to_cpu(log->mbe)); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} + +void nvme_show_fdp_stats(struct nvme_fdp_stats_log *log, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char*)log, sizeof(*log)); + if (flags & JSON) + return json_nvme_fdp_stats(log); + + printf("Host Bytes with Metadata Written (HBMW): %s\n", + uint128_t_to_string(le128_to_cpu(log->hbmw))); + printf("Media Bytes with Metadata Written (MBMW): %s\n", + uint128_t_to_string(le128_to_cpu(log->mbmw))); + printf("Media Bytes Erased (MBE): %s\n", + uint128_t_to_string(le128_to_cpu(log->mbe))); +} + +static void json_nvme_fdp_events(struct nvme_fdp_events_log *log) +{ + struct json_object *root, *obj_events; + uint32_t n; + + root = json_create_object(); + obj_events = json_create_array(); + + n = le32_to_cpu(log->n); + + json_object_add_value_uint(root, "n", n); + + for (unsigned int i = 0; i < n; i++) { + struct nvme_fdp_event *event = &log->events[i]; + + struct json_object *obj_event = json_create_object(); + + json_object_add_value_uint(obj_event, "type", event->type); + json_object_add_value_uint(obj_event, "fdpef", event->flags); + json_object_add_value_uint(obj_event, "pid", le16_to_cpu(event->pid)); + json_object_add_value_uint(obj_event, "timestamp", le64_to_cpu(event->timestamp)); + json_object_add_value_uint(obj_event, "nsid", le32_to_cpu(event->nsid)); + + if (event->type == NVME_FDP_EVENT_REALLOC) { + struct nvme_fdp_event_realloc *mr; + mr = (struct nvme_fdp_event_realloc *)&event->type_specific; + + json_object_add_value_uint(obj_event, "nlbam", le16_to_cpu(mr->nlbam)); + + if (mr->flags & NVME_FDP_EVENT_REALLOC_F_LBAV) + json_object_add_value_uint(obj_event, "lba", le64_to_cpu(mr->lba)); + } + + json_array_add_value_object(obj_events, obj_event); + } + + json_object_add_value_array(root, "events", obj_events); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} + +void nvme_show_fdp_events(struct nvme_fdp_events_log *log, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char*)log, sizeof(*log)); + if (flags & JSON) + return json_nvme_fdp_events(log); + + uint32_t n = le32_to_cpu(log->n); + + for (unsigned int i = 0; i < n; i++) { + struct nvme_fdp_event *event = &log->events[i]; + + printf("Event[%u]\n", i); + printf(" Event Type: 0x%"PRIx8"\n", event->type); + printf(" FDP Event Flags (FDPEF): 0x%"PRIx8"\n", event->flags); + printf(" Placement Identifier (PID): 0x%"PRIx16"\n", le16_to_cpu(event->pid)); + printf(" Event Timestamp: %"PRIu64"\n", le64_to_cpu(event->timestamp)); + printf(" Namespace Identifier (NSID): %"PRIu32"\n", le32_to_cpu(event->nsid)); + + if (event->type == NVME_FDP_EVENT_REALLOC) { + struct nvme_fdp_event_realloc *mr; + mr = (struct nvme_fdp_event_realloc *)&event->type_specific; + + printf(" Number of LBAs Moved (NLBAM): %"PRIu16"\n", le16_to_cpu(mr->nlbam)); + + if (mr->flags & NVME_FDP_EVENT_REALLOC_F_LBAV) { + printf(" Logical Block Address (LBA): 0x%"PRIx64"\n", le64_to_cpu(mr->lba)); + } + } + + printf(" Reclaim Group Identifier: %"PRIu16"\n", le16_to_cpu(event->rgid)); + printf(" Reclaim Unit Handle Identifier %"PRIu8"\n", event->ruhid); + + printf("\n"); + } +} + +static void json_nvme_fdp_ruh_status(struct nvme_fdp_ruh_status *status, size_t len) +{ + struct json_object *root, *obj_ruhss; + uint16_t nruhsd; + + root = json_create_object(); + obj_ruhss = json_create_array(); + + nruhsd = le16_to_cpu(status->nruhsd); + + json_object_add_value_uint(root, "nruhsd", nruhsd); + + for (unsigned int i = 0; i < nruhsd; i++) { + struct nvme_fdp_ruh_status_desc *ruhs = &status->ruhss[i]; + + struct json_object *obj_ruhs = json_create_object(); + + json_object_add_value_uint(obj_ruhs, "pid", le16_to_cpu(ruhs->pid)); + json_object_add_value_uint(obj_ruhs, "ruhid", le16_to_cpu(ruhs->ruhid)); + json_object_add_value_uint(obj_ruhs, "earutr", le32_to_cpu(ruhs->earutr)); + json_object_add_value_uint(obj_ruhs, "ruamw", le64_to_cpu(ruhs->ruamw)); + + json_array_add_value_object(obj_ruhss, obj_ruhs); + } + + json_object_add_value_array(root, "ruhss", obj_ruhss); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} + +void nvme_show_fdp_ruh_status(struct nvme_fdp_ruh_status *status, size_t len, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char *)status, len); + if (flags & JSON) + return json_nvme_fdp_ruh_status(status, len); + + uint16_t nruhsd = le16_to_cpu(status->nruhsd); + + for (unsigned int i = 0; i < nruhsd; i++) { + struct nvme_fdp_ruh_status_desc *ruhs = &status->ruhss[i]; + + printf("Placement Identifier %"PRIu16"; Reclaim Unit Handle Identifier %"PRIu16"\n", + le16_to_cpu(ruhs->pid), le16_to_cpu(ruhs->ruhid)); + printf(" Estimated Active Reclaim Unit Time Remaining (EARUTR): %"PRIu32"\n", + le32_to_cpu(ruhs->earutr)); + printf(" Reclaim Unit Available Media Writes (RUAMW): %"PRIu64"\n", + le64_to_cpu(ruhs->ruamw)); + + printf("\n"); + } +} + void nvme_show_supported_cap_config_log( struct nvme_supported_cap_config_list_log *cap, enum nvme_print_flags flags) @@ -3352,7 +3677,9 @@ static void nvme_show_id_ctrl_oaes(__le32 ctrl_oaes) static void nvme_show_id_ctrl_ctratt(__le32 ctrl_ctratt) { __u32 ctratt = le32_to_cpu(ctrl_ctratt); - __u32 rsvd = ctratt >> 16; + __u32 rsvd20 = (ctratt >> 20); + __u32 fdps = (ctratt >> 19) & 0x1; + __u32 rsvd16 = (ctratt >> 16) & 0x7; __u32 elbas = (ctratt >> 15) & 0x1; __u32 delnvmset = (ctratt >> 14) & 0x1; __u32 delegrp = (ctratt >> 13) & 0x1; @@ -3370,8 +3697,12 @@ static void nvme_show_id_ctrl_ctratt(__le32 ctrl_ctratt) __u32 sqa = (ctratt & NVME_CTRL_CTRATT_SQ_ASSOCIATIONS) >> 8; __u32 uuidlist = (ctratt & NVME_CTRL_CTRATT_UUID_LIST) >> 9; - if (rsvd) - printf(" [31:16] : %#x\tReserved\n", rsvd); + if (rsvd20) + printf(" [31:20] : %#x\tReserved\n", rsvd20); + printf(" [19:19] : %#x\tFlexible Data Placement %sSupported\n", + fdps, fdps ? "" : "Not "); + if (rsvd16) + printf(" [18:16] : %#x\tReserved\n", rsvd16); printf(" [15:15] : %#x\tExtended LBA Formats %sSupported\n", elbas, elbas ? "" : "Not "); printf(" [14:14] : %#x\tDelete NVM Set %sSupported\n", @@ -6517,6 +6848,20 @@ void nvme_show_sanitize_log(struct nvme_sanitize_log_page *sanitize, le32_to_cpu(sanitize->etcend)); } +static const char *nvme_fdp_event_to_string(enum nvme_fdp_event_type event) +{ + switch (event) { + case NVME_FDP_EVENT_RUNFW: return "Reclaim Unit Not Fully Written"; + case NVME_FDP_EVENT_RUTLE: return "Reclaim Unit Active Time Limit Exceeded"; + case NVME_FDP_EVENT_RESET: return "Controller Level Reset Modified Reclaim Unit Handles"; + case NVME_FDP_EVENT_PID: return "Invalid Placement Identifier"; + case NVME_FDP_EVENT_REALLOC: return "Media Reallocated"; + case NVME_FDP_EVENT_MODIFY: return "Implicitly Modified Reclaim Unit Handle"; + } + + return "Unknown"; +} + const char *nvme_feature_to_string(enum nvme_features_id feature) { switch (feature) { @@ -6554,6 +6899,8 @@ const char *nvme_feature_to_string(enum nvme_features_id feature) case NVME_FEAT_FID_RESV_MASK: return "Reservation Notification Mask"; case NVME_FEAT_FID_RESV_PERSIST:return "Reservation Persistence"; case NVME_FEAT_FID_WRITE_PROTECT: return "Namespace Write Protect"; + case NVME_FEAT_FID_FDP: return "Flexible Direct Placement"; + case NVME_FEAT_FID_FDP_EVENTS: return "Flexible Direct Placement Events"; } /* * We don't use the "default:" statement to let the compiler warning if @@ -7119,6 +7466,21 @@ void nvme_feature_show_fields(enum nvme_features_id fid, unsigned int result, un case NVME_FEAT_FID_WRITE_PROTECT: printf("\tNamespace Write Protect: %s\n", nvme_show_ns_wp_cfg(result)); break; + case NVME_FEAT_FID_FDP: + printf("\tFlexible Direct Placement Enable (FDPE) : %s\n", + (result & 0x1) ? "Yes" : "No"); + printf("\tFlexible Direct Placement Configuration Index : %u\n", + (result >> 8) & 0xf); + break; + case NVME_FEAT_FID_FDP_EVENTS: + for (unsigned int i = 0; i < result; i++) { + struct nvme_fdp_supported_event_desc *d; + + d = &((struct nvme_fdp_supported_event_desc *)buf)[i]; + + printf("\t%-53s: %sEnabled\n", nvme_fdp_event_to_string(d->evt), + d->evta & 0x1 ? "" : "Not "); + } default: break; } diff --git a/nvme-print.h b/nvme-print.h index f30f63eb..35a9aa81 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -132,6 +132,17 @@ void json_nvme_finish_zone_list(__u64 nr_zones, struct json_object *zone_list); void nvme_show_list_item(nvme_ns_t n); +void nvme_show_fdp_configs(struct nvme_fdp_config_log *configs, size_t len, + enum nvme_print_flags flags); +void nvme_show_fdp_stats(struct nvme_fdp_stats_log *log, + enum nvme_print_flags flags); +void nvme_show_fdp_events(struct nvme_fdp_events_log *log, + enum nvme_print_flags flags); +void nvme_show_fdp_usage(struct nvme_fdp_ruhu_log *log, size_t len, + enum nvme_print_flags flags); +void nvme_show_fdp_ruh_status(struct nvme_fdp_ruh_status *status, size_t len, + enum nvme_print_flags flags); + const char *nvme_cmd_to_string(int admin, __u8 opcode); const char *nvme_select_to_string(int sel); const char *nvme_feature_to_string(enum nvme_features_id feature); diff --git a/nvme.c b/nvme.c index 075a7347..b2cd5da5 100644 --- a/nvme.c +++ b/nvme.c @@ -75,6 +75,7 @@ struct feat_cfg { __u32 namespace_id; enum nvme_get_features_sel sel; __u32 cdw11; + __u32 cdw12; __u8 uuid_index; __u32 data_len; bool raw_binary; @@ -4282,6 +4283,11 @@ static int get_feature_id(struct nvme_dev *dev, struct feat_cfg *cfg, if (cfg->feature_id == NVME_FEAT_FID_HOST_ID && (cfg->cdw11 & 0x1)) cfg->data_len = 16; + if (cfg->feature_id == NVME_FEAT_FID_FDP_EVENTS) { + cfg->data_len = 0xff * sizeof(__u16); + cfg->cdw11 |= 0xff << 16; + } + if (cfg->sel == 3) cfg->data_len = 0; @@ -4413,7 +4419,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, const char *raw = "show feature in binary format"; const char *feature_id = "feature identifier"; const char *sel = "[0-3,8]: current/default/saved/supported/changed"; - const char *cdw11 = "dword 11 for interrupt vector config"; + const char *cdw11 = "feature specific dword 11"; const char *human_readable = "show feature in readable format"; struct nvme_dev *dev; int err; diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c new file mode 100644 index 00000000..7802ab5d --- /dev/null +++ b/plugins/fdp/fdp.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "nvme-print.h" + +#define CREATE_CMD +#include "fdp.h" + +static int fdp_configs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Get Flexible Data Placement Configurations"; + const char *egid = "Endurance group identifier"; + const char *human_readable = "show log in readable format"; + const char *raw = "use binary output"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + struct nvme_fdp_config_log hdr; + void *log = NULL; + int err; + + struct config { + __u16 egid; + char *output_format; + bool human_readable; + bool raw_binary; + }; + + struct config cfg = { + .egid = 0, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto out; + + if (cfg.raw_binary) + flags = BINARY; + + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.egid) { + fprintf(stderr, "endurance group identifier required\n"); + err = -EINVAL; + goto out; + } + + err = nvme_get_log_fdp_configurations(dev->direct.fd, cfg.egid, 0, + sizeof(hdr), &hdr); + if (err) { + nvme_show_status(errno); + goto out; + } + + log = malloc(hdr.size); + if (!log) { + err = -ENOMEM; + goto out; + } + + err = nvme_get_log_fdp_configurations(dev->direct.fd, cfg.egid, 0, + hdr.size, log); + if (err) { + nvme_show_status(errno); + goto out; + } + + nvme_show_fdp_configs(log, hdr.size, flags); + +out: + dev_close(dev); + free(log); + + return err; +} + +static int fdp_usage(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get Flexible Data Placement Reclaim Unit Handle Usage"; + const char *egid = "Endurance group identifier"; + const char *raw = "use binary output"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + struct nvme_fdp_ruhu_log hdr; + size_t len; + void *log = NULL; + int err; + + struct config { + __u16 egid; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .egid = 0, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto out; + + if (cfg.raw_binary) + flags = BINARY; + + err = nvme_get_log_reclaim_unit_handle_usage(dev->direct.fd, cfg.egid, + 0, sizeof(hdr), &hdr); + if (err) { + nvme_show_status(err); + goto out; + } + + len = sizeof(hdr) + le16_to_cpu(hdr.nruh) * sizeof(struct nvme_fdp_ruhu_desc); + log = malloc(len); + if (!log) { + err = -ENOMEM; + goto out; + } + + err = nvme_get_log_reclaim_unit_handle_usage(dev->direct.fd, cfg.egid, + 0, len, log); + if (err) { + nvme_show_status(err); + goto out; + } + + nvme_show_fdp_usage(log, len, flags); + +out: + dev_close(dev); + free(log); + + return err; +} + +static int fdp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get Flexible Data Placement Statistics"; + const char *egid = "Endurance group identifier"; + const char *raw = "use binary output"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + struct nvme_fdp_stats_log stats; + int err; + + struct config { + __u16 egid; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .egid = 0, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto out; + + if (cfg.raw_binary) + flags = BINARY; + + memset(&stats, 0x0, sizeof(stats)); + + err = nvme_get_log_fdp_stats(dev->direct.fd, cfg.egid, 0, sizeof(stats), &stats); + if (err) { + nvme_show_status(err); + goto out; + } + + nvme_show_fdp_stats(&stats, flags); + +out: + dev_close(dev); + + return err; +} + +static int fdp_events(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get Flexible Data Placement Events"; + const char *egid = "Endurance group identifier"; + const char *host_events = "Get host events"; + const char *raw = "use binary output"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + struct nvme_fdp_events_log events; + int err; + + struct config { + __u16 egid; + bool host_events; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .egid = 0, + .host_events = false, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("endgrp-id", 'e', &cfg.egid, egid), + OPT_FLAG("host-events", 'H', &cfg.host_events, host_events), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto out; + + if (cfg.raw_binary) + flags = BINARY; + + memset(&events, 0x0, sizeof(events)); + + err = nvme_get_log_fdp_events(dev->direct.fd, cfg.egid, + cfg.host_events, 0, sizeof(events), &events); + if (err) { + nvme_show_status(err); + goto out; + } + + nvme_show_fdp_events(&events, flags); + +out: + dev_close(dev); + + return err; +} + +static int fdp_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reclaim Unit Handle Status"; + const char *namespace_id = "Namespace identifier"; + const char *raw = "use binary output"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + struct nvme_fdp_ruh_status hdr; + size_t len; + void *buf = NULL; + int err = -1; + + struct config { + __u32 namespace_id; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto out; + + if (cfg.raw_binary) + flags = BINARY; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto out; + } + } + + err = nvme_fdp_reclaim_unit_handle_status(dev_fd(dev), + cfg.namespace_id, sizeof(hdr), &hdr); + if (err) { + nvme_show_status(err); + goto out; + } + + len = le16_to_cpu(hdr.nruhsd) * sizeof(struct nvme_fdp_ruh_status_desc); + buf = malloc(len); + if (!buf) { + err = -ENOMEM; + goto out; + } + + err = nvme_fdp_reclaim_unit_handle_status(dev_fd(dev), + cfg.namespace_id, len, buf); + if (err) { + nvme_show_status(err); + goto out; + } + + nvme_show_fdp_ruh_status(buf, len, flags); + +out: + free(buf); + dev_close(dev); + + return err; +} + +static int fdp_update(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reclaim Unit Handle Update"; + const char *namespace_id = "Namespace identifier"; + const char *_pids = "Comma-separated list of placement identifiers to update"; + + struct nvme_dev *dev; + unsigned short pids[256]; + __u16 buf[256]; + int npids; + int err = -1; + + struct config { + __u32 namespace_id; + char *pids; + }; + + struct config cfg = { + .pids = "", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_LIST("pids", 'p', &cfg.pids, _pids), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + npids = argconfig_parse_comma_sep_array_short(cfg.pids, pids, ARRAY_SIZE(pids)); + if (npids < 0) { + perror("could not parse pids"); + err = -EINVAL; + goto out; + } else if (npids == 0) { + fprintf(stderr, "no placement identifiers set\n"); + err = -EINVAL; + goto out; + } + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto out; + } + } + + for (unsigned int i = 0; i < npids; i++) { + buf[i] = cpu_to_le16(pids[i]); + } + + err = nvme_fdp_reclaim_unit_handle_update(dev_fd(dev), cfg.namespace_id, npids, buf); + if (err) { + nvme_show_status(err); + goto out; + } + + printf("update: Success\n"); + +out: + dev_close(dev); + + return err; +} + +static int fdp_set_events(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Enable or disable FDP events"; + const char *namespace_id = "Namespace identifier"; + const char *enable = "Enable/disable event"; + const char *event_types = "Comma-separated list of event types"; + const char *ph = "Placement Handle"; + + struct nvme_dev *dev; + int err = -1; + unsigned short evts[255]; + int nev; + __u8 buf[255]; + + struct config { + __u32 namespace_id; + __u16 ph; + char *event_types; + bool enable; + }; + + struct config cfg = { + .enable = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SHRT("placement-handle", 'p', &cfg.ph, ph), + OPT_FLAG("enable", 'e', &cfg.enable, enable), + OPT_LIST("event-types", 't', &cfg.event_types, event_types), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + nev = argconfig_parse_comma_sep_array_short(cfg.event_types, evts, ARRAY_SIZE(evts)); + if (nev < 0) { + perror("could not parse event types"); + err = -EINVAL; + goto out; + } else if (nev == 0) { + fprintf(stderr, "no event types set\n"); + err = -EINVAL; + goto out; + } else if (nev > 255) { + fprintf(stderr, "too many event types (max 255)\n"); + err = -EINVAL; + goto out; + } + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + if (errno != ENOTTY) { + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); + goto out; + } + + cfg.namespace_id = NVME_NSID_ALL; + } + } + + for (unsigned int i = 0; i < nev; i++) { + buf[i] = (__u8)evts[i]; + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = NVME_FEAT_FID_FDP_EVENTS, + .nsid = cfg.namespace_id, + .cdw11 = (nev << 16) | cfg.ph, + .cdw12 = cfg.enable ? 0x1 : 0x0, + .data_len = sizeof(buf), + .data = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + err = nvme_set_features(&args); + if (err) { + nvme_show_status(err); + goto out; + } + + printf("set-events: Success\n"); + +out: + dev_close(dev); + + return err; +} diff --git a/plugins/fdp/fdp.h b/plugins/fdp/fdp.h new file mode 100644 index 00000000..f162b321 --- /dev/null +++ b/plugins/fdp/fdp.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/fdp/fdp + +#if !defined(FDP_NVME) || defined(CMD_HEADER_MULTI_READ) +#define FDP_NVME + +#include "cmd.h" + +PLUGIN(NAME("fdp", "Manage Flexible Data Placement enabled devices", NVME_VERSION), + COMMAND_LIST( + ENTRY("configs", "List configurations", fdp_configs) + ENTRY("usage", "Show reclaim unit handle usage", fdp_usage) + ENTRY("stats", "Show statistics", fdp_stats) + ENTRY("events", "List events affecting reclaim units and media usage", fdp_events) + ENTRY("status", "Show reclaim unit handle status", fdp_status) + ENTRY("update", "Update a reclaim unit handle", fdp_update) + ENTRY("set-events", "Enabled or disable events", fdp_set_events) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/meson.build b/plugins/meson.build index 52b7f4ae..c92b2089 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -23,6 +23,7 @@ sources += [ 'plugins/ymtc/ymtc-nvme.c', 'plugins/zns/zns.c', 'plugins/inspur/inspur-nvme.c', + 'plugins/fdp/fdp.c', ] subdir('solidigm') subdir('ocp')