diff --git a/nvme-print-json.c b/nvme-print-json.c index f4808b85b3..d6923d1824 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -3712,6 +3712,72 @@ static void json_feature_show_fields_power_loss_signal(struct json_object *r, un nvme_pls_mode_to_string(NVME_GET(result, FEAT_PLS_MODE))); } +static void json_feat_perfc_std(struct json_object *r, struct nvme_std_perf_attr *data) +{ + obj_add_str(r, "random 4 kib average read latency", + nvme_feature_perfc_r4karl_to_string(data->r4karl)); + obj_add_uint_02x(r, "R4KARL", data->r4karl); +} + +static void json_feat_perfc_id_list(struct json_object *r, struct nvme_perf_attr_id_list *data) +{ + int i; + int attri_vs; + char json_str[STR_LEN]; + struct json_object *paida = json_create_array(); + struct json_object *paide; + + obj_add_str(r, "attribute type", nvme_feature_perfc_attrtyp_to_string(data->attrtyp)); + obj_add_uint_02x(r, "ATTRTYP", data->attrtyp); + obj_add_int(r, "maximum saveable vendor specific performance attributes (MSVSPA)", + data->msvspa); + obj_add_int(r, "unused saveable vendor specific performance attributes (USVSPA)", + data->usvspa); + + obj_add_array(r, "performance attribute identifier list", paida); + for (i = 0; i < ARRAY_SIZE(data->id_list); i++) { + paide = json_create_object(); + array_add_obj(paida, paide); + attri_vs = i + NVME_FEAT_PERFC_ATTRI_VS_MIN; + sprintf(json_str, "performance attribute %02xh identifier (PA%02XHI)", attri_vs, + attri_vs); + obj_add_str(paide, json_str, util_uuid_to_string(data->id_list[i].id)); + } +} + +static void json_feat_perfc_vs(struct json_object *r, struct nvme_vs_perf_attr *data) +{ + obj_add_str(r, "performance attribute identifier (PAID)", util_uuid_to_string(data->paid)); + obj_add_uint(r, "attribute length (ATTRL)", data->attrl); + obj_d(r, "vendor specific (VS)", (unsigned char *)data->vs, data->attrl, 16, 1); +} + +static void json_feat_perfc(struct json_object *r, enum nvme_features_id fid, unsigned int result, + struct nvme_perf_characteristics *data) +{ + __u8 attri; + bool rvspa; + + nvme_feature_decode_perf_characteristics(result, &attri, &rvspa); + + obj_add_str(r, "attribute index", nvme_feature_perfc_attri_to_string(attri)); + obj_add_uint_02x(r, "ATTRI", attri); + + switch (attri) { + case NVME_FEAT_PERFC_ATTRI_STD: + json_feat_perfc_std(r, data->std_perf); + break; + case NVME_FEAT_PERFC_ATTRI_ID_LIST: + json_feat_perfc_id_list(r, data->id_list); + break; + case NVME_FEAT_PERFC_ATTRI_VS_MIN ... NVME_FEAT_PERFC_ATTRI_VS_MAX: + json_feat_perfc_vs(r, data->vs_perf); + break; + default: + break; + } +} + static void json_host_metadata(struct json_object *r, enum nvme_features_id fid, struct nvme_host_metadata *data) { @@ -3925,6 +3991,9 @@ static void json_feature_show_fields(enum nvme_features_id fid, unsigned int res case NVME_FEAT_FID_POWER_LOSS_SIGNAL: json_feature_show_fields_power_loss_signal(r, result); break; + case NVME_FEAT_FID_PERF_CHARACTERISTICS: + json_feat_perfc(r, fid, result, (struct nvme_perf_characteristics *)buf); + break; case NVME_FEAT_FID_ENH_CTRL_METADATA: case NVME_FEAT_FID_CTRL_METADATA: case NVME_FEAT_FID_NS_METADATA: diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 13bbad803f..aadd0f29bb 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -4882,6 +4882,66 @@ static void stdout_plm_config(struct nvme_plm_config *plmcfg) printf("\tDTWIN Time Threshold :%"PRIu64"\n", le64_to_cpu(plmcfg->dtwintt)); } +static void stdout_feat_perfc_std(struct nvme_std_perf_attr *data) +{ + printf("random 4 kib average read latency (R4KARL): %s (0x%02x)\n", + nvme_feature_perfc_r4karl_to_string(data->r4karl), data->r4karl); +} + +static void stdout_feat_perfc_id_list(struct nvme_perf_attr_id_list *data) +{ + int i; + int attri_vs; + + printf("attribute type (ATTRTYP): %s (0x%02x)\n", + nvme_feature_perfc_attrtyp_to_string(data->attrtyp), data->attrtyp); + printf("maximum saveable vendor specific performance attributes (MSVSPA): %d\n", + data->msvspa); + printf("unused saveable vendor specific performance attributes (USVSPA): %d\n", + data->usvspa); + + printf("performance attribute identifier list\n"); + for (i = 0; i < ARRAY_SIZE(data->id_list); i++) { + attri_vs = i + NVME_FEAT_PERFC_ATTRI_VS_MIN; + printf("performance attribute %02xh identifier (PA%02XHI): %s\n", attri_vs, + attri_vs, util_uuid_to_string(data->id_list[i].id)); + } +} + +static void stdout_feat_perfc_vs(struct nvme_vs_perf_attr *data) +{ + printf("performance attribute identifier (PAID): %s\n", util_uuid_to_string(data->paid)); + printf("attribute length (ATTRL): %u\n", data->attrl); + printf("vendor specific (VS):\n"); + d((unsigned char *)data->vs, data->attrl, 16, 1); +} + +static void stdout_feat_perfc(enum nvme_features_id fid, unsigned int result, + struct nvme_perf_characteristics *data) +{ + __u8 attri; + bool rvspa; + + nvme_feature_decode_perf_characteristics(result, &attri, &rvspa); + + printf("attribute index (ATTRI): %s (0x%02x)\n", nvme_feature_perfc_attri_to_string(attri), + attri); + + switch (attri) { + case NVME_FEAT_PERFC_ATTRI_STD: + stdout_feat_perfc_std(data->std_perf); + break; + case NVME_FEAT_PERFC_ATTRI_ID_LIST: + stdout_feat_perfc_id_list(data->id_list); + break; + case NVME_FEAT_PERFC_ATTRI_VS_MIN ... NVME_FEAT_PERFC_ATTRI_VS_MAX: + stdout_feat_perfc_vs(data->vs_perf); + break; + default: + break; + } +} + static void stdout_host_metadata(enum nvme_features_id fid, struct nvme_host_metadata *data) { @@ -5111,6 +5171,9 @@ static void stdout_feature_show_fields(enum nvme_features_id fid, printf("\tPower Loss Signaling Mode (PLSM): %s\n", nvme_pls_mode_to_string(NVME_GET(result, FEAT_PLS_MODE))); break; + case NVME_FEAT_FID_PERF_CHARACTERISTICS: + stdout_feat_perfc(fid, result, (struct nvme_perf_characteristics *)buf); + break; case NVME_FEAT_FID_ENH_CTRL_METADATA: case NVME_FEAT_FID_CTRL_METADATA: case NVME_FEAT_FID_NS_METADATA: diff --git a/nvme-print.c b/nvme-print.c index 0685cba5d9..102336d8fe 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -1096,6 +1096,96 @@ const char *nvme_feature_temp_sel_to_string(__u8 sel) } } +const char *nvme_feature_perfc_attri_to_string(__u8 attri) +{ + switch (attri) { + case NVME_FEAT_PERFC_ATTRI_STD: + return "standard performance attribute"; + case NVME_FEAT_PERFC_ATTRI_ID_LIST: + return "performance attribute identifier list"; + case NVME_FEAT_PERFC_ATTRI_VS_MIN ... NVME_FEAT_PERFC_ATTRI_VS_MAX: + return "vendor specific performance attribute"; + default: + break; + } + + return "reserved"; +} + +const char *nvme_feature_perfc_r4karl_to_string(__u8 r4karl) +{ + switch (r4karl) { + case NVME_FEAT_PERFC_R4KARL_NO_REPORT: + return "not reported"; + case NVME_FEAT_PERFC_R4KARL_GE_100_SEC: + return "greater than or equal to 100 seconds"; + case NVME_FEAT_PERFC_R4KARL_GE_50_SEC: + return "greater than or equal to 50 seconds and less than 100 seconds"; + case NVME_FEAT_PERFC_R4KARL_GE_10_SEC: + return "greater than or equal to 10 seconds and less than 50 seconds"; + case NVME_FEAT_PERFC_R4KARL_GE_5_SEC: + return "greater than or equal to 5 seconds and less than 10 seconds"; + case NVME_FEAT_PERFC_R4KARL_GE_1_SEC: + return "greater than or equal to 1 second and less than 5 seconds"; + case NVME_FEAT_PERFC_R4KARL_GE_500_MS: + return "greater than or equal to 500 milliseconds and less than 1 second"; + case NVME_FEAT_PERFC_R4KARL_GE_100_MS: + return "greater than or equal to 100 milliseconds and less than 500 milliseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_50_MS: + return "greater than or equal to 50 milliseconds and less than 100 milliseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_10_MS: + return "greater than or equal to 10 milliseconds and less than 50 milliseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_5_MS: + return "greater than or equal to 5 milliseconds and less than 10 milliseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_1_MS: + return "greater than or equal to 1 millisecond and less than 5 milliseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_500_US: + return "greater than or equal to 500 microseconds and less than 1 millisecond"; + case NVME_FEAT_PERFC_R4KARL_GE_100_US: + return "greater than or equal to 100 microseconds and less than 500 microseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_50_US: + return "greater than or equal to 50 microseconds and less than 100 microseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_10_US: + return "greater than or equal to 10 microseconds and less than 50 microseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_5_US: + return "greater than or equal to 5 microseconds and less than 10 microseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_1_US: + return "greater than or equal to 1 microsecond and less than 5 microseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_500_NS: + return "greater than or equal to 500 nanoseconds and less than 1 microsecond"; + case NVME_FEAT_PERFC_R4KARL_GE_100_NS: + return "greater than or equal to 100 nanoseconds and less than 500 nanoseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_50_NS: + return "greater than or equal to 50 nanoseconds and less than 100 nanoseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_10_NS: + return "greater than or equal to 10 nanoseconds and less than 50 nanoseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_5_NS: + return "greater than or equal to 5 nanoseconds and less than 10 nanoseconds"; + case NVME_FEAT_PERFC_R4KARL_GE_1_NS: + return "greater than or equal to 1 nanosecond and less than 5 nanoseconds"; + default: + break; + } + + return "reserved"; +} + +const char *nvme_feature_perfc_attrtyp_to_string(__u8 attrtyp) +{ + switch (attrtyp) { + case NVME_GET_FEATURES_SEL_CURRENT: + return "current attribute"; + case NVME_GET_FEATURES_SEL_DEFAULT: + return "default attribute"; + case NVME_GET_FEATURES_SEL_SAVED: + return "saved attribute"; + default: + break; + } + + return "reserved"; +} + const char *nvme_ns_wp_cfg_to_string(enum nvme_ns_write_protect_cfg state) { switch (state) { diff --git a/nvme-print.h b/nvme-print.h index 4d24d3e1aa..aff633c3ae 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -300,6 +300,9 @@ const char *nvme_feature_temp_sel_to_string(__u8 sel); const char *nvme_feature_temp_type_to_string(__u8 type); const char *nvme_feature_to_string(enum nvme_features_id feature); const char *nvme_feature_wl_hints_to_string(__u8 wh); +const char *nvme_feature_perfc_attri_to_string(__u8 attri); +const char *nvme_feature_perfc_r4karl_to_string(__u8 r4karl); +const char *nvme_feature_perfc_attrtyp_to_string(__u8 attrtyp); const char *nvme_host_metadata_type_to_string(enum nvme_features_id fid, __u8 type); const char *nvme_log_to_string(__u8 lid); const char *nvme_nss_hw_error_to_string(__u16 error_code); diff --git a/nvme.c b/nvme.c index 636ca82ecc..e054c864f0 100644 --- a/nvme.c +++ b/nvme.c @@ -4772,6 +4772,18 @@ static void get_feature_id_print(struct feat_cfg cfg, int err, __u32 result, } } +static bool is_get_feature_result_set(enum nvme_features_id feature_id) +{ + switch (feature_id) { + case NVME_FEAT_FID_PERF_CHARACTERISTICS: + return false; + default: + break; + } + + return true; +} + static int get_feature_id_changed(struct nvme_dev *dev, struct feat_cfg cfg, nvme_print_flags_t flags) { @@ -4792,6 +4804,9 @@ static int get_feature_id_changed(struct nvme_dev *dev, struct feat_cfg cfg, err_def = get_feature_id(dev, &cfg, &buf_def, &result_def); } + if (!err && !is_get_feature_result_set(cfg.feature_id)) + result = cfg.cdw11; + if (err || !cfg.changed || err_def || result != result_def || (buf && buf_def && !strcmp(buf, buf_def))) get_feature_id_print(cfg, err, result, buf, flags); diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 7f1354587f..2330c18372 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include "nvme.h" #include "plugin.h" #include "nvme-print.h" @@ -6,9 +8,22 @@ #define CREATE_CMD #include "feat-nvme.h" +struct perfc_config { + __u32 namespace_id; + __u8 attri; + bool rvspa; + __u8 r4karl; + char *paid; + __u16 attrl; + char *vs_data; + bool save; + __u8 sel; +}; + static const char *power_mgmt_feat = "power management feature"; static const char *sel = "[0-3]: current/default/saved/supported"; static const char *save = "Specifies that the controller shall save the attribute"; +static const char *perfc_feat = "performance characteristics feature"; static int power_mgmt_get(struct nvme_dev *dev, const __u8 fid, __u8 sel) { @@ -106,3 +121,140 @@ static int feat_power_mgmt(int argc, char **argv, struct command *cmd, struct pl return err; } + +static int perfc_get(struct nvme_dev *dev, struct perfc_config *cfg) +{ + __u32 result; + int err; + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = NVME_FEAT_FID_PERF_CHARACTERISTICS, + .cdw11 = NVME_SET(cfg->attri, FEAT_PERFC_ATTRI) | + NVME_SET(cfg->rvspa, FEAT_PERFC_RVSPA), + .sel = cfg->sel, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + if (NVME_CHECK(args.sel, GET_FEATURES_SEL, SUPPORTED)) + nvme_show_select_result(args.fid, result); + else + nvme_feature_show_fields(args.fid, result, NULL); + } else { + nvme_show_error("Get %s", perfc_feat); + } + + return err; +} + +static int perfc_set(struct nvme_dev *dev, struct perfc_config *cfg) +{ + __u32 result; + int err; + + _cleanup_fd_ int ffd = STDIN_FILENO; + + struct nvme_perf_characteristics data = { + .attr_buf = { 0 }, + }; + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = NVME_FEAT_FID_PERF_CHARACTERISTICS, + .cdw11 = NVME_SET(cfg->attri, FEAT_PERFC_ATTRI) | + NVME_SET(cfg->rvspa, FEAT_PERFC_RVSPA), + .save = save, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + .data = &data, + .data_len = sizeof(data), + }; + + switch (cfg->attri) { + case NVME_FEAT_PERFC_ATTRI_STD: + data.std_perf->r4karl = cfg->r4karl; + break; + case NVME_FEAT_PERFC_ATTRI_VS_MIN ... NVME_FEAT_PERFC_ATTRI_VS_MAX: + nvme_uuid_from_string(cfg->paid, data.vs_perf->paid); + data.vs_perf->attrl = cfg->attrl; + if (data.vs_perf->attrl && strlen(cfg->vs_data)) { + ffd = open(cfg->vs_data, O_RDONLY); + if (ffd < 0) { + nvme_show_error("Failed to open file %s: %s", cfg->vs_data, + strerror(errno)); + return -EINVAL; + } + err = read(ffd, data.vs_perf->vs, data.vs_perf->attrl); + if (err < 0) { + nvme_show_error("failed to read data buffer from input file: %s", + strerror(errno)); + return -errno; + } + } + break; + default: + break; + } + + err = nvme_set_features(&args); + + nvme_show_init(); + + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Set %s", perfc_feat); + } else { + nvme_show_result("Set %s: 0x%04x (%s)", perfc_feat, args.cdw11, + save ? "Save" : "Not save"); + nvme_feature_show_fields(args.fid, args.cdw11, NULL); + } + + nvme_show_finish(); + + return err; +} + +static int feat_perfc(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *namespace_id_optional = "optional namespace attached to controller"; + const char *attri = "attribute index"; + const char *rvspa = "revert vendor specific performance attribute"; + const char *r4karl = "random 4 kib average read latency"; + const char *paid = "performance attribute identifier"; + const char *attrl = "attribute length"; + const char *vs_data = "vendor specific data"; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + int err; + + struct perfc_config cfg = { 0 }; + + NVME_ARGS(opts, + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id_optional), + OPT_BYTE("attri", 'a', &cfg.attri, attri), + OPT_FLAG("rvspa", 'r', &cfg.rvspa, rvspa), + OPT_BYTE("r4karl", 'R', &cfg.r4karl, r4karl), + OPT_STR("paid", 'p', &cfg.paid, paid), + OPT_SHRT("attrl", 'A', &cfg.attrl, attrl), + OPT_FILE("vs-data", 'V', &cfg.vs_data, vs_data), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_BYTE("sel", 'S', &cfg.sel, sel)); + + err = parse_and_open(&dev, argc, argv, PERFC_DESC, opts); + if (err) + return err; + + if (argconfig_parse_seen(opts, "rvspa") || argconfig_parse_seen(opts, "r4karl") || + argconfig_parse_seen(opts, "paid")) + err = perfc_set(dev, &cfg); + else + err = perfc_get(dev, &cfg); + + return err; +} diff --git a/plugins/feat/feat-nvme.h b/plugins/feat/feat-nvme.h index 6277300ebc..1e2b778c33 100644 --- a/plugins/feat/feat-nvme.h +++ b/plugins/feat/feat-nvme.h @@ -11,10 +11,12 @@ #define FEAT_PLUGIN_VERSION "1.0" #define POWER_MGMT_DESC "Get and set power management feature" +#define PERFC_DESC "Get and set perf characteristics feature" PLUGIN(NAME("feat", "NVMe feature extensions", FEAT_PLUGIN_VERSION), COMMAND_LIST( ENTRY("power-mgmt", POWER_MGMT_DESC, feat_power_mgmt) + ENTRY("perf-characteristics", PERFC_DESC, feat_perfc) ) ); #endif /* !FEAT_NVME || CMD_HEADER_MULTI_READ */ diff --git a/subprojects/libnvme.wrap b/subprojects/libnvme.wrap index 860a7e4278..462ed85785 100644 --- a/subprojects/libnvme.wrap +++ b/subprojects/libnvme.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/linux-nvme/libnvme.git -revision = 4727ac4fd97e893c74286281ecc997e44b7fa4b9 +revision = ef4df90bf674abb9ee5cd7560c5e92c8705e2c3b [provide] libnvme = libnvme_dep