diff options
Diffstat (limited to '')
-rw-r--r-- | nvme.c | 4792 | ||||
-rw-r--r-- | nvme.control.in | 4 |
2 files changed, 3080 insertions, 1716 deletions
@@ -23,7 +23,9 @@ /** * This program uses NVMe IOCTLs to run native nvme commands to a device. */ - +#include "config.h" +#include "nvme/tree.h" +#include "nvme/types.h" #include <errno.h> #include <getopt.h> #include <fcntl.h> @@ -36,31 +38,46 @@ #include <math.h> #include <dirent.h> #include <libgen.h> +#include <zlib.h> -#ifdef LIBHUGETLBFS +#ifdef CONFIG_LIBHUGETLBFS #include <hugetlbfs.h> #endif #include <linux/fs.h> -#include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> +#if HAVE_SYS_RANDOM + #include <sys/random.h> +#endif + #include "common.h" +#include "nvme.h" +#include "libnvme.h" #include "nvme-print.h" -#include "nvme-ioctl.h" -#include "nvme-status.h" -#include "nvme-lightnvm.h" #include "plugin.h" +#include "util/base64.h" -#include "argconfig.h" +#include "util/argconfig.h" #include "fabrics.h" #define CREATE_CMD #include "nvme-builtin.h" +struct feat_cfg { + enum nvme_features_id feature_id; + __u32 namespace_id; + enum nvme_get_features_sel sel; + __u32 cdw11; + __u8 uuid_index; + __u32 data_len; + bool raw_binary; + bool human_readable; +}; + static struct stat nvme_stat; const char *devicename; @@ -84,23 +101,12 @@ static struct program nvme = { .extensions = &builtin, }; -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, - [NVME_MI_FEAT_CTRL_METADATA] = 4096, - [NVME_MI_FEAT_NS_METADATA] = 4096, -}; - 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) -{ +static void *mmap_registers(nvme_root_t r, const char *dev); + +static void *__nvme_alloc(size_t len, bool *huge) { void *p; if (!posix_memalign(&p, getpagesize(), len)) { @@ -113,11 +119,13 @@ static void *__nvme_alloc(size_t len, bool *huge) #define HUGE_MIN 0x80000 -#ifdef LIBHUGETLBFS +#ifdef CONFIG_LIBHUGETLBFS void nvme_free(void *p, bool huge) { - if (huge) - free_hugepage_region(p); + if (huge) { + if (p) + free_hugepage_region(p); + } else free(p); } @@ -148,6 +156,57 @@ void *nvme_alloc(size_t len, bool *huge) } #endif +const char *nvme_strerror(int errnum) +{ + if (errnum >= ENVME_CONNECT_RESOLVE) + return nvme_errno_to_string(errnum); + return strerror(errnum); +} + +int map_log_level(int verbose, bool quiet) +{ + int log_level; + + switch (verbose) { + case 0: + log_level = LOG_WARNING; + break; + case 1: + log_level = LOG_NOTICE; + break; + case 2: + log_level = LOG_INFO; + break; + default: + log_level = LOG_DEBUG; + break; + } + if (quiet) + log_level = LOG_ERR; + + return log_level; +} + +static ssize_t getrandom_bytes(void *buf, size_t buflen) +{ +#if HAVE_SYS_RANDOM + return getrandom(buf, buflen, GRND_NONBLOCK); +#else + ssize_t result; + int fd, err = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + return fd; + result = read(fd, buf, buflen); + if (result < 0) + err = errno; + close(fd); + errno = err; + return result; +#endif +} + static bool is_chardev(void) { return S_ISCHR(nvme_stat.st_mode); @@ -158,17 +217,12 @@ static bool is_blkdev(void) return S_ISBLK(nvme_stat.st_mode); } -static bool is_ns_chardev(void) -{ - return !strncmp(devicename, "ng", 2) && S_ISCHR(nvme_stat.st_mode); -} - -static int open_dev(char *dev) +static int open_dev(char *dev, int flags) { int err, fd; devicename = basename(dev); - err = open(dev, O_RDONLY); + err = open(dev, flags); if (err < 0) goto perror; fd = err; @@ -199,7 +253,7 @@ static int check_arg_dev(int argc, char **argv) return 0; } -static int get_dev(int argc, char **argv) +static int get_dev(int argc, char **argv, int flags) { int ret; @@ -207,7 +261,7 @@ static int get_dev(int argc, char **argv) if (ret) return ret; - return open_dev(argv[optind]); + return open_dev(argv[optind], flags); } int parse_and_open(int argc, char **argv, const char *desc, @@ -219,13 +273,23 @@ int parse_and_open(int argc, char **argv, const char *desc, if (ret) return ret; - ret = get_dev(argc, argv); + ret = get_dev(argc, argv, O_RDONLY); if (ret < 0) argconfig_print_help(desc, opts); return ret; } +int open_exclusive(int argc, char **argv, int ignore_exclusive) +{ + int flags = O_RDONLY; + + if (!ignore_exclusive) + flags |= O_EXCL; + + return get_dev(argc, argv, flags); +} + enum nvme_print_flags validate_output_format(const char *format) { if (!format) @@ -252,15 +316,17 @@ static int get_smart_log(int argc, char **argv, struct command *cmd, struct plug int err = -1, fd; struct config { - __u32 namespace_id; - int raw_binary; - char *output_format; - int human_readable; + __u32 namespace_id; + char *output_format; + bool raw_binary; + bool human_readable; }; struct config cfg = { - .namespace_id = NVME_NSID_ALL, - .output_format = "normal", + .namespace_id = NVME_NSID_ALL, + .output_format = "normal", + .raw_binary = false, + .human_readable = false, }; @@ -284,18 +350,18 @@ static int get_smart_log(int argc, char **argv, struct command *cmd, struct plug if (cfg.human_readable) flags |= VERBOSE; - err = nvme_smart_log(fd, cfg.namespace_id, &smart_log); + err = nvme_get_log_smart(fd, cfg.namespace_id, true, &smart_log); if (!err) nvme_show_smart_log(&smart_log, cfg.namespace_id, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("smart log"); + fprintf(stderr, "smart log: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_ana_log(int argc, char **argv, struct command *cmd, @@ -309,9 +375,10 @@ static int get_ana_log(int argc, char **argv, struct command *cmd, size_t ana_log_len; struct nvme_id_ctrl ctrl; enum nvme_print_flags flags; + enum nvme_log_ana_lsp lsp; struct config { - char *output_format; + char *output_format; }; struct config cfg = { @@ -333,69 +400,64 @@ static int get_ana_log(int argc, char **argv, struct command *cmd, err = nvme_identify_ctrl(fd, &ctrl); if (err) { - fprintf(stderr, "ERROR : nvme_identify_ctrl() failed 0x%x\n", - err); + fprintf(stderr, "ERROR : nvme_identify_ctrl() failed: %s\n", + nvme_strerror(errno)); goto close_fd; } - ana_log_len = sizeof(struct nvme_ana_rsp_hdr) + + ana_log_len = sizeof(struct nvme_ana_log) + le32_to_cpu(ctrl.nanagrpid) * sizeof(struct nvme_ana_group_desc); if (!(ctrl.anacap & (1 << 6))) ana_log_len += le32_to_cpu(ctrl.mnan) * sizeof(__le32); ana_log = malloc(ana_log_len); if (!ana_log) { - perror("malloc"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_ana_log(fd, ana_log, ana_log_len, groups ? NVME_ANA_LOG_RGO : 0); + lsp = groups ? NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY : + NVME_LOG_ANA_LSP_RGO_NAMESPACES; + + err = nvme_get_log_ana(fd, lsp, true, 0, ana_log_len, ana_log); if (!err) { nvme_show_ana_log(ana_log, devicename, flags, ana_log_len); } else if (err > 0) nvme_show_status(err); else - perror("ana-log"); + fprintf(stderr, "ana-log: %s", nvme_strerror(errno)); free(ana_log); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); -} - -__u16 get_feat_buf_len(unsigned short feature) { - return nvme_feat_buf_len[feature]; + return err; } -static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int get_telemetry_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Retrieve telemetry log and write to binary file"; const char *fname = "File name to save raw binary, includes header"; const char *hgen = "Have the host tell the controller to generate the report"; const char *cgen = "Gather report generated by the controller."; const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4."; - const size_t bs = 512; - struct nvme_telemetry_log_page_hdr *hdr; - struct nvme_id_ctrl ctrl; - size_t full_size, offset = bs; - int err = -1, fd, output; - void *page_log; - __u32 result; - void *buf = NULL; + struct nvme_telemetry_log *log; + int err = 0, fd, output; + size_t total_size; + __u8 *data_ptr = NULL; + int data_written = 0, data_remaining = 0; struct config { - char *file_name; - __u32 host_gen; - int ctrl_init; - int data_area; + char *file_name; + __u32 host_gen; + bool ctrl_init; + int data_area; }; struct config cfg = { - .file_name = NULL, - .host_gen = 1, - .ctrl_init = 0, - .data_area = 3, + .file_name = NULL, + .host_gen = 1, + .ctrl_init = false, + .data_area = 3, }; OPT_ARGS(opts) = { @@ -412,135 +474,68 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct if (!cfg.file_name) { fprintf(stderr, "Please provide an output file!\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } cfg.host_gen = !!cfg.host_gen; - hdr = malloc(bs); - page_log = malloc(bs); - if (!hdr || !page_log) { - perror("failed to allocate buf for log\n"); - errno = ENOMEM; - err = -1; - goto free_mem; - } - memset(hdr, 0, bs); - output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { fprintf(stderr, "Failed to open output file %s: %s!\n", cfg.file_name, strerror(errno)); err = output; - goto free_mem; + goto close_fd; } - err = nvme_get_telemetry_log(fd, hdr, cfg.host_gen, cfg.ctrl_init, bs, 0); - if (err < 0) - perror("get-telemetry-log"); - else if (err > 0) { - nvme_show_status(err); - fprintf(stderr, "Failed to acquire telemetry header %d!\n", err); - goto close_output; - } + if (cfg.ctrl_init) + err = nvme_get_ctrl_telemetry(fd, true, &log, cfg.data_area, &total_size); + else if (cfg.host_gen) + err = nvme_get_new_host_telemetry(fd, &log, cfg.data_area, &total_size); + else + err = nvme_get_host_telemetry(fd, &log, cfg.data_area, &total_size); - err = write(output, (void *) hdr, bs); - if (err != bs) { - fprintf(stderr, "Failed to flush all data to file!\n"); + if (err < 0) { + fprintf(stderr, "get-telemetry-log: %s\n", + nvme_strerror(errno)); + } else if (err > 0) { + nvme_show_status(err); + fprintf(stderr, "Failed to acquire telemetry log %d!\n", err); goto close_output; } - switch (cfg.data_area) { - case 1: - full_size = (le16_to_cpu(hdr->dalb1) * bs) + offset; - break; - case 2: - full_size = (le16_to_cpu(hdr->dalb2) * bs) + offset; - break; - case 3: - full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset; - break; - case 4: - err = nvme_identify_ctrl(fd, &ctrl); - if (err) { - perror("identify-ctrl"); - goto close_output; - } - - if (posix_memalign(&buf, getpagesize(), nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR])) { - fprintf(stderr, "can not allocate feature payload\n"); - errno = ENOMEM; - err = -1; - goto close_output; - } - memset(buf, 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR]); - - err = nvme_get_feature(fd, NVME_NSID_ALL, NVME_FEAT_HOST_BEHAVIOR, 0, 0, - 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR], buf, &result); - if (err > 0) { - nvme_show_status(err); - } else if (err < 0) { - perror("get-feature"); - } else { - if ((ctrl.lpa & 0x40)) { - if (((unsigned char *)buf)[1] == 1) - full_size = (le32_to_cpu(hdr->dalb4) * bs) + offset; - else { - fprintf(stderr, "Data area 4 unsupported, Host Behavior Support ETDAS not set to 1\n"); - errno = EINVAL; - err = -1; - } - } else { - fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n"); - errno = EINVAL; - err = -1; - } - } - free(buf); - if (err) - goto close_output; - break; - default: - fprintf(stderr, "Invalid data area requested\n"); - errno = EINVAL; - err = -1; - goto close_output; - } + data_written = 0; + data_remaining = total_size; + data_ptr = (__u8 *)log; - /* - * Continuously pull data until the offset hits the end of the last - * block. - */ - while (offset != full_size) { - err = nvme_get_telemetry_log(fd, page_log, 0, cfg.ctrl_init, bs, offset); - if (err < 0) { - perror("get-telemetry-log"); + while (data_remaining) { + data_written = write(output, data_ptr, data_remaining); + if (data_written < 0) { + data_remaining = data_written; break; - } else if (err > 0) { - fprintf(stderr, "Failed to acquire full telemetry log!\n"); - nvme_show_status(err); + } else if (data_written <= data_remaining) { + data_remaining -= data_written; + data_ptr += data_written; + } else { + /* Unexpected overwrite */ + fprintf(stderr, "Failure: Unexpected telemetry log overwrite - data_remaining = 0x%x, data_written = 0x%x\n", + data_remaining, data_written); break; } + } - err = write(output, (void *) page_log, bs); - if (err != bs) { - fprintf(stderr, "Failed to flush all data to file!\n"); - break; - } - err = 0; - offset += bs; + if (fsync(output) < 0) { + fprintf(stderr, "ERROR : %s: : fsync : %s\n", __func__, strerror(errno)); + return -1; } + free(log); + close_output: close(output); -free_mem: - free(hdr); - free(page_log); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_endurance_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -549,16 +544,16 @@ static int get_endurance_log(int argc, char **argv, struct command *cmd, struct const char *desc = "Retrieves endurance groups log page and prints the log."; const char *group_id = "The endurance group identifier"; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - char *output_format; - __u16 group_id; + char *output_format; + __u16 group_id; }; struct config cfg = { - .output_format = "normal", - .group_id = 0, + .output_format = "normal", + .group_id = 0, }; OPT_ARGS(opts) = { @@ -575,17 +570,38 @@ static int get_endurance_log(int argc, char **argv, struct command *cmd, struct if (flags < 0) goto close_fd; - err = nvme_endurance_log(fd, cfg.group_id, &endurance_log); + err = nvme_get_log_endurance_group(fd, cfg.group_id, &endurance_log); if (!err) nvme_show_endurance_log(&endurance_log, cfg.group_id, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("endurance log"); + fprintf(stderr, "endurance log: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; +} + +void collect_effects_log(int fd, enum nvme_csi csi, struct list_head *list, int flags) +{ + int err; + nvme_effects_log_node_t *node = malloc(sizeof(nvme_effects_log_node_t)); + if (!node) + return; + node->csi = csi; + + err = nvme_get_log_cmd_effects(fd, csi, &node->effects); + if (!err) { + list_add(list, &node->node); + return; + } + else if (err > 0) + nvme_show_status(err); + else + fprintf(stderr, "effects log page: %s\n", nvme_strerror(errno)); + + free(node); } static int get_effects_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -593,25 +609,34 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl const char *desc = "Retrieve command effects log page and print the table."; const char *raw = "show log in binary format"; const char *human_readable = "show log in readable format"; - struct nvme_effects_log_page effects; + const char *csi = ""; + struct list_head log_pages; + nvme_effects_log_node_t *node; + + void *bar = NULL; int err = -1, fd; enum nvme_print_flags flags; struct config { - int raw_binary; - int human_readable; - char *output_format; + char *output_format; + bool human_readable; + bool raw_binary; + int csi; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .human_readable = false, + .raw_binary = false, + .csi = -1, }; OPT_ARGS(opts) = { 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), + OPT_INT("csi", 'c', &cfg.csi, csi), OPT_END() }; @@ -627,17 +652,52 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl if (cfg.human_readable) flags |= VERBOSE; - err = nvme_effects_log(fd, &effects); - if (!err) - nvme_show_effects_log(&effects, flags); - else if (err > 0) - nvme_show_status(err); - else - perror("effects log page"); + list_head_init(&log_pages); + + if (cfg.csi < 0) { + nvme_root_t nvme_root; + uint64_t cap; + int nvme_command_set_supported; + int other_command_sets_supported; + nvme_root = nvme_scan(NULL); + bar = mmap_registers(nvme_root, devicename); + nvme_free_tree(nvme_root); + + if (!bar) { + goto close_fd; + } + cap = mmio_read64(bar + NVME_REG_CAP); + munmap(bar, getpagesize()); + + nvme_command_set_supported = NVME_CAP_CSS(cap) & NVME_CAP_CSS_NVM; + other_command_sets_supported = NVME_CAP_CSS(cap) & NVME_CAP_CSS_CSI; + + + if (nvme_command_set_supported) { + collect_effects_log(fd, NVME_CSI_NVM, &log_pages, flags); + } + + if (other_command_sets_supported) { + collect_effects_log(fd, NVME_CSI_ZNS, &log_pages, flags); + } + + nvme_print_effects_log_pages(&log_pages, flags); + + } + else { + collect_effects_log(fd, cfg.csi, &log_pages, flags); + nvme_print_effects_log_pages(&log_pages, flags); + } + + close_fd: + while ((node = list_pop(&log_pages, nvme_effects_log_node_t, node))) { + free(node); + } + close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_supported_log_pages(int argc, char **argv, struct command *cmd, @@ -645,18 +705,19 @@ static int get_supported_log_pages(int argc, char **argv, struct command *cmd, { const char *desc = "Retrieve supported logs and print the table."; const char *verbose = "Increase output verbosity"; - struct nvme_support_log_pages supports; + struct nvme_supported_log_pages supports; int err = -1, fd; enum nvme_print_flags flags; struct config { - int verbose; - char *output_format; + char *output_format; + bool verbose; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .verbose = false }; OPT_ARGS(opts) = { @@ -676,18 +737,19 @@ static int get_supported_log_pages(int argc, char **argv, struct command *cmd, if (cfg.verbose) flags |= VERBOSE; - err = nvme_supported_log(fd, &supports); + err = nvme_get_log_supported_log_pages(fd, false, &supports); if (!err) nvme_show_supported_log(&supports, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("supported log pages"); + fprintf(stderr, "supported log pages: %s", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -703,14 +765,15 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug int err = -1, fd; struct config { - __u32 log_entries; - int raw_binary; - char *output_format; + __u32 log_entries; + char *output_format; + bool raw_binary; }; struct config cfg = { - .log_entries = 64, - .output_format = "normal", + .log_entries = 64, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -732,7 +795,6 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug if (!cfg.log_entries) { fprintf(stderr, "non-zero log-entries is required param\n"); - errno = EINVAL; err = -1; goto close_fd; } @@ -743,7 +805,6 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug goto close_fd; } else if (err) { fprintf(stderr, "could not identify controller\n"); - errno = ENODEV; err = -1; goto close_fd; } @@ -751,13 +812,11 @@ 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) { - perror("could not alloc buffer for error log\n"); - errno = ENOMEM; err = -1; goto close_fd; } - err = nvme_error_log(fd, cfg.log_entries, err_log); + err = nvme_get_log_error(fd, cfg.log_entries, true, err_log); if (!err) nvme_show_error_log(err_log, cfg.log_entries, devicename, flags); else if (err > 0) @@ -768,7 +827,7 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -776,17 +835,18 @@ static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin const char *desc = "Retrieve the firmware log for the "\ "specified device in either decoded format (default) or binary."; const char *raw = "use binary output"; - struct nvme_firmware_log_page fw_log; + struct nvme_firmware_slot fw_log; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - int raw_binary; - char *output_format; + char *output_format; + bool raw_binary; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -805,36 +865,37 @@ static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin if (cfg.raw_binary) flags = BINARY; - err = nvme_fw_log(fd, &fw_log); + err = nvme_get_log_fw_slot(fd, true, &fw_log); if (!err) nvme_show_fw_log(&fw_log, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("fw log"); + fprintf(stderr, "fw log: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_changed_ns_list_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct nvme_changed_ns_list_log changed_ns_list_log; const char *desc = "Retrieve Changed Namespaces log for the given device "\ "in either decoded format "\ "(default) or binary."; const char *raw = "output in binary format"; + struct nvme_ns_list changed_ns_list_log; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - int raw_binary; - char *output_format; + char *output_format; + bool raw_binary; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -853,18 +914,19 @@ static int get_changed_ns_list_log(int argc, char **argv, struct command *cmd, s if (cfg.raw_binary) flags = BINARY; - err = nvme_changed_ns_list_log(fd, &changed_ns_list_log); + err = nvme_get_log_changed_ns_list(fd, true, &changed_ns_list_log); if (!err) nvme_show_changed_ns_list_log(&changed_ns_list_log, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("changed ns list log"); + fprintf(stderr, "changed ns list log: %s\n", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_pred_lat_per_nvmset_log(int argc, char **argv, @@ -875,25 +937,26 @@ static int get_pred_lat_per_nvmset_log(int argc, char **argv, "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; + struct nvme_nvmset_predictable_lat_log plpns_log; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - __u16 nvmset_id; - char *output_format; - int raw_binary; + __u16 nvmset_id; + char *output_format; + bool raw_binary; }; struct config cfg = { - .nvmset_id = 1, - .output_format = "normal", + .nvmset_id = 1, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { - OPT_SHRT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id), + OPT_SHRT("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_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_END() }; @@ -907,20 +970,20 @@ static int get_pred_lat_per_nvmset_log(int argc, char **argv, if (cfg.raw_binary) flags = BINARY; - err = nvme_predictable_latency_per_nvmset_log(fd, - cfg.nvmset_id, &plpns_log); + err = nvme_get_log_predictable_lat_nvmset(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"); + fprintf(stderr, "predictable latency per nvm set: %s\n", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_pred_lat_event_agg_log(int argc, char **argv, @@ -934,23 +997,24 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, "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 = -1, fd; + struct nvme_id_ctrl ctrl; __u32 log_size; + void *pea_log; + int err, fd; struct config { - __u64 log_entries; - bool rae; - char *output_format; - int raw_binary; + __u64 log_entries; + bool rae; + char *output_format; + bool raw_binary; }; struct config cfg = { - .log_entries = 2044, - .rae = false, - .output_format = "normal", + .log_entries = 2044, + .rae = false, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -973,14 +1037,14 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, if (!cfg.log_entries) { fprintf(stderr, "non-zero log-entries is required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } err = nvme_identify_ctrl(fd, &ctrl); if (err < 0) { - perror("identify controller"); + fprintf(stderr, "identify controller: %s\n", + nvme_strerror(errno)); goto close_fd; } else if (err) { nvme_show_status(err); @@ -991,28 +1055,25 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, 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"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_predictable_latency_event_agg_log(fd, pea_log, cfg.rae, - log_size); + err = nvme_get_log_predictable_lat_event(fd, cfg.rae, 0, log_size, pea_log); 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"); + fprintf(stderr, "predictable latency event aggregate log page: %s", + nvme_strerror(errno)); free(pea_log); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_persistent_event_log(int argc, char **argv, @@ -1025,28 +1086,29 @@ static int get_persistent_event_log(int argc, char **argv, " 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, *collected_head; + struct nvme_persistent_event_log *pevent, *pevent_collected; enum nvme_print_flags flags; - int err = -1, fd; + void *pevent_log_info; + int err, fd; bool huge; struct config { - __u8 action; - __u32 log_len; - int raw_binary; - char *output_format; + __u8 action; + __u32 log_len; + char *output_format; + bool raw_binary; }; struct config cfg = { - .action = 0xff, - .log_len = 0, - .output_format = "normal", + .action = 0xff, + .log_len = 0, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { OPT_BYTE("action", 'a', &cfg.action, action), - OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + 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() @@ -1062,42 +1124,33 @@ static int get_persistent_event_log(int argc, char **argv, if (cfg.raw_binary) flags = BINARY; - if (cfg.action > 3) { - fprintf(stderr, "invalid action field: %u\n", cfg.action); - errno = EINVAL; - err = -1; - goto close_fd; - } - - pevent_log_head = calloc(sizeof(*pevent_log_head), 1); - if (!pevent_log_head) { - perror("could not alloc buffer for persistent " \ - "event log header\n"); - errno = ENOMEM; - err = -1; + pevent = calloc(sizeof(*pevent), 1); + if (!pevent) { + err = -ENOMEM; goto close_fd; } - err = nvme_persistent_event_log(fd, cfg.action, - sizeof(*pevent_log_head), pevent_log_head); + err = nvme_get_log_persistent_event(fd, cfg.action, + sizeof(*pevent), pevent); if (err < 0) { - perror("persistent event log"); - goto free_head; + fprintf(stderr, "persistent event log: %s\n", + nvme_strerror(errno)); + goto free_pevent; } else if (err) { nvme_show_status(err); - goto free_head; + goto free_pevent; } if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) { printf("Releasing Persistent Event Log Context\n"); - goto free_head; + goto free_pevent; } if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { - cfg.log_len = le64_to_cpu(pevent_log_head->tll); + cfg.log_len = le64_to_cpu(pevent->tll); } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { printf("Establishing Persistent Event Log Context\n"); - goto free_head; + goto free_pevent; } /* @@ -1112,45 +1165,45 @@ static int get_persistent_event_log(int argc, char **argv, pevent_log_info = nvme_alloc(cfg.log_len, &huge); if (!pevent_log_info) { - perror("could not alloc buffer for persistent event log page\n"); - errno = ENOMEM; - err = -1; - goto free_head; + err = -ENOMEM; + goto free_pevent; } - err = nvme_persistent_event_log(fd, cfg.action, + err = nvme_get_log_persistent_event(fd, cfg.action, cfg.log_len, pevent_log_info); if (!err) { - err = nvme_persistent_event_log(fd, cfg.action, - sizeof(*pevent_log_head), pevent_log_head); + err = nvme_get_log_persistent_event(fd, cfg.action, + sizeof(*pevent), pevent); if (err < 0) { - perror("persistent event log"); + fprintf(stderr, "persistent event log: %s\n", + nvme_strerror(errno)); goto free; } else if (err) { nvme_show_status(err); goto free; } - collected_head = pevent_log_info; - if(collected_head->gen_number != pevent_log_head->gen_number) { + pevent_collected = pevent_log_info; + if (pevent_collected->gen_number != pevent->gen_number) { printf("Collected Persistent Event Log may be invalid, "\ "Re-read the log is reiquired\n"); goto free; } + nvme_show_persistent_event_log(pevent_log_info, cfg.action, cfg.log_len, devicename, flags); - } - else if (err > 0) + } else if (err > 0) nvme_show_status(err); else - perror("persistent event log"); + fprintf(stderr, "persistent event log: %s\n", + nvme_strerror(errno)); free: nvme_free(pevent_log_info, huge); -free_head: - free(pevent_log_head); +free_pevent: + free(pevent); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_endurance_event_agg_log(int argc, char **argv, @@ -1167,20 +1220,21 @@ static int get_endurance_event_agg_log(int argc, char **argv, void *endurance_log; struct nvme_id_ctrl ctrl; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; __u32 log_size; struct config { - __u64 log_entries; - bool rae; - char *output_format; - int raw_binary; + __u64 log_entries; + bool rae; + char *output_format; + bool raw_binary; }; struct config cfg = { - .log_entries = 2044, - .rae = false, - .output_format = "normal", + .log_entries = 2044, + .rae = false, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -1203,19 +1257,18 @@ static int get_endurance_event_agg_log(int argc, char **argv, if (!cfg.log_entries) { fprintf(stderr, "non-zero log-entries is required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } err = nvme_identify_ctrl(fd, &ctrl); if (err < 0) { - perror("identify controller"); + fprintf(stderr, "identify controller: %s\n", + nvme_strerror(errno)); goto close_fd; } else if (err) { fprintf(stderr, "could not identify controller\n"); - errno = ENODEV; - err = -1; + err = -ENODEV; goto close_fd; } @@ -1223,28 +1276,25 @@ static int get_endurance_event_agg_log(int argc, char **argv, 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"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_endurance_group_event_agg_log(fd, endurance_log, cfg.rae, - log_size); + err = nvme_get_log_endurance_grp_evt(fd, cfg.rae, 0, log_size, endurance_log); 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"); + fprintf(stderr, "endurance group event aggregate log page: %s\n", + nvme_strerror(errno)); free(endurance_log); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_lba_status_log(int argc, char **argv, @@ -1256,17 +1306,17 @@ static int get_lba_status_log(int argc, char **argv, const char *rae = "Retain an Asynchronous Event"; void *lab_status; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; __u32 lslplen; struct config { - bool rae; - char *output_format; + bool rae; + char *output_format; }; struct config cfg = { - .rae = false, - .output_format = "normal", + .rae = false, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -1283,9 +1333,10 @@ static int get_lba_status_log(int argc, char **argv, if (flags < 0) goto close_fd; - err = nvme_lba_status_log(fd, &lslplen, true, sizeof(__u32)); + err = nvme_get_log_lba_status(fd, true, 0, sizeof(__u32), &lslplen); if (err < 0) { - perror("lba status log page"); + fprintf(stderr, "lba status log page: %s\n", + nvme_strerror(errno)); goto close_fd; } else if (err) { nvme_show_status(err); @@ -1294,25 +1345,24 @@ static int get_lba_status_log(int argc, char **argv, lab_status = calloc(lslplen, 1); if (!lab_status) { - perror("could not alloc buffer for lba status log"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_lba_status_log(fd, lab_status, cfg.rae, lslplen); + err = nvme_get_log_lba_status(fd, cfg.rae, 0, lslplen, lab_status); 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"); + fprintf(stderr, "lba status log page: %s\n", + nvme_strerror(errno)); free(lab_status); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_resv_notif_log(int argc, char **argv, @@ -1323,16 +1373,16 @@ static int get_resv_notif_log(int argc, char **argv, "log page and prints it, for the given " \ "device in either decoded format(default), " \ "json or binary."; - struct nvme_resv_notif_log resv; + struct nvme_resv_notification_log resv; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - char *output_format; + char *output_format; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -1348,18 +1398,19 @@ static int get_resv_notif_log(int argc, char **argv, if (flags < 0) goto close_fd; - err = nvme_resv_notif_log(fd, &resv); + err = nvme_get_log_reservation(fd, true, &resv); if (!err) nvme_show_resv_notif_log(&resv, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("resv notifi log"); + fprintf(stderr, "resv notifi log: %s\n", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } @@ -1371,22 +1422,22 @@ static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct "json or binary."; const char *lsp = "log specific field"; const char *fname = "boot partition data output file name"; - struct nvme_boot_part_hdr boot; + struct nvme_boot_partition boot; __u8 *bp_log; enum nvme_print_flags flags; int err = -1, fd = 0, output = 0; __u32 bpsz = 0; struct config { - __u8 lsp; - char *output_format; - char *file_name; + __u8 lsp; + char *file_name; + char *output_format; }; struct config cfg = { - .lsp = 0, - .file_name = NULL, - .output_format = "normal", + .lsp = 0, + .output_format = "normal", + .file_name = NULL, }; OPT_ARGS(opts) = { @@ -1406,14 +1457,12 @@ static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct if (!cfg.file_name) { fprintf(stderr, "Please provide an output file!\n"); - errno = EINVAL; err = -1; goto close_fd; } if (cfg.lsp > 128) { fprintf(stderr, "invalid lsp param: %u\n", cfg.lsp); - errno = EINVAL; err = -1; goto close_fd; } @@ -1426,9 +1475,11 @@ static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct goto close_fd; } - err = nvme_boot_part_log(fd, cfg.lsp, &boot, sizeof(boot)); + err = nvme_get_log_boot_partition(fd, false, cfg.lsp, + sizeof(boot), &boot); if (err < 0) { - perror("boot partition log"); + fprintf(stderr, "boot partition log: %s\n", + nvme_strerror(errno)); goto close_output; } else if (err) { nvme_show_status(err); @@ -1438,19 +1489,20 @@ static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct bpsz = (boot.bpinfo & 0x7fff) * 128 * 1024; bp_log = calloc(sizeof(boot) + bpsz, 1); if (!bp_log) { - perror("could not alloc buffer for boot partition log"); - errno = ENOMEM; err = -1; goto close_output; } - err = nvme_boot_part_log(fd, cfg.lsp, &bp_log, sizeof(boot) + bpsz); + err = nvme_get_log_boot_partition(fd, false, cfg.lsp, + sizeof(boot) + bpsz, + (struct nvme_boot_partition *)bp_log); if (!err) nvme_show_boot_part_log(&bp_log, devicename, flags, sizeof(boot) + bpsz); else if (err > 0) nvme_show_status(err); else - perror("boot partition log"); + fprintf(stderr, "boot partition log: %s\n", + nvme_strerror(errno)); err = write(output, (void *) bp_log + sizeof(boot), bpsz); if (err != bpsz) { @@ -1467,7 +1519,118 @@ close_output: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; +} + +static int get_media_unit_stat_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve the configuration and wear of media units and print it"; + const char *domainid = "Domain Identifier"; + const char *raw = "use binary output"; + struct nvme_media_unit_stat_log mus; + + int err = -1, fd; + enum nvme_print_flags flags; + + struct config { + __u16 domainid; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .domainid = 0, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("domain-id", 'd', &cfg.domainid, domainid), + 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_get_log_media_unit_stat(fd, cfg.domainid, &mus); + if (!err) + nvme_show_media_unit_stat_log(&mus, flags); + else if (err > 0) + nvme_show_status(err); + else + fprintf(stderr, "media unit status log: %s\n", + nvme_strerror(errno)); + +close_fd: + close(fd); +ret: + return err; +} + +static int get_supp_cap_config_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve the list of Supported Capacity Configuration Descriptors"; + const char *domainid = "Domain Identifier"; + const char *raw = "use binary output"; + struct nvme_supported_cap_config_list_log cap_log; + + int err = -1, fd; + enum nvme_print_flags flags; + + struct config { + __u16 domainid; + char *output_format; + bool raw_binary; + }; + + struct config cfg = { + .domainid = 0, + .output_format = "normal", + .raw_binary = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("domain-id", 'd', &cfg.domainid, domainid), + 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_get_log_support_cap_config_list(fd, cfg.domainid, &cap_log); + if (!err) + nvme_show_supported_cap_config_log(&cap_log, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("supported capacity configuration list log"); + +close_fd: + close(fd); +ret: + return err; } static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1487,35 +1650,37 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl const char *uuid_index = "UUID index"; const char *csi = "command set identifier"; const char *offset_type = "offset type"; - int err = -1, fd; + int err, fd; unsigned char *log; struct config { - __u16 lsi; - __u32 namespace_id; - __u8 log_id; - __u32 log_len; - __u32 aen; - __u64 lpo; - __u8 lsp; - __u8 uuid_index; - __u8 csi; - int ot; - int rae; - int raw_binary; + __u32 namespace_id; + __u8 log_id; + __u32 log_len; + __u32 aen; + __u64 lpo; + __u8 lsp; + __u16 lsi; + bool rae; + __u8 uuid_index; + bool raw_binary; + __u8 csi; + bool ot; }; struct config cfg = { - .namespace_id = NVME_NSID_ALL, - .log_id = 0xff, - .log_len = 0, - .lpo = NVME_NO_LOG_LPO, - .lsp = NVME_NO_LOG_LSP, - .lsi = 0, - .rae = 0, - .uuid_index = 0, - .csi = 0, - .ot = 0, + .namespace_id = NVME_NSID_ALL, + .log_id = 0xff, + .log_len = 0, + .aen = 0, + .lpo = NVME_LOG_LPO_NONE, + .lsp = NVME_LOG_LSP_NONE, + .lsi = NVME_LOG_LSI_NONE, + .rae = false, + .uuid_index = NVME_UUID_NONE, + .raw_binary = false, + .csi = NVME_CSI_NVM, + .ot = false, }; OPT_ARGS(opts) = { @@ -1528,9 +1693,9 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl OPT_SHRT("lsi", 'S', &cfg.lsi, lsi), OPT_FLAG("rae", 'r', &cfg.rae, rae), OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_BYTE("csi", 'y', &cfg.csi, csi), OPT_FLAG("ot", 'O', &cfg.ot, offset_type), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_END() }; @@ -1544,23 +1709,20 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl } if (!cfg.log_len) { - fprintf(stderr, "non-zero log-len is required param\n"); - errno = EINVAL; - err = -1; + perror("non-zero log-len is required param\n"); + err = -EINVAL; goto close_fd; } if (cfg.lsp > 128) { - fprintf(stderr, "invalid lsp param: %u\n", cfg.lsp); - errno = EINVAL; - err = -1; + perror("invalid lsp param\n"); + err = -EINVAL; goto close_fd; } if (cfg.uuid_index > 128) { - fprintf(stderr, "invalid uuid index param: %u\n", cfg.uuid_index); - errno = EINVAL; - err = -1; + perror("invalid uuid index param\n"); + err = -EINVAL; goto close_fd; } @@ -1571,27 +1733,42 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl goto close_fd; } - err = nvme_get_log14(fd, cfg.namespace_id, cfg.log_id, - cfg.lsp, cfg.lpo, cfg.lsi, cfg.rae, - cfg.uuid_index, cfg.csi, cfg.ot, cfg.log_len, log); + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .lid = cfg.log_id, + .nsid = cfg.namespace_id, + .lpo = cfg.lpo, + .lsp = cfg.lsp, + .lsi = cfg.lsi, + .rae = cfg.rae, + .uuidx = cfg.uuid_index, + .csi = cfg.csi, + .ot = cfg.ot, + .len = cfg.log_len, + .log = log, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_get_log(&args); if (!err) { if (!cfg.raw_binary) { printf("Device:%s log-id:%d namespace-id:%#x\n", - devicename, cfg.log_id, - cfg.namespace_id); + devicename, cfg.log_id, + cfg.namespace_id); d(log, cfg.log_len, 16, 1); } else d_raw((unsigned char *)log, cfg.log_len); } else if (err > 0) nvme_show_status(err); else - perror("log page"); + fprintf(stderr, "log page: %s\n", nvme_strerror(errno)); free(log); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int sanitize_log(int argc, char **argv, struct command *command, struct plugin *plugin) @@ -1602,18 +1779,20 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p const char *human_readable = "show log in readable format"; struct nvme_sanitize_log_page sanitize_log; enum nvme_print_flags flags; - int fd, err = -1; + int fd, err; struct config { - bool rae; - int raw_binary; - int human_readable; - char *output_format; + bool rae; + char *output_format; + bool human_readable; + bool raw_binary; }; struct config cfg = { - .rae = false, - .output_format = "normal", + .rae = false, + .output_format = "normal", + .human_readable = false, + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -1636,17 +1815,18 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p if (cfg.human_readable) flags |= VERBOSE; - err = nvme_sanitize_log(fd, cfg.rae, &sanitize_log); + err = nvme_get_log_sanitize(fd, cfg.rae, &sanitize_log); if (!err) nvme_show_sanitize_log(&sanitize_log, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("sanitize status log"); + fprintf(stderr, "sanitize status log: %s\n", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_fid_support_effects_log(int argc, char **argv, struct command *cmd, @@ -1654,17 +1834,18 @@ static int get_fid_support_effects_log(int argc, char **argv, struct command *cm { const char *desc = "Retrieve FID Support and Effects log and show it."; const char *human_readable = "show log in readable format"; - struct nvme_fid_support_effects fid_support_log; + struct nvme_fid_supported_effects_log fid_support_log; enum nvme_print_flags flags; int fd, err = -1; struct config { - int human_readable; - char *output_format; + char *output_format; + bool human_readable; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .human_readable = false, }; OPT_ARGS(opts) = { @@ -1683,17 +1864,67 @@ static int get_fid_support_effects_log(int argc, char **argv, struct command *cm if (cfg.human_readable) flags |= VERBOSE; - err = nvme_fid_support_effects_log(fd, &fid_support_log); + err = nvme_get_log_fid_supported_effects(fd, false, &fid_support_log); if (!err) nvme_show_fid_support_effects_log(&fid_support_log, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("fid support effects log"); + fprintf(stderr, "fid support effects log: %s\n", + nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; +} + +static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve NVMe-MI Command Support and Effects log and show it."; + const char *human_readable = "show log in readable format"; + struct nvme_mi_cmd_supported_effects_log mi_cmd_support_log; + enum nvme_print_flags flags; + int fd, err = -1; + + struct config { + char *output_format; + bool human_readable; + }; + + struct config cfg = { + .output_format = "normal", + .human_readable = false, + }; + + 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_get_log_mi_cmd_supported_effects(fd, false, &mi_cmd_support_log); + if (!err) + nvme_show_mi_cmd_support_effects_log(&mi_cmd_support_log, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + fprintf(stderr, "mi command support effects log: %s\n", + nvme_strerror(errno)); +close_fd: + close(fd); +ret: + return err; } static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1702,19 +1933,20 @@ static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin * "given device is part of, or optionally controllers attached to a specific namespace."; const char *controller = "controller to display"; const char *namespace_id = "optional namespace attached to controller"; - int err = -1, fd; - struct nvme_controller_list *cntlist; + int err, fd; + struct nvme_ctrl_list *cntlist; enum nvme_print_flags flags; struct config { - __u16 cntid; - __u32 namespace_id; - char *output_format; + __u16 cntid; + __u32 namespace_id; + char *output_format; }; struct config cfg = { - .cntid = 0, - .output_format = "normal", + .cntid = 0, + .namespace_id = NVME_NSID_NONE, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -1731,31 +1963,31 @@ static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin * err = flags = validate_output_format(cfg.output_format); if (flags < 0) goto close_fd; - if (flags != JSON && flags != NORMAL) { - err = -EINVAL; - goto close_fd; - } if (posix_memalign((void *)&cntlist, getpagesize(), 0x1000)) { fprintf(stderr, "can not allocate controller list payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_identify_ctrl_list(fd, cfg.namespace_id, cfg.cntid, cntlist); + if (cfg.namespace_id == NVME_NSID_NONE) + err = nvme_identify_ctrl_list(fd, cfg.cntid, cntlist); + else + err = nvme_identify_nsid_ctrl_list(fd, cfg.namespace_id, + cfg.cntid, cntlist); if (!err) nvme_show_list_ctrl(cntlist, flags); else if (err > 0) nvme_show_status(err); else - perror("id controller list"); + fprintf(stderr, "id controller list: %s\n", + nvme_strerror(errno)); free(cntlist); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1765,25 +1997,27 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl 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 = -1, fd; - __le32 ns_list[1024]; + int err, fd; + struct nvme_ns_list ns_list; enum nvme_print_flags flags; struct config { - __u32 namespace_id; - int all; - __u8 csi; - char *output_format; + __u32 namespace_id; + int csi; + bool all; + char *output_format; }; struct config cfg = { - .namespace_id = 1, + .namespace_id = 1, + .csi = -1, + .all = false, .output_format = "normal", }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_BYTE("csi", 'y', &cfg.csi, csi), + OPT_INT("csi", 'y', &cfg.csi, csi), OPT_FLAG("all", 'a', &cfg.all, all), OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), OPT_END() @@ -1802,20 +2036,95 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl } if (!cfg.namespace_id) { - errno = EINVAL; - err = -1; + err = -EINVAL; fprintf(stderr, "invalid nsid parameter\n"); goto close_fd; } - err = nvme_identify_ns_list_csi(fd, cfg.namespace_id - 1, cfg.csi, - !!cfg.all, ns_list); + if (cfg.csi < 0) { + if (cfg.all) + err = nvme_identify_allocated_ns_list(fd, + cfg.namespace_id - 1, &ns_list); + else + err = nvme_identify_active_ns_list(fd, + cfg.namespace_id - 1, &ns_list); + + } else { + if (cfg.all) + err = nvme_identify_allocated_ns_list_csi(fd, + cfg.namespace_id - 1, cfg.csi, &ns_list); + else + err = nvme_identify_active_ns_list_csi(fd, + cfg.namespace_id - 1, cfg.csi, &ns_list); + } + + if (!err) + nvme_show_list_ns(&ns_list, flags); + else if (err > 0) + nvme_show_status(err); + else + fprintf(stderr, "id namespace list: %s", + nvme_strerror(errno)); +close_fd: + close(fd); +ret: + return err; +} + +static int id_ns_lba_format(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an Identify Namespace command to the given "\ + "device, returns capability field properties of the specified "\ + "LBA Format index in various formats."; + const char *lba_format_index = "The index into the LBA Format list "\ + "identifying the LBA Format capabilities that are to be returned"; + const char *uuid_index = "UUID index"; + const char *verbose = "Increase output verbosity"; + enum nvme_print_flags flags; + struct nvme_id_ns ns; + int err = -1, fd; + + struct config { + __u16 lba_format_index; + __u8 uuid_index; + bool verbose; + char *output_format; + }; + + struct config cfg = { + .lba_format_index = 0, + .uuid_index = NVME_UUID_NONE, + .verbose = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + 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; + + if (cfg.verbose) + flags |= VERBOSE; + + err = nvme_identify_ns_csi_user_data_format(fd, cfg.lba_format_index, + cfg.uuid_index, NVME_CSI_NVM, &ns); if (!err) - nvme_show_list_ns(ns_list, flags); + nvme_show_id_ns(&ns, 0, cfg.lba_format_index, true, flags); else if (err > 0) nvme_show_status(err); else - perror("id namespace list"); + perror("identify namespace for specific LBA format"); close_fd: close(fd); ret: @@ -1829,17 +2138,17 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *cmd, "group id"; const char *endurance_grp_id = "Endurance Group ID"; int err = -1, fd; - struct nvme_endurance_group_list *endgrp_list; + struct nvme_id_endurance_group_list *endgrp_list; enum nvme_print_flags flags; struct config { - __u16 endgrp_id; - char *output_format; + __u16 endgrp_id; + char *output_format; }; struct config cfg = { - .endgrp_id = 0, - .output_format = "normal", + .endgrp_id = 0, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -1862,8 +2171,6 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *cmd, } if (posix_memalign((void *)&endgrp_list, getpagesize(), 0x1000)) { - fprintf(stderr, "can not allocate memory for endurance gropu list\n"); - errno = ENOMEM; err = -1; goto close_fd; } @@ -1874,13 +2181,14 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *cmd, else if (err > 0) nvme_show_status(err); else - perror("id endurance group list"); + fprintf(stderr, "Id endurance group list: %s", + nvme_strerror(errno)); free(endgrp_list); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1893,7 +2201,7 @@ static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin * "the namespace is not already inactive, once deleted."; const char *namespace_id = "namespace to delete"; const char *timeout = "timeout value, in milliseconds"; - int err = -1, fd; + int err, fd; struct config { __u32 namespace_id; @@ -1902,7 +2210,7 @@ static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin * struct config cfg = { .namespace_id = 0, - .timeout = NVME_IOCTL_TIMEOUT, + .timeout = 120000, }; OPT_ARGS(opts) = { @@ -1916,44 +2224,46 @@ static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin * goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", + nvme_strerror(errno)); goto close_fd; } } - err = nvme_ns_delete(fd, cfg.namespace_id, cfg.timeout); + err = nvme_ns_mgmt_delete(fd, cfg.namespace_id); if (!err) printf("%s: Success, deleted nsid:%d\n", cmd->name, cfg.namespace_id); else if (err > 0) nvme_show_status(err); else - perror("delete namespace"); + fprintf(stderr, "delete namespace: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, struct command *cmd) { - int err = -1, num, i, fd, list[2048]; + int err, num, fd, i, list[2048]; + struct nvme_ctrl_list cntlist; __u16 ctrlist[2048]; const char *namespace_id = "namespace to attach"; const char *cont = "optional comma-sep controller id list"; struct config { - char *cntlist; - __u32 namespace_id; + __u32 namespace_id; + char *cntlist; }; struct config cfg = { - .cntlist = "", - .namespace_id = 0, + .namespace_id = 0, + .cntlist = "", }; OPT_ARGS(opts) = { @@ -1969,8 +2279,7 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s if (!cfg.namespace_id) { fprintf(stderr, "%s: namespace-id parameter required\n", cmd->name); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } @@ -1982,15 +2291,19 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s if (num == -1) { fprintf(stderr, "%s: controller id list is malformed\n", cmd->name); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } for (i = 0; i < num; i++) - ctrlist[i] = (uint16_t)list[i]; + ctrlist[i] = (__u16)list[i]; + + nvme_init_ctrl_list(&cntlist, num, ctrlist); - err = nvme_ns_attachment(fd, cfg.namespace_id, num, ctrlist, attach); + if (attach) + err = nvme_ns_attach_ctrls(fd, cfg.namespace_id, &cntlist); + else + err = nvme_ns_detach_ctrls(fd, cfg.namespace_id, &cntlist); if (!err) printf("%s: Success, nsid:%d\n", cmd->name, cfg.namespace_id); @@ -2002,7 +2315,7 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int attach_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2040,11 +2353,12 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * const char *anagrpid = "ANA Group Identifier (ANAGRPID)"; const char *nvmsetid = "NVM Set Identifier (NVMSETID)"; const char *csi = "command set identifier (CSI)"; + const char *lbstm = "logical block storage tag mask (LBSTM)"; const char *timeout = "timeout value, in milliseconds"; const char *bs = "target block size, specify only if \'FLBAS\' "\ "value not entered"; - int err = -1, fd, i; + int err = 0, fd, i; struct nvme_id_ns ns; __u32 nsid; @@ -2058,15 +2372,22 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * __u16 nvmsetid; __u64 bs; __u32 timeout; - __u8 csi; + __u8 csi; + __u64 lbstm; }; struct config cfg = { + .nsze = 0, + .ncap = 0, .flbas = 0xff, + .dps = 0, + .nmic = 0, .anagrpid = 0, .nvmsetid = 0, .bs = 0x00, - .timeout = NVME_IOCTL_TIMEOUT, + .timeout = 120000, + .csi = 0, + .lbstm = 0, }; OPT_ARGS(opts) = { @@ -2080,6 +2401,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), OPT_UINT("timeout", 't', &cfg.timeout, timeout), OPT_BYTE("csi", 'y', &cfg.csi, csi), + OPT_SUFFIX("lbstm", 'l', &cfg.lbstm, lbstm), OPT_END() }; @@ -2090,8 +2412,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * if (cfg.flbas != 0xff && cfg.bs != 0x00) { fprintf(stderr, "Invalid specification of both FLBAS and Block Size, please specify only one\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.bs) { @@ -2099,14 +2420,14 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * fprintf(stderr, "Invalid value for block size (%"PRIu64"). Block size must be a power of two\n", (uint64_t)cfg.bs); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_identify_ns(fd, NVME_NSID_ALL, 0, &ns); + err = nvme_identify_ns(fd, NVME_NSID_ALL, &ns); if (err) { if (err < 0) - perror("identify-namespace"); + fprintf(stderr, "identify-namespace: %s", + nvme_strerror(errno)); else { fprintf(stderr, "identify failed\n"); nvme_show_status(err); @@ -2128,51 +2449,93 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * fprintf(stderr, "Please correct block size, or specify FLBAS directly\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_ns_create(fd, cfg.nsze, cfg.ncap, cfg.flbas, cfg.dps, cfg.nmic, - cfg.anagrpid, cfg.nvmsetid, cfg.csi, cfg.timeout, - &nsid); + struct nvme_id_ns ns2 = { + .nsze = cpu_to_le64(cfg.nsze), + .ncap = cpu_to_le64(cfg.ncap), + .flbas = cfg.flbas, + .dps = cfg.dps, + .nmic = cfg.nmic, + .anagrpid = cpu_to_le32(cfg.anagrpid), + .nvmsetid = cpu_to_le16(cfg.nvmsetid), + .lbstm = cpu_to_le64(cfg.lbstm), + }; + + err = nvme_ns_mgmt_create(fd, &ns2, &nsid, cfg.timeout, cfg.csi); if (!err) printf("%s: Success, created nsid:%d\n", cmd->name, nsid); else if (err > 0) nvme_show_status(err); else - perror("create namespace"); + fprintf(stderr, "create namespace: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; +} + +static bool nvme_match_device_filter(nvme_subsystem_t s, + nvme_ctrl_t c, nvme_ns_t ns, void *f_args) +{ + int ret, instance, nsid, s_num; + char *devname = f_args; + + if (!devname || !strlen(devname)) + return true; + + ret = sscanf(devname, "nvme%dn%d", &instance, &nsid); + if (ret != 2) + return true; + + if (s) { + ret = sscanf(nvme_subsystem_get_name(s), "nvme-subsys%d", + &s_num); + if (ret == 1 && s_num == instance) + return true; + } + if (c) { + s = nvme_ctrl_get_subsystem(c); + + ret = sscanf(nvme_subsystem_get_name(s), "nvme-subsys%d", + &s_num); + if (ret == 1 && s_num == instance) + return true; + } + if (ns) { + if (!strcmp(devname, nvme_ns_get_name(ns))) + return true; + } + + return false; } static int list_subsys(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct nvme_topology t = { }; + nvme_root_t r = NULL; enum nvme_print_flags flags; - char *subsysnqn = NULL; const char *desc = "Retrieve information for subsystems"; const char *verbose = "Increase output verbosity"; - __u32 ns_instance = 0; - int err = -1, nsid = 0; + nvme_scan_filter_t filter = NULL; + int err; struct config { - char *output_format; - int verbose; + char *output_format; + int verbose; }; struct config cfg = { - .output_format = "normal", - .verbose = 0, + .output_format = "normal", + .verbose = 0, }; OPT_ARGS(opts) = { OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), - OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_INCR("verbose", 'v', &cfg.verbose, verbose), OPT_END() }; @@ -2181,95 +2544,77 @@ static int list_subsys(int argc, char **argv, struct command *cmd, goto ret; devicename = NULL; - if (optind < argc) { - char path[512]; - int id, fd; - - devicename = basename(argv[optind]); - if (sscanf(devicename, "nvme%dn%d", &id, &ns_instance) != 2) { - fprintf(stderr, "%s is not a NVMe namespace device\n", - argv[optind]); - errno = EINVAL; - err = -1; - goto ret; - } - sprintf(path, "/dev/%s", devicename); - fd = open(path, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Cannot read nsid from %s\n", - devicename); - errno = EINVAL; - err = -1; - goto ret; - } - nsid = nvme_get_nsid(fd); - close(fd); - if (nsid < 0) { - fprintf(stderr, "Cannot read nsid from %s\n", - devicename); - errno = EINVAL; - err = -1; - goto ret; - } - sprintf(path, "/sys/block/%s/device", devicename); - subsysnqn = get_nvme_subsnqn(path); - if (!subsysnqn) { - fprintf(stderr, "Cannot read subsys NQN from %s\n", - devicename); - errno = EINVAL; - err = -1; - goto ret; - } - optind++; - } + if (optind < argc) + devicename = basename(argv[optind++]); err = flags = validate_output_format(cfg.output_format); if (flags < 0) - goto free; + goto ret; if (flags != JSON && flags != NORMAL) { - errno = EINVAL; - err = -1; - goto free; + err = -EINVAL; + goto ret; } if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, subsysnqn, ns_instance, nsid, NULL); + r = nvme_create_root(stderr, map_log_level(cfg.verbose, false)); + if (!r) { + if (devicename) + fprintf(stderr, + "Failed to scan nvme subsystem for %s\n", + devicename); + else + fprintf(stderr, "Failed to scan nvme subsystem\n"); + err = -errno; + goto ret; + } + + if (devicename) { + int subsys_num, nsid; + + if (sscanf(devicename,"nvme%dn%d", + &subsys_num, &nsid) != 2) { + fprintf(stderr, "Invalid device name %s\n", devicename); + err = -EINVAL; + goto ret; + } + filter = nvme_match_device_filter; + } + + err = nvme_scan_topology(r, filter, (void *)devicename); if (err) { - fprintf(stderr, "Failed to scan namespaces\n"); - goto free; + fprintf(stderr, "Failed to scan topology: %s\n", + nvme_strerror(errno)); + goto ret; } - nvme_show_subsystem_list(&t, flags); -free: - free_topology(&t); - free(subsysnqn); + + nvme_show_subsystem_list(r, flags); + ret: - return nvme_status_to_errno(err, false); + if (r) + nvme_free_tree(r); + return err; } 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 = -1; + nvme_root_t r; + int err = 0; struct config { - char *device_dir; - char *output_format; - int verbose; + char *output_format; + bool verbose; }; struct config cfg = { - .device_dir = NULL, - .output_format = "normal", - .verbose = 0, + .output_format = "normal", + .verbose = false, }; 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() @@ -2289,15 +2634,24 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, NULL, 0, 0, cfg.device_dir); - if (err) { - fprintf(stderr, "Failed to scan namespaces\n"); + r = nvme_create_root(stderr, map_log_level(cfg.verbose, false)); + if (!r) { + fprintf(stderr, "Failed to create topology root: %s\n", + nvme_strerror(errno)); + return -errno; + } + err = nvme_scan_topology(r, NULL, NULL); + if (err < 0) { + fprintf(stderr, "Failed to scan topoplogy: %s\n", + nvme_strerror(errno)); + nvme_free_tree(r); return err; } - nvme_show_list_items(&t, flags); - free_topology(&t); - return 0; + nvme_show_list_items(r, flags); + nvme_free_tree(r); + + return err; } int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, @@ -2313,17 +2667,20 @@ int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, const char *human_readable = "show identify in readable format"; enum nvme_print_flags flags; struct nvme_id_ctrl ctrl; - int err = -1, fd; + int err, fd; struct config { - int vendor_specific; - int raw_binary; - int human_readable; - char *output_format; + bool vendor_specific; + char *output_format; + bool raw_binary; + bool human_readable; }; struct config cfg = { - .output_format = "normal", + .vendor_specific = false, + .output_format = "normal", + .raw_binary = false, + .human_readable = false, }; OPT_ARGS(opts) = { @@ -2350,15 +2707,15 @@ int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, err = nvme_identify_ctrl(fd, &ctrl); if (!err) - __nvme_show_id_ctrl(&ctrl, flags, vs); + nvme_show_id_ctrl(&ctrl, flags, vs); else if (err > 0) nvme_show_status(err); else - perror("identify controller"); + fprintf(stderr, "identify controller: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2377,15 +2734,15 @@ static int nvm_id_ctrl(int argc, char **argv, struct command *cmd, int fd, err = -1; struct config { - char *output_format; + char *output_format; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_END() }; @@ -2397,13 +2754,156 @@ static int nvm_id_ctrl(int argc, char **argv, struct command *cmd, if (flags < 0) goto close_fd; - err = nvme_identify_ctrl_nvm(fd, &ctrl_nvm); + err = nvme_nvm_identify_ctrl(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"); + fprintf(stderr, "nvm identify controller: %s\n", nvme_strerror(errno)); +close_fd: + close(fd); +ret: + return err; +} + +static int nvm_id_ns(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Send an Identify Namespace NVM Command Set "\ + "command to the given device and report information about "\ + "the specified namespace in various formats."; + const char *namespace_id = "identifier of desired namespace"; + const char *uuid_index = "UUID index"; + const char *verbose = "Increase output verbosity"; + enum nvme_print_flags flags; + struct nvme_nvm_id_ns id_ns; + struct nvme_id_ns ns; + int fd, err = -1; + + struct config { + __u32 namespace_id; + __u8 uuid_index; + char *output_format; + bool verbose; + }; + + struct config cfg = { + .namespace_id = 0, + .uuid_index = NVME_UUID_NONE, + .output_format = "normal", + .verbose = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + 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; + + if (cfg.verbose) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(fd, &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + err = nvme_identify_ns_csi(fd, cfg.namespace_id, cfg.uuid_index, + NVME_CSI_NVM, &id_ns); + if (!err) + nvme_show_nvm_id_ns(&id_ns, cfg.namespace_id, &ns, 0, false, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("nvm identify namespace"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int nvm_id_ns_lba_format(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an NVM Command Set specific Identify Namespace " + "command to the given device, returns capability field properties of " + "the specified LBA Format index in the specified namespace in various " + "formats."; + const char *lba_format_index = "The index into the LBA Format list "\ + "identifying the LBA Format capabilities that are to be returned"; + const char *uuid_index = "UUID index"; + const char *verbose = "Increase output verbosity"; + enum nvme_print_flags flags; + struct nvme_id_ns ns; + struct nvme_nvm_id_ns nvm_ns; + int err = -1, fd; + + struct config { + __u16 lba_format_index; + __u8 uuid_index; + bool verbose; + char *output_format; + }; + + struct config cfg = { + .lba_format_index = 0, + .uuid_index = NVME_UUID_NONE, + .verbose = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + 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; + + if (cfg.verbose) + flags |= VERBOSE; + + err = nvme_identify_ns(fd, NVME_NSID_ALL, &ns); + if (err) { + ns.nlbaf = NVME_FEAT_LBA_RANGE_MAX - 1; + ns.nulbaf = 0; + } + err = nvme_identify_iocs_ns_csi_user_data_format(fd, cfg.lba_format_index, + cfg.uuid_index, NVME_CSI_NVM, &nvm_ns); + if (!err) + nvme_show_nvm_id_ns(&nvm_ns, 0, &ns, cfg.lba_format_index, true, + flags); + else if (err > 0) + nvme_show_status(err); + else + perror("NVM identify namespace for specific LBA format"); close_fd: close(fd); ret: @@ -2418,18 +2918,19 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p const char *raw = "show descriptors in binary format"; const char *namespace_id = "identifier of desired namespace"; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; void *nsdescs; struct config { - __u32 namespace_id; - int raw_binary; - char *output_format; + __u32 namespace_id; + char *output_format; + bool raw_binary; }; struct config cfg = { - .namespace_id = 0, - .output_format = "normal", + .namespace_id = 0, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -2450,17 +2951,15 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p flags = BINARY; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } if (posix_memalign(&nsdescs, getpagesize(), 0x1000)) { - fprintf(stderr, "can not allocate controller list payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } @@ -2470,12 +2969,12 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p else if (err > 0) nvme_show_status(err); else - perror("identify namespace"); + fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); free(nsdescs); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2492,25 +2991,29 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug enum nvme_print_flags flags; struct nvme_id_ns ns; - int err = -1, fd; + int err, fd; struct config { - __u32 namespace_id; - int vendor_specific; - int raw_binary; - int human_readable; - int force; - char *output_format; + __u32 namespace_id; + bool force; + bool vendor_specific; + bool raw_binary; + char *output_format; + bool human_readable; }; struct config cfg = { - .namespace_id = 0, - .output_format = "normal", + .namespace_id = 0, + .force = false, + .vendor_specific = false, + .raw_binary = false, + .output_format = "normal", + .human_readable = false, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_FLAG("force", 'f', &cfg.force, force), + OPT_FLAG("force", 0, &cfg.force, force), OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), @@ -2533,24 +3036,28 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug flags |= VERBOSE; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } - err = nvme_identify_ns(fd, cfg.namespace_id, cfg.force, &ns); + if (cfg.force) + err = nvme_identify_allocated_ns(fd, cfg.namespace_id, &ns); + else + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); + if (!err) - nvme_show_id_ns(&ns, cfg.namespace_id, flags); + nvme_show_id_ns(&ns, cfg.namespace_id, 0, false, flags); else if (err > 0) nvme_show_status(err); else - perror("identify namespace"); + fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int cmd_set_independent_id_ns(int argc, char **argv, @@ -2564,19 +3071,21 @@ static int cmd_set_independent_id_ns(int argc, char **argv, const char *namespace_id = "identifier of desired namespace"; enum nvme_print_flags flags; - struct nvme_cmd_set_independent_id_ns ns; + struct nvme_id_independent_id_ns ns; int err = -1, fd; struct config { - __u32 namespace_id; - int raw_binary; - int human_readable; - char *output_format; + __u32 namespace_id; + bool raw_binary; + char *output_format; + bool human_readable; }; struct config cfg = { - .namespace_id = 0, - .output_format = "normal", + .namespace_id = 0, + .raw_binary = false, + .output_format = "normal", + .human_readable = false, }; OPT_ARGS(opts) = { @@ -2600,24 +3109,24 @@ static int cmd_set_independent_id_ns(int argc, char **argv, flags |= VERBOSE; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = cfg.namespace_id = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { perror("get-namespace-id"); goto close_fd; } } - err = nvme_cmd_set_independent_identify_ns(fd, cfg.namespace_id, &ns); + err = nvme_identify_independent_identify_ns(fd, cfg.namespace_id, &ns); if (!err) nvme_show_cmd_set_independent_id_ns(&ns, cfg.namespace_id, flags); else if (err > 0) nvme_show_status(err); else - perror("I/O command set independent identify namespace"); + fprintf(stderr, "I/O command set independent identify namespace: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2628,14 +3137,14 @@ static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct struct nvme_id_ns_granularity_list *granularity_list; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - char *output_format; + char *output_format; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -2653,8 +3162,7 @@ static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct if (posix_memalign((void *)&granularity_list, getpagesize(), NVME_IDENTIFY_DATA_SIZE)) { fprintf(stderr, "can not allocate granularity list payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } @@ -2664,12 +3172,12 @@ static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct else if (err > 0) nvme_show_status(err); else - perror("identify namespace granularity"); + fprintf(stderr, "identify namespace granularity: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_nvmset(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2680,18 +3188,18 @@ static int id_nvmset(int argc, char **argv, struct command *cmd, struct plugin * "in either binary format or json format"; const char *nvmset_id = "NVM Set Identify value"; - struct nvme_id_nvmset nvmset; + struct nvme_id_nvmset_list nvmset; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - __u16 nvmset_id; - char *output_format; + __u16 nvmset_id; + char *output_format; }; struct config cfg = { - .nvmset_id = 0, - .output_format = "normal", + .nvmset_id = 0, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -2708,18 +3216,18 @@ static int id_nvmset(int argc, char **argv, struct command *cmd, struct plugin * if (flags < 0) goto close_fd; - err = nvme_identify_nvmset(fd, cfg.nvmset_id, &nvmset); + err = nvme_identify_nvmset_list(fd, cfg.nvmset_id, &nvmset); if (!err) nvme_show_id_nvmset(&nvmset, cfg.nvmset_id, flags); else if (err > 0) nvme_show_status(err); else - perror("identify nvm set list"); + fprintf(stderr, "identify nvm set list: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_uuid(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2732,16 +3240,18 @@ static int id_uuid(int argc, char **argv, struct command *cmd, struct plugin *pl struct nvme_id_uuid_list uuid_list; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - int raw_binary; - int human_readable; - char *output_format; + char *output_format; + bool raw_binary; + bool human_readable; }; struct config cfg = { - .output_format = "normal", + .output_format = "normal", + .raw_binary = false, + .human_readable = false, }; OPT_ARGS(opts) = { @@ -2769,11 +3279,11 @@ static int id_uuid(int argc, char **argv, struct command *cmd, struct plugin *pl else if (err > 0) nvme_show_status(err); else - perror("identify UUID list"); + fprintf(stderr, "identify UUID list: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false);; + return err;; } static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2782,26 +3292,19 @@ static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *pl "given device, returns properties of the specified controller "\ "in either human-readable or binary format."; const char *controller_id = "identifier of desired controller"; - const char *human_readable = "show info in human readable format"; struct nvme_id_iocs iocs; - enum nvme_print_flags flags; int err, fd; struct config { - __u16 cntid; - char *output_format; - int human_readable; + __u16 cntid; }; struct config cfg = { - .cntid = 0xffff, - .output_format = "normal", + .cntid = 0xffff, }; OPT_ARGS(opts) = { - OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id), - OPT_FMT("output-format", 'o', &cfg.output_format, output_format), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id), OPT_END() }; @@ -2811,25 +3314,18 @@ static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *pl 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_iocs(fd, cfg.cntid, &iocs); if (!err) { printf("NVMe Identify I/O Command Set:\n"); - nvme_show_id_iocs(&iocs, flags); + nvme_show_id_iocs(&iocs); } else if (err > 0) nvme_show_status(err); else - perror("NVMe Identify I/O Command Set"); + fprintf(stderr, "NVMe Identify I/O Command Set: %s\n", nvme_strerror(errno)); -close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int id_domain(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -2842,13 +3338,13 @@ static int id_domain(int argc, char **argv, struct command *cmd, struct plugin * int err, fd; struct config { - __u16 dom_id; - char *output_format; + __u16 dom_id; + char *output_format; }; struct config cfg = { - .dom_id = 0xffff, - .output_format = "normal", + .dom_id = 0xffff, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -2875,18 +3371,19 @@ static int id_domain(int argc, char **argv, struct command *cmd, struct plugin * } else if (err > 0) nvme_show_status(err); else - perror("NVMe Identify Domain List"); + fprintf(stderr, "NVMe Identify Domain List: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - int err = 0, nsid, fd; - const char *desc = "Get namespce ID of a the block device."; + const char *desc = "Get namespace ID of a the block device."; + int err = 0, fd; + unsigned int nsid; OPT_ARGS(opts) = { OPT_END() @@ -2896,9 +3393,9 @@ static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin * if (fd < 0) goto ret; - nsid = nvme_get_nsid(fd); - if (nsid <= 0) { - perror(devicename); + err = nvme_get_nsid(fd, &nsid); + if (err < 0) { + fprintf(stderr, "get namespace ID: %s\n", nvme_strerror(errno)); err = errno; goto close_fd; } @@ -2908,7 +3405,7 @@ static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin * close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -2928,21 +3425,21 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi "8h: Secondary Assign\n"\ "9h: Secondary Online"; const char *nr = "Number of Controller Resources(NR)"; - int fd, err = -1; - __u32 result, cdw10; + int fd, err; + __u32 result; struct config { - __u16 cntlid; - __u8 rt; - __u8 act; - __u16 nr; + __u16 cntlid; + __u8 rt; + __u8 act; + __u16 nr; }; struct config cfg = { - .cntlid = 0, - .rt = 0, - .act = 0, - .nr = 0, + .cntlid = 0, + .rt = 0, + .act = 0, + .nr = 0, }; OPT_ARGS(opts) = { @@ -2957,43 +3454,56 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi if (fd < 0) goto ret; - cdw10 = cfg.act | (cfg.rt << 8) | (cfg.cntlid << 16); - - err = nvme_virtual_mgmt(fd, cdw10, cfg.nr, &result); + struct nvme_virtual_mgmt_args args = { + .args_size = sizeof(args), + .fd = fd, + .act = cfg.act, + .rt = cfg.rt, + .cntlid = cfg.cntlid, + .nr = cfg.nr, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_virtual_mgmt(&args); if (!err) { printf("success, Number of Controller Resources Modified "\ "(NRM):%#x\n", result); } else if (err > 0) { nvme_show_status(err); } else - perror("virt-mgmt"); + fprintf(stderr, "virt-mgmt: %s\n", nvme_strerror(errno)); close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int primary_ctrl_caps(int argc, char **argv, struct command *cmd, struct plugin *plugin) { + const char *cntlid = "Controller ID"; 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; + struct nvme_primary_ctrl_cap caps; - int err = -1, fd; + int err, fd; enum nvme_print_flags flags; struct config { - char *output_format; - int human_readable; + __u16 cntlid; + char *output_format; + bool human_readable; }; struct config cfg = { - .output_format = "normal", + .cntlid = 0, + .output_format = "normal", + .human_readable = false, }; OPT_ARGS(opts) = { + OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), OPT_END() @@ -3009,17 +3519,17 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *cmd, struct if (cfg.human_readable) flags |= VERBOSE; - err = nvme_identify_primary_ctrl_caps(fd, &caps); + err = nvme_identify_primary_ctrl(fd, cfg.cntlid, &caps); if (!err) - nvme_show_primary_ctrl_caps(&caps, flags); + nvme_show_primary_ctrl_cap(&caps, flags); else if (err > 0) nvme_show_status(err); else - perror("identify primary controller capabilities"); + fprintf(stderr, "identify primary controller capabilities: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3030,22 +3540,22 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struc const char *namespace_id = "optional namespace attached to controller"; const char *num_entries = "number of entries to retrieve"; - struct nvme_secondary_controllers_list *sc_list; + struct nvme_secondary_ctrl_list *sc_list; enum nvme_print_flags flags; - int err = -1, fd; + int err, fd; struct config { - __u16 cntid; - __u32 num_entries; - __u32 namespace_id; - char *output_format; + __u16 cntid; + __u32 namespace_id; + __u32 num_entries; + char *output_format; }; struct config cfg = { - .cntid = 0, - .namespace_id = 0, - .output_format = "normal", - .num_entries = ARRAY_SIZE(sc_list->sc_entry), + .cntid = 0, + .namespace_id = 0, + .num_entries = ARRAY_SIZE(sc_list->sc_entry), + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -3066,15 +3576,13 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struc if (!cfg.num_entries) { fprintf(stderr, "non-zero num-entries is required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (posix_memalign((void *)&sc_list, getpagesize(), sizeof(*sc_list))) { fprintf(stderr, "can not allocate controller list payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } @@ -3084,14 +3592,14 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struc else if (err > 0) nvme_show_status(err); else - perror("id secondary controller list"); + fprintf(stderr, "id secondary controller list: %s\n", nvme_strerror(errno)); free(sc_list); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int device_self_test(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3105,16 +3613,16 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p "2h Start a extended device self-test operation\n"\ "eh Start a vendor specific device self-test operation\n"\ "fh abort the device self-test operation\n"; - int fd, err = -1; + int fd, err; struct config { - __u32 namespace_id; - __u8 stc; + __u32 namespace_id; + __u8 stc; }; struct config cfg = { - .namespace_id = NVME_NSID_ALL, - .stc = 0, + .namespace_id = NVME_NSID_ALL, + .stc = 0, }; OPT_ARGS(opts) = { @@ -3127,7 +3635,15 @@ 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.stc); + struct nvme_dev_self_test_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .stc = cfg.stc, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_dev_self_test(&args); if (!err) { if (cfg.stc == 0xf) printf("Aborting device self-test operation\n"); @@ -3138,11 +3654,11 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p } else if (err > 0) { nvme_show_status(err); } else - perror("Device self-test"); + fprintf(stderr, "Device self-test: %s\n", nvme_strerror(errno)); close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int self_test_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3154,20 +3670,20 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug "by default all the 20 entries will be retrieved"; const char *verbose = "Increase output verbosity"; - struct nvme_self_test_log self_test_log; + struct nvme_self_test_log log; enum nvme_print_flags flags; - int err = -1, fd; - __u32 log_size; + int err, fd; struct config { - __u8 dst_entries; - char *output_format; - int verbose; + __u8 dst_entries; + char *output_format; + bool verbose; }; struct config cfg = { - .dst_entries = NVME_ST_REPORTS, - .output_format = "normal", + .dst_entries = NVME_LOG_ST_MAX_RESULTS, + .output_format = "normal", + .verbose = false, }; OPT_ARGS(opts) = { @@ -3187,22 +3703,146 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug if (cfg.verbose) flags |= VERBOSE; - 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); + err = nvme_get_log_device_self_test(fd, &log); if (!err) - nvme_show_self_test_log(&self_test_log, cfg.dst_entries, log_size, + nvme_show_self_test_log(&log, cfg.dst_entries, 0, devicename, flags); else if (err > 0) nvme_show_status(err); else - perror("self test log"); + fprintf(stderr, "self test log: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } -static int get_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int get_feature_id(int fd, struct feat_cfg *cfg, void **buf, + __u32 *result) +{ + if (!cfg->data_len) + nvme_get_feature_length(cfg->feature_id, cfg->cdw11, + &cfg->data_len); + + /* check for Extended Host Identifier */ + if (cfg->feature_id == NVME_FEAT_FID_HOST_ID && (cfg->cdw11 & 0x1)) + cfg->data_len = 16; + + if (cfg->sel == 3) + cfg->data_len = 0; + + if (cfg->data_len) { + if (posix_memalign(buf, getpagesize(), cfg->data_len)) { + return -1; + } + memset(*buf, 0, cfg->data_len); + } + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = cfg->feature_id, + .nsid = cfg->namespace_id, + .sel = cfg->sel, + .cdw11 = cfg->cdw11, + .uuidx = cfg->uuid_index, + .data_len = cfg->data_len, + .data = *buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = result, + }; + return nvme_get_features(&args); +} + +static void get_feature_id_print(struct feat_cfg cfg, int err, __u32 result, + void *buf) +{ + if (!err) { + if (!cfg.raw_binary || !buf) { + printf("get-feature:%#0*x (%s), %s value:%#0*x\n", + cfg.feature_id ? 4 : 2, cfg.feature_id, + nvme_feature_to_string(cfg.feature_id), + nvme_select_to_string(cfg.sel), result ? 10 : 8, + result); + if (cfg.sel == 3) + nvme_show_select_result(result); + else if (cfg.human_readable) + nvme_feature_show_fields(cfg.feature_id, result, + buf); + else if (buf) + d(buf, cfg.data_len, 16, 1); + } else if (buf) { + d_raw(buf, cfg.data_len); + } + } else if (err > 0) { + if (err != NVME_SC_INVALID_FIELD) + nvme_show_status(err); + } else { + fprintf(stderr, "get-feature: %s\n", nvme_strerror(errno)); + } +} + +static int get_feature_id_changed(int fd, struct feat_cfg cfg, bool changed) +{ + int err; + int err_def = 0; + __u32 result; + __u32 result_def; + void *buf = NULL; + void *buf_def = NULL; + + if (changed) + cfg.sel = 0; + + err = get_feature_id(fd, &cfg, &buf, &result); + + if (!err && changed) { + cfg.sel = 1; + err_def = get_feature_id(fd, &cfg, &buf_def, &result_def); + } + + if (changed) + cfg.sel = 8; + + if (err || !changed || err_def || result != result_def || + (buf && buf_def && !strcmp(buf, buf_def))) + get_feature_id_print(cfg, err, result, buf); + + free(buf); + free(buf_def); + + return err; +} + +static int get_feature_ids(int fd, struct feat_cfg cfg) +{ + int err = 0; + int i; + int feat_max = 0x100; + int feat_num = 0; + bool changed = false; + + if (cfg.sel == 8) + changed = true; + + if (cfg.feature_id) + feat_max = cfg.feature_id + 1; + + for (i = cfg.feature_id; i < feat_max; i++, feat_num++) { + cfg.feature_id = i; + err = get_feature_id_changed(fd, cfg, changed); + if (err && err != NVME_SC_INVALID_FIELD) + break; + } + + if (err == NVME_SC_INVALID_FIELD && feat_num == 1) + nvme_show_status(err); + + return err; +} + +static int get_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Read operating parameters of the "\ "specified controller. Operating parameters are grouped "\ @@ -3214,40 +3854,30 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin "are vendor-specific and not changeable. Use set-feature to "\ "change saveable Features."; const char *raw = "show feature in binary format"; - const char *namespace_id = "identifier of desired namespace"; const char *feature_id = "feature identifier"; - const char *sel = "[0-3]: current/default/saved/supported"; + const char *namespace_id = "identifier of desired namespace"; + const char *sel = "[0-3,8]: current/default/saved/supported/changed"; const char *data_len = "buffer len if data is returned through host memory buffer"; const char *cdw11 = "dword 11 for interrupt vector config"; const char *human_readable = "show feature in readable format"; const char *uuid_index = "specify uuid index"; - int err = -1, fd; - __u32 result; - void *buf = NULL; - - struct config { - __u32 namespace_id; - __u8 feature_id; - __u8 sel; - __u32 cdw11; - __u8 uuid_index; - __u32 data_len; - int raw_binary; - int human_readable; - }; + int err; + int fd; - struct config cfg = { - .namespace_id = 0, - .feature_id = 0, - .sel = 0, - .cdw11 = 0, - .uuid_index = 0, - .data_len = 0, + struct feat_cfg cfg = { + .feature_id = 0, + .namespace_id = 0, + .sel = 0, + .data_len = 0, + .raw_binary = false, + .cdw11 = 0, + .uuid_index = 0, + .human_readable = false, }; OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_BYTE("sel", 's', &cfg.sel, sel), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), @@ -3262,85 +3892,35 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { if (errno != ENOTTY) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } - cfg.namespace_id = NVME_NSID_ALL; } } - if (cfg.sel > 7) { + if (cfg.sel > 8) { fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel); - errno = EINVAL; - err = -1; - goto close_fd; - } - if (!cfg.feature_id) { - fprintf(stderr, "feature-id required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.uuid_index > 128) { fprintf(stderr, "invalid uuid index param: %u\n", cfg.uuid_index); - errno = EINVAL; err = -1; goto close_fd; } - if (!cfg.data_len) - 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; - - if (cfg.data_len) { - if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { - fprintf(stderr, "can not allocate feature payload\n"); - errno = ENOMEM; - err = -1; - goto close_fd; - } - memset(buf, 0, cfg.data_len); - } - - err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11, - cfg.uuid_index, cfg.data_len, buf, &result); - if (!err) { - if (!cfg.raw_binary || !buf) { - printf("get-feature:%#0*x (%s), %s value:%#0*x\n", - cfg.feature_id ? 4 : 2, cfg.feature_id, - nvme_feature_to_string(cfg.feature_id), - nvme_select_to_string(cfg.sel), result ? 10 : 8, - result); - if (cfg.sel == 3) - nvme_show_select_result(result); - else if (cfg.human_readable) - nvme_feature_show_fields(cfg.feature_id, result, buf); - else if (buf) - d(buf, cfg.data_len, 16, 1); - } else if (buf) - d_raw(buf, cfg.data_len); - } else if (err > 0) { - nvme_show_status(err); - } else - perror("get-feature"); - - free(buf); + err = get_feature_ids(fd, cfg); close_fd: close(fd); + ret: - return nvme_status_to_errno(err, false); + return err; } static int fw_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3356,22 +3936,22 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin const char *fw = "firmware file (required)"; const char *xfer = "transfer chunksize limit"; const char *offset = "starting dword offset, default 0"; - int err = -1, fd, fw_fd = -1; + int err, fd, fw_fd = -1; unsigned int fw_size; struct stat sb; void *fw_buf, *buf; bool huge; struct config { - char *fw; - __u32 xfer; - __u32 offset; + char *fw; + __u32 xfer; + __u32 offset; }; struct config cfg = { - .fw = "", - .xfer = 4096, - .offset = 0, + .fw = "", + .xfer = 4096, + .offset = 0, }; OPT_ARGS(opts) = { @@ -3390,8 +3970,7 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin if (fw_fd < 0) { fprintf(stderr, "Failed to open firmware file %s: %s\n", cfg.fw, strerror(errno)); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } @@ -3404,8 +3983,7 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin fw_size = sb.st_size; if ((fw_size & 0x3) || (fw_size == 0)) { fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fw_fd; } @@ -3418,9 +3996,7 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin fw_buf = nvme_alloc(fw_size, &huge); if (!fw_buf) { - perror("No memory for f/w size:\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fw_fd; } @@ -3434,9 +4010,18 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin while (fw_size > 0) { cfg.xfer = min(cfg.xfer, fw_size); - err = nvme_fw_download(fd, cfg.offset, cfg.xfer, fw_buf); + struct nvme_fw_download_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = cfg.offset, + .data_len = cfg.xfer, + .data = fw_buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_fw_download(&args); if (err < 0) { - perror("fw-download"); + fprintf(stderr, "fw-download: %s\n", nvme_strerror(errno)); break; } else if (err != 0) { nvme_show_status(err); @@ -3456,7 +4041,7 @@ close_fw_fd: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static char *nvme_fw_status_reset_type(__u16 status) @@ -3479,19 +4064,19 @@ static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin * const char *slot = "[0-7]: firmware slot for commit action"; const char *action = "[0-7]: commit action"; const char *bpid = "[0,1]: boot partition identifier, if applicable (default: 0)"; - int err = -1, fd; + int err, fd; __u32 result; struct config { - __u8 slot; - __u8 action; - __u8 bpid; + __u8 slot; + __u8 action; + __u8 bpid; }; struct config cfg = { - .slot = 0, - .action = 0, - .bpid = 0, + .slot = 0, + .action = 0, + .bpid = 0, }; OPT_ARGS(opts) = { @@ -3507,26 +4092,32 @@ static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin * if (cfg.slot > 7) { fprintf(stderr, "invalid slot:%d\n", cfg.slot); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.action > 7 || cfg.action == 4 || cfg.action == 5) { fprintf(stderr, "invalid action:%d\n", cfg.action); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.bpid > 1) { fprintf(stderr, "invalid boot partition id:%d\n", cfg.bpid); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_fw_commit(fd, cfg.slot, cfg.action, cfg.bpid, &result); + struct nvme_fw_commit_args args = { + .args_size = sizeof(args), + .fd = fd, + .slot = cfg.slot, + .action = cfg.action, + .bpid = cfg.bpid, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_fw_commit(&args); if (err < 0) - perror("fw-commit"); + fprintf(stderr, "fw-commit: %s\n", nvme_strerror(errno)); else if (err != 0) switch (err & 0x7ff) { case NVME_SC_FW_NEEDS_CONV_RESET: @@ -3563,13 +4154,13 @@ static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin * close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int subsystem_reset(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Resets the NVMe subsystem\n"; - int err = -1, fd; + int err, fd; OPT_ARGS(opts) = { OPT_END() @@ -3585,18 +4176,18 @@ static int subsystem_reset(int argc, char **argv, struct command *cmd, struct pl fprintf(stderr, "Subsystem-reset: NVM Subsystem Reset not supported.\n"); else - perror("Subsystem-reset"); + fprintf(stderr, "Subsystem-reset: %s\n", nvme_strerror(errno)); } close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int reset(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Resets the NVMe controller\n"; - int err = -1, fd; + int err, fd; OPT_ARGS(opts) = { OPT_END() @@ -3606,13 +4197,13 @@ static int reset(int argc, char **argv, struct command *cmd, struct plugin *plug if (fd < 0) goto ret; - err = nvme_reset_controller(fd); + err = nvme_ctrl_reset(fd); if (err < 0) - perror("Reset"); + fprintf(stderr, "Reset: %s\n", nvme_strerror(errno)); close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int ns_rescan(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3630,11 +4221,11 @@ static int ns_rescan(int argc, char **argv, struct command *cmd, struct plugin * err = nvme_ns_rescan(fd); if (err < 0) - perror("Namespace Rescan"); + fprintf(stderr, "Namespace Rescan"); close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3646,25 +4237,24 @@ static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *p const char *ause_desc = "Allow unrestricted sanitize exit."; const char *sanact_desc = "Sanitize action."; const char *ovrpat_desc = "Overwrite pattern."; - - int fd, ret = -1; + int fd, err; struct config { - int no_dealloc; - int oipbp; - __u8 owpass; - int ause; - __u8 sanact; - __u32 ovrpat; + bool no_dealloc; + bool oipbp; + __u8 owpass; + bool ause; + __u8 sanact; + __u32 ovrpat; }; struct config cfg = { - .no_dealloc = 0, - .oipbp = 0, - .owpass = 0, - .ause = 0, - .sanact = 0, - .ovrpat = 0, + .no_dealloc = false, + .oipbp = false, + .owpass = 0, + .ause = false, + .sanact = 0, + .ovrpat = 0, }; OPT_ARGS(opts) = { @@ -3677,59 +4267,149 @@ static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *p OPT_END() }; - ret = fd = parse_and_open(argc, argv, desc, opts); + err = fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) goto ret; switch (cfg.sanact) { - case NVME_SANITIZE_ACT_CRYPTO_ERASE: - case NVME_SANITIZE_ACT_BLOCK_ERASE: - case NVME_SANITIZE_ACT_EXIT: - case NVME_SANITIZE_ACT_OVERWRITE: + case NVME_SANITIZE_SANACT_EXIT_FAILURE: + case NVME_SANITIZE_SANACT_START_BLOCK_ERASE: + case NVME_SANITIZE_SANACT_START_OVERWRITE: + case NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: break; default: fprintf(stderr, "Invalid Sanitize Action\n"); - errno = EINVAL; - ret = -1; + err = -EINVAL; goto close_fd; } - if (cfg.sanact == NVME_SANITIZE_ACT_EXIT) { + if (cfg.sanact == NVME_SANITIZE_SANACT_EXIT_FAILURE) { if (cfg.ause || cfg.no_dealloc) { fprintf(stderr, "SANACT is Exit Failure Mode\n"); - errno = EINVAL; - ret = -1; + err = -EINVAL; goto close_fd; } } - if (cfg.sanact == NVME_SANITIZE_ACT_OVERWRITE) { - if (cfg.owpass >= 16) { - fprintf(stderr, "OWPASS out of range [0-15]\n"); - errno = EINVAL; - ret = -1; + if (cfg.sanact == NVME_SANITIZE_SANACT_START_OVERWRITE) { + if (cfg.owpass > 16) { + fprintf(stderr, "OWPASS out of range [0-16]\n"); + err = -EINVAL; goto close_fd; } } else { if (cfg.owpass || cfg.oipbp || cfg.ovrpat) { fprintf(stderr, "SANACT is not Overwrite\n"); - errno = EINVAL; - ret = -1; + err = -EINVAL; goto close_fd; } } - ret = nvme_sanitize(fd, cfg.sanact, cfg.ause, cfg.owpass, cfg.oipbp, - cfg.no_dealloc, cfg.ovrpat); - if (ret < 0) - perror("sanitize"); - else if (ret > 0) - nvme_show_status(ret); + struct nvme_sanitize_nvm_args args = { + .args_size = sizeof(args), + .fd = fd, + .sanact = cfg.sanact, + .ause = cfg.ause, + .owpass = cfg.owpass, + .oipbp = cfg.oipbp, + .nodas = cfg.no_dealloc, + .ovrpat = cfg.ovrpat, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_sanitize_nvm(&args); + if (err < 0) + fprintf(stderr, "sanitize: %s\n", nvme_strerror(errno)); + else if (err > 0) + nvme_show_status(err); close_fd: close(fd); ret: - return nvme_status_to_errno(ret, false); + return err; +} + +static int nvme_get_properties(int fd, void **pbar) +{ + int offset, err, size = getpagesize(); + __u64 value; + + *pbar = malloc(size); + if (!*pbar) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + return -1; + } + + memset(*pbar, 0xff, size); + for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;) { + struct nvme_get_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = offset, + .value = &value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + err = nvme_get_property(&args); + if (err > 0 && (err & 0xff) == NVME_SC_INVALID_FIELD) { + err = 0; + value = -1; + } else if (err) { + free(*pbar); + break; + } + if (nvme_is_64bit_reg(offset)) { + *(uint64_t *)(*pbar + offset) = value; + offset += 8; + } else { + *(uint32_t *)(*pbar + offset) = value; + offset += 4; + } + } + + return err; +} + +static void *mmap_registers(nvme_root_t r, const char *dev) +{ + nvme_ctrl_t c = NULL; + nvme_ns_t n = NULL; + + char path[512]; + void *membase; + int fd; + + c = nvme_scan_ctrl(r, devicename); + if (c) { + snprintf(path, sizeof(path), "%s/device/resource0", + nvme_ctrl_get_sysfs_dir(c)); + nvme_free_ctrl(c); + } else { + n = nvme_scan_namespace(devicename); + if (!n) { + fprintf(stderr, "Unable to find %s\n", devicename); + return NULL; + } + snprintf(path, sizeof(path), "%s/device/device/resource0", + nvme_ns_get_sysfs_dir(n)); + nvme_free_ns(n); + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s did not find a pci resource, open failed %s\n", + devicename, strerror(errno)); + return NULL; + } + + membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + if (membase == MAP_FAILED) { + fprintf(stderr, "%s failed to map. ", devicename); + fprintf(stderr, "Did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n"); + membase = NULL; + } + + close(fd); + return membase; } static int show_registers(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3740,18 +4420,19 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu "output_format == normal"; enum nvme_print_flags flags; + nvme_root_t r; bool fabrics = true; - int fd, err = -1; + int fd, err; void *bar; struct config { - int human_readable; - char *output_format; + char *output_format; + bool human_readable; }; struct config cfg = { - .human_readable = 0, - .output_format = "normal", + .output_format = "normal", + .human_readable = false, }; OPT_ARGS(opts) = { @@ -3764,6 +4445,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu if (fd < 0) goto ret; + r = nvme_scan(NULL); err = flags = validate_output_format(cfg.output_format); if (flags < 0) goto close_fd; @@ -3772,7 +4454,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu err = nvme_get_properties(fd, &bar); if (err) { - bar = mmap_registers(devicename); + bar = mmap_registers(r, devicename); fabrics = false; if (bar) err = 0; @@ -3787,8 +4469,9 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu munmap(bar, getpagesize()); close_fd: close(fd); + nvme_free_tree(r); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_property(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3799,17 +4482,17 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi const char *offset = "offset of the requested property"; const char *human_readable = "show property in readable format"; - int fd, err = -1; - uint64_t value; + int fd, err; + __u64 value; struct config { - int offset; - int human_readable; + int offset; + bool human_readable; }; struct config cfg = { - .offset = -1, - .human_readable = 0, + .offset = -1, + .human_readable = false, }; OPT_ARGS(opts) = { @@ -3824,14 +4507,20 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi if (cfg.offset == -1) { fprintf(stderr, "offset required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_get_property(fd, cfg.offset, &value); + struct nvme_get_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = cfg.offset, + .value = &value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + err = nvme_get_property(&args); if (err < 0) { - perror("get-property"); + fprintf(stderr, "get-property: %s\n", nvme_strerror(errno)); } else if (!err) { nvme_show_single_property(cfg.offset, value, cfg.human_readable); } else if (err > 0) { @@ -3841,7 +4530,7 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int set_property(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3850,16 +4539,16 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi "for NVMe ove Fabric"; const char *offset = "the offset of the property"; const char *value = "the value of the property to be set"; - int fd, err = -1; + int fd, err; struct config { - int offset; - int value; + int offset; + int value; }; struct config cfg = { - .offset = -1, - .value = -1, + .offset = -1, + .value = -1, }; OPT_ARGS(opts) = { @@ -3874,20 +4563,26 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi if (cfg.offset == -1) { fprintf(stderr, "offset required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.value == -1) { fprintf(stderr, "value required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_set_property(fd, cfg.offset, cfg.value); + struct nvme_set_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = cfg.offset, + .value = cfg.value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_set_property(&args); if (err < 0) { - perror("set-property"); + fprintf(stderr, "set-property: %s\n", nvme_strerror(errno)); } else if (!err) { printf("set-property: %02x (%s), value: %#08x\n", cfg.offset, nvme_register_to_string(cfg.offset), cfg.value); @@ -3898,7 +4593,7 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int format(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -3908,8 +4603,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu "data erase) or delete data encryption key if specified. "\ "Can also be used to change LBAF to change the namespaces reported physical block format."; const char *namespace_id = "identifier of desired namespace"; - const char *lbaf = "[0-63]: LBA format lower (LBAFL) and upper (LBAFU), "\ - "mention directly LBAF format that needs be applied (required)"; + const char *lbaf = "LBA format to apply (required)"; const char *ses = "[0-2]: secure erase"; const char *pil = "[0-1]: protection info location last/first 8 bytes of metadata"; const char *pi = "[0-3]: protection info off/Type 1/Type 2/Type 3"; @@ -3920,32 +4614,34 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; struct nvme_id_ns ns; struct nvme_id_ctrl ctrl; - int err = -1, fd, i; + int err, fd, i; int block_size; __u8 prev_lbaf = 0; struct config { - __u32 namespace_id; - __u32 timeout; - __u8 lbaf; - __u8 ses; - __u8 pi; - __u8 pil; - __u8 ms; - __u64 bs; - int reset; - int force; + __u32 namespace_id; + __u32 timeout; + __u8 lbaf; + __u8 ses; + __u8 pi; + __u8 pil; + __u8 ms; + bool reset; + bool force; + __u64 bs; }; struct config cfg = { - .namespace_id = 0, - .timeout = 600000, - .lbaf = 0xff, - .ses = 0, - .pi = 0, - .reset = 0, - .force = 0, - .bs = 0, + .namespace_id = 0, + .timeout = 600000, + .lbaf = 0xff, + .ses = 0, + .pi = 0, + .pil = 0, + .ms = 0, + .reset = false, + .force = false, + .bs = 0, }; OPT_ARGS(opts) = { @@ -3957,20 +4653,35 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu OPT_BYTE("pil", 'p', &cfg.pil, pil), OPT_BYTE("ms", 'm', &cfg.ms, ms), OPT_FLAG("reset", 'r', &cfg.reset, reset), - OPT_FLAG("force", 'f', &cfg.force, force), + OPT_FLAG("force", 0, &cfg.force, force), OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), OPT_END() }; - err = fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) + err = argconfig_parse(argc, argv, desc, opts); + if (err) + goto ret; + + err = fd = open_exclusive(argc, argv, cfg.force); + if (fd < 0) { + if (errno == EBUSY) { + fprintf(stderr, "Failed to open %s.\n", + basename(argv[optind])); + fprintf(stderr, + "Namespace is currently busy.\n"); + if (!cfg.force) + fprintf(stderr, + "Use the force [--force] option to ignore that.\n"); + } else { + argconfig_print_help(desc, opts); + } goto ret; + } if (cfg.lbaf != 0xff && cfg.bs !=0) { fprintf(stderr, "Invalid specification of both LBAF and Block Size, please specify only one\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.bs) { @@ -3978,15 +4689,14 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu fprintf(stderr, "Invalid value for block size (%"PRIu64"), must be a power of two\n", (uint64_t) cfg.bs); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } } err = nvme_identify_ctrl(fd, &ctrl); if (err) { - perror("identify-ctrl"); + fprintf(stderr, "identify-ctrl: %s\n", nvme_strerror(errno)); goto close_fd; } @@ -3998,9 +4708,9 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu */ cfg.namespace_id = NVME_NSID_ALL; } else if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } @@ -4010,23 +4720,22 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu "Invalid namespace ID, " "specify a namespace to format or use '-n 0xffffffff' " "to format all namespaces on this controller.\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.namespace_id != NVME_NSID_ALL) { - err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns); + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); if (err) { if (err < 0) - perror("identify-namespace"); + fprintf(stderr, "identify-namespace: %s\n", nvme_strerror(errno)); else { fprintf(stderr, "identify failed\n"); nvme_show_status(err); } goto close_fd; } - prev_lbaf = ns.flbas & 0xf; + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &prev_lbaf); if (cfg.bs) { for (i = 0; i < 16; ++i) { @@ -4042,8 +4751,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu (uint64_t)cfg.bs); fprintf(stderr, "Please correct block size, or specify LBAF directly\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } } else if (cfg.lbaf == 0xff) @@ -4055,32 +4763,27 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu /* ses & pi checks set to 7 for forward-compatibility */ if (cfg.ses > 7) { fprintf(stderr, "invalid secure erase settings:%d\n", cfg.ses); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - if (cfg.lbaf > 63) { + if (cfg.lbaf > 15) { fprintf(stderr, "invalid lbaf:%d\n", cfg.lbaf); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.pi > 7) { fprintf(stderr, "invalid pi:%d\n", cfg.pi); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.pil > 1) { fprintf(stderr, "invalid pil:%d\n", cfg.pil); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.ms > 1) { fprintf(stderr, "invalid ms:%d\n", cfg.ms); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } @@ -4091,15 +4794,26 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu nvme_show_relatives(devicename); fprintf(stderr, "WARNING: Format may irrevocably delete this device's data.\n" "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n" - "Use the force [--force|-f] option to suppress this warning.\n"); + "Use the force [--force] option to suppress this warning.\n"); sleep(10); fprintf(stderr, "Sending format operation ... \n"); } - err = nvme_format(fd, cfg.namespace_id, cfg.lbaf, cfg.ses, cfg.pi, - cfg.pil, cfg.ms, cfg.timeout); + struct nvme_format_nvm_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .lbaf = cfg.lbaf, + .mset = cfg.ms, + .pi = cfg.pi, + .pil = cfg.pil, + .ses = cfg.ses, + .timeout = cfg.timeout, + .result = NULL, + }; + err = nvme_format_nvm(&args); if (err < 0) - perror("format"); + fprintf(stderr, "format: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else { @@ -4136,13 +4850,13 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu } } if (cfg.reset && is_chardev()) - nvme_reset_controller(fd); + nvme_ctrl_reset(fd); } close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } #define STRTOUL_AUTO_BASE (0) @@ -4167,30 +4881,30 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin const char *cdw12 = "feature cdw12, if used"; const char *save = "specifies that the controller shall save the attribute"; const char *uuid_index = "specify uuid index"; - int err = -1; + int err; __u32 result; void *buf = NULL; int fd, ffd = STDIN_FILENO; struct config { - char *file; - __u32 namespace_id; - __u8 feature_id; - __u64 value; - __u32 cdw12; - __u8 uuid_index; - __u32 data_len; - int save; + __u32 namespace_id; + __u8 feature_id; + __u64 value; + __u32 cdw12; + __u8 uuid_index; + __u32 data_len; + char *file; + bool save; }; struct config cfg = { - .file = "", - .namespace_id = 0, - .feature_id = 0, - .value = 0, - .uuid_index = 0, - .data_len = 0, - .save = 0, + .namespace_id = 0, + .feature_id = 0, + .value = 0, + .uuid_index = 0, + .data_len = 0, + .file = "", + .save = false, }; OPT_ARGS(opts) = { @@ -4210,10 +4924,10 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { if (errno != ENOTTY) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } @@ -4223,26 +4937,24 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin if (!cfg.feature_id) { fprintf(stderr, "feature-id required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.uuid_index > 128) { fprintf(stderr, "invalid uuid index param: %u\n", cfg.uuid_index); - errno = EINVAL; err = -1; goto close_fd; } if (!cfg.data_len) - cfg.data_len = nvme_feat_buf_len[cfg.feature_id]; + nvme_get_feature_length(cfg.feature_id, cfg.value, + &cfg.data_len); if (cfg.data_len) { if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { fprintf(stderr, "can not allocate feature payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } memset(buf, 0, cfg.data_len); @@ -4255,16 +4967,15 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin * should use the buffer method if the value exceeds this * length. */ - if (NVME_FEAT_TIMESTAMP == cfg.feature_id && cfg.value) { + if (NVME_FEAT_FID_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) { - errno = EINVAL; fprintf(stderr, "Failed to open file %s: %s\n", - cfg.file, strerror(errno)); - err = -1; + cfg.file, strerror(errno)); + err = -EINVAL; goto free; } } @@ -4279,21 +4990,35 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin } } - err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value, - cfg.cdw12, cfg.save, cfg.uuid_index, cfg.data_len, buf, &result); + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = cfg.feature_id, + .nsid = cfg.namespace_id, + .cdw11 = cfg.value, + .cdw12 = cfg.cdw12, + .save = cfg.save, + .uuidx = cfg.uuid_index, + .cdw15 = 0, + .data_len = cfg.data_len, + .data = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_set_features(&args); if (err < 0) { - perror("set-feature"); + fprintf(stderr, "set-feature: %s\n", nvme_strerror(errno)); } else if (!err) { printf("set-feature:%#0*x (%s), value:%#0*"PRIx64", cdw12:%#0*x, save:%#x\n", cfg.feature_id ? 4 : 2, cfg.feature_id, nvme_feature_to_string(cfg.feature_id), cfg.value ? 10 : 8, (uint64_t)cfg.value, cfg.cdw12 ? 10 : 8, cfg.cdw12, cfg.save); - if (cfg.feature_id == NVME_LBA_STATUS_INFO) { + if (cfg.feature_id == NVME_FEAT_FID_LBA_STS_INTERVAL) { nvme_show_lba_status_info(result); } if (buf) { - if (cfg.feature_id == NVME_FEAT_LBA_RANGE) + if (cfg.feature_id == NVME_FEAT_FID_LBA_RANGE) nvme_show_lba_range((struct nvme_lba_range_type *)buf, result); else @@ -4309,7 +5034,7 @@ free: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -4326,24 +5051,26 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p const char *tl = "transfer length (cf. SPC-4)"; const char *namespace_id = "desired namespace"; const char *nssf = "NVMe Security Specific Field"; - int err = -1, fd, sec_fd = -1; + int err, fd, sec_fd = STDIN_FILENO; void *sec_buf; unsigned int sec_size; struct config { - __u32 namespace_id; - char *file; - __u8 nssf; - __u8 secp; - __u16 spsp; - __u32 tl; + __u32 namespace_id; + char *file; + __u8 nssf; + __u8 secp; + __u16 spsp; + __u32 tl; }; struct config cfg = { - .file = "", - .secp = 0, - .spsp = 0, - .tl = 0, + .namespace_id = 0, + .file = "", + .nssf = 0, + .secp = 0, + .spsp = 0, + .tl = 0, }; OPT_ARGS(opts) = { @@ -4365,16 +5092,6 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p err = -EINVAL; goto close_fd; } - - sec_fd = open(cfg.file, O_RDONLY); - if (sec_fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", - cfg.file, strerror(errno)); - errno = EINVAL; - err = -1; - goto close_fd; - } - if ((cfg.tl & 3) != 0) fprintf(stderr, "WARNING: --tl not dword aligned; unaligned bytes may be truncated\n"); @@ -4399,15 +5116,13 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p sec_size = cfg.tl > sb.st_size ? cfg.tl : sb.st_size; } - sec_size = sb.st_size; - if (posix_memalign(&sec_buf, getpagesize(), sec_size)) { - fprintf(stderr, "No memory for security size:%d\n", sec_size); - errno = ENOMEM; - err = -1; + if (posix_memalign(&sec_buf, getpagesize(), cfg.tl)) { + fprintf(stderr, "No memory for security size:%d\n", cfg.tl); + err = -ENOMEM; goto close_sec_fd; } - memset(sec_buf, 0, cfg.tl); // ensure zero fill if cfg.tl > sec_size + memset(sec_buf, 0, cfg.tl); // ensure zero fill if buf_size > sec_size err = read(sec_fd, sec_buf, sec_size); if (err < 0) { @@ -4417,10 +5132,23 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p goto free; } - err = nvme_sec_send(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, cfg.secp, - cfg.tl, sec_buf); + struct nvme_security_send_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .nssf = cfg.nssf, + .spsp0 = cfg.spsp & 0xff, + .spsp1 = cfg.spsp >> 8, + .secp = cfg.secp, + .tl = cfg.tl, + .data_len = cfg.tl, + .data = sec_buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_security_send(&args); if (err < 0) - perror("security-send"); + fprintf(stderr, "security-send: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -4433,7 +5161,7 @@ close_sec_fd: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -4449,34 +5177,37 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p const char *endir = "directive enable"; const char *ttype = "target directive type to be enabled/disabled"; const char *human_readable = "show directive in readable format"; - int err = -1, fd; + const char *input = "write/send file (default stdin)"; + int err, fd; __u32 result; __u32 dw12 = 0; void *buf = NULL; int ffd = STDIN_FILENO; struct config { - char *file; - __u32 namespace_id; - __u32 data_len; - __u16 dspec; - __u8 dtype; - __u8 doper; - __u16 endir; - __u8 ttype; - int raw_binary; - int human_readable; + __u32 namespace_id; + __u32 data_len; + __u8 dtype; + __u8 ttype; + __u16 dspec; + __u8 doper; + __u16 endir; + bool human_readable; + bool raw_binary; + char *file; }; struct config cfg = { - .file = "", - .namespace_id = 1, - .data_len = 0, - .dspec = 0, - .dtype = 0, - .ttype = 0, - .doper = 0, - .endir = 1, + .namespace_id = 1, + .data_len = 0, + .dtype = 0, + .ttype = 0, + .dspec = 0, + .doper = 0, + .endir = 1, + .human_readable = false, + .raw_binary = false, + .file = "", }; OPT_ARGS(opts) = { @@ -4489,6 +5220,7 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p OPT_SHRT("endir", 'e', &cfg.endir, endir), OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FILE("input-file", 'i', &cfg.file, input), OPT_END() }; @@ -4497,48 +5229,43 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p goto ret; switch (cfg.dtype) { - case NVME_DIR_IDENTIFY: + case NVME_DIRECTIVE_DTYPE_IDENTIFY: switch (cfg.doper) { - case NVME_DIR_SND_ID_OP_ENABLE: + case NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR: if (!cfg.ttype) { fprintf(stderr, "target-dir required param\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } dw12 = cfg.ttype << 8 | cfg.endir; break; default: fprintf(stderr, "invalid directive operations for Identify Directives\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } break; - case NVME_DIR_STREAMS: + case NVME_DIRECTIVE_DTYPE_STREAMS: switch (cfg.doper) { - case NVME_DIR_SND_ST_OP_REL_ID: - case NVME_DIR_SND_ST_OP_REL_RSC: + case NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER: + case NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE: break; default: fprintf(stderr, "invalid directive operations for Streams Directives\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } break; default: fprintf(stderr, "invalid directive type\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.data_len) { if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } memset(buf, 0, cfg.data_len); @@ -4550,8 +5277,7 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p if (ffd <= 0) { fprintf(stderr, "Failed to open file %s: %s\n", cfg.file, strerror(errno)); - errno = EINVAL; - err = -1; + err = -EINVAL; goto free; } } @@ -4564,10 +5290,22 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p } } - err = nvme_dir_send(fd, cfg.namespace_id, cfg.dspec, cfg.dtype, cfg.doper, - cfg.data_len, dw12, buf, &result); + struct nvme_directive_send_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .dspec = cfg.dspec, + .doper = cfg.doper, + .dtype = cfg.dtype, + .cdw12 = dw12, + .data_len = cfg.data_len, + .data = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_directive_send(&args); if (err < 0) { - perror("dir-send"); + fprintf(stderr, "dir-send: %s\n", nvme_strerror(errno)); goto close_ffd; } if (!err) { @@ -4589,12 +5327,12 @@ free: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - int err = -1, fd; + int err, fd; const char *desc = "The Write Uncorrectable command is used to set a "\ "range of logical blocks to invalid."; const char *namespace_id = "desired namespace"; @@ -4602,15 +5340,15 @@ static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin const char *block_count = "number of blocks (zeroes based) on device to access"; struct config { - __u64 start_block; - __u32 namespace_id; - __u16 block_count; + __u32 namespace_id; + __u64 start_block; + __u16 block_count; }; struct config cfg = { - .start_block = 0, - .namespace_id = 0, - .block_count = 0, + .namespace_id = 0, + .start_block = 0, + .block_count = 0, }; OPT_ARGS(opts) = { @@ -4625,17 +5363,25 @@ static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } - err = nvme_write_uncorrectable(fd, cfg.namespace_id, cfg.start_block, - cfg.block_count); + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .slba = cfg.start_block, + .nlb = cfg.block_count, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_write_uncorrectable(&args); if (err < 0) - perror("write uncorrectable"); + fprintf(stderr, "write uncorrectable: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -4644,12 +5390,12 @@ static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - int err = -1, fd; + int err, fd; __u16 control = 0; const char *desc = "The Write Zeroes command is used to set a "\ "range of logical blocks to zero."; @@ -4657,7 +5403,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi const char *start_block = "64-bit LBA of first block to access"; const char *block_count = "number of blocks (zeroes based) on device to access"; const char *limited_retry = "limit media access attempts"; - const char *force = "force device to commit data before command completes"; + const char *force_unit_access = "force device to commit data before command completes"; const char *prinfo = "PI and check field"; const char *ref_tag = "reference tag (for end to end PI)"; const char *app_tag_mask = "app tag mask (for end to end PI)"; @@ -4669,29 +5415,33 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi "part of end-to-end data protection processing"; struct config { - __u64 start_block; - __u32 namespace_id; - __u32 ref_tag; - __u16 app_tag; - __u16 app_tag_mask; - __u16 block_count; - __u8 prinfo; - __u64 storage_tag; - int deac; - int limited_retry; - int force_unit_access; - int storage_tag_check; + __u32 namespace_id; + __u64 start_block; + __u16 block_count; + bool deac; + bool limited_retry; + bool force_unit_access; + __u8 prinfo; + __u32 ref_tag; + __u16 app_tag_mask; + __u16 app_tag; + __u64 storage_tag; + bool storage_tag_check; }; struct config cfg = { - .start_block = 0, - .block_count = 0, - .prinfo = 0, - .ref_tag = 0, - .app_tag_mask = 0, - .app_tag = 0, - .storage_tag = 0, - .storage_tag_check = 0, + .namespace_id = 0, + .start_block = 0, + .block_count = 0, + .deac = false, + .limited_retry = false, + .force_unit_access = false, + .prinfo = 0, + .ref_tag = 0, + .app_tag_mask = 0, + .app_tag = 0, + .storage_tag = 0, + .storage_tag_check = false, }; OPT_ARGS(opts) = { @@ -4700,7 +5450,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_FLAG("deac", 'd', &cfg.deac, deac), OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access), OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask), @@ -4715,33 +5465,44 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (cfg.prinfo > 0xf) { - fprintf(stderr, "invalid prinfo: %u\n", cfg.prinfo); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } control |= (cfg.prinfo << 10); if (cfg.limited_retry) - control |= NVME_RW_LR; + control |= NVME_IO_LR; if (cfg.force_unit_access) - control |= NVME_RW_FUA; + control |= NVME_IO_FUA; if (cfg.deac) - control |= NVME_RW_DEAC; + control |= NVME_IO_DEAC; if (cfg.storage_tag_check) - control |= NVME_RW_STORAGE_TAG_CHECK; + control |= NVME_SC_STORAGE_TAG_CHECK; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } - err = nvme_write_zeros(fd, cfg.namespace_id, cfg.start_block, cfg.block_count, - control, cfg.ref_tag, cfg.app_tag, cfg.app_tag_mask, cfg.storage_tag); + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .slba = cfg.start_block, + .nlb = cfg.block_count, + .control = control, + .reftag = cfg.ref_tag, + .apptag = cfg.app_tag, + .appmask = cfg.app_tag_mask, + .storage_tag = cfg.storage_tag, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_write_zeros(&args); if (err < 0) - perror("write-zeroes"); + fprintf(stderr, "write-zeroes: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -4750,7 +5511,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -4768,43 +5529,43 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin const char *idr = "Attribute Integral Dataset for Read"; const char *cdw11 = "All the command DWORD 11 attributes. Use instead of specifying individual attributes"; - int err = -1, fd; + int err, fd; uint16_t nr, nc, nb, ns; - int ctx_attrs[256] = {0,}; - int nlbs[256] = {0,}; - unsigned long long slbas[256] = {0,}; - struct nvme_dsm_range *dsm; + __u32 ctx_attrs[256] = {0,}; + __u32 nlbs[256] = {0,}; + __u64 slbas[256] = {0,}; + struct nvme_dsm_range dsm[256]; struct config { - char *ctx_attrs; - char *blocks; - char *slbas; - int ad; - int idw; - int idr; - __u32 cdw11; - __u32 namespace_id; + __u32 namespace_id; + char *ctx_attrs; + char *blocks; + char *slbas; + bool ad; + bool idw; + bool idr; + __u32 cdw11; }; struct config cfg = { - .ctx_attrs = "", - .blocks = "", - .slbas = "", - .namespace_id = 0, - .ad = 0, - .idw = 0, - .idr = 0, - .cdw11 = 0, + .namespace_id = 0, + .ctx_attrs = "", + .blocks = "", + .slbas = "", + .ad = false, + .idw = false, + .idr = false, + .cdw11 = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_LIST("ctx-attrs", 'a', &cfg.ctx_attrs, context_attrs), - OPT_LIST("blocks", 'b', &cfg.blocks, blocks), - OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks), - OPT_FLAG("ad", 'd', &cfg.ad, ad), - OPT_FLAG("idw", 'w', &cfg.idw, idw), - OPT_FLAG("idr", 'r', &cfg.idr, idr), + OPT_LIST("blocks", 'b', &cfg.blocks, blocks), + OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks), + OPT_FLAG("ad", 'd', &cfg.ad, ad), + OPT_FLAG("idw", 'w', &cfg.idw, idw), + OPT_FLAG("idr", 'r', &cfg.idr, idr), OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), OPT_END() }; @@ -4813,49 +5574,49 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin if (fd < 0) goto ret; - nc = argconfig_parse_comma_sep_array(cfg.ctx_attrs, ctx_attrs, ARRAY_SIZE(ctx_attrs)); - nb = argconfig_parse_comma_sep_array(cfg.blocks, nlbs, ARRAY_SIZE(nlbs)); - ns = argconfig_parse_comma_sep_array_long(cfg.slbas, slbas, ARRAY_SIZE(slbas)); + nc = argconfig_parse_comma_sep_array(cfg.ctx_attrs, (int *)ctx_attrs, ARRAY_SIZE(ctx_attrs)); + nb = argconfig_parse_comma_sep_array(cfg.blocks, (int *)nlbs, ARRAY_SIZE(nlbs)); + ns = argconfig_parse_comma_sep_array_long(cfg.slbas, (unsigned long long *)slbas, ARRAY_SIZE(slbas)); nr = max(nc, max(nb, ns)); if (!nr || nr > 256) { fprintf(stderr, "No range definition provided\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } if (!cfg.cdw11) cfg.cdw11 = (cfg.ad << 2) | (cfg.idw << 1) | (cfg.idr << 0); - dsm = nvme_setup_dsm_range(ctx_attrs, nlbs, slbas, nr); - if (!dsm) { - fprintf(stderr, "failed to allocate data set payload\n"); - errno = ENOMEM; - err = -1; - goto close_fd; - } - - err = nvme_dsm(fd, cfg.namespace_id, cfg.cdw11, dsm, nr); + nvme_init_dsm_range(dsm, ctx_attrs, nlbs, slbas, nr); + struct nvme_dsm_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .attrs = cfg.cdw11, + .nr_ranges = nr, + .dsm = dsm, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_dsm(&args); if (err < 0) - perror("data-set management"); + fprintf(stderr, "data-set management: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else printf("NVMe DSM: success\n"); - free(dsm); - close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -4883,62 +5644,73 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi const char *d_dspec = "directive specific (write part)"; const char *d_format = "source range entry format"; - int err = -1, fd; + int err, fd; uint16_t nr, nb, ns, nrts, natms, nats; - int nlbs[128] = { 0 }; + __u16 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; + __u32 eilbrts[128] = { 0 }; + __u32 elbatms[128] = { 0 }; + __u32 elbats[128] = { 0 }; + struct nvme_copy_range copy[128]; 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; + __u32 namespace_id; + __u64 sdlba; + char *slbas; + char *nlbs; + bool lr; + bool fua; + __u8 prinfow; + __u8 prinfor; + __u32 ilbrt; + char *eilbrts; + __u16 lbat; + char *elbatms; + __u16 lbatm; + char *elbats; + __u8 dtype; + __u16 dspec; + __u8 format; }; struct config cfg = { - .namespace_id = 0, - .nlbs = "", - .slbas = "", - .eilbrts = "", - .elbatms = "", - .elbats = "", + .namespace_id = 0, + .sdlba = 0, + .slbas = "", + .nlbs = "", + .lr = false, + .fua = false, + .prinfow = 0, + .prinfor = 0, + .ilbrt = 0, + .eilbrts = "", + .lbat = 0, + .elbats = "", + .lbatm = 0, + .elbatms = "", + .dtype = 0, + .dspec = 0, + .format = 0, }; 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_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() }; @@ -4948,52 +5720,61 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi goto ret; } - nb = argconfig_parse_comma_sep_array(cfg.nlbs, nlbs, ARRAY_SIZE(nlbs)); + nb = argconfig_parse_comma_sep_array(cfg.nlbs, (int *)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)); + nrts = argconfig_parse_comma_sep_array(cfg.eilbrts, (int *)eilbrts, ARRAY_SIZE(eilbrts)); + natms = argconfig_parse_comma_sep_array(cfg.elbatms, (int *)elbatms, ARRAY_SIZE(elbatms)); + nats = argconfig_parse_comma_sep_array(cfg.elbats, (int *)elbats, ARRAY_SIZE(elbats)); nr = max(nb, max(ns, max(nrts, max(natms, nats)))); if (!nr || nr > 128) { fprintf(stderr, "invalid range\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } - copy = nvme_setup_copy_range(nlbs, slbas, eilbrts, elbatms, elbats, nr); - if (!copy) { - fprintf(stderr, "failed to allocate payload\n"); - errno = ENOMEM; - err = -1; - 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); + nvme_init_copy_range(copy, nlbs, (__u64 *)slbas, eilbrts, elbatms, elbats, nr); + + struct nvme_copy_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .copy = copy, + .sdlba = cfg.sdlba, + .nr = nr, + .prinfor = cfg.prinfor, + .prinfow = cfg.prinfow, + .dtype = cfg.dtype, + .dspec = cfg.dspec, + .format = cfg.format, + .lr = cfg.lr, + .fua = cfg.fua, + .ilbrt = cfg.ilbrt, + .lbatm = cfg.lbatm, + .lbat = cfg.lbat, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_copy(&args); if (err < 0) - perror("NVMe Copy"); + fprintf(stderr, "NVMe Copy: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else printf("NVMe Copy: success\n"); - free(copy); - close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int flush(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5004,14 +5785,14 @@ static int flush(int argc, char **argv, struct command *cmd, struct plugin *plug "flushed by the controller, from any namespace, depending on controller and "\ "associated namespace status."; const char *namespace_id = "identifier of desired namespace"; - int err = -1, fd; + int err, fd; struct config { - __u32 namespace_id; + __u32 namespace_id; }; struct config cfg = { - .namespace_id = 0, + .namespace_id = 0, }; OPT_ARGS(opts) = { @@ -5024,16 +5805,16 @@ static int flush(int argc, char **argv, struct command *cmd, struct plugin *plug goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } err = nvme_flush(fd, cfg.namespace_id); if (err < 0) - perror("flush"); + fprintf(stderr, "flush: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -5041,7 +5822,7 @@ static int flush(int argc, char **argv, struct command *cmd, struct plugin *plug close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5058,23 +5839,24 @@ static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugi const char *rtype = "reservation type"; const char *racqa = "reservation acquire action"; const char *iekey = "ignore existing res. key"; - int err = -1, fd; + int err, fd; struct config { - __u32 namespace_id; - __u64 crkey; - __u64 prkey; - __u8 rtype; - __u8 racqa; - int iekey; + __u32 namespace_id; + __u64 crkey; + __u64 prkey; + __u8 rtype; + __u8 racqa; + bool iekey; }; struct config cfg = { - .namespace_id = 0, - .crkey = 0, - .prkey = 0, - .rtype = 0, - .racqa = 0, + .namespace_id = 0, + .crkey = 0, + .prkey = 0, + .rtype = 0, + .racqa = 0, + .iekey = false, }; OPT_ARGS(opts) = { @@ -5092,23 +5874,33 @@ static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } if (cfg.racqa > 7) { fprintf(stderr, "invalid racqa:%d\n", cfg.racqa); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_resv_acquire(fd, cfg.namespace_id, cfg.rtype, cfg.racqa, - !!cfg.iekey, cfg.crkey, cfg.prkey); + struct nvme_resv_acquire_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .rtype = cfg.rtype, + .racqa = cfg.racqa, + .iekey = !!cfg.iekey, + .crkey = cfg.crkey, + .nrkey = cfg.prkey, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_resv_acquire(&args); if (err < 0) - perror("reservation acquire"); + fprintf(stderr, "reservation acquire: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -5117,7 +5909,7 @@ static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int resv_register(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5131,23 +5923,23 @@ static int resv_register(int argc, char **argv, struct command *cmd, struct plug const char *nrkey = "new reservation key"; const char *rrega = "reservation registration action"; const char *cptpl = "change persistence through power loss setting"; - int err = -1, fd; + int err, fd; struct config { - __u32 namespace_id; - __u64 crkey; - __u64 nrkey; - __u8 rrega; - __u8 cptpl; - int iekey; + __u32 namespace_id; + __u64 crkey; + __u64 nrkey; + __u8 rrega; + __u8 cptpl; + bool iekey; }; struct config cfg = { - .namespace_id = 0, - .crkey = 0, - .nrkey = 0, - .rrega = 0, - .cptpl = 0, + .namespace_id = 0, + .crkey = 0, + .nrkey = 0, + .rrega = 0, + .cptpl = false, }; OPT_ARGS(opts) = { @@ -5165,30 +5957,39 @@ static int resv_register(int argc, char **argv, struct command *cmd, struct plug goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } if (cfg.cptpl > 3) { fprintf(stderr, "invalid cptpl:%d\n", cfg.cptpl); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.rrega > 7) { fprintf(stderr, "invalid rrega:%d\n", cfg.rrega); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_resv_register(fd, cfg.namespace_id, cfg.rrega, cfg.cptpl, - !!cfg.iekey, cfg.crkey, cfg.nrkey); + struct nvme_resv_register_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .rrega = cfg.rrega, + .cptpl = cfg.cptpl, + .iekey = !!cfg.iekey, + .crkey = cfg.crkey, + .nrkey = cfg.nrkey, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_resv_register(&args); if (err < 0) - perror("reservation register"); + fprintf(stderr, "reservation register: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -5197,7 +5998,7 @@ static int resv_register(int argc, char **argv, struct command *cmd, struct plug close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int resv_release(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5215,22 +6016,22 @@ static int resv_release(int argc, char **argv, struct command *cmd, struct plugi const char *iekey = "ignore existing res. key"; const char *rtype = "reservation type"; const char *rrela = "reservation release action"; - int err = -1, fd; + int err, fd; struct config { - __u32 namespace_id; - __u64 crkey; - __u8 rtype; - __u8 rrela; - __u8 iekey; + __u32 namespace_id; + __u64 crkey; + __u8 rtype; + __u8 rrela; + __u8 iekey; }; struct config cfg = { - .namespace_id = 0, - .crkey = 0, - .rtype = 0, - .rrela = 0, - .iekey = 0, + .namespace_id = 0, + .crkey = 0, + .rtype = 0, + .rrela = 0, + .iekey = 0, }; OPT_ARGS(opts) = { @@ -5247,23 +6048,32 @@ static int resv_release(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } if (cfg.rrela > 7) { fprintf(stderr, "invalid rrela:%d\n", cfg.rrela); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - err = nvme_resv_release(fd, cfg.namespace_id, cfg.rtype, cfg.rrela, - !!cfg.iekey, cfg.crkey); + struct nvme_resv_release_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .rtype = cfg.rtype, + .rrela = cfg.rrela, + .iekey = !!cfg.iekey, + .crkey = cfg.crkey, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_resv_release(&args); if (err < 0) - perror("reservation release"); + fprintf(stderr, "reservation release: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -5272,7 +6082,7 @@ static int resv_release(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int resv_report(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5284,32 +6094,33 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin "namespace."; const char *namespace_id = "identifier of desired namespace"; const char *numd = "number of dwords to transfer"; - const char *cdw11 = "command dword 11 value"; + const char *eds = "request extended data structure"; const char *raw = "dump output in binary format"; - struct nvme_reservation_status *status; + struct nvme_resv_status *status; enum nvme_print_flags flags; - int err = -1, fd, size; + int err, fd, size; struct config { - __u32 namespace_id; - __u32 numd; - __u32 cdw11; - int raw_binary; - char *output_format; + __u32 namespace_id; + __u32 numd; + __u8 eds; + char *output_format; + bool raw_binary; }; struct config cfg = { - .namespace_id = 0, - .numd = 0, - .cdw11 = 0, - .output_format = "normal", + .namespace_id = 0, + .numd = 0, + .eds = false, + .output_format = "normal", + .raw_binary = false, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("numd", 'd', &cfg.numd, numd), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), + OPT_FLAG("eds", 'e', &cfg.eds, eds), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_END() @@ -5326,9 +6137,9 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin flags = BINARY; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } @@ -5342,24 +6153,33 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin if (posix_memalign((void **)&status, getpagesize(), size)) { fprintf(stderr, "No memory for resv report:%d\n", size); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } memset(status, 0, size); - err = nvme_resv_report(fd, cfg.namespace_id, cfg.numd, cfg.cdw11, status); + struct nvme_resv_report_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .eds = cfg.eds, + .len = size, + .report = status, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_resv_report(&args); if (!err) - nvme_show_resv_report(status, size, cfg.cdw11, flags); + nvme_show_resv_report(status, size, cfg.eds, flags); else if (err > 0) nvme_show_status(err); else - perror("reservation report"); + fprintf(stderr, "reservation report: %s\n", nvme_strerror(errno)); free(status); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } unsigned long long elapsed_utime(struct timeval start_time, @@ -5375,19 +6195,19 @@ static int submit_io(int opcode, char *command, const char *desc, { struct timeval start_time, end_time; void *buffer, *mbuffer = NULL; - int err = -1; + int err = 0; int dfd, mfd, fd; 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, nsid = 0; + __u32 dsmgmt = 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 *namespace_id = "desired namespace"; + const char *namespace_id = "Identifier of desired namespace"; 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"; const char *data_size = "size of data in bytes"; @@ -5400,55 +6220,66 @@ static int submit_io(int opcode, char *command, const char *desc, const char *app_tag = "app tag (for end to end PI)"; const char *limited_retry = "limit num. media access attempts"; const char *latency = "output latency statistics"; - const char *force = "force device to commit data before command completes"; + const char *force_unit_access = "force device to commit data before command completes"; const char *show = "show command before sending"; const char *dry = "show command instead of sending"; const char *dtype = "directive type (for write-only)"; const char *dspec = "directive specific (for write-only)"; - const char *dsm = "dataset management attributes (lower 16 bits)"; + const char *dsm = "dataset management attributes (lower 8 bits)"; const char *storage_tag_check = "This bit specifies the Storage Tag field shall be " \ "checked as part of end-to-end data protection processing"; const char *storage_tag = "storage tag, CDW2 and CDW3 (00:47) bits "\ "(for end to end PI)"; + const char *force = "The \"I know what I'm doing\" flag, do not enforce exclusive access for write"; struct config { - __u32 namespace_id; - __u64 start_block; - __u16 block_count; - __u64 data_size; - __u64 metadata_size; - __u32 ref_tag; - char *data; - char *metadata; - __u8 prinfo; - __u8 dtype; - __u16 dspec; - __u16 dsmgmt; - __u16 app_tag_mask; - __u16 app_tag; - __u64 storage_tag; - int limited_retry; - int force_unit_access; - int storage_tag_check; - int show; - int dry_run; - int latency; + __u32 namespace_id; + __u64 start_block; + __u16 block_count; + __u64 data_size; + __u64 metadata_size; + __u32 ref_tag; + char *data; + char *metadata; + __u8 prinfo; + __u16 app_tag_mask; + __u16 app_tag; + __u64 storage_tag; + bool limited_retry; + bool force_unit_access; + bool storage_tag_check; + __u8 dtype; + __u16 dspec; + __u8 dsmgmt; + bool show; + bool dry_run; + bool latency; + bool force; }; struct config cfg = { .namespace_id = 0, - .start_block = 0, - .block_count = 0, - .data_size = 0, - .metadata_size = 0, - .ref_tag = 0, - .data = "", - .metadata = "", - .prinfo = 0, - .app_tag_mask = 0, - .app_tag = 0, - .storage_tag_check = 0, + .start_block = 0, + .block_count = 0, + .data_size = 0, + .metadata_size = 0, + .ref_tag = 0, + .data = "", + .metadata = "", + .prinfo = 0, + .app_tag_mask = 0, + .app_tag = 0, .storage_tag = 0, + .limited_retry = false, + .force_unit_access = false, + .storage_tag_check = false, + .dtype = 0, + .dspec = 0, + .dsmgmt = 0, + .show = false, + .dry_run = false, + .latency = false, + .force = false, }; OPT_ARGS(opts) = { @@ -5465,50 +6296,70 @@ static int submit_io(int opcode, char *command, const char *desc, OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag), OPT_SUFFIX("storage-tag", 'g', &cfg.storage_tag, storage_tag), OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access), OPT_FLAG("storage-tag-check", 'C', &cfg.storage_tag_check, storage_tag_check), OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype), OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec), - OPT_SHRT("dsm", 'D', &cfg.dsmgmt, dsm), + OPT_BYTE("dsm", 'D', &cfg.dsmgmt, dsm), OPT_FLAG("show-command", 'v', &cfg.show, show), OPT_FLAG("dry-run", 'w', &cfg.dry_run, dry), OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_FLAG("force", 0, &cfg.force, force), OPT_END() }; - err = fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) - goto ret; + if (opcode != nvme_cmd_write) { + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + } else { + err = argconfig_parse(argc, argv, desc, opts); + if (err) + goto ret; + err = fd = open_exclusive(argc, argv, cfg.force); + if (err < 0) { + if (errno == EBUSY) { + fprintf(stderr, "Failed to open %s.\n", + basename(argv[optind])); + fprintf(stderr, + "Namespace is currently busy.\n"); + if (!cfg.force) + fprintf(stderr, + "Use the force [--force] option to ignore that.\n"); + } else { + argconfig_print_help(desc, opts); + } + goto ret; + } + } if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } dfd = mfd = opcode & 1 ? STDIN_FILENO : STDOUT_FILENO; if (cfg.prinfo > 0xf) { - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } dsmgmt = cfg.dsmgmt; control |= (cfg.prinfo << 10); if (cfg.limited_retry) - control |= NVME_RW_LR; + control |= NVME_IO_LR; if (cfg.force_unit_access) - control |= NVME_RW_FUA; + control |= NVME_IO_FUA; if (cfg.storage_tag_check) - control |= NVME_RW_STORAGE_TAG_CHECK; + control |= NVME_SC_STORAGE_TAG_CHECK; if (cfg.dtype) { if (cfg.dtype > 0xf) { fprintf(stderr, "Invalid directive type, %x\n", cfg.dtype); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } control |= cfg.dtype << 4; @@ -5519,36 +6370,29 @@ static int submit_io(int opcode, char *command, const char *desc, dfd = open(cfg.data, flags, mode); if (dfd < 0) { perror(cfg.data); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } - mfd = dfd; } + if (strlen(cfg.metadata)) { mfd = open(cfg.metadata, flags, mode); if (mfd < 0) { perror(cfg.metadata); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_dfd; } } if (!cfg.data_size) { fprintf(stderr, "data size not provided\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_mfd; } - if (is_ns_chardev()) { - logical_block_size = - nvme_logical_block_size_from_ns_char(devicename); - } else { - if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) + if (nvme_get_logical_block_size(fd, cfg.namespace_id, + &logical_block_size) < 0) goto close_mfd; - } buffer_size = (cfg.block_count + 1) * logical_block_size; if (cfg.data_size < buffer_size) { @@ -5560,27 +6404,20 @@ static int submit_io(int opcode, char *command, const char *desc, buffer = nvme_alloc(buffer_size, &huge); if (!buffer) { - perror("can not allocate io payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_mfd; } if (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); + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); if (err) { nvme_show_status(err); goto free_buffer; } else if (err < 0) { - perror("identify namespace"); + fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); goto free_buffer; } - lba_index = ns.flbas & NVME_NS_FLBAS_LBA_MASK; + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index); ms = ns.lbaf[lba_index].ms; mbuffer_size = (cfg.block_count + 1) * ms; if (ms && cfg.metadata_size < mbuffer_size) { @@ -5591,9 +6428,7 @@ static int submit_io(int opcode, char *command, const char *desc, } mbuffer = malloc(mbuffer_size); if (!mbuffer) { - perror("can not allocate buf for io metadata payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto free_buffer; } memset(mbuffer, 0, mbuffer_size); @@ -5619,48 +6454,68 @@ static int submit_io(int opcode, char *command, const char *desc, } } - if (cfg.show) { - printf("opcode : %02x\n", opcode); - printf("nsid : %02x\n", cfg.namespace_id); - printf("control : %04x\n", control); - printf("nblocks : %04x\n", cfg.block_count); - 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); - printf("dsmgmt : %08x\n", dsmgmt); - printf("reftag : %08x\n", cfg.ref_tag); - printf("apptag : %04x\n", cfg.app_tag); - printf("appmask : %04x\n", cfg.app_tag_mask); + if (cfg.show || cfg.dry_run) { + printf("opcode : %02x\n", opcode); + printf("nsid : %02x\n", cfg.namespace_id); + printf("flags : %02x\n", 0); + printf("control : %04x\n", control); + printf("nblocks : %04x\n", cfg.block_count); + 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); + printf("dsmgmt : %08x\n", dsmgmt); + printf("reftag : %08x\n", cfg.ref_tag); + printf("apptag : %04x\n", cfg.app_tag); + printf("appmask : %04x\n", cfg.app_tag_mask); printf("storagetagcheck : %04x\n", cfg.storage_tag_check); printf("storagetag : %"PRIx64"\n", (uint64_t)cfg.storage_tag); } if (cfg.dry_run) goto free_mbuffer; + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .slba = cfg.start_block, + .nlb = cfg.block_count, + .control = control, + .dsm = cfg.dsmgmt, + .dspec = cfg.dspec, + .reftag = cfg.ref_tag, + .apptag = cfg.app_tag, + .appmask = cfg.app_tag_mask, + .storage_tag = cfg.storage_tag, + .data_len = buffer_size, + .data = buffer, + .metadata_len = cfg.metadata_size, + .metadata = mbuffer, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; gettimeofday(&start_time, NULL); - err = nvme_io(fd, opcode, cfg.namespace_id, cfg.start_block, cfg.block_count, - control, dsmgmt, cfg.ref_tag, cfg.app_tag, cfg.app_tag_mask, - cfg.storage_tag, buffer, buffer_size, mbuffer, mbuffer_size); + if (opcode & 1) + err = nvme_write(&args); + else + err = nvme_read(&args); gettimeofday(&end_time, NULL); if (cfg.latency) printf(" latency: %s: %llu us\n", command, elapsed_utime(start_time, end_time)); if (err < 0) - perror("submit-io"); + fprintf(stderr, "submit-io: %s\n", nvme_strerror(errno)); else if (err) nvme_show_status(err); else { if (!(opcode & 1) && write(dfd, (void *)buffer, cfg.data_size) < 0) { fprintf(stderr, "write: %s: failed to write buffer to output file\n", strerror(errno)); - errno = EINVAL; - err = -1; + err = -EINVAL; } else if (!(opcode & 1) && cfg.metadata_size && write(mfd, (void *)mbuffer, mbuffer_size) < 0) { fprintf(stderr, "write: %s: failed to write meta-data buffer to output file\n", strerror(errno)); - errno = EINVAL; - err = -1; + err = -EINVAL; } else fprintf(stderr, "%s: Success\n", command); } @@ -5677,7 +6532,7 @@ close_dfd: close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int compare(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5705,14 +6560,14 @@ static int write_cmd(int argc, char **argv, struct command *cmd, struct plugin * static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - int err = -1, fd; + int err, fd; __u16 control = 0; const char *desc = "Verify specified logical blocks on the given device."; const char *namespace_id = "desired namespace"; const char *start_block = "64-bit LBA of first block to access"; const char *block_count = "number of blocks (zeroes based) on device to access"; const char *limited_retry = "limit media access attempts"; - const char *force = "force device to commit cached data before performing the verify operation"; + const char *force_unit_access = "force device to commit cached data before performing the verify operation"; const char *prinfo = "PI and check field"; const char *ref_tag = "reference tag (for end to end PI)"; const char *app_tag_mask = "app tag mask (for end to end PI)"; @@ -5723,31 +6578,31 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin "be checked as part of Verify operation"; struct config { - __u64 start_block; - __u32 namespace_id; - __u32 ref_tag; - __u16 app_tag; - __u16 app_tag_mask; - __u16 block_count; - __u8 prinfo; - __u64 storage_tag; - int storage_tag_check; - int limited_retry; - int force_unit_access; + __u32 namespace_id; + __u64 start_block; + __u16 block_count; + bool limited_retry; + bool force_unit_access; + __u8 prinfo; + __u32 ref_tag; + __u16 app_tag; + __u16 app_tag_mask; + __u64 storage_tag; + bool storage_tag_check; }; struct config cfg = { - .namespace_id = 0, - .start_block = 0, - .block_count = 0, - .prinfo = 0, - .ref_tag = 0, - .app_tag = 0, - .app_tag_mask = 0, - .limited_retry = 0, - .force_unit_access = 0, - .storage_tag = 0, - .storage_tag_check = 0, + .namespace_id = 0, + .start_block = 0, + .block_count = 0, + .limited_retry = false, + .force_unit_access = false, + .prinfo = 0, + .ref_tag = 0, + .app_tag = 0, + .app_tag_mask = 0, + .storage_tag = 0, + .storage_tag_check = false, }; OPT_ARGS(opts) = { @@ -5755,7 +6610,7 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block), OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), - OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force), + OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access), OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag), @@ -5770,32 +6625,43 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (cfg.prinfo > 0xf) { - fprintf(stderr, "invalid 'prinfo' param:%u\n", cfg.prinfo); - errno = EINVAL; - err = -1; + err = EINVAL; goto close_fd; } control |= (cfg.prinfo << 10); if (cfg.limited_retry) - control |= NVME_RW_LR; + control |= NVME_IO_LR; if (cfg.force_unit_access) - control |= NVME_RW_FUA; + control |= NVME_IO_FUA; if (cfg.storage_tag_check) - control |= NVME_RW_STORAGE_TAG_CHECK; + control |= NVME_SC_STORAGE_TAG_CHECK; if (!cfg.namespace_id) { - err = cfg.namespace_id = nvme_get_nsid(fd); + err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { - perror("get-namespace-id"); + fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno)); goto close_fd; } } - err = nvme_verify(fd, cfg.namespace_id, cfg.start_block, cfg.block_count, - control, cfg.ref_tag, cfg.app_tag, cfg.app_tag_mask, cfg.storage_tag); + struct nvme_io_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .slba = cfg.start_block, + .nlb = cfg.block_count, + .control = control, + .reftag = cfg.ref_tag, + .apptag = cfg.app_tag, + .appmask = cfg.app_tag_mask, + .storage_tag = cfg.storage_tag, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_verify(&args); if (err < 0) - perror("verify"); + fprintf(stderr, "verify: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else @@ -5804,7 +6670,7 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin close_fd: close(fd); ret: - return nvme_status_to_errno(err, false);; + return err; } static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5822,24 +6688,27 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p const char *raw = "dump output in binary format"; const char *namespace_id = "desired namespace"; const char *nssf = "NVMe Security Specific Field"; - int err = -1, fd; + int err, fd; void *sec_buf = NULL; struct config { - __u32 namespace_id; - __u32 size; - __u8 secp; - __u8 nssf; - __u16 spsp; - __u32 al; - int raw_binary; + __u32 namespace_id; + __u32 size; + __u8 nssf; + __u8 secp; + __u16 spsp; + __u32 al; + bool raw_binary; }; struct config cfg = { - .size = 0, - .secp = 0, - .spsp = 0, - .al = 0, + .namespace_id = 0, + .size = 0, + .nssf = 0, + .secp = 0, + .spsp = 0, + .al = 0, + .raw_binary = false, }; OPT_ARGS(opts) = { @@ -5861,16 +6730,28 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p if (posix_memalign(&sec_buf, getpagesize(), cfg.size)) { fprintf(stderr, "No memory for security size:%d\n", cfg.size); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } } - err = nvme_sec_recv(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, - cfg.secp, cfg.al, cfg.size, sec_buf); + struct nvme_security_receive_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .nssf = cfg.nssf, + .spsp0 = cfg.spsp & 0xff, + .spsp1 = cfg.spsp >> 8, + .secp = cfg.secp, + .al = cfg.al, + .data_len = cfg.size, + .data = sec_buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_security_receive(&args); if (err < 0) - perror("security receive"); + fprintf(stderr, "security receive: %s\n", nvme_strerror(errno)); else if (err != 0) nvme_show_status(err); else { @@ -5886,7 +6767,7 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int get_lba_status(int argc, char **argv, struct command *cmd, @@ -5907,27 +6788,27 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, enum nvme_print_flags flags; unsigned long buf_len; - int err = -1, fd; + int err, fd; void *buf; struct config { - __u32 namespace_id; - __u64 slba; - __u32 mndw; - __u8 atype; - __u16 rl; - __u32 timeout; - char *output_format; + __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", + .namespace_id = 0, + .slba = 0, + .mndw = 0, + .atype = 0, + .rl = 0, + .timeout = 0, + .output_format = "normal", }; OPT_ARGS(opts) = { @@ -5951,33 +6832,41 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, if (!cfg.atype) { fprintf(stderr, "action type (--action) has to be given\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } buf_len = (cfg.mndw + 1) * 4; buf = calloc(1, buf_len); if (!buf) { - perror("could not alloc memory for get lba status"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } - err = nvme_get_lba_status(fd, cfg.namespace_id, cfg.slba, cfg.mndw, - cfg.atype, cfg.rl, buf, cfg.timeout); + struct nvme_get_lba_status_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .slba = cfg.slba, + .mndw = cfg.mndw, + .rl = cfg.rl, + .atype = cfg.atype, + .lbas = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_get_lba_status(&args); if (!err) nvme_show_lba_status(buf, buf_len, flags); else if (err > 0) nvme_show_status(err); else - perror("get lba status"); + fprintf(stderr, "get lba status: %s\n", nvme_strerror(errno)); free(buf); close_fd: close(fd); err: - return nvme_status_to_errno(err, false); + return err; } static int capacity_mgmt(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -5997,23 +6886,23 @@ static int capacity_mgmt(int argc, char **argv, struct command *cmd, struct plug __u32 result; struct config { - __u8 operation; - __u16 element_id; - __u32 dw11; - __u32 dw12; + __u8 operation; + __u16 element_id; + __u32 dw11; + __u32 dw12; }; struct config cfg = { - .operation = 0xff, - .element_id = 0xffff, - .dw11 = 0, - .dw12 = 0, + .operation = 0xff, + .element_id = 0xffff, + .dw11 = 0, + .dw12 = 0, }; OPT_ARGS(opts) = { OPT_BYTE("operation", 'o', &cfg.operation, operation), OPT_SHRT("element-id", 'i', &cfg.element_id, element_id), - OPT_UINT("cap-lower", 'l', &cfg.dw11, cap_lower), + OPT_UINT("cap-lower", 'l', &cfg.dw11, cap_lower), OPT_UINT("cap-upper", 'u', &cfg.dw12, cap_upper), OPT_END() }; @@ -6024,13 +6913,21 @@ static int capacity_mgmt(int argc, char **argv, struct command *cmd, struct plug if (cfg.operation > 0xf) { fprintf(stderr, "invalid operation field: %u\n", cfg.operation); - errno = EINVAL; err = -1; goto close_fd; } - err = nvme_cap_mgmt(fd, cfg.operation, cfg.element_id, cfg.dw11, - cfg.dw12, &result); + struct nvme_capacity_mgmt_args args = { + .args_size = sizeof(args), + .fd = fd, + .op = cfg.operation, + .element_id = cfg.element_id, + .cdw11 = cfg.dw11, + .cdw12 = cfg.dw12, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_capacity_mgmt(&args); if (!err) { printf("Capacity Management Command is Success\n"); if (cfg.operation == 1) { @@ -6041,12 +6938,12 @@ static int capacity_mgmt(int argc, char **argv, struct command *cmd, struct plug } else if (err > 0) nvme_show_status(err); else if (err < 0) - perror("capacity management"); + fprintf(stderr, "capacity management: %s\n", nvme_strerror(errno)); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -6063,29 +6960,31 @@ static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin const char *human_readable = "show directive in readable format"; enum nvme_print_flags flags = NORMAL; - int err = -1, fd; + int err, fd; __u32 result; __u32 dw12 = 0; void *buf = NULL; struct config { - __u32 namespace_id; - __u32 data_len; - __u16 dspec; - __u8 dtype; - __u8 doper; - __u16 nsr; /* dw12 for NVME_DIR_ST_RCVOP_STATUS */ - int raw_binary; - int human_readable; + __u32 namespace_id; + __u32 data_len; + bool raw_binary; + __u8 dtype; + __u16 dspec; + __u8 doper; + __u16 nsr; /* dw12 for NVME_DIR_ST_RCVOP_STATUS */ + bool human_readable; }; struct config cfg = { - .namespace_id = 1, - .data_len = 0, - .dspec = 0, - .dtype = 0, - .doper = 0, - .nsr = 0, + .namespace_id = 1, + .data_len = 0, + .raw_binary = false, + .dtype = 0, + .dspec = 0, + .doper = 0, + .nsr = 0, + .human_readable = false, }; OPT_ARGS(opts) = { @@ -6110,57 +7009,65 @@ static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin flags = BINARY; switch (cfg.dtype) { - case NVME_DIR_IDENTIFY: + case NVME_DIRECTIVE_DTYPE_IDENTIFY: switch (cfg.doper) { - case NVME_DIR_RCV_ID_OP_PARAM: + case NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM: if (!cfg.data_len) cfg.data_len = 4096; break; default: fprintf(stderr, "invalid directive operations for Identify Directives\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } break; - case NVME_DIR_STREAMS: + case NVME_DIRECTIVE_DTYPE_STREAMS: switch (cfg.doper) { - case NVME_DIR_RCV_ST_OP_PARAM: + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM: if (!cfg.data_len) cfg.data_len = 32; break; - case NVME_DIR_RCV_ST_OP_STATUS: + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS: if (!cfg.data_len) cfg.data_len = 128 * 1024; break; - case NVME_DIR_RCV_ST_OP_RESOURCE: + case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE: dw12 = cfg.nsr; break; default: fprintf(stderr, "invalid directive operations for Streams Directives\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } break; default: fprintf(stderr, "invalid directive type\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } if (cfg.data_len) { if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto close_fd; } memset(buf, 0, cfg.data_len); } - err = nvme_dir_recv(fd, cfg.namespace_id, cfg.dspec, cfg.dtype, cfg.doper, - cfg.data_len, dw12, buf, &result); + struct nvme_directive_recv_args args = { + .args_size = sizeof(args), + .fd = fd, + .nsid = cfg.namespace_id, + .dspec = cfg.dspec, + .doper = cfg.doper, + .dtype = cfg.dtype, + .cdw12 = dw12, + .data_len = cfg.data_len, + .data = buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_directive_recv(&args); if (!err) nvme_directive_show(cfg.dtype, cfg.doper, cfg.dspec, cfg.namespace_id, result, buf, cfg.data_len, @@ -6168,13 +7075,13 @@ static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin else if (err > 0) nvme_show_status(err); else if (err < 0) - perror("dir-receive"); + fprintf(stderr, "dir-receive: %s\n", nvme_strerror(errno)); free(buf); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } /* rpmb_cmd_option is defined in nvme-rpmb.c */ @@ -6208,23 +7115,23 @@ static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugi int fd, err = -1; struct config { - __u8 ofi; - __u8 ifc; - __u8 prhbt; - __u8 scp; - __u8 uuid; + __u8 ofi; + __u8 ifc; + __u8 prhbt; + __u8 scp; + __u8 uuid; }; struct config cfg = { - .ofi = 0, - .ifc = 0, - .prhbt = 0, - .scp = 0, - .uuid = 0, + .ofi = 0, + .ifc = 0, + .prhbt = 0, + .scp = 0, + .uuid = 0, }; OPT_ARGS(opts) = { - OPT_BYTE("ofi", 'o', &cfg.ofi, ofi_desc), + OPT_BYTE("ofi", 'o', &cfg.ofi, ofi_desc), OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc), OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc), OPT_BYTE("scp", 's', &cfg.scp, scp_desc), @@ -6239,34 +7146,39 @@ static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugi /* check for input arguement limit */ if (cfg.ifc > 3) { fprintf(stderr, "invalid interface settings:%d\n", cfg.ifc); - errno = EINVAL; err = -1; goto close_fd; } if (cfg.prhbt > 1) { fprintf(stderr, "invalid prohibit settings:%d\n", cfg.prhbt); - errno = EINVAL; err = -1; goto close_fd; } if (cfg.scp > 15) { fprintf(stderr, "invalid scope settings:%d\n", cfg.scp); - errno = EINVAL; err = -1; goto close_fd; } if (cfg.uuid > 127) { fprintf(stderr, "invalid UUID index settings:%d\n", cfg.uuid); - errno = EINVAL; err = -1; goto close_fd; } - err = nvme_lockdown(fd, cfg.scp,cfg.prhbt,cfg.ifc,cfg.ofi, - cfg.uuid); - + struct nvme_lockdown_args args = { + .args_size = sizeof(args), + .fd = fd, + .scp = cfg.scp, + .prhbt = cfg.prhbt, + .ifc = cfg.ifc, + .ofi = cfg.ofi, + .uuidx = cfg.uuid, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_lockdown(&args); if (err < 0) - perror("lockdown"); + fprintf(stderr, "lockdown: %s\n", nvme_strerror(errno)); else if (err > 0) nvme_show_status(err); else @@ -6275,18 +7187,19 @@ static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugi close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } -static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, - const char *desc, struct command *cmd) +static int passthru(int argc, char **argv, bool admin, + const char *desc, struct command *cmd) { const char *opcode = "opcode (required)"; - const char *flags = "command flags"; + const char *cflags = "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 *metadata = "metadata input or output file"; const char *timeout = "timeout value, in milliseconds"; const char *cdw2 = "command dword 2 value"; const char *cdw3 = "command dword 3 value"; @@ -6296,7 +7209,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, 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 *input = "data input or output file"; const char *raw_binary = "dump output in binary format"; const char *show = "print command before sending"; const char *dry = "show command instead of sending"; @@ -6305,62 +7218,72 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, const char *prefill = "prefill buffers with known byte-value, default 0"; const char *latency = "output latency statistics"; - void *data = NULL, *metadata = NULL; - int err = -1, wfd = STDIN_FILENO, fd; + int flags; + int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH; + void *data = NULL, *mdata = NULL; + int err = 0, dfd, mfd, fd; __u32 result; - bool huge; + bool huge = false; const char *cmd_name = NULL; struct timeval start_time, end_time; struct config { - __u8 opcode; - __u8 flags; - __u16 rsvd; - __u32 namespace_id; - __u32 data_len; - __u32 metadata_len; - __u32 timeout; - __u32 cdw2; - __u32 cdw3; - __u32 cdw10; - __u32 cdw11; - __u32 cdw12; - __u32 cdw13; - __u32 cdw14; - __u32 cdw15; - char *input_file; - int raw_binary; - int show_command; - int dry_run; - int read; - int write; - __u8 prefill; - int latency; + __u8 opcode; + __u8 flags; + __u16 rsvd; + __u32 namespace_id; + __u32 data_len; + __u32 metadata_len; + __u32 timeout; + __u32 cdw2; + __u32 cdw3; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + char *input_file; + char *metadata; + bool raw_binary; + bool show_command; + bool dry_run; + bool read; + bool write; + __u8 prefill; + bool latency; }; struct config cfg = { - .opcode = 0, - .flags = 0, - .rsvd = 0, - .namespace_id = 0, - .data_len = 0, - .metadata_len = 0, - .timeout = 0, - .cdw2 = 0, - .cdw3 = 0, - .cdw10 = 0, - .cdw11 = 0, - .cdw12 = 0, - .cdw13 = 0, - .cdw14 = 0, - .cdw15 = 0, - .input_file = "", - .prefill = 0, + .opcode = 0, + .flags = 0, + .prefill = 0, + .rsvd = 0, + .namespace_id = 0, + .data_len = 0, + .metadata_len = 0, + .timeout = 0, + .cdw2 = 0, + .cdw3 = 0, + .cdw10 = 0, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .input_file = "", + .metadata = "", + .raw_binary = false, + .show_command = false, + .dry_run = false, + .read = false, + .write = false, + .latency = false, }; OPT_ARGS(opts) = { OPT_BYTE("opcode", 'o', &cfg.opcode, opcode), - OPT_BYTE("flags", 'f', &cfg.flags, flags), + OPT_BYTE("flags", 'f', &cfg.flags, cflags), OPT_BYTE("prefill", 'p', &cfg.prefill, prefill), OPT_SHRT("rsvd", 'R', &cfg.rsvd, rsvd), OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), @@ -6376,6 +7299,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, OPT_UINT("cdw14", '8', &cfg.cdw14, cdw14), OPT_UINT("cdw15", '9', &cfg.cdw15, cdw15), OPT_FILE("input-file", 'i', &cfg.input_file, input), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary), OPT_FLAG("show-command", 's', &cfg.show_command, show), OPT_FLAG("dry-run", 'd', &cfg.dry_run, dry), @@ -6389,33 +7313,47 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, if (fd < 0) goto ret; + flags = cfg.opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT; + dfd = mfd = cfg.opcode & 1 ? STDIN_FILENO : STDOUT_FILENO; if (strlen(cfg.input_file)) { - wfd = open(cfg.input_file, O_RDONLY, - S_IRUSR | S_IRGRP | S_IROTH); - if (wfd < 0) { + dfd = open(cfg.input_file, flags, mode); + if (dfd < 0) { perror(cfg.input_file); - errno = EINVAL; - err = -1; + err = -EINVAL; goto close_fd; } } + if (cfg.metadata && strlen(cfg.metadata)) { + mfd = open(cfg.metadata, flags, mode); + if (mfd < 0) { + perror(cfg.metadata); + err = -EINVAL; + goto close_dfd; + } + } + if (cfg.metadata_len) { - metadata = malloc(cfg.metadata_len); - if (!metadata) { - perror("can not allocate metadata payload\n"); - errno = ENOMEM; - err = -1; - goto close_wfd; + mdata = malloc(cfg.metadata_len); + if (!mdata) { + err = -ENOMEM; + goto close_mfd; } - memset(metadata, cfg.prefill, cfg.metadata_len); + + if (cfg.write) { + if (read(mfd, mdata, cfg.metadata_len) < 0) { + err = -errno; + perror("failed to read metadata write buffer"); + goto free_metadata; + } + } else + memset(mdata, cfg.prefill, cfg.metadata_len); } + if (cfg.data_len) { data = nvme_alloc(cfg.data_len, &huge); if (!data) { - perror("can not allocate data payload\n"); - errno = ENOMEM; - err = -1; + err = -ENOMEM; goto free_metadata; } @@ -6430,11 +7368,10 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, memset(data, cfg.prefill, cfg.data_len); if (!cfg.read && !cfg.write) { fprintf(stderr, "data direction not given\n"); - errno = EINVAL; - err = -1; + err = -EINVAL; goto free_data; } else if (cfg.write) { - if (read(wfd, data, cfg.data_len) < 0) { + if (read(dfd, data, cfg.data_len) < 0) { err = -errno; fprintf(stderr, "failed to read write buffer " "%s\n", strerror(errno)); @@ -6443,7 +7380,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, } } - if (cfg.show_command) { + if (cfg.show_command || cfg.dry_run) { printf("opcode : %02x\n", cfg.opcode); printf("flags : %02x\n", cfg.flags); printf("rsvd1 : %04x\n", cfg.rsvd); @@ -6453,7 +7390,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, printf("data_len : %08x\n", cfg.data_len); printf("metadata_len : %08x\n", cfg.metadata_len); printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)data); - printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)metadata); + printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mdata); printf("cdw10 : %08x\n", cfg.cdw10); printf("cdw11 : %08x\n", cfg.cdw11); printf("cdw12 : %08x\n", cfg.cdw12); @@ -6467,86 +7404,90 @@ static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, gettimeofday(&start_time, NULL); - err = nvme_passthru(fd, ioctl_cmd, cfg.opcode, cfg.flags, cfg.rsvd, + if (admin) + err = nvme_admin_passthru(fd, cfg.opcode, cfg.flags, cfg.rsvd, cfg.namespace_id, cfg.cdw2, cfg.cdw3, cfg.cdw10, - cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, cfg.cdw15, - cfg.data_len, data, cfg.metadata_len, metadata, - cfg.timeout, &result); + cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, + cfg.cdw15, cfg.data_len, data, cfg.metadata_len, + mdata, cfg.timeout, &result); + else + err = nvme_io_passthru(fd, cfg.opcode, cfg.flags, cfg.rsvd, + cfg.namespace_id, cfg.cdw2, cfg.cdw3, cfg.cdw10, + cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, + cfg.cdw15, cfg.data_len, data, cfg.metadata_len, + mdata, cfg.timeout, &result); gettimeofday(&end_time, NULL); - cmd_name = nvme_cmd_to_string(cmd_type, cfg.opcode); + cmd_name = nvme_cmd_to_string(admin, cfg.opcode); if (cfg.latency) printf("%s Command %s latency: %llu us\n", - cmd_type ? "Admin": "IO", + admin ? "Admin": "IO", strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific", elapsed_utime(start_time, end_time)); if (err < 0) - perror("passthru"); + fprintf(stderr, "passthru: %s\n", nvme_strerror(errno)); else if (err) nvme_show_status(err); else { fprintf(stderr, "%s Command %s is Success and result: 0x%08x\n", - cmd_type ? "Admin": "IO", + admin ? "Admin": "IO", strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific", result); - if (!cfg.raw_binary) { + if (cfg.read && cfg.input_file) { + if (write(dfd, (void *)data, cfg.data_len) < 0) + perror("failed to write data buffer"); + if (cfg.metadata_len && cfg.metadata) + if (write(mfd, (void *)mdata, cfg.metadata_len) < 0) + perror("failed to write metadata buffer"); + } else if (!cfg.raw_binary) { 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_metadata: + free(mdata); free_data: nvme_free(data, huge); -free_metadata: - free(metadata); -close_wfd: +close_dfd: if (strlen(cfg.input_file)) - close(wfd); + close(dfd); +close_mfd: + if (strlen(cfg.metadata)) + close(mfd); close_fd: close(fd); ret: - return nvme_status_to_errno(err, false); + return err; } static int io_passthru(int argc, char **argv, struct command *cmd, struct plugin *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, 0, desc, cmd); + return passthru(argc, argv, false, 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, 1, desc, cmd); + return passthru(argc, argv, true, desc, cmd); } static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { - int ret; - char uuid_str[37]; /* e.g. 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ -#ifdef LIBUUID - uuid_t uuid; -#endif + char *hostnqn; - 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) { + hostnqn = nvmf_hostnqn_generate(); + if (!hostnqn) { 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); + printf("%s\n", hostnqn); + free(hostnqn); return 0; } @@ -6554,45 +7495,468 @@ static int show_hostnqn_cmd(int argc, char **argv, struct command *command, stru { char *hostnqn; - hostnqn = hostnqn_read(); - if (hostnqn) { - fputs(hostnqn, stdout); - free(hostnqn); - return 0; - } else { + hostnqn = nvmf_hostnqn_from_file(); + if (!hostnqn) + hostnqn = nvmf_hostnqn_generate(); + + if (!hostnqn) { fprintf(stderr, "hostnqn is not available -- use nvme gen-hostnqn\n"); - return -ENOENT; + return ENOENT; + } + + fprintf(stdout, "%s\n", hostnqn); + free(hostnqn); + + return 0; +} + + +static int gen_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Generate a DH-HMAC-CHAP host key usable "\ + "for NVMe In-Band Authentication."; + const char *secret = "Optional secret (in hexadecimal characters) "\ + "to be used to initialize the host key."; + const char *key_len = "Length of the resulting key "\ + "(32, 48, or 64 bytes)."; + const char *hmac = "HMAC function to use for key transformation "\ + "(0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512)."; + const char *nqn = "Host NQN to use for key transformation."; + + unsigned char *raw_secret; + unsigned char key[68]; + char encoded_key[128]; + unsigned long crc = crc32(0L, NULL, 0); + int err = 0; + + struct config { + char *secret; + unsigned int key_len; + char *nqn; + unsigned int hmac; + }; + + struct config cfg = { + .secret = NULL, + .key_len = 0, + .nqn = NULL, + .hmac = 0, + }; + + OPT_ARGS(opts) = { + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_UINT("key-length", 'l', &cfg.key_len, key_len), + OPT_STR("nqn", 'n', &cfg.nqn, nqn), + OPT_UINT("hmac", 'm', &cfg.hmac, hmac), + OPT_END() + }; + + err = argconfig_parse(argc, argv, desc, opts); + if (err) + return err; + + if (cfg.hmac > 3) { + fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac); + return -EINVAL; + } + if (cfg.hmac > 0) { + switch (cfg.hmac) { + case 1: + if (!cfg.key_len) + cfg.key_len = 32; + else if (cfg.key_len != 32) { + fprintf(stderr, "Invalid key length %d for SHA(256)\n", + cfg.key_len); + return -EINVAL; + } + break; + case 2: + if (!cfg.key_len) + cfg.key_len = 48; + else if (cfg.key_len != 48) { + fprintf(stderr, "Invalid key length %d for SHA(384)\n", + cfg.key_len); + return -EINVAL; + } + break; + case 3: + if (!cfg.key_len) + cfg.key_len = 64; + else if (cfg.key_len != 64) { + fprintf(stderr, "Invalid key length %d for SHA(512)\n", + cfg.key_len); + return -EINVAL; + } + break; + default: + break; + } + } else if (!cfg.key_len) + cfg.key_len = 32; + + if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) { + fprintf(stderr, "Invalid key length %u\n", cfg.key_len); + return -EINVAL; + } + raw_secret = malloc(cfg.key_len); + if (!raw_secret) + return -ENOMEM; + if (!cfg.secret) { + if (getrandom_bytes(raw_secret, cfg.key_len) < 0) + return -errno; + } else { + int secret_len = 0, i; + unsigned int c; + + for (i = 0; i < strlen(cfg.secret); i+=2) { + if (sscanf(&cfg.secret[i], "%02x", &c) != 1) { + fprintf(stderr, "Invalid secret '%s'\n", + cfg.secret); + return -EINVAL; + } + raw_secret[secret_len++] = (unsigned char)c; + } + if (secret_len != cfg.key_len) { + fprintf(stderr, "Invalid key length (%d bytes)\n", + secret_len); + return -EINVAL; + } + } + + if (!cfg.nqn) { + cfg.nqn = nvmf_hostnqn_from_file(); + if (!cfg.nqn) { + fprintf(stderr, "Could not read host NQN\n"); + return -ENOENT; + } + } + + if (nvme_gen_dhchap_key(cfg.nqn, cfg.hmac, cfg.key_len, + raw_secret, key) < 0) + return -errno; + + crc = crc32(crc, key, cfg.key_len); + key[cfg.key_len++] = crc & 0xff; + key[cfg.key_len++] = (crc >> 8) & 0xff; + key[cfg.key_len++] = (crc >> 16) & 0xff; + key[cfg.key_len++] = (crc >> 24) & 0xff; + + memset(encoded_key, 0, sizeof(encoded_key)); + base64_encode(key, cfg.key_len, encoded_key); + + printf("DHHC-1:%02x:%s:\n", cfg.hmac, encoded_key); + return 0; +} + +static int check_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Check a DH-HMAC-CHAP host key for usability "\ + "for NVMe In-Band Authentication."; + const char *key = "DH-HMAC-CHAP key (in hexadecimal characters) "\ + "to be validated."; + + unsigned char decoded_key[128]; + unsigned int decoded_len; + u_int32_t crc = crc32(0L, NULL, 0); + u_int32_t key_crc; + int err = 0, hmac; + struct config { + char *key; + }; + + struct config cfg = { + .key = NULL, + }; + + OPT_ARGS(opts) = { + OPT_STR("key", 'k', &cfg.key, key), + OPT_END() + }; + + err = argconfig_parse(argc, argv, desc, opts); + if (err) + return err; + + if (!cfg.key) { + fprintf(stderr, "Key not specified\n"); + return -EINVAL; + } + + if (sscanf(cfg.key, "DHHC-1:%02x:*s", &hmac) != 1) { + fprintf(stderr, "Invalid key header '%s'\n", cfg.key); + return -EINVAL; + } + switch (hmac) { + case 0: + break; + case 1: + if (strlen(cfg.key) != 59) { + fprintf(stderr, "Invalid key length for SHA(256)\n"); + return -EINVAL; + } + break; + case 2: + if (strlen(cfg.key) != 83) { + fprintf(stderr, "Invalid key length for SHA(384)\n"); + return -EINVAL; + } + break; + case 3: + if (strlen(cfg.key) != 103) { + fprintf(stderr, "Invalid key length for SHA(512)\n"); + return -EINVAL; + } + break; + default: + fprintf(stderr, "Invalid HMAC identifier %d\n", hmac); + return -EINVAL; + break; + } + + err = base64_decode(cfg.key + 10, strlen(cfg.key) - 11, + decoded_key); + if (err < 0) { + fprintf(stderr, "Base64 decoding failed, error %d\n", + err); + return err; + } + decoded_len = err; + if (decoded_len < 32) { + fprintf(stderr, "Base64 decoding failed (%s, size %u)\n", + cfg.key + 10, decoded_len); + return -EINVAL; + } + decoded_len -= 4; + if (decoded_len != 32 && decoded_len != 48 && decoded_len != 64) { + fprintf(stderr, "Invalid key length %d\n", decoded_len); + return -EINVAL; } + crc = crc32(crc, decoded_key, decoded_len); + key_crc = ((u_int32_t)decoded_key[decoded_len]) | + ((u_int32_t)decoded_key[decoded_len + 1] << 8) | + ((u_int32_t)decoded_key[decoded_len + 2] << 16) | + ((u_int32_t)decoded_key[decoded_len + 3] << 24); + if (key_crc != crc) { + fprintf(stderr, "CRC mismatch (key %08x, crc %08x)\n", + key_crc, crc); + return -EINVAL; + } + printf("Key is valid (HMAC %d, length %d, CRC %08x)\n", + hmac, decoded_len, crc); + return 0; +} + +static int gen_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Generate a TLS key in NVMe PSK Interchange format."; + const char *secret = "Optional secret (in hexadecimal characters) "\ + "to be used for the TLS key."; + const char *hmac = "HMAC function to use for the retained key "\ + "(1 = SHA-256, 2 = SHA-384)."; + + unsigned char *raw_secret; + char encoded_key[128]; + int key_len = 32; + unsigned long crc = crc32(0L, NULL, 0); + int err = 0; + + struct config { + char *secret; + unsigned int hmac; + }; + + struct config cfg = { + .secret = NULL, + .hmac = 1, + }; + + OPT_ARGS(opts) = { + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_UINT("hmac", 'm', &cfg.hmac, hmac), + OPT_END() + }; + + err = argconfig_parse(argc, argv, desc, opts); + if (err) + return err; + if (cfg.hmac < 1 || cfg.hmac > 3) { + fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac); + return -EINVAL; + } + + if (cfg.hmac == 2) + key_len = 48; + + raw_secret = malloc(key_len + 4); + if (!raw_secret) + return -ENOMEM; + if (!cfg.secret) { + if (getrandom_bytes(raw_secret, key_len) < 0) + return -errno; + } else { + int secret_len = 0, i; + unsigned int c; + + for (i = 0; i < strlen(cfg.secret); i+=2) { + if (sscanf(&cfg.secret[i], "%02x", &c) != 1) { + fprintf(stderr, "Invalid secret '%s'\n", + cfg.secret); + return -EINVAL; + } + if (i >= key_len) { + fprintf(stderr, + "Skipping excess secret bytes\n"); + break; + } + raw_secret[secret_len++] = (unsigned char)c; + } + if (secret_len != key_len) { + fprintf(stderr, "Invalid key length (%d bytes)\n", + secret_len); + return -EINVAL; + } + } + + crc = crc32(crc, raw_secret, key_len); + raw_secret[key_len++] = crc & 0xff; + raw_secret[key_len++] = (crc >> 8) & 0xff; + raw_secret[key_len++] = (crc >> 16) & 0xff; + raw_secret[key_len++] = (crc >> 24) & 0xff; + + memset(encoded_key, 0, sizeof(encoded_key)); + base64_encode(raw_secret, key_len, encoded_key); + + printf("NVMeTLSkey-1:%02x:%s:\n", cfg.hmac, encoded_key); + return 0; +} + +static int check_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Check a TLS key for NVMe PSK Interchange format.\n"; + const char *key = "TLS key (in PSK Interchange format) "\ + "to be validated."; + + unsigned char decoded_key[128]; + unsigned int decoded_len; + u_int32_t crc = crc32(0L, NULL, 0); + u_int32_t key_crc; + int err = 0, hmac; + struct config { + char *key; + }; + + struct config cfg = { + .key = NULL, + }; + + OPT_ARGS(opts) = { + OPT_STR("key", 'k', &cfg.key, key), + OPT_END() + }; + + err = argconfig_parse(argc, argv, desc, opts); + if (err) + return err; + + if (!cfg.key) { + fprintf(stderr, "Key not specified\n"); + return -EINVAL; + } + + if (sscanf(cfg.key, "NVMeTLSkey-1:%02x:*s", &hmac) != 1) { + fprintf(stderr, "Invalid key header '%s'\n", cfg.key); + return -EINVAL; + } + switch (hmac) { + case 1: + if (strlen(cfg.key) != 65) { + fprintf(stderr, "Invalid key length %lu for SHA(256)\n", + strlen(cfg.key)); + return -EINVAL; + } + break; + case 2: + if (strlen(cfg.key) != 89) { + fprintf(stderr, "Invalid key length %lu for SHA(384)\n", + strlen(cfg.key)); + return -EINVAL; + } + break; + default: + fprintf(stderr, "Invalid HMAC identifier %d\n", hmac); + return -EINVAL; + break; + } + + err = base64_decode(cfg.key + 16, strlen(cfg.key) - 17, + decoded_key); + if (err < 0) { + fprintf(stderr, "Base64 decoding failed (%s, error %d)\n", + cfg.key + 16, err); + return err; + } + decoded_len = err; + decoded_len -= 4; + if (decoded_len != 32 && decoded_len != 48) { + fprintf(stderr, "Invalid key length %d\n", decoded_len); + return -EINVAL; + } + crc = crc32(crc, decoded_key, decoded_len); + key_crc = ((u_int32_t)decoded_key[decoded_len]) | + ((u_int32_t)decoded_key[decoded_len + 1] << 8) | + ((u_int32_t)decoded_key[decoded_len + 2] << 16) | + ((u_int32_t)decoded_key[decoded_len + 3] << 24); + if (key_crc != crc) { + fprintf(stderr, "CRC mismatch (key %08x, crc %08x)\n", + key_crc, crc); + return -EINVAL; + } + printf("Key is valid (HMAC %d, length %d, CRC %08x)\n", + hmac, decoded_len, crc); + return 0; } static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Send Get Log Page request to Discovery Controller."; - return fabrics_discover(desc, argc, argv, false); + return nvmf_discover(desc, argc, argv, false); } static int connect_all_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Discover NVMeoF subsystems and connect to them"; - return fabrics_discover(desc, argc, argv, true); + return nvmf_discover(desc, argc, argv, true); } static int connect_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Connect to NVMeoF subsystem"; - return fabrics_connect(desc, argc, argv); + return nvmf_connect(desc, argc, argv); } static int disconnect_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Disconnect from NVMeoF subsystem"; - return fabrics_disconnect(desc, argc, argv); + return nvmf_disconnect(desc, argc, argv); } -static int disconnect_all_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) +int disconnect_all_cmd(int argc, char **argv, struct command *command, + struct plugin *plugin) { const char *desc = "Disconnect from all connected NVMeoF subsystems"; - return fabrics_disconnect_all(desc, argc, argv); + return nvmf_disconnect_all(desc, argc, argv); +} + +static int config_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Configuration of NVMeoF subsystems"; + return nvmf_config(desc, argc, argv); +} + +static int dim_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Send Discovery Information Management command to a Discovery Controller (DC)"; + return nvmf_dim(desc, argc, argv); } void register_extension(struct plugin *plugin) @@ -6617,5 +7981,5 @@ int main(int argc, char **argv) if (err == -ENOTTY) general_help(&builtin); - return err; + return err ? 1 : 0; } diff --git a/nvme.control.in b/nvme.control.in index 46714d4..74d36aa 100644 --- a/nvme.control.in +++ b/nvme.control.in @@ -1,9 +1,9 @@ Package: nvme -Version: @@VERSION@@ +Version: @VERSION@ Section: base Priority: optional Architecture: amd64 -Depends: @@DEPENDS@@ +Depends: @DEPENDS@ Maintainer: Keith Busch <kbusch@kernel.org> Description: NVM-Express Command Line Interface The nvme management tool |