diff options
Diffstat (limited to '')
-rw-r--r-- | nvme.c | 1427 |
1 files changed, 1143 insertions, 284 deletions
@@ -47,7 +47,6 @@ #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> -#include <sys/time.h> #include "common.h" #include "nvme-print.h" @@ -85,7 +84,17 @@ static struct program nvme = { .extensions = &builtin, }; -static const char *output_format = "Output format: normal|json|binary"; +static __u16 nvme_feat_buf_len[0x100] = { + [NVME_FEAT_LBA_RANGE] = 4096, + [NVME_FEAT_AUTO_PST] = 256, + [NVME_FEAT_HOST_MEM_BUF] = 4096, + [NVME_FEAT_HOST_ID] = 8, + [NVME_FEAT_PLM_CONFIG] = 512, + [NVME_FEAT_TIMESTAMP] = 8, + [NVME_FEAT_HOST_BEHAVIOR] = 512 +}; + +const char *output_format = "Output format: normal|json|binary"; static const char *output_format_no_binary = "Output format: normal|json"; static void *__nvme_alloc(size_t len, bool *huge) @@ -100,10 +109,10 @@ static void *__nvme_alloc(size_t len, bool *huge) return NULL; } -#ifdef LIBHUGETLBFS #define HUGE_MIN 0x80000 -static void nvme_free(void *p, bool huge) +#ifdef LIBHUGETLBFS +void nvme_free(void *p, bool huge) { if (huge) free_hugepage_region(p); @@ -111,7 +120,7 @@ static void nvme_free(void *p, bool huge) free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { void *p; @@ -126,17 +135,27 @@ static void *nvme_alloc(size_t len, bool *huge) return p; } #else -static void nvme_free(void *p, bool huge) +void nvme_free(void *p, bool huge) { free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { return __nvme_alloc(len, huge); } #endif +static bool is_chardev(void) +{ + return S_ISCHR(nvme_stat.st_mode); +} + +static bool is_blkdev(void) +{ + return S_ISBLK(nvme_stat.st_mode); +} + static int open_dev(char *dev) { int err, fd; @@ -152,7 +171,7 @@ static int open_dev(char *dev) close(fd); goto perror; } - if (!S_ISCHR(nvme_stat.st_mode) && !S_ISBLK(nvme_stat.st_mode)) { + if (!is_chardev() && !is_blkdev()) { fprintf(stderr, "%s is not a block or character device\n", dev); close(fd); return -ENODEV; @@ -200,7 +219,7 @@ int parse_and_open(int argc, char **argv, const char *desc, return ret; } -enum nvme_print_flags validate_output_format(char *format) +enum nvme_print_flags validate_output_format(const char *format) { if (!format) return -EINVAL; @@ -386,8 +405,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct hdr = malloc(bs); page_log = malloc(bs); if (!hdr || !page_log) { - fprintf(stderr, "Failed to allocate %zu bytes for log: %s\n", - bs, strerror(errno)); + perror("failed to allocate buf for log\n"); err = -ENOMEM; goto free_mem; } @@ -623,7 +641,7 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug cfg.log_entries = min(cfg.log_entries, ctrl.elpe + 1); err_log = calloc(cfg.log_entries, sizeof(struct nvme_error_log_page)); if (!err_log) { - fprintf(stderr, "could not alloc buffer for error log\n"); + perror("could not alloc buffer for error log\n"); err = -ENOMEM; goto close_fd; } @@ -738,6 +756,470 @@ ret: return nvme_status_to_errno(err, false); } +static int get_pred_lat_per_nvmset_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Predictable latency per nvm set log "\ + "page and prints it for the given device in either decoded " \ + "format(default),json or binary."; + const char *nvmset_id = "NVM Set Identifier"; + const char *raw = "use binary output"; + struct nvme_predlat_per_nvmset_log_page plpns_log; + enum nvme_print_flags flags; + int err, fd; + + struct config { + __u16 nvmset_id; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .nvmset_id = 1, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + err = nvme_predictable_latency_per_nvmset_log(fd, + cfg.nvmset_id, &plpns_log); + if (!err) + nvme_show_predictable_latency_per_nvmset(&plpns_log, + cfg.nvmset_id, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("predictable latency per nvm set"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_pred_lat_event_agg_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Predictable Latency Event" \ + "Aggregate Log page and prints it, for the given" \ + "device in either decoded format(default)," \ + "json or binary."; + const char *log_entries = "Number of pending NVM Set" \ + "log Entries list"; + const char *rae = "Retain an Asynchronous Event"; + const char *raw = "use binary output"; + void *pea_log; + struct nvme_id_ctrl ctrl; + enum nvme_print_flags flags; + int err, fd; + __u32 log_size; + + struct config { + __u64 log_entries; + bool rae; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .log_entries = 2044, + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + if (!cfg.log_entries) { + fprintf(stderr, "non-zero log-entries is required param\n"); + err = -EINVAL; + goto close_fd; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err < 0) { + perror("identify controller"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + cfg.log_entries = min(cfg.log_entries, le32_to_cpu(ctrl.nsetidmax)); + log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16); + pea_log = calloc(log_size, 1); + if (!pea_log) { + perror("could not alloc buffer for predictable " \ + "latency event agggregate log entries\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_predictable_latency_event_agg_log(fd, pea_log, cfg.rae, + log_size); + if (!err) + nvme_show_predictable_latency_event_agg_log(pea_log, cfg.log_entries, + log_size, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("predictable latency event gggregate log page"); + free(pea_log); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_persistent_event_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Persistent Event log info for"\ + " the given device in either decoded format(default),"\ + " json or binary."; + const char *action = "action the controller shall take during"\ + " processing this persistent log page command."; + const char *log_len = "number of bytes to retrieve"; + const char *raw = "use binary output"; + void *pevent_log_info; + struct nvme_persistent_event_log_head *pevent_log_head; + enum nvme_print_flags flags; + int err, fd; + bool huge; + + struct config { + __u8 action; + __u32 log_len; + int raw_binary; + char *output_format; + }; + + struct config cfg = { + .action = 0xff, + .log_len = 0, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + pevent_log_head = calloc(sizeof(*pevent_log_head), 1); + if (!pevent_log_head) { + perror("could not alloc buffer for persistent " \ + "event log header\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_persistent_event_log(fd, cfg.action, + sizeof(*pevent_log_head), pevent_log_head); + if (err < 0) { + perror("persistent event log"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) { + printf("Releasing Persistent Event Log Context\n"); + goto close_fd; + } + + if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { + cfg.log_len = le64_to_cpu(pevent_log_head->tll); + } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { + printf("Establishing Persistent Event Log Context\n"); + goto close_fd; + } + + /* + * if header aleady read with context establish action 0x1, + * action shall not be 0x1 again in the subsequent request, + * until the current context is released by issuing action + * with 0x2, otherwise throws command sequence error, make + * it as zero to read the log page + */ + if (cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) + cfg.action = NVME_PEVENT_LOG_READ; + + pevent_log_info = nvme_alloc(cfg.log_len, &huge); + if (!pevent_log_info) { + perror("could not alloc buffer for persistent event log page\n"); + err = -ENOMEM; + goto close_fd; + } + err = nvme_persistent_event_log(fd, cfg.action, + cfg.log_len, pevent_log_info); + if (!err) + nvme_show_persistent_event_log(pevent_log_info, cfg.action, + cfg.log_len, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("persistent event log"); + + nvme_free(pevent_log_info, huge); + +close_fd: + free(pevent_log_head); + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_endurance_event_agg_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Retrieve Predictable Latency " \ + "Event Aggregate page and prints it, for the given " \ + "device in either decoded format(default), " \ + "json or binary."; + const char *log_entries = "Number of pending Endurance Group " \ + "Event log Entries list"; + const char *rae = "Retain an Asynchronous Event"; + const char *raw = "use binary output"; + void *endurance_log; + struct nvme_id_ctrl ctrl; + enum nvme_print_flags flags; + int err, fd; + __u32 log_size; + + struct config { + __u64 log_entries; + bool rae; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .log_entries = 2044, + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + if (!cfg.log_entries) { + fprintf(stderr, "non-zero log-entries is required param\n"); + err = -EINVAL; + goto close_fd; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err < 0) { + perror("identify controller"); + goto close_fd; + } else if (err) { + fprintf(stderr, "could not identify controller\n"); + err = -ENODEV; + goto close_fd; + } + + cfg.log_entries = min(cfg.log_entries, le16_to_cpu(ctrl.endgidmax)); + log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16); + endurance_log = calloc(log_size, 1); + if (!endurance_log) { + perror("could not alloc buffer for endurance group" \ + " event agggregate log entries\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_endurance_group_event_agg_log(fd, endurance_log, cfg.rae, + log_size); + if (!err) + nvme_show_endurance_group_event_agg_log(endurance_log, cfg.log_entries, + log_size, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("endurance group event aggregate log page"); + free(endurance_log); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_lba_status_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Get LBA Status Info Log " \ + "and prints it, for the given device in either " \ + "decoded format(default),json or binary."; + const char *rae = "Retain an Asynchronous Event"; + void *lab_status; + enum nvme_print_flags flags; + int err, fd; + __u32 lslplen; + + struct config { + bool rae; + char *output_format; + }; + + struct config cfg = { + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_lba_status_log(fd, &lslplen, true, sizeof(__u32)); + if (err < 0) { + perror("lba status log page"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + lab_status = calloc(lslplen, 1); + if (!lab_status) { + perror("could not alloc buffer for lba status log"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_lba_status_log(fd, lab_status, cfg.rae, lslplen); + if (!err) + nvme_show_lba_status_log(lab_status, lslplen, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("lba status log page"); + free(lab_status); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_resv_notif_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + + const char *desc = "Retrieve Reservation Notification " \ + "log page and prints it, for the given " \ + "device in either decoded format(default), " \ + "json or binary."; + struct nvme_resv_notif_log resv; + enum nvme_print_flags flags; + int err, fd; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_resv_notif_log(fd, &resv); + if (!err) + nvme_show_resv_notif_log(&resv, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("resv notifi log"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); + +} + static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve desired number of bytes "\ @@ -756,7 +1238,7 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl struct config { __u32 namespace_id; - __u32 log_id; + __u8 log_id; __u32 log_len; __u32 aen; __u64 lpo; @@ -768,7 +1250,7 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl struct config cfg = { .namespace_id = NVME_NSID_ALL, - .log_id = 0xffffffff, + .log_id = 0xff, .log_len = 0, .lpo = NVME_NO_LOG_LPO, .lsp = NVME_NO_LOG_LSP, @@ -798,12 +1280,6 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl cfg.log_id = (cfg.aen >> 16) & 0xff; } - if (cfg.log_id > 0xff) { - fprintf(stderr, "Invalid log identifier: %d. Valid range: 0-255\n", cfg.log_id); - err = -EINVAL; - goto close_fd; - } - if (!cfg.log_len) { fprintf(stderr, "non-zero log-len is required param\n"); err = -EINVAL; @@ -812,9 +1288,8 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl log = malloc(cfg.log_len); if (!log) { - fprintf(stderr, "could not alloc buffer for log: %s\n", - strerror(errno)); - err = -EINVAL; + perror("could not alloc buffer for log\n"); + err = -ENOMEM; goto close_fd; } @@ -844,6 +1319,7 @@ ret: static int sanitize_log(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve sanitize log and show it."; + const char *rae = "Retain an Asynchronous Event"; const char *raw = "show log in binary format"; const char *human_readable = "show log in readable format"; struct nvme_sanitize_log_page sanitize_log; @@ -851,16 +1327,19 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p int fd, err; struct config { + bool rae; int raw_binary; int human_readable; char *output_format; }; struct config cfg = { + .rae = false, .output_format = "normal", }; OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), @@ -879,7 +1358,7 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p if (cfg.human_readable) flags |= VERBOSE; - err = nvme_sanitize_log(fd, &sanitize_log); + err = nvme_sanitize_log(fd, cfg.rae, &sanitize_log); if (!err) nvme_show_sanitize_log(&sanitize_log, devicename, flags); else if (err > 0) @@ -932,8 +1411,7 @@ static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin * for (i = 0; i < (min(num, 2048)); i++) printf("[%4u]:%#x\n", i, le16_to_cpu(cntlist->identifier[i])); - } - else if (err > 0) + } else if (err > 0) nvme_show_status(err); else perror("id controller list"); @@ -950,6 +1428,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl const char *desc = "For the specified controller handle, show the "\ "namespace list in the associated NVMe subsystem, optionally starting with a given nsid."; const char *namespace_id = "first nsid returned list should start from"; + const char *csi = "I/O command set identifier"; const char *all = "show all namespaces in the subsystem, whether attached or inactive"; int err, i, fd; __le32 ns_list[1024]; @@ -957,6 +1436,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl struct config { __u32 namespace_id; int all; + __u16 csi; }; struct config cfg = { @@ -965,6 +1445,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_BYTE("csi", 'y', &cfg.csi, csi), OPT_FLAG("all", 'a', &cfg.all, all), OPT_END() }; @@ -979,8 +1460,8 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl goto close_fd; } - err = nvme_identify_ns_list(fd, cfg.namespace_id - 1, !!cfg.all, - ns_list); + err = nvme_identify_ns_list_csi(fd, cfg.namespace_id - 1, cfg.csi, + !!cfg.all, ns_list); if (!err) { for (i = 0; i < 1024; i++) if (ns_list[i]) @@ -1028,13 +1509,9 @@ static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin * goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id == 0) { - err = -EINVAL; - goto close_fd; - } - else if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1145,15 +1622,18 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * "parameters. The next available namespace ID is used for the "\ "create operation. Note that create-ns does not attach the "\ "namespace to a controller, the attach-ns command is needed."; - const char *nsze = "size of ns"; - const char *ncap = "capacity of ns"; - const char *flbas = "FLBA size"; - const char *dps = "data protection capabilities"; - const char *nmic = "multipath and sharing capabilities"; - const char *anagrpid = "ANA Group Identifier"; - const char *nvmsetid = "NVM Set Identifier"; + const char *nsze = "size of ns (NSZE)"; + const char *ncap = "capacity of ns (NCAP)"; + const char *flbas = "Formatted LBA size (FLBAS), if entering this "\ + "value ignore \'block-size\' field"; + const char *dps = "data protection settings (DPS)"; + const char *nmic = "multipath and sharing capabilities (NMIC)"; + const char *anagrpid = "ANA Group Identifier (ANAGRPID)"; + const char *nvmsetid = "NVM Set Identifier (NVMSETID)"; + const char *csi = "command set identifier (CSI)"; const char *timeout = "timeout value, in milliseconds"; - const char *bs = "target block size"; + const char *bs = "target block size, specify only if \'FLBAS\' "\ + "value not entered"; int err = 0, fd, i; struct nvme_id_ns ns; @@ -1169,6 +1649,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * __u16 nvmsetid; __u64 bs; __u32 timeout; + __u8 csi; }; struct config cfg = { @@ -1189,6 +1670,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * OPT_UINT("nvmset-id", 'i', &cfg.nvmsetid, nvmsetid), OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_BYTE("csi", 'y', &cfg.csi, csi), OPT_END() }; @@ -1240,7 +1722,8 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * } err = nvme_ns_create(fd, cfg.nsze, cfg.ncap, cfg.flbas, cfg.dps, cfg.nmic, - cfg.anagrpid, cfg.nvmsetid, cfg.timeout, &nsid); + cfg.anagrpid, cfg.nvmsetid, cfg.csi, cfg.timeout, + &nsid); if (!err) printf("%s: Success, created nsid:%d\n", cmd->name, nsid); else if (err > 0) @@ -1263,7 +1746,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, const char *desc = "Retrieve information for subsystems"; const char *verbose = "Increase output verbosity"; __u32 ns_instance = 0; - int err; + int err, nsid = 0; struct config { char *output_format; @@ -1288,7 +1771,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, devicename = NULL; if (optind < argc) { char path[512]; - int id; + int id, fd; devicename = basename(argv[optind]); if (sscanf(devicename, "nvme%dn%d", &id, &ns_instance) != 2) { @@ -1297,6 +1780,22 @@ static int list_subsys(int argc, char **argv, struct command *cmd, err = -EINVAL; goto ret; } + sprintf(path, "/dev/%s", devicename); + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot read nsid from %s\n", + devicename); + err = -EINVAL; + goto ret; + } + nsid = nvme_get_nsid(fd); + close(fd); + if (nsid < 0) { + fprintf(stderr, "Cannot read nsid from %s\n", + devicename); + err = -EINVAL; + goto ret; + } sprintf(path, "/sys/block/%s/device", devicename); subsysnqn = get_nvme_subsnqn(path); if (!subsysnqn) { @@ -1318,7 +1817,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, subsysnqn, ns_instance); + err = scan_subsystems(&t, subsysnqn, ns_instance, nsid, NULL); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); goto free; @@ -1326,8 +1825,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, nvme_show_subsystem_list(&t, flags); free: free_topology(&t); - if (subsysnqn) - free(subsysnqn); + free(subsysnqn); ret: return nvme_status_to_errno(err, false); } @@ -1335,22 +1833,26 @@ ret: static int list(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve basic information for all NVMe namespaces"; + const char *device_dir = "Additional directory to search for devices"; const char *verbose = "Increase output verbosity"; struct nvme_topology t = { }; enum nvme_print_flags flags; int err = 0; struct config { + char *device_dir; char *output_format; int verbose; }; struct config cfg = { + .device_dir = NULL, .output_format = "normal", .verbose = 0, }; OPT_ARGS(opts) = { + OPT_STRING("directory", 'd', "DIR", &cfg.device_dir, device_dir), OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -1370,7 +1872,7 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, NULL, 0); + err = scan_subsystems(&t, NULL, 0, 0, cfg.device_dir); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); return err; @@ -1447,6 +1949,50 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl return __id_ctrl(argc, argv, cmd, plugin, NULL); } +static int nvm_id_ctrl(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Send an Identify Controller NVM Command Set "\ + "command to the given device and report information about "\ + "the specified controller in various formats."; + enum nvme_print_flags flags; + struct nvme_id_ctrl_nvm ctrl_nvm; + int fd, err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_identify_ctrl_nvm(fd, &ctrl_nvm); + if (!err) + nvme_show_id_ctrl_nvm(&ctrl_nvm, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("nvm identify controller"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Send Namespace Identification Descriptors command to the "\ @@ -1487,9 +2033,9 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p flags = BINARY; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1569,15 +2115,9 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug flags |= VERBOSE; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; - goto close_fd; - } - else if (!cfg.namespace_id) { - fprintf(stderr, - "Error: requesting namespace-id from non-block device\n"); - err = -ENOTBLK; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1749,6 +2289,48 @@ close_fd: return err; } +static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an Identify Command Set Data command to the "\ + "given device, returns properties of the specified controller "\ + "in either human-readable or binary format."; + const char *controller_id = "identifier of desired controller"; + struct nvme_id_iocs iocs; + int err, fd; + + struct config { + __u16 cntid; + }; + + struct config cfg = { + .cntid = 0xffff, + }; + + OPT_ARGS(opts) = { + OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + err = fd; + goto ret; + } + + err = nvme_identify_iocs(fd, cfg.cntid, &iocs); + if (!err) { + printf("NVMe Identify I/O Command Set:\n"); + nvme_show_id_iocs(&iocs); + } else if (err > 0) + nvme_show_status(err); + else + perror("NVMe Identify I/O Command Set"); + + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = 0, nsid, fd; @@ -1795,29 +2377,27 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi "9h: Secondary Online"; const char *nr = "Number of Controller Resources(NR)"; int fd, err; - __u32 result; + __u32 result, cdw10; struct config { - int cntlid; - int rt; - int act; - __u32 cdw10; - __u32 cdw11; + __u16 cntlid; + __u8 rt; + __u8 act; + __u16 nr; }; struct config cfg = { .cntlid = 0, .rt = 0, .act = 0, - .cdw10 = 0, - .cdw11 = 0, + .nr = 0, }; OPT_ARGS(opts) = { OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid), OPT_UINT("rt", 'r', &cfg.rt, rt), OPT_UINT("act", 'a', &cfg.act, act), - OPT_UINT("nr", 'n', &cfg.cdw11, nr), + OPT_UINT("nr", 'n', &cfg.nr, nr), OPT_END() }; @@ -1825,13 +2405,12 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi if (fd < 0) goto ret; - cfg.cdw10 = cfg.cntlid << 16; - cfg.cdw10 = cfg.cdw10 | (cfg.rt << 8); - cfg.cdw10 = cfg.cdw10 | cfg.act; + cdw10 = cfg.act | (cfg.rt << 8) | (cfg.cntlid << 16); - err = nvme_virtual_mgmt(fd, cfg.cdw10, cfg.cdw11, &result); + err = nvme_virtual_mgmt(fd, cdw10, cfg.nr, &result); if (!err) { - printf("success, Number of Resources allocated:%#x\n", result); + printf("success, Number of Controller Resources Modified "\ + "(NRM):%#x\n", result); } else if (err > 0) { nvme_show_status(err); } else @@ -1842,6 +2421,55 @@ ret: return nvme_status_to_errno(err, false); } +static int primary_ctrl_caps(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an Identify Primary Controller Capabilities "\ + "command to the given device and report the information in a "\ + "decoded format (default), json or binary."; + const char *human_readable = "show info in readable format"; + struct nvme_primary_ctrl_caps caps; + + int err, fd; + enum nvme_print_flags flags; + + struct config { + char *output_format; + int human_readable; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.human_readable) + flags |= VERBOSE; + + err = nvme_identify_primary_ctrl_caps(fd, &caps); + if (!err) + nvme_show_primary_ctrl_caps(&caps, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("identify primary controller capabilities"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Show secondary controller list associated with the primary controller "\ @@ -1927,17 +2555,17 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p struct config { __u32 namespace_id; - __u32 cdw10; + __u8 stc; }; struct config cfg = { .namespace_id = NVME_NSID_ALL, - .cdw10 = 0, + .stc = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("self-test-code", 's', &cfg.cdw10, self_test_code), + OPT_UINT("self-test-code", 's', &cfg.stc, self_test_code), OPT_END() }; @@ -1945,12 +2573,14 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p if (fd < 0) goto ret; - err = nvme_self_test_start(fd, cfg.namespace_id, cfg.cdw10); + err = nvme_self_test_start(fd, cfg.namespace_id, cfg.stc); if (!err) { - if ((cfg.cdw10 & 0xf) == 0xf) + if (cfg.stc == 0xf) printf("Aborting device self-test operation\n"); - else - printf("Device self-test started\n"); + else if (cfg.stc == 0x2) + printf("Extended Device self-test started\n"); + else if (cfg.stc == 0x1) + printf("Short Device self-test started\n"); } else if (err > 0) { nvme_show_status(err); } else @@ -1966,27 +2596,28 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug const char *desc = "Retrieve the self-test log for the given device and given test "\ "(or optionally a namespace) in either decoded format "\ "(default) or binary."; - const char *namespace_id = "Indicate the namespace from which the self-test "\ - "log has to be obtained"; + const char *dst_entries = "Indicate how many DST log entries to be retrieved, "\ + "by default all the 20 entries will be retrieved"; const char *verbose = "Increase output verbosity"; struct nvme_self_test_log self_test_log; enum nvme_print_flags flags; int err, fd; + __u32 log_size; struct config { - __u32 namespace_id; + __u8 dst_entries; char *output_format; int verbose; }; struct config cfg = { - .namespace_id = NVME_NSID_ALL, + .dst_entries = NVME_ST_REPORTS, .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("dst-entries", 'e', &cfg.dst_entries, dst_entries), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -2002,9 +2633,11 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug if (cfg.verbose) flags |= VERBOSE; - err = nvme_self_test_log(fd, cfg.namespace_id, &self_test_log); + log_size = NVME_ST_LOG_HEAD_SIZE + cfg.dst_entries * NVME_ST_LOG_ENTRY_SIZE; + err = nvme_self_test_log(fd, log_size, &self_test_log); if (!err) - nvme_show_self_test_log(&self_test_log, devicename, flags); + nvme_show_self_test_log(&self_test_log, cfg.dst_entries, log_size, + devicename, flags); else if (err > 0) nvme_show_status(err); else @@ -2039,7 +2672,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin struct config { __u32 namespace_id; - __u32 feature_id; + __u8 feature_id; __u8 sel; __u32 cdw11; __u32 data_len; @@ -2048,7 +2681,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin }; struct config cfg = { - .namespace_id = 1, + .namespace_id = 0, .feature_id = 0, .sel = 0, .cdw11 = 0, @@ -2070,6 +2703,18 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) goto ret; + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + if (errno != ENOTTY) { + perror("get-namespace-id"); + goto close_fd; + } + + cfg.namespace_id = NVME_NSID_ALL; + } + } + if (cfg.sel > 7) { fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel); err = -EINVAL; @@ -2081,32 +2726,11 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin goto close_fd; } - switch (cfg.feature_id) { - case NVME_FEAT_LBA_RANGE: - cfg.data_len = 4096; - break; - case NVME_FEAT_AUTO_PST: - cfg.data_len = 256; - break; - case NVME_FEAT_HOST_MEM_BUF: - cfg.data_len = 4096; - break; - case NVME_FEAT_HOST_ID: - cfg.data_len = 8; - /* check for Extended Host Identifier */ - if (cfg.cdw11 & 0x1) - cfg.data_len = 16; - break; - case NVME_FEAT_PLM_CONFIG: - cfg.data_len = 512; - break; - case NVME_FEAT_TIMESTAMP: - cfg.data_len = 8; - break; - case NVME_FEAT_HOST_BEHAVIOR: - cfg.data_len = 512; - break; - } + cfg.data_len = nvme_feat_buf_len[cfg.feature_id]; + + /* check for Extended Host Identifier */ + if (cfg.feature_id == NVME_FEAT_HOST_ID && (cfg.cdw11 & 0x1)) + cfg.data_len = 16; if (cfg.sel == 3) cfg.data_len = 0; @@ -2140,8 +2764,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin } else perror("get-feature"); - if (buf) - free(buf); + free(buf); close_fd: close(fd); @@ -2207,22 +2830,27 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin } fw_size = sb.st_size; - if (fw_size & 0x3) { + if ((fw_size & 0x3) || (fw_size == 0)) { fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); err = -EINVAL; goto close_fw_fd; } - fw_buf = nvme_alloc(fw_size, &huge); + if (cfg.xfer == 0 || cfg.xfer % 4096) + cfg.xfer = 4096; + + if (cfg.xfer < HUGE_MIN) + fw_buf = __nvme_alloc(fw_size, &huge); + else + fw_buf = nvme_alloc(fw_size, &huge); + if (!fw_buf) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + perror("No memory for f/w size:\n"); err = -ENOMEM; goto close_fw_fd; } buf = fw_buf; - if (cfg.xfer == 0 || cfg.xfer % 4096) - cfg.xfer = 4096; if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) { err = -errno; fprintf(stderr, "read :%s :%s\n", cfg.fw, strerror(errno)); @@ -2257,9 +2885,9 @@ ret: return nvme_status_to_errno(err, false); } -static char *nvme_fw_status_reset_type(__u32 status) +static char *nvme_fw_status_reset_type(__u16 status) { - switch (status & 0x3ff) { + switch (status & 0x7ff) { case NVME_SC_FW_NEEDS_CONV_RESET: return "conventional"; case NVME_SC_FW_NEEDS_SUBSYS_RESET: return "subsystem"; case NVME_SC_FW_NEEDS_RESET: return "any controller"; @@ -2322,7 +2950,7 @@ static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin * if (err < 0) perror("fw-commit"); else if (err != 0) - switch (err & 0x3ff) { + switch (err & 0x7ff) { case NVME_SC_FW_NEEDS_CONV_RESET: case NVME_SC_FW_NEEDS_SUBSYS_RESET: case NVME_SC_FW_NEEDS_RESET: @@ -2486,8 +3114,8 @@ static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *p } if (cfg.sanact == NVME_SANITIZE_ACT_OVERWRITE) { - if (cfg.owpass > 16) { - fprintf(stderr, "OWPASS out of range [0-16]\n"); + if (cfg.owpass >= 16) { + fprintf(stderr, "OWPASS out of range [0-15]\n"); ret = -EINVAL; goto close_fd; } @@ -2697,8 +3325,8 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu struct nvme_id_ns ns; struct nvme_id_ctrl ctrl; int err, fd, i; + int block_size; __u8 prev_lbaf = 0; - __u8 lbads = 0; struct config { __u32 namespace_id; @@ -2771,13 +3399,11 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu * format of all namespaces. */ cfg.namespace_id = NVME_NSID_ALL; - } else { - if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; - goto close_fd; - } + } else if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; } } @@ -2813,8 +3439,8 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu } if (cfg.lbaf == 0xff) { fprintf(stderr, - "LBAF corresponding to block size %"PRIu64"(LBAF %u) not found\n", - (uint64_t)cfg.bs, lbads); + "LBAF corresponding to given block size %"PRIu64" not found\n", + (uint64_t)cfg.bs); fprintf(stderr, "Please correct block size, or specify LBAF directly\n"); err = -EINVAL; @@ -2822,8 +3448,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu } } else if (cfg.lbaf == 0xff) cfg.lbaf = prev_lbaf; - } - else { + } else { if (cfg.lbaf == 0xff) cfg.lbaf = 0; } @@ -2874,13 +3499,38 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu nvme_show_status(err); else { printf("Success formatting namespace:%x\n", cfg.namespace_id); - if (cfg.lbaf != prev_lbaf && ioctl(fd, BLKRRPART) < 0) { - fprintf(stderr, "failed to re-read partition table\n"); - err = -errno; - goto close_fd; - } + if (cfg.lbaf != prev_lbaf){ + if (is_chardev()) { + if(ioctl(fd, NVME_IOCTL_RESCAN) < 0){ + fprintf(stderr, "failed to rescan namespaces\n"); + err = -errno; + goto close_fd; + } + } else { + block_size = 1 << ns.lbaf[cfg.lbaf].ds; + + /* + * If block size has been changed by the format + * command up there, we should notify it to + * kernel blkdev to update its own block size + * to the given one because blkdev will not + * update by itself without re-opening fd. + */ + if (ioctl(fd, BLKBSZSET, &block_size) < 0) { + fprintf(stderr, "failed to set block size to %d\n", + block_size); + err = -errno; + goto close_fd; + } - if (cfg.reset && S_ISCHR(nvme_stat.st_mode)) + if(ioctl(fd, BLKRRPART) < 0) { + fprintf(stderr, "failed to re-read partition table\n"); + err = -errno; + goto close_fd; + } + } + } + if (cfg.reset && is_chardev()) nvme_reset_controller(fd); } @@ -2890,6 +3540,9 @@ ret: return nvme_status_to_errno(err, false); } +#define STRTOUL_AUTO_BASE (0) +#define NVME_FEAT_TIMESTAMP_DATA_SIZE (6) + static int set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Modify the saveable or changeable "\ @@ -2912,12 +3565,14 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin __u32 result; void *buf = NULL; int fd, ffd = STDIN_FILENO; + char *endptr = NULL; + uint64_t number = 0; struct config { char *file; __u32 namespace_id; - __u32 feature_id; - __u32 value; + __u8 feature_id; + __u64 value; __u32 cdw12; __u32 data_len; int save; @@ -2935,7 +3590,7 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), + OPT_LONG("value", 'v', &cfg.value, value), OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_FILE("data", 'd', &cfg.file, data), @@ -2947,13 +3602,26 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) goto ret; + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + if (errno != ENOTTY) { + perror("get-namespace-id"); + goto close_fd; + } + + cfg.namespace_id = NVME_NSID_ALL; + } + } + if (!cfg.feature_id) { fprintf(stderr, "feature-id required param\n"); err = -EINVAL; goto close_fd; } - if (cfg.feature_id == NVME_FEAT_LBA_RANGE) - cfg.data_len = 4096; + + cfg.data_len = nvme_feat_buf_len[cfg.feature_id]; + if (cfg.data_len) { if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { fprintf(stderr, "can not allocate feature payload\n"); @@ -2964,22 +3632,33 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin } if (buf) { - if (strlen(cfg.file)) { - ffd = open(cfg.file, O_RDONLY); - if (ffd <= 0) { - fprintf(stderr, "Failed to open file %s: %s\n", - cfg.file, strerror(errno)); - err = -EINVAL; - goto free; - } - } - err = read(ffd, (void *)buf, cfg.data_len); - if (err < 0) { - err = -errno; - fprintf(stderr, "failed to read data buffer from input" - " file: %s\n", strerror(errno)); - goto close_ffd; - } + /* if feature ID is 0x0E, get timestamp value by -v option */ + if (NVME_FEAT_TIMESTAMP == cfg.feature_id && cfg.value) { + memcpy(buf, &cfg.value, NVME_FEAT_TIMESTAMP_DATA_SIZE); + } else { + if (strlen(cfg.file)) { + ffd = open(cfg.file, O_RDONLY); + if (ffd <= 0) { + fprintf(stderr, "Failed to open file %s: %s\n", + cfg.file, strerror(errno)); + err = -EINVAL; + goto free; + } + } + err = read(ffd, (void *)buf, cfg.data_len); + if (err < 0) { + err = -errno; + fprintf(stderr, "failed to read data buffer from input" + " file: %s\n", strerror(errno)); + goto close_ffd; + } + /* if feature ID is 0x0E, then change string from file to integer */ + if (NVME_FEAT_TIMESTAMP == cfg.feature_id) { + number = strtoul(buf, &endptr, STRTOUL_AUTO_BASE); + memset(buf, 0, cfg.data_len); + memcpy(buf, &number, NVME_FEAT_TIMESTAMP_DATA_SIZE); + } + } } err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value, @@ -2987,8 +3666,12 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin if (err < 0) { perror("set-feature"); } else if (!err) { - printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, - nvme_feature_to_string(cfg.feature_id), cfg.value); + printf("set-feature:%#02x (%s), value:%#08"PRIx64", cdw12:%#08"PRIx32", \ + save:%#x\n", cfg.feature_id, nvme_feature_to_string(cfg.feature_id), + (uint64_t)cfg.value, cfg.cdw12, cfg.save); + if (cfg.feature_id == NVME_LBA_STATUS_INFO) { + nvme_show_lba_status_info(result); + } if (buf) { if (cfg.feature_id == NVME_FEAT_LBA_RANGE) nvme_show_lba_range((struct nvme_lba_range_type *)buf, @@ -3002,8 +3685,7 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin close_ffd: close(ffd); free: - if (buf) - free(buf); + free(buf); close_fd: close(fd); ret: @@ -3027,7 +3709,6 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p int err, fd, sec_fd = -1; void *sec_buf; unsigned int sec_size; - __u32 result; struct config { __u32 namespace_id; @@ -3089,13 +3770,13 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p } err = nvme_sec_send(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, cfg.secp, - cfg.tl, sec_size, sec_buf, &result); + cfg.tl, sec_size, sec_buf); if (err < 0) perror("security-send"); else if (err != 0) - fprintf(stderr, "NVME Security Send Command Error:%d\n", err); + nvme_show_status(err); else - printf("NVME Security Send Command Success:%d\n", result); + printf("NVME Security Send Command Success\n"); free: free(sec_buf); @@ -3244,15 +3925,13 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p else d_raw(buf, cfg.data_len); } - } - else if (err > 0) + } else if (err > 0) nvme_show_status(err); close_ffd: close(ffd); free: - if (buf) - free(buf); + free(buf); close_fd: close(fd); ret: @@ -3292,9 +3971,9 @@ static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3384,9 +4063,9 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi if (cfg.deac) control |= NVME_RW_DEAC; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3477,16 +4156,16 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin } if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } if (!cfg.cdw11) cfg.cdw11 = (cfg.ad << 2) | (cfg.idw << 1) | (cfg.idr << 0); - dsm = nvme_setup_dsm_range((__u32 *)ctx_attrs, (__u32 *)nlbs, (__u64 *)slbas, nr); + dsm = nvme_setup_dsm_range(ctx_attrs, nlbs, slbas, nr); if (!dsm) { fprintf(stderr, "failed to allocate data set payload\n"); err = -ENOMEM; @@ -3501,6 +4180,144 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin else printf("NVMe DSM: success\n"); + free(dsm); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The Copy command is used by the host to copy data " + "from one or more source logical block ranges to a " + "single consecutive destination logical block " + "range."; + + const char *d_nsid = "identifier of desired namespace"; + const char *d_sdlba = "64-bit addr of first destination logical block"; + const char *d_slbas = "64-bit addr of first block per range (comma-separated list)"; + const char *d_nlbs = "number of blocks per range (comma-separated list, zeroes-based values)"; + const char *d_lr = "limited retry"; + const char *d_fua = "force unit access"; + const char *d_prinfor = "protection information and check field (read part)"; + const char *d_prinfow = "protection information and check field (write part)"; + const char *d_ilbrt = "initial lba reference tag (write part)"; + const char *d_eilbrts = "expected lba reference tags (read part, comma-separated list)"; + const char *d_lbat = "lba application tag (write part)"; + const char *d_elbats = "expected lba application tags (read part, comma-separated list)"; + const char *d_lbatm = "lba application tag mask (write part)"; + const char *d_elbatms = "expected lba application tag masks (read part, comma-separated list)"; + const char *d_dtype = "directive type (write part)"; + const char *d_dspec = "directive specific (write part)"; + const char *d_format = "source range entry format"; + + int err, fd; + uint16_t nr, nb, ns, nrts, natms, nats; + int nlbs[128] = { 0 }; + unsigned long long slbas[128] = {0,}; + int eilbrts[128] = { 0 }; + int elbatms[128] = { 0 }; + int elbats[128] = { 0 }; + struct nvme_copy_range *copy; + + struct config { + __u32 namespace_id; + __u64 sdlba; + char *nlbs; + char *slbas; + __u32 ilbrt; + char *eilbrts; + __u16 lbatm; + char *elbatms; + __u16 lbat; + char *elbats; + __u8 prinfow; + __u8 prinfor; + int lr; + int fua; + __u8 dtype; + __u16 dspec; + __u8 format; + }; + + struct config cfg = { + .namespace_id = 0, + .nlbs = "", + .slbas = "", + .eilbrts = "", + .elbatms = "", + .elbats = "", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, d_nsid), + OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba), + OPT_LIST("slbs", 's', &cfg.slbas, d_slbas), + OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs), + OPT_FLAG("limited-retry", 'l', &cfg.lr, d_lr), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, d_fua), + OPT_BYTE("prinfow", 'p', &cfg.prinfow, d_prinfow), + OPT_BYTE("prinfor", 'P', &cfg.prinfor, d_prinfor), + OPT_UINT("ref-tag", 'r', &cfg.ilbrt, d_ilbrt), + OPT_LIST("expected-ref-tags", 'R', &cfg.eilbrts, d_eilbrts), + OPT_SHRT("app-tag", 'a', &cfg.lbat, d_lbat), + OPT_LIST("expected-app-tags", 'A', &cfg.elbats, d_elbats), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, d_lbatm), + OPT_LIST("expected-app-tag-masks", 'M', &cfg.elbatms, d_elbatms), + OPT_BYTE("dir-type", 'T', &cfg.dtype, d_dtype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, d_dspec), + OPT_BYTE("format", 'F', &cfg.format, d_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + err = fd; + goto ret; + } + + nb = argconfig_parse_comma_sep_array(cfg.nlbs, nlbs, ARRAY_SIZE(nlbs)); + ns = argconfig_parse_comma_sep_array_long(cfg.slbas, slbas, ARRAY_SIZE(slbas)); + nrts = argconfig_parse_comma_sep_array(cfg.eilbrts, eilbrts, ARRAY_SIZE(eilbrts)); + natms = argconfig_parse_comma_sep_array(cfg.elbatms, elbatms, ARRAY_SIZE(elbatms)); + nats = argconfig_parse_comma_sep_array(cfg.elbats, elbats, ARRAY_SIZE(elbats)); + + nr = max(nb, max(ns, max(nrts, max(natms, nats)))); + if (!nr || nr > 128) { + fprintf(stderr, "invalid range\n"); + err = -EINVAL; + goto close_fd; + } + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + copy = nvme_setup_copy_range(nlbs, slbas, eilbrts, elbatms, elbats, nr); + if (!copy) { + fprintf(stderr, "failed to allocate payload\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_copy(fd, cfg.namespace_id, copy, cfg.sdlba, nr, cfg.prinfor, + cfg.prinfow, cfg.dtype, cfg.dspec, cfg.format, cfg.lr, + cfg.fua, cfg.ilbrt, cfg.lbatm, cfg.lbat); + if (err < 0) + perror("NVMe Copy"); + else if (err != 0) + nvme_show_status(err); + else + printf("NVMe Copy: success\n"); + + free(copy); + close_fd: close(fd); ret: @@ -3535,9 +4352,9 @@ static int flush(int argc, char **argv, struct command *cmd, struct plugin *plug goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3603,9 +4420,9 @@ static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3675,9 +4492,9 @@ static int resv_register(int argc, char **argv, struct command *cmd, struct plug goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3755,9 +4572,9 @@ static int resv_release(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3833,9 +4650,9 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin flags = BINARY; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3858,7 +4675,7 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin if (!err) nvme_show_resv_report(status, size, cfg.cdw11, flags); else if (err > 0) - fprintf(stderr, "NVME IO command error:%04x\n", err); + nvme_show_status(err); else perror("reservation report"); free(status); @@ -3868,7 +4685,7 @@ ret: return nvme_status_to_errno(err, false); } -static unsigned long long elapsed_utime(struct timeval start_time, +unsigned long long elapsed_utime(struct timeval start_time, struct timeval end_time) { unsigned long long err = (end_time.tv_sec - start_time.tv_sec) * 1000000 + @@ -3886,10 +4703,12 @@ static int submit_io(int opcode, char *command, const char *desc, int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT; int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH; __u16 control = 0; - __u32 dsmgmt = 0; - int phys_sector_size = 0; - long long buffer_size = 0; + __u32 dsmgmt = 0, nsid = 0; + int logical_block_size = 0; + long long buffer_size = 0, mbuffer_size = 0; bool huge; + struct nvme_id_ns ns; + __u8 lba_index, ms = 0; const char *start_block = "64-bit addr of first block to access"; const char *block_count = "number of blocks (zeroes based) on device to access"; @@ -4017,10 +4836,10 @@ static int submit_io(int opcode, char *command, const char *desc, goto close_mfd; } - if (ioctl(fd, BLKPBSZGET, &phys_sector_size) < 0) + if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) goto close_mfd; - buffer_size = (cfg.block_count + 1) * phys_sector_size; + buffer_size = (cfg.block_count + 1) * logical_block_size; if (cfg.data_size < buffer_size) { fprintf(stderr, "Rounding data size to fit block count (%lld bytes)\n", buffer_size); @@ -4030,20 +4849,41 @@ static int submit_io(int opcode, char *command, const char *desc, buffer = nvme_alloc(buffer_size, &huge); if (!buffer) { - fprintf(stderr, "can not allocate io payload\n"); + perror("can not allocate io payload\n"); err = -ENOMEM; goto close_mfd; } if (cfg.metadata_size) { - mbuffer = malloc(cfg.metadata_size); + err = nsid = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_mfd; + } + err = nvme_identify_ns(fd, nsid, false, &ns); + if (err) { + nvme_show_status(err); + goto free_buffer; + } else if (err < 0) { + perror("identify namespace"); + goto free_buffer; + } + lba_index = ns.flbas & NVME_NS_FLBAS_LBA_MASK; + ms = ns.lbaf[lba_index].ms; + mbuffer_size = (cfg.block_count + 1) * ms; + if (ms && cfg.metadata_size < mbuffer_size) { + fprintf(stderr, "Rounding metadata size to fit block count (%lld bytes)\n", + mbuffer_size); + } else { + mbuffer_size = cfg.metadata_size; + } + mbuffer = malloc(mbuffer_size); if (!mbuffer) { - fprintf(stderr, "can not allocate io metadata " - "payload: %s\n", strerror(errno)); + perror("can not allocate buf for io metadata payload\n"); err = -ENOMEM; goto free_buffer; } - memset(mbuffer, 0, cfg.metadata_size); + memset(mbuffer, 0, mbuffer_size); } if ((opcode & 1)) { @@ -4057,7 +4897,7 @@ static int submit_io(int opcode, char *command, const char *desc, } if ((opcode & 1) && cfg.metadata_size) { - err = read(mfd, (void *)mbuffer, cfg.metadata_size); + err = read(mfd, (void *)mbuffer, mbuffer_size); if (err < 0) { err = -errno; fprintf(stderr, "failed to read meta-data buffer from" @@ -4071,7 +4911,6 @@ static int submit_io(int opcode, char *command, const char *desc, printf("flags : %02x\n", 0); printf("control : %04x\n", control); printf("nblocks : %04x\n", cfg.block_count); - printf("rsvd : %04x\n", 0); printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mbuffer); printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)buffer); printf("slba : %"PRIx64"\n", (uint64_t)cfg.start_block); @@ -4100,7 +4939,7 @@ static int submit_io(int opcode, char *command, const char *desc, strerror(errno)); err = -EINVAL; } else if (!(opcode & 1) && cfg.metadata_size && - write(mfd, (void *)mbuffer, cfg.metadata_size) < 0) { + write(mfd, (void *)mbuffer, mbuffer_size) < 0) { fprintf(stderr, "write: %s: failed to write meta-data buffer to output file\n", strerror(errno)); err = -EINVAL; @@ -4109,8 +4948,7 @@ static int submit_io(int opcode, char *command, const char *desc, } free_mbuffer: - if (cfg.metadata_size) - free(mbuffer); + free(mbuffer); free_buffer: nvme_free(buffer, huge); close_mfd: @@ -4215,9 +5053,9 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin control |= NVME_RW_FUA; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -4254,7 +5092,6 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p const char *nssf = "NVMe Security Specific Field"; int err, fd; void *sec_buf = NULL; - __u32 result; struct config { __u32 namespace_id; @@ -4298,16 +5135,14 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p } err = nvme_sec_recv(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, - cfg.secp, cfg.al, cfg.size, sec_buf, &result); + cfg.secp, cfg.al, cfg.size, sec_buf); if (err < 0) perror("security receive"); else if (err != 0) - fprintf(stderr, "NVME Security Receive Command Error:%d\n", - err); + nvme_show_status(err); else { + printf("NVME Security Receive Command Success\n"); if (!cfg.raw_binary) { - printf("NVME Security Receive Command Success:%d\n", - result); d(sec_buf, cfg.size, 16, 1); } else if (cfg.size) d_raw((unsigned char *)sec_buf, cfg.size); @@ -4325,6 +5160,7 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Information about potentially unrecoverable LBAs."; + const char *namespace_id = "Desired Namespace"; const char *slba = "Starting LBA(SLBA) in 64-bit address of the first"\ " logical block addressed by this command"; const char *mndw = "Maximum Number of Dwords(MNDW) specifies maximum"\ @@ -4334,6 +5170,7 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, " Status Descriptors to return."; const char *rl = "Range Length(RL) specifies the length of the range"\ " of contiguous LBAs beginning at SLBA"; + const char *timeout = "timeout value, in milliseconds"; enum nvme_print_flags flags; unsigned long buf_len; @@ -4341,26 +5178,32 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, void *buf; struct config { + __u32 namespace_id; __u64 slba; __u32 mndw; __u8 atype; __u16 rl; + __u32 timeout; char *output_format; }; struct config cfg = { + .namespace_id = 0, .slba = 0, .mndw = 0, .atype = 0, .rl = 0, + .timeout = 0, .output_format = "normal", }; OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_SUFFIX("start-lba", 's', &cfg.slba, slba), OPT_UINT("max-dw", 'm', &cfg.mndw, mndw), OPT_BYTE("action", 'a', &cfg.atype, atype), OPT_SHRT("range-len", 'l', &cfg.rl, rl), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_END() }; @@ -4382,16 +5225,17 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, buf_len = (cfg.mndw + 1) * 4; buf = calloc(1, buf_len); if (!buf) { + perror("could not alloc memory for get lba status"); err = -ENOMEM; goto close_fd; } - err = nvme_get_lba_status(fd, cfg.slba, cfg.mndw, cfg.atype, cfg.rl, - buf); + err = nvme_get_lba_status(fd, cfg.namespace_id, cfg.slba, cfg.mndw, + cfg.atype, cfg.rl, buf, cfg.timeout); if (!err) nvme_show_lba_status(buf, buf_len, flags); else if (err > 0) - fprintf(stderr, "NVME command error:%04x\n", err); + nvme_show_status(err); else perror("get lba status"); free(buf); @@ -4518,20 +5362,50 @@ static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin else if (err < 0) perror("dir-receive"); - if (cfg.data_len) - free(buf); + free(buf); close_fd: close(fd); ret: return nvme_status_to_errno(err, false); } -static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, struct command *cmd) +/* rpmb_cmd_option is defined in nvme-rpmb.c */ +extern int rpmb_cmd_option(int, char **, struct command *, struct plugin *); +static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) { + return rpmb_cmd_option(argc, argv, cmd, plugin); +} + +static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, + const char *desc, struct command *cmd) +{ + const char *opcode = "opcode (required)"; + const char *flags = "command flags"; + const char *rsvd = "value for reserved field"; + const char *namespace_id = "desired namespace"; + const char *data_len = "data I/O length (bytes)"; + const char *metadata_len = "metadata seg. length (bytes)"; + const char *timeout = "timeout value, in milliseconds"; + const char *cdw2 = "command dword 2 value"; + const char *cdw3 = "command dword 3 value"; + const char *cdw10 = "command dword 10 value"; + const char *cdw11 = "command dword 11 value"; + const char *cdw12 = "command dword 12 value"; + const char *cdw13 = "command dword 13 value"; + const char *cdw14 = "command dword 14 value"; + const char *cdw15 = "command dword 15 value"; + const char *input = "write/send file (default stdin)"; + const char *raw_binary = "dump output in binary format"; + const char *show = "print command before sending"; + const char *dry = "show command instead of sending"; + const char *re = "set dataflow direction to receive"; + const char *wr = "set dataflow direction to send"; + const char *prefill = "prefill buffers with known byte-value, default 0"; void *data = NULL, *metadata = NULL; int err = 0, wfd = STDIN_FILENO, fd; __u32 result; bool huge; + const char *cmd_name = NULL; struct config { __u8 opcode; @@ -4578,29 +5452,6 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru .prefill = 0, }; - const char *opcode = "opcode (required)"; - const char *flags = "command flags"; - const char *rsvd = "value for reserved field"; - const char *namespace_id = "desired namespace"; - const char *data_len = "data I/O length (bytes)"; - const char *metadata_len = "metadata seg. length (bytes)"; - const char *timeout = "timeout value, in milliseconds"; - const char *cdw2 = "command dword 2 value"; - const char *cdw3 = "command dword 3 value"; - const char *cdw10 = "command dword 10 value"; - const char *cdw11 = "command dword 11 value"; - const char *cdw12 = "command dword 12 value"; - const char *cdw13 = "command dword 13 value"; - const char *cdw14 = "command dword 14 value"; - const char *cdw15 = "command dword 15 value"; - const char *input = "write/send file (default stdin)"; - const char *raw_binary = "dump output in binary format"; - const char *show = "print command before sending"; - const char *dry = "show command instead of sending"; - const char *re = "set dataflow direction to receive"; - const char *wr = "set dataflow direction to send"; - const char *prefill = "prefill buffers with known byte-value, default 0"; - OPT_ARGS(opts) = { OPT_BYTE("opcode", 'o', &cfg.opcode, opcode), OPT_BYTE("flags", 'f', &cfg.flags, flags), @@ -4631,7 +5482,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (fd < 0) goto ret; - if (strlen(cfg.input_file)){ + if (strlen(cfg.input_file)) { wfd = open(cfg.input_file, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH); if (wfd < 0) { @@ -4644,8 +5495,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (cfg.metadata_len) { metadata = malloc(cfg.metadata_len); if (!metadata) { - fprintf(stderr, "can not allocate metadata " - "payload: %s\n", strerror(errno)); + perror("can not allocate metadata payload\n"); err = -ENOMEM; goto close_wfd; } @@ -4654,7 +5504,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (cfg.data_len) { data = nvme_alloc(cfg.data_len, &huge); if (!data) { - fprintf(stderr, "can not allocate data payload\n"); + perror("can not allocate data payload\n"); err = -ENOMEM; goto free_metadata; } @@ -4714,19 +5564,21 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru else if (err) nvme_show_status(err); else { + cmd_name = nvme_cmd_to_string(cmd_type, cfg.opcode); + fprintf(stderr, "%s Command %s is Success and result: 0x%08x\n", + cmd_type ? "Admin": "IO", + strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific", + result); if (!cfg.raw_binary) { - fprintf(stderr, "NVMe command result:%08x\n", result); if (data && cfg.read && !err) d((unsigned char *)data, cfg.data_len, 16, 1); } else if (data && cfg.read) d_raw((unsigned char *)data, cfg.data_len); } free_data: - if (cfg.data_len) - nvme_free(data, huge); + nvme_free(data, huge); free_metadata: - if (cfg.metadata_len) - free(metadata); + free(metadata); close_wfd: if (strlen(cfg.input_file)) close(wfd); @@ -4740,35 +5592,42 @@ static int io_passthru(int argc, char **argv, struct command *cmd, struct plugin { const char *desc = "Send a user-defined IO command to the specified "\ "device via IOCTL passthrough, return results."; - return passthru(argc, argv, NVME_IOCTL_IO_CMD, desc, cmd); + return passthru(argc, argv, NVME_IOCTL_IO_CMD, 0, desc, cmd); } static int admin_passthru(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Send a user-defined Admin command to the specified "\ "device via IOCTL passthrough, return results."; - return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, desc, cmd); + return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, 1, desc, cmd); } -#ifdef LIBUUID static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { - uuid_t uuid; + int ret; char uuid_str[37]; /* e.g. 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ +#ifdef LIBUUID + uuid_t uuid; +#endif - uuid_generate_random(uuid); - uuid_unparse_lower(uuid, uuid_str); + ret = uuid_from_dmi(uuid_str); + if (ret < 0) + ret = uuid_from_systemd(uuid_str); +#ifdef LIBUUID + if (ret < 0) { + uuid_generate_random(uuid); + uuid_unparse_lower(uuid, uuid_str); + ret = 0; + } +#endif + if (ret < 0) { + fprintf(stderr, "\"%s\" not supported. Install lib uuid and rebuild.\n", + command->name); + return -ENOTSUP; + } printf("nqn.2014-08.org.nvmexpress:uuid:%s\n", uuid_str); return 0; } -#else -static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) -{ - fprintf(stderr, "\"%s\" not supported. Install lib uuid and rebuild.\n", - command->name); - return -ENOTSUP; -} -#endif static int show_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { |