diff --git a/nvme-print-json.c b/nvme-print-json.c index 4ac24146a9..775bbc0031 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -119,12 +119,9 @@ static void obj_add_result(struct json_object *o, const char *v, ...) va_start(ap, v); if (vasprintf(&value, v, ap) < 0) - value = NULL; + value = alloc_error; - if (value) - obj_add_str(o, "Result", value); - else - obj_add_str(o, "Result", "Could not allocate string"); + obj_add_str(o, "Result", value); va_end(ap); } @@ -138,12 +135,9 @@ static void obj_add_key(struct json_object *o, const char *k, const char *v, ... va_start(ap, v); if (vasprintf(&value, v, ap) < 0) - value = NULL; + value = alloc_error; - if (value) - obj_add_str(o, k, value); - else - obj_add_str(o, k, "Could not allocate string"); + obj_add_str(o, k, value); va_end(ap); } @@ -4588,9 +4582,9 @@ static void json_output_error_status(int status, const char *msg, va_list ap) _cleanup_free_ char *value = NULL; if (vasprintf(&value, msg, ap) < 0) - value = NULL; + value = alloc_error; - sprintf(json_str, "Error: %s", value ? value : "Could not allocate string"); + sprintf(json_str, "Error: %s", value); r = obj_create(json_str); if (status < 0) { @@ -4628,26 +4622,23 @@ static void json_output_message(bool error, const char *msg, va_list ap) _cleanup_free_ char *value = NULL; if (vasprintf(&value, msg, ap) < 0) - value = NULL; + value = alloc_error; - obj_add_str(r, error ? "error" : "result", value ? value : "Could not allocate string"); + obj_add_str(r, error ? "error" : "result", value); obj_print(r); } -static void json_output_perror(const char *msg) +static void json_output_perror(const char *msg, va_list ap) { struct json_object *r = json_create_object(); _cleanup_free_ char *error = NULL; - if (asprintf(&error, "%s: %s", msg, strerror(errno)) < 0) - error = NULL; + if (vasprintf(&error, msg, ap) < 0) + error = alloc_error; - if (error) - obj_add_str(r, "error", error); - else - obj_add_str(r, "error", "Could not allocate string"); + obj_add_key(r, "error", "%s: %s", error, strerror(errno)); json_output_object(r); } diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index e87c605db4..de6497782b 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -5604,9 +5604,14 @@ static void stdout_message(bool error, const char *msg, va_list ap) fprintf(error ? stderr : stdout, "\n"); } -static void stdout_perror(const char *msg) +static void stdout_perror(const char *msg, va_list ap) { - perror(msg); + _cleanup_free_ char *error = NULL; + + if (vasprintf(&error, msg, ap) < 0) + error = alloc_error; + + perror(error); } static void stdout_discovery_log(struct nvmf_discovery_log *log, int numrec) diff --git a/nvme-print.c b/nvme-print.c index 8492225e7f..0685cba5d9 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -26,6 +26,8 @@ #define nvme_print_output_format(name, ...) \ nvme_print(name, nvme_is_output_format_json() ? JSON : NORMAL, ##__VA_ARGS__); +char *alloc_error = "Could not allocate string"; + static struct print_ops *nvme_print_ops(nvme_print_flags_t flags) { struct print_ops *ops = NULL; @@ -1460,15 +1462,20 @@ void nvme_show_message(bool error, const char *msg, ...) va_end(ap); } -void nvme_show_perror(const char *msg) +void nvme_show_perror(const char *msg, ...) { struct print_ops *ops = nvme_print_ops(NORMAL); + va_list ap; + + va_start(ap, msg); if (nvme_is_output_format_json()) ops = nvme_print_ops(JSON); if (ops && ops->show_perror) - ops->show_perror(msg); + ops->show_perror(msg, ap); + + va_end(ap); } void nvme_show_discovery_log(struct nvmf_discovery_log *log, uint64_t numrec, diff --git a/nvme-print.h b/nvme-print.h index ad5d4c829b..4d24d3e1aa 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -108,7 +108,7 @@ struct print_ops { /* status and error messages */ void (*connect_msg)(nvme_ctrl_t c); void (*show_message)(bool error, const char *msg, va_list ap); - void (*show_perror)(const char *msg); + void (*show_perror)(const char *msg, va_list ap); void (*show_status)(int status); void (*show_error_status)(int status, const char *msg, va_list ap); @@ -326,7 +326,7 @@ const char *nvme_pls_mode_to_string(__u8 mode); void nvme_dev_full_path(nvme_ns_t n, char *path, size_t len); void nvme_generic_full_path(nvme_ns_t n, char *path, size_t len); void nvme_show_message(bool error, const char *msg, ...); -void nvme_show_perror(const char *msg); +void nvme_show_perror(const char *msg, ...); void nvme_show_error_status(int status, const char *msg, ...); void nvme_show_init(void); void nvme_show_finish(void); @@ -352,4 +352,6 @@ void nvme_show_host_discovery_log(struct nvme_host_discover_log *log, nvme_print void nvme_show_ave_discovery_log(struct nvme_ave_discover_log *log, nvme_print_flags_t flags); void nvme_show_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, nvme_print_flags_t flags); + +extern char *alloc_error; #endif /* NVME_PRINT_H */ diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c new file mode 100644 index 0000000000..7f1354587f --- /dev/null +++ b/plugins/feat/feat-nvme.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "nvme.h" +#include "plugin.h" +#include "nvme-print.h" + +#define CREATE_CMD +#include "feat-nvme.h" + +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 int power_mgmt_get(struct nvme_dev *dev, const __u8 fid, __u8 sel) +{ + __u32 result; + int err; + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .sel = sel, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + if (NVME_CHECK(sel, GET_FEATURES_SEL, SUPPORTED)) + nvme_show_select_result(fid, result); + else + nvme_feature_show_fields(fid, result, NULL); + } else { + nvme_show_error("Get %s", power_mgmt_feat); + } + + return err; +} + +static int power_mgmt_set(struct nvme_dev *dev, const __u8 fid, __u8 ps, __u8 wh, bool save) +{ + __u32 result; + int err; + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .cdw11 = NVME_SET(ps, FEAT_PWRMGMT_PS) | NVME_SET(wh, FEAT_PWRMGMT_WH), + .save = save, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + + nvme_show_init(); + + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Set %s", power_mgmt_feat); + } else { + nvme_show_result("Set %s: 0x%04x (%s)", power_mgmt_feat, args.cdw11, + save ? "Save" : "Not save"); + nvme_feature_show_fields(fid, args.cdw11, NULL); + } + + nvme_show_finish(); + + return err; +} + +static int feat_power_mgmt(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *ps = "power state"; + const char *wh = "workload hint"; + const __u8 fid = NVME_FEAT_FID_POWER_MGMT; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + int err; + + struct config { + __u8 ps; + __u8 wh; + bool save; + __u8 sel; + }; + + struct config cfg = { 0 }; + + NVME_ARGS(opts, + OPT_BYTE("ps", 'p', &cfg.ps, ps), + OPT_BYTE("wh", 'w', &cfg.wh, wh), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_BYTE("sel", 'S', &cfg.sel, sel)); + + err = parse_and_open(&dev, argc, argv, POWER_MGMT_DESC, opts); + if (err) + return err; + + if (argconfig_parse_seen(opts, "ps")) + err = power_mgmt_set(dev, fid, cfg.ps, cfg.wh, cfg.save); + else + err = power_mgmt_get(dev, fid, cfg.sel); + + return err; +} diff --git a/plugins/feat/feat-nvme.h b/plugins/feat/feat-nvme.h new file mode 100644 index 0000000000..6277300ebc --- /dev/null +++ b/plugins/feat/feat-nvme.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "cmd.h" + +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/feat/feat-nvme + +#include "define_cmd.h" + +#if !defined(FEAT_NVME) || defined(CMD_HEADER_MULTI_READ) +#define FEAT_NVME + +#define FEAT_PLUGIN_VERSION "1.0" +#define POWER_MGMT_DESC "Get and set power management feature" + +PLUGIN(NAME("feat", "NVMe feature extensions", FEAT_PLUGIN_VERSION), + COMMAND_LIST( + ENTRY("power-mgmt", POWER_MGMT_DESC, feat_power_mgmt) + ) +); +#endif /* !FEAT_NVME || CMD_HEADER_MULTI_READ */ + +#ifndef FEAT_NVME_H +#define FEAT_NVME_H + +#endif /* FEAT_NVME_H */ diff --git a/plugins/feat/meson.build b/plugins/feat/meson.build new file mode 100644 index 0000000000..588b5d23c5 --- /dev/null +++ b/plugins/feat/meson.build @@ -0,0 +1,3 @@ +sources += [ + 'plugins/feat/feat-nvme.c', +] diff --git a/plugins/meson.build b/plugins/meson.build index e0ac31404a..ebefedcb7a 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -30,6 +30,7 @@ sources += [ 'plugins/zns/zns.c', ] +subdir('feat') subdir('lm') subdir('ocp')