From: Keith Busch Date: Mon, 8 Jun 2020 23:13:50 +0000 (-0700) Subject: support for zoned command set X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=c1fc890937e7d644f1a4a6f3934af6aae33d018a;p=users%2Fhch%2Fnvme-cli.git support for zoned command set Signed-off-by: Keith Busch --- diff --git a/Makefile b/Makefile index 9db121c..f5e9853 100644 --- a/Makefile +++ b/Makefile @@ -77,9 +77,10 @@ PLUGIN_OBJS := \ plugins/seagate/seagate-nvme.o \ plugins/virtium/virtium-nvme.o \ plugins/shannon/shannon-nvme.o \ - plugins/dera/dera-nvme.o \ - plugins/scaleflux/sfx-nvme.o \ - plugins/transcend/transcend-nvme.o + plugins/dera/dera-nvme.o \ + plugins/scaleflux/sfx-nvme.o \ + plugins/transcend/transcend-nvme.o \ + plugins/zns/zns.o nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) NVME-VERSION-FILE $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $(INC) $< -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) $(LDFLAGS) diff --git a/linux/nvme.h b/linux/nvme.h index f2c4fdb..98780ce 100644 --- a/linux/nvme.h +++ b/linux/nvme.h @@ -814,6 +814,9 @@ enum nvme_opcode { nvme_cmd_resv_report = 0x0e, nvme_cmd_resv_acquire = 0x11, nvme_cmd_resv_release = 0x15, + nvme_zns_cmd_mgmt_send = 0x79, + nvme_zns_cmd_mgmt_recv = 0x7a, + nvme_zns_cmd_append = 0x7d, }; /* @@ -1415,4 +1418,139 @@ enum { #define NVME_MINOR(ver) (((ver) >> 8) & 0xff) #define NVME_TERTIARY(ver) ((ver) & 0xff) + +/** + * struct nvme_zns_lbafe - + * zsze: + * zdes: + */ +struct nvme_zns_lbafe { + __le64 zsze; + __u8 zdes; + __u8 rsvd9[7]; +}; + +/** + * struct nvme_zns_id_ns - + * @zoc: + * @ozcs: + * @mar: + * @mor: + * @rrl: + * @frl: + * @lbafe: + * @vs: + */ +struct nvme_zns_id_ns { + __le16 zoc; + __le16 ozcs; + __le32 mar; + __le32 mor; + __le32 rrl; + __le32 frl; + __u8 rsvd20[2796]; + struct nvme_zns_lbafe lbafe[16]; + __u8 rsvd3072[768]; + __u8 vs[256]; +}; + +/** + * struct nvme_zns_id_ctrl - + * @zamds: + */ +struct nvme_zns_id_ctrl { + __u8 zamds; + __u8 rsvd1[4095]; +}; + +#define NVME_ZNS_CHANGED_ZONES_MAX 511 + +/** + * struct nvme_zns_changed_zone_log - ZNS Changed Zone List log + * @nrzid: + * @zid: + */ +struct nvme_zns_changed_zone_log { + __le16 nrzid; + __u8 rsvd2[6]; + __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX]; +}; + +/** + * enum nvme_zns_zt - + */ +enum nvme_zns_zt { + NVME_ZONE_TYPE_SEQWRITE_REQ = 0x2, +}; + +/** + * enum nvme_zns_za - + */ +enum nvme_zns_za { + NVME_ZNS_ZA_ZFC = 1 << 0, + NVME_ZNS_ZA_FZR = 1 << 1, + NVME_ZNS_ZA_RZR = 1 << 2, + NVME_ZNS_ZA_ZDEV = 1 << 7, +}; + +/** + * enum nvme_zns_zs - + */ +enum nvme_zns_zs { + NVME_ZNS_ZS_EMPTY = 0x1, + NVME_ZNS_ZS_IMPL_OPEN = 0x2, + NVME_ZNS_ZS_EXPL_OPEN = 0x3, + NVME_ZNS_ZS_CLOSED = 0x4, + NVME_ZNS_ZS_READ_ONLY = 0xd, + NVME_ZNS_ZS_FULL = 0xe, + NVME_ZNS_ZS_OFFLINE = 0xf, +}; + +/** + * struct nvme_zns_desc - + */ +struct nvme_zns_desc { + __u8 zt; + __u8 zs; + __u8 za; + __u8 rsvd3[5]; + __le64 zcap; + __le64 zslba; + __le64 wp; + __u8 rsvd32[32]; +}; + +/** + * struct nvme_zone_report - + */ +struct nvme_zone_report { + __le64 nr_zones; + __u8 resv8[56]; + struct nvme_zns_desc entries[]; +}; + +enum nvme_zns_send_action { + NVME_ZNS_ZSA_CLOSE = 0x1, + NVME_ZNS_ZSA_FINISH = 0x2, + NVME_ZNS_ZSA_OPEN = 0x3, + NVME_ZNS_ZSA_RESET = 0x4, + NVME_ZNS_ZSA_OFFLINE = 0x5, + NVME_ZNS_ZSA_SET_DESC_EXT = 0x10, +}; + +enum nvme_zns_recv_action { + NVME_ZNS_ZRA_REPORT_ZONES = 0x0, + NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES = 0x1, +}; + +enum nvme_zns_report_options { + NVME_ZNS_ZRAS_REPORT_ALL = 0x0, + NVME_ZNS_ZRAS_REPORT_EMPTY = 0x1, + NVME_ZNS_ZRAS_REPORT_IMPL_OPENED = 0x2, + NVME_ZNS_ZRAS_REPORT_EXPL_OPENED = 0x3, + NVME_ZNS_ZRAS_REPORT_CLOSED = 0x4, + NVME_ZNS_ZRAS_REPORT_FULL = 0x5, + NVME_ZNS_ZRAS_REPORT_READ_ONLY = 0x6, + NVME_ZNS_ZRAS_REPORT_OFFLINE = 0x7, +}; #endif /* _LINUX_NVME_H */ diff --git a/nvme-ioctl.c b/nvme-ioctl.c index 39685d6..403ac5b 100644 --- a/nvme-ioctl.c +++ b/nvme-ioctl.c @@ -405,6 +405,16 @@ int nvme_identify_uuid(int fd, void *data) return nvme_identify(fd, 0, NVME_ID_CNS_UUID_LIST, data); } +int nvme_zns_identify_ns(int fd, __u32 nsid, void *data) +{ + return nvme_identify13(fd, nsid, 6, 2 << 24, data); +} + +int nvme_zns_identify_ctrl(int fd, void *data) +{ + return nvme_identify13(fd, 0, 6, 2 << 24, data); +} + int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, __u16 lsi, bool rae, __u8 uuid_ix, __u32 data_len, void *data) { @@ -877,3 +887,101 @@ int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result) return err; } + +int nvme_zns_mgmt_send(int fd, __u32 nsid, __u64 slba, bool select_all, + enum nvme_zns_send_action zsa, __u32 data_len, + void *data) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw13 = zsa | (!!select_all) << 8; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_send, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + }; + + return nvme_submit_io_passthru(fd, &cmd); +} + +int nvme_zns_mgmt_recv(int fd, __u32 nsid, __u64 slba, + enum nvme_zns_recv_action zra, __u16 zrasf, + bool zras_feat, __u32 data_len, void *data) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw12 = (data_len >> 2) - 1; + __u32 cdw13 = zra | zrasf << 8 | zras_feat << 16; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_recv, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + }; + + return nvme_submit_io_passthru(fd, &cmd); +} + +int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba, bool extended, + enum nvme_zns_report_options opts, bool partial, + __u32 data_len, void *data) +{ + enum nvme_zns_recv_action zra; + + if (extended) + zra = NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES; + else + zra = NVME_ZNS_ZRA_REPORT_ZONES; + + return nvme_zns_mgmt_recv(fd, nsid, slba, zra, opts, partial, + data_len, data); +} + +int nvme_zns_append(int fd, __u32 nsid, __u64 zslba, __u16 nlb, __u16 control, + __u32 ilbrt, __u16 lbat, __u16 lbatm, __u32 data_len, + void *data, __u32 metadata_len, void *metadata, + __u64 *result) +{ + __u32 cdw10 = zslba & 0xffffffff; + __u32 cdw11 = zslba >> 32; + __u32 cdw12 = nlb | (control << 16); + __u32 cdw14 = ilbrt; + __u32 cdw15 = lbat | (lbatm << 16); + + struct nvme_passthru_cmd64 cmd = { + .opcode = nvme_zns_cmd_append, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw14 = cdw14, + .cdw15 = cdw15, + .metadata = (__u64)(uintptr_t)metadata, + .addr = (__u64)(uintptr_t)data, + .metadata_len = metadata_len, + .data_len = data_len, + }; + + int err; + + err = ioctl(fd, NVME_IOCTL_IO64_CMD, cmd); + if (!err && result) + *result = cmd.result; + return err; +} + +int nvme_get_log_zns_changed_zones(int fd, __u32 nsid, bool rae, + struct nvme_zns_changed_zone_log *log) +{ + return 0; +} diff --git a/nvme-ioctl.h b/nvme-ioctl.h index aae4e49..ad8237e 100644 --- a/nvme-ioctl.h +++ b/nvme-ioctl.h @@ -85,6 +85,8 @@ int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data); int nvme_identify_uuid(int fd, void *data); int nvme_identify_secondary_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data); int nvme_identify_ns_granularity(int fd, void *data); +int nvme_zns_identify_ctrl(int fd, void *data); +int nvme_zns_identify_ns(int fd, __u32 nsid, void *data); int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, __u32 data_len, void *data); int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, @@ -152,4 +154,21 @@ int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10); int nvme_self_test_log(int fd, __u32 nsid, struct nvme_self_test_log *self_test_log); int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result); + +int nvme_zns_mgmt_send(int fd, __u32 nsid, __u64 slba, bool select_all, + enum nvme_zns_send_action zsa, __u32 data_len, + void *data); +int nvme_zns_mgmt_recv(int fd, __u32 nsid, __u64 slba, + enum nvme_zns_recv_action zra, __u16 zrasf, + bool zras_feat, __u32 data_len, void *data); +int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba, bool extended, + enum nvme_zns_report_options opts, bool partial, + __u32 data_len, void *data); +int nvme_zns_append(int fd, __u32 nsid, __u64 zslba, __u16 nlb, __u16 control, + __u32 ilbrt, __u16 lbat, __u16 lbatm, __u32 data_len, + void *data, __u32 metadata_len, void *metadata, + __u64 *result); +int nvme_get_log_zns_changed_zones(int fd, __u32 nsid, bool rae, + struct nvme_zns_changed_zone_log *log); + #endif /* _NVME_LIB_H */ diff --git a/nvme-print.c b/nvme-print.c index 30fca29..364bfe2 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -2858,6 +2858,100 @@ void nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode) __nvme_show_id_ctrl(ctrl, mode, NULL); } +static void json_nvme_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl, unsigned int mode) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_int(root, "zamds", ctrl->zamds); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl, unsigned int mode) +{ + if (mode & BINARY) + return d_raw((unsigned char *)ctrl, sizeof(*ctrl)); + else if (mode & JSON) + return json_nvme_zns_id_ctrl(ctrl, mode); + + printf("NVMe ZNS Identify Controller:\n"); + printf("zamds: %u\n", ctrl->zamds); +} + +void nvme_show_zns_id_ns(struct nvme_zns_id_ns *ns, + struct nvme_id_ns *id_ns, unsigned long flags) +{ + uint8_t lbaf = id_ns->flbas & NVME_NS_FLBAS_LBA_MASK; + int i; + + if (flags & BINARY) + return d_raw((unsigned char *)ns, sizeof(*ns)); + + printf("NVMe ZNS Identify Namespace:\n"); + printf("zoc: %u", le16_to_cpu(ns->zoc)); + printf("ozcs: %u", le16_to_cpu(ns->ozcs)); + printf("mar: %u", le16_to_cpu(ns->mar)); + printf("mor: %u", le16_to_cpu(ns->mor)); + printf("rrl: %u", ns->rrl); + printf("frl: %u", ns->frl); + + for (i = 0; i <= id_ns->nlbaf; i++) + printf("lbafe %02d: zsze:%"PRIx64" zdes:%u%s\n", i, + (uint64_t)le64_to_cpu(ns->lbafe[i].zsze), + ns->lbafe[i].zdes, i == lbaf ? " in use" : ""); +} + +void nvme_show_zns_changed( struct nvme_zns_changed_zone_log *log, + unsigned long flags) +{ + uint16_t nrzid; + int i; + + if (flags & BINARY) + return d_raw((unsigned char *)log, sizeof(*log)); + + nrzid = le16_to_cpu(log->nrzid); + printf("NVMe Changed Zone List:\n"); + printf("nrzid: %u\n", nrzid); + + for (i = 0; i < min(nrzid, (uint16_t)NVME_ZNS_CHANGED_ZONES_MAX); i++) + printf("zid %03d: %"PRIu64"\n", i, (uint64_t)le64_to_cpu(log->zid[i])); +} + +void nvme_show_zns_report_zones(void *report, __u32 descs, + __u8 ext_size, __u32 report_size, unsigned long flags) +{ + struct nvme_zone_report *r = report; + struct nvme_zns_desc *desc; + int i; + + __u64 nr_zones = le64_to_cpu(r->nr_zones); + + if (nr_zones < descs) + descs = nr_zones; + + if (flags & BINARY) + return d_raw((unsigned char *)report, report_size); + + printf("nr_zones : %"PRIu64"\n", (uint64_t)le64_to_cpu(r->nr_zones)); + for (i = 0; i < descs; i++) { + desc = (struct nvme_zns_desc *) + (report + sizeof(*r) + i * (sizeof(*desc) + ext_size)); + printf(" desc %02d:\n", i); + printf(".................\n"); + printf("zt : %x\n", desc->zt); + printf("zs : %x\n", desc->zs); + printf("za : %x\n", desc->za); + printf("zcap : %"PRIx64"\n", le64_to_cpu(desc->zcap)); + printf("zslba : %"PRIx64"\n", le64_to_cpu(desc->zslba)); + printf("wp : %"PRIx64"\n", le64_to_cpu(desc->wp)); + printf(".................\n"); + } +} + static void json_nvme_id_nvmset(struct nvme_id_nvmset *nvmset) { __u32 nent = nvmset->nid; diff --git a/nvme-print.h b/nvme-print.h index 2a6f5ee..318f708 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -59,6 +59,14 @@ void nvme_directive_show(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __u32 res void *buf, __u32 len, enum nvme_print_flags flags); void nvme_show_select_result(__u32 result); +void nvme_show_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl, unsigned int mode); +void nvme_show_zns_id_ns(struct nvme_zns_id_ns *ns, + struct nvme_id_ns *id_ns, unsigned long flags); +void nvme_show_zns_changed( struct nvme_zns_changed_zone_log *log, + unsigned long flags); +void nvme_show_zns_report_zones(void *report, __u32 descs, + __u8 ext_size, __u32 report_size, unsigned long flags); + const char *nvme_status_to_string(__u32 status); const char *nvme_select_to_string(int sel); const char *nvme_feature_to_string(int feature); diff --git a/nvme.c b/nvme.c index 107f012..f4b2289 100644 --- a/nvme.c +++ b/nvme.c @@ -85,7 +85,7 @@ static struct program nvme = { .extensions = &builtin, }; -static const char *output_format = "Output format: normal|json|binary"; +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) @@ -103,7 +103,7 @@ static void *__nvme_alloc(size_t len, bool *huge) #ifdef LIBHUGETLBFS #define HUGE_MIN 0x80000 -static void nvme_free(void *p, bool huge) +void nvme_free(void *p, bool huge) { if (huge) free_hugepage_region(p); @@ -111,7 +111,7 @@ static void nvme_free(void *p, bool huge) free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { void *p; @@ -126,12 +126,12 @@ static void *nvme_alloc(size_t len, bool *huge) return p; } #else -static void nvme_free(void *p, bool huge) +void nvme_free(void *p, bool huge) { free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { return __nvme_alloc(len, huge); } diff --git a/nvme.h b/nvme.h index 017148a..361191b 100644 --- a/nvme.h +++ b/nvme.h @@ -87,6 +87,7 @@ int parse_and_open(int argc, char **argv, const char *desc, const struct argconfig_commandline_options *clo); extern const char *devicename; +extern const char *output_format; enum nvme_print_flags validate_output_format(char *format); int __id_ctrl(int argc, char **argv, struct command *cmd, @@ -107,4 +108,6 @@ void free_topology(struct nvme_topology *t); char *get_nvme_subsnqn(char *path); char *nvme_get_ctrl_attr(char *path, const char *attr); +void *nvme_alloc(size_t len, bool *huge); +void nvme_free(void *p, bool huge); #endif /* _NVME_H */ diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c new file mode 100644 index 0000000..8218a91 --- /dev/null +++ b/plugins/zns/zns.c @@ -0,0 +1,814 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvme.h" +#include "nvme-ioctl.h" +#include "nvme-print.h" + +#define CREATE_CMD +#include "zns.h" + +static const char *namespace_id = "Namespace identify to use"; + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send ZNS specific Identify Controller command to "\ + "the given device and report information about the specified "\ + "controller in varios formats."; + + enum nvme_print_flags flags; + struct nvme_zns_id_ctrl ctrl; + int fd, err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_zns_identify_ctrl(fd, &ctrl); + if (!err) + nvme_show_zns_id_ctrl(&ctrl, flags); + else + nvme_show_status(err); +close_fd: + close(fd); + return err; +} + +static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send ZNS specific Identify Namespace command to "\ + "the given device and report information about the specified "\ + "namespace in varios formats."; + const char *verbose = "verbosely decode fields"; + + enum nvme_print_flags flags; + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + int fd, err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + int verbose; + }; + + 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("verbose", 'v', &cfg.verbose, verbose), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.verbose) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &id_ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + err = nvme_zns_identify_ns(fd, cfg.namespace_id, &ns); + if (!err) + nvme_show_zns_id_ns(&ns, &id_ns, flags); + else + nvme_show_status(err); +close_fd: + close(fd); + return err; +} + +static int __zns_mgmt_send(int fd, __u32 namespace_id, __u64 zslba, + bool select_all, enum nvme_zns_send_action zsa, __u32 data_len, void *buf) +{ + int err; + + if (!namespace_id) { + err = namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_zns_mgmt_send(fd, namespace_id, zslba, select_all, zsa, + data_len, buf); +close_fd: + close(fd); + 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"; + + int err, fd; + char *command; + + struct config { + __u64 zslba; + int namespace_id; + bool select_all; + }; + + 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_END() + }; + + err = asprintf(&command, "%s-%s", plugin->name, cmd->name); + if (err < 0) + return errno; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto free; + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, + cfg.select_all, zsa, 0, NULL); + if (!err) + printf("%s: Success, action:%d zone:%"PRIx64" nsid:%d\n", command, + zsa, (uint64_t)zslba, cfg.namespace_id); + else + nvme_show_status(err); +free: + free(command); + return err; +} + +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"; + 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)"; + + int fd, ffd = STDIN_FILENO, err = -1; + void *buf = NULL; + + struct config { + __u64 zslba; + int namespace_id; + bool select_all; + __u8 zsa; + __u32 data_len; + char *file; + }; + + 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_BYTE("zsa", 'z', &cfg.zsa, zsa), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (cfg.data_len) { + if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { + fprintf(stderr, "can not allocate feature payload\n"); + goto close_fd; + } + 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; + } + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, cfg.select_all, + cfg.zsa, cfg.data_len, buf); + if (!err) + printf("zone-mgmt-send: Success, action:%d zone:%"PRIx64" nsid:%d\n", + cfg.zsa, (uint64_t)cfg.zslba, cfg.namespace_id); + else + nvme_show_status(err); + +close_ffd: + if (cfg.file) + close(ffd); +free: + if (buf) + free(buf); +close_fd: + close(fd); + 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"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OPEN); +} + +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 *data = "optional file for zone extention data (default stdin)"; + + int fd, ffd = STDIN_FILENO, err; + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + void *buf = NULL; + __u32 data_len; + uint8_t lbaf; + + struct config { + __u64 zslba; + int namespace_id; + char *file; + }; + + 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_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &id_ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + err = nvme_zns_identify_ns(fd, cfg.namespace_id, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + lbaf = id_ns.flbas & NVME_NS_FLBAS_LBA_MASK; + data_len = ns.lbafe[lbaf].zdes; + + if (!data_len) { + fprintf(stderr, + "zone format does not provide descriptor extention\n"); + errno = EINVAL; + err = -1; + goto close_fd; + } + + buf = calloc(1, data_len); + if (!buf) { + err = -1; + goto close_fd; + } + + 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; + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, 0, + NVME_ZNS_ZSA_SET_DESC_EXT, data_len, buf); + if (!err) + printf("set-zone-desc: Success, zone:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.zslba, cfg.namespace_id); + else + nvme_show_status(err); +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_fd: + close(fd); + return err; +} + +static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Receive"; + + enum nvme_print_flags flags; + int fd, err = -1; + void *data = NULL; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + __u16 zra; + __u16 zrasf; + bool zrass; + __u32 data_len; + }; + + struct config cfg = { + .output_format = "normal", + }; + + + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.data_len) { + data = calloc(1, cfg.data_len); + if (!data) { + err = -1; + goto close_fd; + } + } + + err = nvme_zns_mgmt_recv(fd, cfg.namespace_id, cfg.zslba, cfg.zra, + cfg.zrasf, cfg.zrass, cfg.data_len, data); + if (!err) + printf("zone-mgmt-recv: Success, action:%d zone:%"PRIx64" nsid:%d\n", + cfg.zra, (uint64_t)cfg.zslba, cfg.namespace_id); + else + nvme_show_status(err); + + if (data) + free(data); +close_fd: + close(fd); + return err; +} + +static int get_zdes(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, false, &id_ns); + if (err) { + nvme_show_status(err); + return err; + } + + err = nvme_zns_identify_ns(fd, nsid, &ns); + if (err) { + nvme_show_status(err); + return err; + } + + lbaf = id_ns.flbas & NVME_NS_FLBAS_LBA_MASK; + return ns.lbafe[lbaf].zdes; +} + +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"; + 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 = "verbosely decode fields"; + + enum nvme_print_flags flags; + int fd, zdes = 0, err = -1; + __u32 report_size; + void *report; + bool huge = false; + + 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", + }; + + 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() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.verbose) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.extended) { + zdes = get_zdes(fd, cfg.namespace_id); + if (zdes < 0) { + err = zdes; + goto close_fd; + } + } + + report_size = sizeof(struct nvme_zone_report) + cfg.num_descs * + (sizeof(struct nvme_zns_desc) + zdes); + + report = nvme_alloc(report_size, &huge); + if (!report) { + perror("alloc"); + err = -1; + goto close_fd; + } + + err = nvme_zns_report_zones(fd, cfg.namespace_id, cfg.zslba, + cfg.extended, cfg.state, cfg.partial, report_size, report); + if (!err) + nvme_show_zns_report_zones(report, cfg.num_descs, zdes, + report_size, flags); + else + nvme_show_status(err); + + nvme_free(report, huge); +close_fd: + close(fd); + 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 "\ + "using the slba of the zone, and the write will be appended from the "\ + "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 *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"; + + int err = -1, fd, dfd = STDIN_FILENO, mfd = STDIN_FILENO; + unsigned int lba_size, meta_size; + void *buf = NULL, *mbuf = NULL; + __u16 nblocks, control = 0; + __u64 result; + + struct nvme_id_ns ns; + + struct config { + char *data; + char *metadata; + __u64 zslba; + __u64 data_size; + __u64 metadata_size; + int limited_retry; + int fua; + int namespace_id; + __u32 ref_tag; + __u16 lbat; + __u16 lbatm; + __u8 prinfo; + }; + + 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_UINT("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_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.data_size) { + fprintf(stderr, "Append size not provided\n"); + errno = EINVAL; + goto close_fd; + } + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + lba_size = 1 << ns.lbaf[(ns.flbas & 0x0f)].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_ns; + } + + meta_size = ns.lbaf[(ns.flbas & 0x0f)].ms; + if (meta_size && (!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_ns; + } + + if (cfg.prinfo > 0xf) { + fprintf(stderr, "Invalid value for prinfo:%#x\n", cfg.prinfo); + errno = EINVAL; + goto close_ns; + } + + if (cfg.data) { + dfd = open(cfg.data, O_RDONLY); + if (dfd < 0) { + perror(cfg.data); + goto close_ns; + } + } + + 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 close_dfd; + } + } + + 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_RW_LR; + if (cfg.fua) + control |= NVME_RW_FUA; + + err = nvme_zns_append(fd, cfg.namespace_id, cfg.zslba, nblocks, + control, cfg.ref_tag, cfg.lbat, cfg.lbatm, + cfg.data_size, buf, cfg.metadata_size, mbuf, + &result); + if (!err) + printf("Success appended data to LBA %"PRIx64"\n", (uint64_t)result); + else + nvme_show_status(err); + +free_meta: + free(mbuf); +close_mfd: + if (cfg.metadata) + close(mfd); +free_data: + free(buf); +close_dfd: + if (cfg.data) + close(dfd); +close_ns: +close_fd: + close(fd); + return err; +} + +static int change_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; + int fd, 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() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_get_log_zns_changed_zones(fd, cfg.namespace_id, cfg.rae, &log); + if (!err) + nvme_show_zns_changed(&log, flags); + else + nvme_show_status(err); + +close_fd: + close(fd); + return err; +} diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h new file mode 100644 index 0000000..4e9d363 --- /dev/null +++ b/plugins/zns/zns.h @@ -0,0 +1,30 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/zns/zns + +#if !defined(ZNS_NVME) || defined(CMD_HEADER_MULTI_READ) +#define ZNS_NVME + +#include "cmd.h" + +PLUGIN(NAME("zns", "Zoned Namespace Command Set"), + COMMAND_LIST( + ENTRY("id-ctrl", "Retrieve ZNS controller identification", id_ctrl) + ENTRY("id-ns", "Retrieve ZNS namespace identification", id_ns) + ENTRY("zone-mgmt-recv", "Sends the zone management receive command", zone_mgmt_recv) + ENTRY("zone-mgmt-send", "Sends the zone management send command", zone_mgmt_send) + ENTRY("report-zones", "Retrieve the Report Zones report", report_zones) + ENTRY("close-zone", "Closes one or more zones", close_zone) + ENTRY("finish-zone", "Finishes one or more zones", finish_zone) + ENTRY("open-zone", "Opens one or more zones", open_zone) + ENTRY("reset-zone", "Resets one or more zones", reset_zone) + ENTRY("offline-zone", "Offlines one or more zones", offline_zone) + ENTRY("set-zone-desc", "Attaches zone descriptor extension data", set_zone_desc) + ENTRY("zone-append", "Writes data and metadata (if applicable), appended to the end of the requested zone", zone_append) + ENTRY("changed-zone-list", "Retrieves the changed zone list log", change_zone_list) + ) +); + +#endif + +#include "define_cmd.h" +