diff options
Diffstat (limited to '')
-rw-r--r-- | plugins/zns/zns.c | 1274 |
1 files changed, 1274 insertions, 0 deletions
diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c new file mode 100644 index 0000000..a7a3766 --- /dev/null +++ b/plugins/zns/zns.c @@ -0,0 +1,1274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <linux/fs.h> +#include <sys/stat.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "nvme-print.h" +#include "util/cleanup.h" + +#define CREATE_CMD +#include "zns.h" + +static const char *namespace_id = "Namespace identifier to use"; +static const char dash[100] = { [0 ... 99] = '-' }; + +static int detect_zns(nvme_ns_t ns, int *out_supported) +{ + int err = 0; + char *zoned; + + *out_supported = 0; + + zoned = nvme_get_attr(nvme_ns_get_sysfs_dir(ns), "queue/zoned"); + if (!zoned) { + *out_supported = 0; + return err; + } + + *out_supported = strcmp("host-managed", zoned) == 0; + free(zoned); + + return err; +} + +static int print_zns_list_ns(nvme_ns_t ns) +{ + int supported; + int err = 0; + + err = detect_zns(ns, &supported); + if (err) { + perror("Failed to enumerate namespace"); + return err; + } + + if (supported) + nvme_show_list_item(ns); + + return err; +} + +static int print_zns_list(nvme_root_t nvme_root) +{ + int err = 0; + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c; + nvme_ns_t n; + + nvme_for_each_host(nvme_root, h) { + nvme_for_each_subsystem(h, s) { + nvme_subsystem_for_each_ns(s, n) { + err = print_zns_list_ns(n); + if (err) + return err; + } + + nvme_subsystem_for_each_ctrl(s, c) { + nvme_ctrl_for_each_ns(c, n) { + err = print_zns_list_ns(n); + if (err) + return err; + } + } + } + } + + return err; +} + +static int list(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = 0; + nvme_root_t nvme_root; + + printf("%-21s %-20s %-40s %-9s %-26s %-16s %-8s\n", "Node", "SN", + "Model", "Namespace", "Usage", "Format", "FW Rev"); + printf("%-.21s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash, + dash, dash, dash, dash, dash); + + nvme_root = nvme_scan(NULL); + if (nvme_root) { + err = print_zns_list(nvme_root); + nvme_free_tree(nvme_root); + } else { + fprintf(stderr, "Failed to scan nvme subsystems\n"); + err = -errno; + } + + return err; +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send a ZNS specific Identify Controller command to\n" + "the given device and report information about the specified\n" + "controller in various formats."; + + enum nvme_print_flags flags; + struct nvme_zns_id_ctrl ctrl; + struct nvme_dev *dev; + int err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; + + err = nvme_zns_identify_ctrl(dev_fd(dev), &ctrl); + if (!err) + nvme_show_zns_id_ctrl(&ctrl, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify controller"); +close_dev: + dev_close(dev); + return err; +} + +static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send a ZNS specific Identify Namespace command to\n" + "the given device and report information about the specified\n" + "namespace in varios formats."; + const char *vendor_specific = "dump binary vendor fields"; + const char *human_readable = "show identify in readable format"; + + enum nvme_print_flags flags; + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + struct nvme_dev *dev; + int err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + bool human_readable; + bool vendor_specific; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; + if (cfg.vendor_specific) + flags |= VS; + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &id_ns); + if (err) { + nvme_show_status(err); + goto close_dev; + } + + err = nvme_zns_identify_ns(dev_fd(dev), cfg.namespace_id, &ns); + if (!err) + nvme_show_zns_id_ns(&ns, &id_ns, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify namespace"); +close_dev: + dev_close(dev); + return err; +} + +static int zns_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin, + const char *desc, enum nvme_zns_send_action zsa) +{ + const char *zslba = "starting LBA of the zone for this command"; + const char *select_all = "send command to all zones"; + const char *timeout = "timeout value, in milliseconds"; + struct nvme_dev *dev; + int err, zcapc = 0; + char *command; + __u32 result; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool select_all; + __u32 timeout; + }; + + struct config cfg = {}; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + goto ret; + + err = asprintf(&command, "%s-%s", plugin->name, cmd->name); + if (err < 0) + goto close_dev; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto free; + } + } + + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.zslba, + .zsa = zsa, + .select_all = cfg.select_all, + .zsaso = 0, + .data_len = 0, + .data = NULL, + .timeout = cfg.timeout, + .result = &result, + }; + err = nvme_zns_mgmt_send(&args); + if (!err) { + if (zsa == NVME_ZNS_ZSA_RESET) + zcapc = result & 0x1; + + printf("%s: Success, action:%d zone:%"PRIx64" all:%d zcapc:%u nsid:%d\n", + command, zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, + zcapc, cfg.namespace_id); + } else if (err > 0) { + nvme_show_status(err); + } else { + perror(desc); + } +free: + free(command); +close_dev: + dev_close(dev); +ret: + return err; +} + +static int get_zdes_bytes(int fd, __u32 nsid) +{ + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + __u8 lbaf; + int err; + + err = nvme_identify_ns(fd, nsid, &id_ns); + if (err > 0) { + nvme_show_status(err); + return -1; + } else if (err < 0) { + perror("identify namespace"); + return -1; + } + + err = nvme_zns_identify_ns(fd, nsid, &ns); + if (err > 0) { + nvme_show_status(err); + return -1; + } else if (err < 0) { + perror("zns identify namespace"); + return -1; + } + + nvme_id_ns_flbas_to_lbaf_inuse(id_ns.flbas, &lbaf); + return ns.lbafe[lbaf].zdes << 6; +} + +static int zone_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Send"; + const char *zslba = + "starting LBA of the zone for this command(for flush action, last lba to flush)"; + const char *zsaso = "Zone Send Action Specific Option"; + const char *select_all = "send command to all zones"; + const char *zsa = "zone send action"; + const char *data_len = "buffer length if data required"; + const char *data = "optional file for data (default stdin)"; + const char *timeout = "timeout value, in milliseconds"; + + int ffd = STDIN_FILENO, err = -1; + struct nvme_dev *dev; + void *buf = NULL; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool zsaso; + bool select_all; + __u8 zsa; + int data_len; + char *file; + __u32 timeout; + }; + + struct config cfg = {}; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zsaso", 'o', &cfg.zsaso, zsaso), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_BYTE("zsa", 'z', &cfg.zsa, zsa), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + if (!cfg.zsa) { + fprintf(stderr, "zone send action must be specified\n"); + err = -EINVAL; + goto close_dev; + } + + if (cfg.zsa == NVME_ZNS_ZSA_SET_DESC_EXT) { + if (!cfg.data_len) { + int data_len = get_zdes_bytes(dev_fd(dev), + cfg.namespace_id); + + if (data_len == 0) { + fprintf(stderr, "Zone Descriptor Extensions are not supported\n"); + goto close_dev; + } else if (data_len < 0) { + err = data_len; + goto close_dev; + } + cfg.data_len = data_len; + } + if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { + fprintf(stderr, "can not allocate feature payload\n"); + goto close_dev; + } + memset(buf, 0, cfg.data_len); + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + goto free; + } + } + + err = read(ffd, (void *)buf, cfg.data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + } else { + if (cfg.file || cfg.data_len) { + fprintf(stderr, "data, data_len only valid with set extended descriptor\n"); + err = -EINVAL; + goto close_dev; + } + } + + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.zslba, + .zsa = cfg.zsa, + .select_all = cfg.select_all, + .zsaso = cfg.zsaso, + .data_len = cfg.data_len, + .data = buf, + .timeout = cfg.timeout, + .result = NULL, + }; + err = nvme_zns_mgmt_send(&args); + if (!err) + printf("zone-mgmt-send: Success, action:%d zone:%"PRIx64" all:%d nsid:%d\n", + cfg.zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-send"); + +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_dev: + dev_close(dev); + return err; +} + +static int close_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Close zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_CLOSE); +} + +static int finish_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Finish zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_FINISH); +} + +static int open_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Open zones\n"; + const char *zslba = "starting LBA of the zone for this command"; + const char *zrwaa = "Allocate Zone Random Write Area to zone"; + const char *select_all = "send command to all zones"; + const char *timeout = "timeout value, in milliseconds"; + struct nvme_dev *dev; + int err; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool zrwaa; + bool select_all; + __u32 timeout; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.zslba, + .zsa = NVME_ZNS_ZSA_OPEN, + .select_all = cfg.select_all, + .zsaso = cfg.zrwaa, + .data_len = 0, + .data = NULL, + .timeout = cfg.timeout, + .result = NULL, + }; + err = nvme_zns_mgmt_send(&args); + if (!err) + printf("zns-open-zone: Success zone slba:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.zslba, cfg.namespace_id); + else + nvme_show_status(err); +close_dev: + dev_close(dev); + return err; +} + +static int reset_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reset zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_RESET); +} + +static int offline_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Offline zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OFFLINE); +} + +static int set_zone_desc(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Set Zone Descriptor Extension\n"; + const char *zslba = "starting LBA of the zone for this command"; + const char *zrwaa = "Allocate Zone Random Write Area to zone"; + const char *data = "optional file for zone extension data (default stdin)"; + const char *timeout = "timeout value, in milliseconds"; + + int ffd = STDIN_FILENO, err; + struct nvme_dev *dev; + void *buf = NULL; + int data_len; + + struct config { + __u64 zslba; + bool zrwaa; + __u32 namespace_id; + char *file; + __u32 timeout; + }; + + struct config cfg = {}; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("zrwaa", 'r', &cfg.zrwaa, zrwaa), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + data_len = get_zdes_bytes(dev_fd(dev), cfg.namespace_id); + + if (!data_len || data_len < 0) { + fprintf(stderr, + "zone format does not provide descriptor extension\n"); + errno = EINVAL; + err = -1; + goto close_dev; + } + + buf = calloc(1, data_len); + if (!buf) { + perror("could not alloc memory for zone desc"); + err = -ENOMEM; + goto close_dev; + } + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + err = -1; + goto free; + } + } + + err = read(ffd, (void *)buf, data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.zslba, + .zsa = NVME_ZNS_ZSA_SET_DESC_EXT, + .select_all = 0, + .zsaso = cfg.zrwaa, + .data_len = data_len, + .data = buf, + .timeout = cfg.timeout, + .result = NULL, + }; + err = nvme_zns_mgmt_send(&args); + if (!err) + printf("set-zone-desc: Success, zone:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns set-zone-desc"); +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_dev: + dev_close(dev); + return err; +} + + +static int zrwa_flush_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Flush Explicit ZRWA Range"; + const char *slba = "LBA to flush up to"; + const char *timeout = "timeout value, in milliseconds"; + struct nvme_dev *dev; + int err; + + struct config { + __u64 lba; + __u32 namespace_id; + __u32 timeout; + }; + + struct config cfg = {}; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("lba", 'l', &cfg.lba, slba), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + struct nvme_zns_mgmt_send_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.lba, + .zsa = NVME_ZNS_ZSA_ZRWA_FLUSH, + .select_all = 0, + .zsaso = 0, + .data_len = 0, + .data = NULL, + .timeout = cfg.timeout, + .result = NULL, + }; + err = nvme_zns_mgmt_send(&args); + if (!err) + printf("zrwa-flush-zone: Success, lba:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.lba, cfg.namespace_id); + else + nvme_show_status(err); +close_dev: + dev_close(dev); + return err; +} + +static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Receive"; + const char *zslba = "starting LBA of the zone"; + const char *zra = "Zone Receive Action"; + const char *zrasf = "Zone Receive Action Specific Field(Reporting Options)"; + const char *partial = "Zone Receive Action Specific Features(Partial Report)"; + const char *data_len = "length of data in bytes"; + + enum nvme_print_flags flags; + struct nvme_dev *dev; + void *data = NULL; + int err = -1; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + __u8 zra; + __u8 zrasf; + bool partial; + __u32 data_len; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_BYTE("zra", 'z', &cfg.zra, zra), + OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), + OPT_FLAG("partial", 'p', &cfg.partial, partial), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + if (cfg.zra == NVME_ZNS_ZRA_REPORT_ZONES && !cfg.data_len) { + fprintf(stderr, "error: data len is needed for NVME_ZRA_ZONE_REPORT\n"); + err = -EINVAL; + goto close_dev; + } + if (cfg.data_len) { + data = calloc(1, cfg.data_len); + if (!data) { + perror("could not alloc memory for zone mgmt receive data"); + err = -ENOMEM; + goto close_dev; + } + } + + struct nvme_zns_mgmt_recv_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .slba = cfg.zslba, + .zra = cfg.zra, + .zrasf = cfg.zrasf, + .zras_feat = cfg.partial, + .data_len = cfg.data_len, + .data = data, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_zns_mgmt_recv(&args); + if (!err) + printf("zone-mgmt-recv: Success, action:%d zone:%"PRIx64" nsid:%d\n", + cfg.zra, (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-recv"); + + free(data); +close_dev: + dev_close(dev); + return err; +} + +static int report_zones(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve the Report Zones data structure"; + const char *zslba = "starting LBA of the zone"; + const char *num_descs = "number of descriptors to retrieve (default: all of them)"; + const char *state = "state of zones to list"; + const char *ext = "set to use the extended report zones"; + const char *part = "set to use the partial report"; + const char *verbose = "show report zones verbosity"; + + enum nvme_print_flags flags; + int zdes = 0, err = -1; + struct nvme_dev *dev; + __u32 report_size; + struct nvme_zone_report *report, *buff; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; + + unsigned int nr_zones_chunks = 1024, /* 1024 entries * 64 bytes per entry = 64k byte transfer */ + nr_zones_retrieved = 0, + nr_zones, + log_len; + __u64 offset; + int total_nr_zones = 0; + struct nvme_zns_id_ns id_zns; + struct nvme_id_ns id_ns; + uint8_t lbaf; + __le64 zsze; + struct json_object *zone_list = NULL; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + int num_descs; + int state; + bool verbose; + bool extended; + bool partial; + }; + + struct config cfg = { + .output_format = "normal", + .num_descs = -1, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), + OPT_UINT("state", 'S', &cfg.state, state), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_FLAG("extended", 'e', &cfg.extended, ext), + OPT_FLAG("partial", 'p', &cfg.partial, part), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; + if (cfg.verbose) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + if (cfg.extended) { + zdes = get_zdes_bytes(dev_fd(dev), cfg.namespace_id); + if (zdes < 0) { + err = zdes; + goto close_dev; + } + } + + err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &id_ns); + if (err) { + nvme_show_status(err); + goto close_dev; + } + + err = nvme_zns_identify_ns(dev_fd(dev), cfg.namespace_id, &id_zns); + if (!err) { + /* get zsze field from zns id ns data - needed for offset calculation */ + nvme_id_ns_flbas_to_lbaf_inuse(id_ns.flbas, &lbaf); + zsze = le64_to_cpu(id_zns.lbafe[lbaf].zsze); + } else { + nvme_show_status(err); + goto close_dev; + } + + log_len = sizeof(struct nvme_zone_report); + buff = calloc(1, log_len); + if (!buff) { + err = -ENOMEM; + goto close_dev; + } + + err = nvme_zns_report_zones(dev_fd(dev), cfg.namespace_id, 0, + cfg.state, false, false, + log_len, buff, + NVME_DEFAULT_IOCTL_TIMEOUT, NULL); + if (err > 0) { + nvme_show_status(err); + goto free_buff; + } else if (err < 0) { + perror("zns report-zones"); + goto free_buff; + } + + total_nr_zones = le64_to_cpu(buff->nr_zones); + + if (cfg.num_descs == -1) + cfg.num_descs = total_nr_zones; + + nr_zones = cfg.num_descs; + if (nr_zones < nr_zones_chunks) + nr_zones_chunks = nr_zones; + + log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); + report_size = log_len; + + report = nvme_alloc_huge(report_size, &mh); + if (!report) { + perror("alloc"); + err = -ENOMEM; + goto close_dev; + } + + offset = cfg.zslba; + + nvme_zns_start_zone_list(total_nr_zones, &zone_list, flags); + + while (nr_zones_retrieved < nr_zones) { + if (nr_zones_retrieved >= nr_zones) + break; + + if (nr_zones_retrieved + nr_zones_chunks > nr_zones) { + nr_zones_chunks = nr_zones - nr_zones_retrieved; + log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); + } + + err = nvme_zns_report_zones(dev_fd(dev), cfg.namespace_id, + offset, + cfg.state, cfg.extended, + cfg.partial, log_len, report, + NVME_DEFAULT_IOCTL_TIMEOUT, NULL); + if (err > 0) { + nvme_show_status(err); + break; + } + + if (!err) + nvme_show_zns_report_zones(report, nr_zones_chunks, + zdes, log_len, zone_list, flags); + + nr_zones_retrieved += nr_zones_chunks; + offset = le64_to_cpu(report->entries[nr_zones_chunks-1].zslba) + zsze; + } + + nvme_zns_finish_zone_list(total_nr_zones, zone_list, flags); + +free_buff: + free(buff); +close_dev: + dev_close(dev); + return err; +} + +static int zone_append(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The zone append command is used to write to a zone\n" + "using the slba of the zone, and the write will be appended from the\n" + "write pointer of the zone"; + const char *zslba = "starting LBA of the zone"; + const char *data = "file containing data to write"; + const char *metadata = "file with metadata to be written"; + const char *limited_retry = "limit media access attempts"; + const char *fua = "force unit access"; + const char *prinfo = "protection information action and checks field"; + const char *piremap = "protection information remap (for type 1 PI)"; + const char *ref_tag = "reference tag for end-to-end PI"; + const char *lbat = "logical block application tag for end-to-end PI"; + const char *lbatm = "logical block application tag mask for end-to-end PI"; + const char *metadata_size = "size of metadata in bytes"; + const char *data_size = "size of data in bytes"; + const char *latency = "output latency statistics"; + + int err = -1, dfd = STDIN_FILENO, mfd = STDIN_FILENO; + unsigned int lba_size, meta_size; + void *buf = NULL, *mbuf = NULL; + __u16 nblocks, control = 0; + struct nvme_dev *dev; + __u64 result; + __u8 lba_index; + struct timeval start_time, end_time; + + struct nvme_id_ns ns; + + struct config { + char *data; + char *metadata; + __u64 zslba; + __u64 data_size; + __u64 metadata_size; + bool limited_retry; + bool fua; + __u32 namespace_id; + __u64 ref_tag; + __u16 lbat; + __u16 lbatm; + __u8 prinfo; + bool piremap; + bool latency; + }; + + struct config cfg = {}; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), + OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), + OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), + OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, lbatm), + OPT_SHRT("app-tag", 'a', &cfg.lbat, lbat), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_FLAG("piremap", 'P', &cfg.piremap, piremap), + OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + if (!cfg.data_size) { + fprintf(stderr, "Append size not provided\n"); + errno = EINVAL; + goto close_dev; + } + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &ns); + if (err) { + nvme_show_status(err); + goto close_dev; + } + + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index); + lba_size = 1 << ns.lbaf[lba_index].ds; + if (cfg.data_size & (lba_size - 1)) { + fprintf(stderr, + "Data size:%#"PRIx64" not aligned to lba size:%#x\n", + (uint64_t)cfg.data_size, lba_size); + errno = EINVAL; + goto close_dev; + } + + meta_size = ns.lbaf[lba_index].ms; + if (meta_size && !(meta_size == 8 && (cfg.prinfo & 0x8)) && + (!cfg.metadata_size || cfg.metadata_size % meta_size)) { + fprintf(stderr, + "Metadata size:%#"PRIx64" not aligned to metadata size:%#x\n", + (uint64_t)cfg.metadata_size, meta_size); + errno = EINVAL; + goto close_dev; + } + + if (cfg.prinfo > 0xf) { + fprintf(stderr, "Invalid value for prinfo:%#x\n", cfg.prinfo); + errno = EINVAL; + goto close_dev; + } + + if (cfg.data) { + dfd = open(cfg.data, O_RDONLY); + if (dfd < 0) { + perror(cfg.data); + goto close_dev; + } + } + + if (posix_memalign(&buf, getpagesize(), cfg.data_size)) { + fprintf(stderr, "No memory for data size:%"PRIx64"\n", + (uint64_t)cfg.data_size); + goto close_dfd; + } + + memset(buf, 0, cfg.data_size); + err = read(dfd, buf, cfg.data_size); + if (err < 0) { + perror("read-data"); + goto free_data; + } + + if (cfg.metadata) { + mfd = open(cfg.metadata, O_RDONLY); + if (mfd < 0) { + perror(cfg.metadata); + err = -1; + goto free_data; + } + } + + if (cfg.metadata_size) { + if (posix_memalign(&mbuf, getpagesize(), meta_size)) { + fprintf(stderr, "No memory for metadata size:%d\n", + meta_size); + err = -1; + goto close_mfd; + } + + memset(mbuf, 0, cfg.metadata_size); + err = read(mfd, mbuf, cfg.metadata_size); + if (err < 0) { + perror("read-metadata"); + goto free_meta; + } + } + + nblocks = (cfg.data_size / lba_size) - 1; + control |= (cfg.prinfo << 10); + if (cfg.limited_retry) + control |= NVME_IO_LR; + if (cfg.fua) + control |= NVME_IO_FUA; + if (cfg.piremap) + control |= NVME_IO_ZNS_APPEND_PIREMAP; + + struct nvme_zns_append_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .nsid = cfg.namespace_id, + .zslba = cfg.zslba, + .nlb = nblocks, + .control = control, + .ilbrt_u64 = cfg.ref_tag, + .lbat = cfg.lbat, + .lbatm = cfg.lbatm, + .data_len = cfg.data_size, + .data = buf, + .metadata_len = cfg.metadata_size, + .metadata = mbuf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + gettimeofday(&start_time, NULL); + err = nvme_zns_append(&args); + gettimeofday(&end_time, NULL); + if (cfg.latency) + printf(" latency: zone append: %llu us\n", + elapsed_utime(start_time, end_time)); + + if (!err) + printf("Success appended data to LBA %"PRIx64"\n", (uint64_t)result); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-append"); + +free_meta: + free(mbuf); +close_mfd: + if (cfg.metadata) + close(mfd); +free_data: + free(buf); +close_dfd: + if (cfg.data) + close(dfd); +close_dev: + dev_close(dev); + return err; +} + +static int changed_zone_list(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Changed Zone log for the given device"; + const char *rae = "retain an asynchronous event"; + + struct nvme_zns_changed_zone_log log; + enum nvme_print_flags flags; + struct nvme_dev *dev; + int err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + bool rae; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return errno; + + err = validate_output_format(cfg.output_format, &flags); + if (err < 0) + goto close_dev; + + if (!cfg.namespace_id) { + err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id); + if (err < 0) { + perror("get-namespace-id"); + goto close_dev; + } + } + + err = nvme_get_log_zns_changed_zones(dev_fd(dev), cfg.namespace_id, + cfg.rae, &log); + if (!err) + nvme_show_zns_changed(&log, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns changed-zone-list"); + +close_dev: + dev_close(dev); + return err; +} |