diff options
Diffstat (limited to 'nvme.c')
-rw-r--r-- | nvme.c | 329 |
1 files changed, 235 insertions, 94 deletions
@@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * nvme.c -- NVM-Express command line utility. * @@ -369,23 +370,26 @@ static int get_ana_log(int argc, char **argv, struct command *cmd, { const char *desc = "Retrieve ANA log for the given device in " \ "decoded format (default), json or binary."; + const char *groups = "Return ANA groups only."; void *ana_log; int err = -1, fd; - int groups = 0; /* Right now get all the per ANA group NSIDS */ size_t ana_log_len; struct nvme_id_ctrl ctrl; enum nvme_print_flags flags; enum nvme_log_ana_lsp lsp; struct config { + bool groups; char *output_format; }; struct config cfg = { + .groups = false, .output_format = "normal", }; OPT_ARGS(opts) = { + OPT_FLAG("groups", 'g', &cfg.groups, groups), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_END() }; @@ -416,8 +420,8 @@ static int get_ana_log(int argc, char **argv, struct command *cmd, goto close_fd; } - lsp = groups ? NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY : - NVME_LOG_ANA_LSP_RGO_NAMESPACES; + lsp = cfg.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) { @@ -583,18 +587,22 @@ ret: return err; } -void collect_effects_log(int fd, enum nvme_csi csi, struct list_head *list, int flags) +static int collect_effects_log(int fd, enum nvme_csi csi, + struct list_head *list, int flags) { + nvme_effects_log_node_t *node; int err; - nvme_effects_log_node_t *node = malloc(sizeof(nvme_effects_log_node_t)); + + node = malloc(sizeof(nvme_effects_log_node_t)); if (!node) - return; + return -ENOMEM; + node->csi = csi; err = nvme_get_log_cmd_effects(fd, csi, &node->effects); if (!err) { list_add(list, &node->node); - return; + return err; } else if (err > 0) nvme_show_status(err); @@ -602,6 +610,7 @@ void collect_effects_log(int fd, enum nvme_csi csi, struct list_head *list, int fprintf(stderr, "effects log page: %s\n", nvme_strerror(errno)); free(node); + return err; } static int get_effects_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -672,29 +681,28 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl 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) + err = collect_effects_log(fd, NVME_CSI_NVM, + &log_pages, flags); - if (nvme_command_set_supported) { - collect_effects_log(fd, NVME_CSI_NVM, &log_pages, flags); - } + if (!err && other_command_sets_supported) + err = collect_effects_log(fd, NVME_CSI_ZNS, + &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); + } else { + err = collect_effects_log(fd, cfg.csi, &log_pages, flags); } + if (!err) + nvme_print_effects_log_pages(&log_pages, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("effects log page"); close_fd: - while ((node = list_pop(&log_pages, nvme_effects_log_node_t, node))) { + while ((node = list_pop(&log_pages, nvme_effects_log_node_t, node))) free(node); - } - close(fd); ret: return err; @@ -1154,7 +1162,7 @@ static int get_persistent_event_log(int argc, char **argv, } /* - * if header aleady read with context establish action 0x1, + * if header already read with context establish action 0x1, * action shall not be 0x1 again in the subsequent request, * until the current context is released by issuing action * with 0x2, otherwise throws command sequence error, make @@ -1184,7 +1192,7 @@ static int get_persistent_event_log(int argc, char **argv, 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"); + "Re-read the log is required\n"); goto free; } @@ -2434,7 +2442,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * } goto close_fd; } - for (i = 0; i < 16; ++i) { + for (i = 0; i <= ns.nlbaf; ++i) { if ((1 << ns.lbaf[i].ds) == cfg.bs && ns.lbaf[i].ms == 0) { cfg.flbas = i; break; @@ -2522,6 +2530,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, const char *verbose = "Increase output verbosity"; nvme_scan_filter_t filter = NULL; int err; + int nsid = NVME_NSID_ALL; struct config { char *output_format; @@ -2570,7 +2579,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, } if (devicename) { - int subsys_num, nsid; + int subsys_num; if (sscanf(devicename,"nvme%dn%d", &subsys_num, &nsid) != 2) { @@ -2588,7 +2597,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, goto ret; } - nvme_show_subsystem_list(r, flags); + nvme_show_subsystem_list(r, nsid != NVME_NSID_ALL, flags); ret: if (r) @@ -2642,7 +2651,7 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi } err = nvme_scan_topology(r, NULL, NULL); if (err < 0) { - fprintf(stderr, "Failed to scan topoplogy: %s\n", + fprintf(stderr, "Failed to scan topology: %s\n", nvme_strerror(errno)); nvme_free_tree(r); return err; @@ -2983,7 +2992,7 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug "given device, returns properties of the specified namespace "\ "in either human-readable or binary format. Can also return "\ "binary vendor-specific namespace attributes."; - const char *force = "Return this namespace, even if not attaced (1.2 devices only)"; + const char *force = "Return this namespace, even if not attached (1.2 devices only)"; const char *vendor_specific = "dump binary vendor fields"; const char *raw = "show identify in binary format"; const char *human_readable = "show identify in readable format"; @@ -3173,7 +3182,7 @@ static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct nvme_show_status(err); else fprintf(stderr, "identify namespace granularity: %s\n", nvme_strerror(errno)); - + free(granularity_list); close_fd: close(fd); ret: @@ -3848,7 +3857,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, "specified controller. Operating parameters are grouped "\ "and identified by Feature Identifiers; each Feature "\ "Identifier contains one or more attributes that may affect "\ - "behaviour of the feature. Each Feature has three possible "\ + "behavior of the feature. Each Feature has three possible "\ "settings: default, saveable, and current. If a Feature is "\ "saveable, it may be modified by set-feature. Default values "\ "are vendor-specific and not changeable. Use set-feature to "\ @@ -4536,7 +4545,7 @@ ret: static int set_property(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Writes and shows the defined NVMe controller property "\ - "for NVMe ove Fabric"; + "for NVMe over Fabric"; const char *offset = "the offset of the property"; const char *value = "the value of the property to be set"; int fd, err; @@ -4738,7 +4747,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &prev_lbaf); if (cfg.bs) { - for (i = 0; i < 16; ++i) { + for (i = 0; i < ns.nlbaf; ++i) { if ((1ULL << ns.lbaf[i].ds) == cfg.bs && ns.lbaf[i].ms == 0) { cfg.lbaf = i; @@ -4766,7 +4775,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu err = -EINVAL; goto close_fd; } - if (cfg.lbaf > 15) { + if (cfg.lbaf > 63) { fprintf(stderr, "invalid lbaf:%d\n", cfg.lbaf); err = -EINVAL; goto close_fd; @@ -4803,7 +4812,8 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu .args_size = sizeof(args), .fd = fd, .nsid = cfg.namespace_id, - .lbaf = cfg.lbaf, + .lbafu = (cfg.lbaf & NVME_NS_FLBAS_HIGHER_MASK) >> 4, + .lbaf = cfg.lbaf & NVME_NS_FLBAS_LOWER_MASK, .mset = cfg.ms, .pi = cfg.pi, .pil = cfg.pil, @@ -5028,7 +5038,8 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin nvme_show_status(err); close_ffd: - close(ffd); + if (ffd != STDIN_FILENO) + close(ffd); free: free(buf); close_fd: @@ -5393,10 +5404,47 @@ ret: return err; } +static int invalid_tags(__u64 storage_tag, __u64 ref_tag, __u8 sts, __u8 pif) +{ + int result = 0; + + if (sts < 64 && storage_tag >= (1LL << sts)) { + fprintf(stderr, "Storage tag larger than storage tag size\n"); + return 1; + } + + switch (pif) { + case 0: + if (ref_tag >= (1LL << (32 - sts))) + result = 1; + break; + case 1: + if (sts > 16 && ref_tag >= (1LL << (80 - sts))) + result = 1; + break; + case 2: + if (sts > 0 && ref_tag >= (1LL << (64 - sts))) + result = 1; + break; + default: + fprintf(stderr, "Invalid PIF\n"); + result = 1; + } + + if (result) + fprintf(stderr, "Reference tag larger than allowed by PIF\n"); + + return result; +} + static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err, fd; __u16 control = 0; + __u8 lba_index, sts = 0, pif = 0; + struct nvme_id_ns ns; + struct nvme_nvm_id_ns nvm_ns; + const char *desc = "The Write Zeroes command is used to set a "\ "range of logical blocks to zero."; const char *namespace_id = "desired namespace"; @@ -5405,11 +5453,10 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi const char *limited_retry = "limit media access attempts"; 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)"; - const char *app_tag = "app tag (for end to end PI)"; - const char *storage_tag = "storage tag, CDW2 and CDW3 (00:47) bits "\ - "(for end to end PI)"; + const char *ref_tag = "reference tag for end-to-end PI"; + const char *app_tag_mask = "app tag mask for end-to-end PI"; + const char *app_tag = "app tag for end-to-end PI"; + const char *storage_tag = "storage tag for end-to-end PI"; const char *deac = "Set DEAC bit, requesting controller to deallocate specified logical blocks"; const char *storage_tag_check = "This bit specifies the Storage Tag field shall be checked as "\ "part of end-to-end data protection processing"; @@ -5422,7 +5469,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi bool limited_retry; bool force_unit_access; __u8 prinfo; - __u32 ref_tag; + __u64 ref_tag; __u16 app_tag_mask; __u16 app_tag; __u64 storage_tag; @@ -5452,7 +5499,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), 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_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask), OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag), OPT_SUFFIX("storage-tag", 'S', &cfg.storage_tag, storage_tag), @@ -5477,7 +5524,7 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi if (cfg.deac) control |= NVME_IO_DEAC; if (cfg.storage_tag_check) - control |= NVME_SC_STORAGE_TAG_CHECK; + control |= NVME_IO_STC; if (!cfg.namespace_id) { err = nvme_get_nsid(fd, &cfg.namespace_id); if (err < 0) { @@ -5486,6 +5533,27 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi } } + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } else if (err < 0) { + fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); + goto close_fd; + } + + err = nvme_identify_ns_csi(fd, cfg.namespace_id, 0, NVME_CSI_NVM, &nvm_ns); + if (!err) { + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index); + sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK; + pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7; + } + + if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) { + err = -EINVAL; + goto close_fd; + } + struct nvme_io_args args = { .args_size = sizeof(args), .fd = fd, @@ -5493,9 +5561,11 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi .slba = cfg.start_block, .nlb = cfg.block_count, .control = control, - .reftag = cfg.ref_tag, + .reftag_u64 = cfg.ref_tag, .apptag = cfg.app_tag, .appmask = cfg.app_tag_mask, + .sts = sts, + .pif = pif, .storage_tag = cfg.storage_tag, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = NULL, @@ -5648,10 +5718,19 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi uint16_t nr, nb, ns, nrts, natms, nats; __u16 nlbs[128] = { 0 }; unsigned long long slbas[128] = {0,}; - __u32 eilbrts[128] = { 0 }; + + union { + __u32 f0[128]; + __u64 f1[101]; + } eilbrts; + __u32 elbatms[128] = { 0 }; __u32 elbats[128] = { 0 }; - struct nvme_copy_range copy[128]; + + union { + struct nvme_copy_range f0[128]; + struct nvme_copy_range_f1 f1[101]; + } copy; struct config { __u32 namespace_id; @@ -5662,12 +5741,12 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi bool fua; __u8 prinfow; __u8 prinfor; - __u32 ilbrt; + __u64 ilbrt; char *eilbrts; __u16 lbat; - char *elbatms; - __u16 lbatm; char *elbats; + __u16 lbatm; + char *elbatms; __u8 dtype; __u16 dspec; __u8 format; @@ -5702,7 +5781,7 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi 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_SUFFIX("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), @@ -5722,12 +5801,22 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi 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, (int *)eilbrts, ARRAY_SIZE(eilbrts)); + + if (cfg.format == 0) + nrts = argconfig_parse_comma_sep_array(cfg.eilbrts, (int *)eilbrts.f0, ARRAY_SIZE(eilbrts.f0)); + else if (cfg.format == 1) + nrts = argconfig_parse_comma_sep_array_long(cfg.eilbrts, (__u64 *)eilbrts.f1, ARRAY_SIZE(eilbrts.f1)); + else { + fprintf(stderr, "invalid format\n"); + err = -EINVAL; + goto close_fd; + } + 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) { + if (!nr || nr > 128 || (cfg.format == 1 && nr > 101)) { fprintf(stderr, "invalid range\n"); err = -EINVAL; goto close_fd; @@ -5741,13 +5830,18 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi } } - nvme_init_copy_range(copy, nlbs, (__u64 *)slbas, eilbrts, elbatms, elbats, nr); + if (cfg.format == 0) + nvme_init_copy_range(copy.f0, nlbs, (__u64 *)slbas, + eilbrts.f0, elbatms, elbats, nr); + else if (cfg.format == 1) + nvme_init_copy_range_f1(copy.f1, nlbs, (__u64 *)slbas, + eilbrts.f1, elbatms, elbats, nr); struct nvme_copy_args args = { .args_size = sizeof(args), .fd = fd, .nsid = cfg.namespace_id, - .copy = copy, + .copy = copy.f0, .sdlba = cfg.sdlba, .nr = nr, .prinfor = cfg.prinfor, @@ -5757,7 +5851,8 @@ static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugi .format = cfg.format, .lr = cfg.lr, .fua = cfg.fua, - .ilbrt = cfg.ilbrt, + .prinfow = cfg.prinfow, + .ilbrt_u64 = cfg.ilbrt, .lbatm = cfg.lbatm, .lbat = cfg.lbat, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, @@ -6202,17 +6297,18 @@ static int submit_io(int opcode, char *command, const char *desc, __u16 control = 0; __u32 dsmgmt = 0; int logical_block_size = 0; - long long buffer_size = 0, mbuffer_size = 0; + unsigned long long buffer_size = 0, mbuffer_size = 0; bool huge; struct nvme_id_ns ns; - __u8 lba_index, ms = 0; + struct nvme_nvm_id_ns nvm_ns; + __u8 lba_index, ms = 0, sts = 0, pif = 0; 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"; const char *metadata_size = "size of metadata in bytes"; - const char *ref_tag = "reference tag (for end to end PI)"; + const char *ref_tag = "reference tag for end-to-end PI"; const char *data = "data file"; const char *metadata = "metadata file"; const char *prinfo = "PI and check field"; @@ -6228,8 +6324,7 @@ static int submit_io(int opcode, char *command, const char *desc, 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 *storage_tag = "storage tag 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 { @@ -6238,7 +6333,7 @@ static int submit_io(int opcode, char *command, const char *desc, __u16 block_count; __u64 data_size; __u64 metadata_size; - __u32 ref_tag; + __u64 ref_tag; char *data; char *metadata; __u8 prinfo; @@ -6288,7 +6383,7 @@ static int submit_io(int opcode, char *command, const char *desc, OPT_SHRT("block-count", 'c', &cfg.block_count, block_count), OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), - OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), + OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_FILE("data", 'd', &cfg.data, data), OPT_FILE("metadata", 'M', &cfg.metadata, metadata), OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), @@ -6354,7 +6449,7 @@ static int submit_io(int opcode, char *command, const char *desc, if (cfg.force_unit_access) control |= NVME_IO_FUA; if (cfg.storage_tag_check) - control |= NVME_SC_STORAGE_TAG_CHECK; + control |= NVME_IO_STC; if (cfg.dtype) { if (cfg.dtype > 0xf) { fprintf(stderr, "Invalid directive type, %x\n", @@ -6394,7 +6489,7 @@ static int submit_io(int opcode, char *command, const char *desc, &logical_block_size) < 0) goto close_mfd; - buffer_size = (cfg.block_count + 1) * logical_block_size; + buffer_size = ((long long)cfg.block_count + 1) * logical_block_size; if (cfg.data_size < buffer_size) { fprintf(stderr, "Rounding data size to fit block count (%lld bytes)\n", buffer_size); @@ -6410,16 +6505,24 @@ static int submit_io(int opcode, char *command, const char *desc, if (cfg.metadata_size) { err = nvme_identify_ns(fd, cfg.namespace_id, &ns); - if (err) { + if (err > 0) { nvme_show_status(err); goto free_buffer; } else if (err < 0) { fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); goto free_buffer; } + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index); ms = ns.lbaf[lba_index].ms; - mbuffer_size = (cfg.block_count + 1) * ms; + + err = nvme_identify_ns_csi(fd, 1, 0, NVME_CSI_NVM, &nvm_ns); + if (!err) { + sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK; + pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7; + } + + mbuffer_size = ((unsigned long long)cfg.block_count + 1) * ms; if (ms && cfg.metadata_size < mbuffer_size) { fprintf(stderr, "Rounding metadata size to fit block count (%lld bytes)\n", mbuffer_size); @@ -6434,6 +6537,11 @@ static int submit_io(int opcode, char *command, const char *desc, memset(mbuffer, 0, mbuffer_size); } + if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) { + err = -EINVAL; + goto free_buffer; + } + if ((opcode & 1)) { err = read(dfd, (void *)buffer, cfg.data_size); if (err < 0) { @@ -6464,11 +6572,13 @@ static int submit_io(int opcode, char *command, const char *desc, 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("reftag : %"PRIx64"\n", (uint64_t)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); + printf("pif : %02x\n", pif); + printf("sts : %02x\n", sts); } if (cfg.dry_run) goto free_mbuffer; @@ -6481,8 +6591,10 @@ static int submit_io(int opcode, char *command, const char *desc, .nlb = cfg.block_count, .control = control, .dsm = cfg.dsmgmt, + .sts = sts, + .pif = pif, .dspec = cfg.dspec, - .reftag = cfg.ref_tag, + .reftag_u64 = cfg.ref_tag, .apptag = cfg.app_tag, .appmask = cfg.app_tag_mask, .storage_tag = cfg.storage_tag, @@ -6494,10 +6606,7 @@ static int submit_io(int opcode, char *command, const char *desc, .result = NULL, }; gettimeofday(&start_time, NULL); - if (opcode & 1) - err = nvme_write(&args); - else - err = nvme_read(&args); + err = nvme_io(&args, opcode); gettimeofday(&end_time, NULL); if (cfg.latency) printf(" latency: %s: %llu us\n", @@ -6562,6 +6671,10 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin { int err, fd; __u16 control = 0; + __u8 lba_index, sts = 0, pif = 0; + struct nvme_id_ns ns; + struct nvme_nvm_id_ns nvm_ns; + 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"; @@ -6569,11 +6682,10 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin const char *limited_retry = "limit media access attempts"; 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)"; - const char *app_tag = "app tag (for end to end PI)"; - const char *storage_tag = "storage tag, CDW2 and CDW3 (00:47) bits "\ - "(for end to end PI)"; + const char *ref_tag = "reference tag for end-to-end PI"; + const char *app_tag_mask = "app tag mask for end-to-end PI"; + const char *app_tag = "app tag for end-to-end PI"; + const char *storage_tag = "storage tag for end-to-end PI"; const char *storage_tag_check = "This bit specifies the Storage Tag field shall "\ "be checked as part of Verify operation"; @@ -6612,7 +6724,7 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), 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_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag), OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask), OPT_SUFFIX("storage-tag", 'S', &cfg.storage_tag, storage_tag), @@ -6635,7 +6747,7 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin if (cfg.force_unit_access) control |= NVME_IO_FUA; if (cfg.storage_tag_check) - control |= NVME_SC_STORAGE_TAG_CHECK; + control |= NVME_IO_STC; if (!cfg.namespace_id) { err = nvme_get_nsid(fd, &cfg.namespace_id); @@ -6645,6 +6757,27 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin } } + err = nvme_identify_ns(fd, cfg.namespace_id, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } else if (err < 0) { + fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno)); + goto close_fd; + } + + err = nvme_identify_ns_csi(fd, cfg.namespace_id, 0, NVME_CSI_NVM, &nvm_ns); + if (!err) { + nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index); + sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK; + pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7; + } + + if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) { + err = -EINVAL; + goto close_fd; + } + struct nvme_io_args args = { .args_size = sizeof(args), .fd = fd, @@ -6652,9 +6785,11 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin .slba = cfg.start_block, .nlb = cfg.block_count, .control = control, - .reftag = cfg.ref_tag, + .reftag_u64 = cfg.ref_tag, .apptag = cfg.app_tag, .appmask = cfg.app_tag_mask, + .sts = sts, + .pif = pif, .storage_tag = cfg.storage_tag, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = NULL, @@ -7143,7 +7278,7 @@ static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugi if (fd < 0) goto ret; - /* check for input arguement limit */ + /* check for input argument limit */ if (cfg.ifc > 3) { fprintf(stderr, "invalid interface settings:%d\n", cfg.ifc); err = -1; @@ -7313,8 +7448,22 @@ static int passthru(int argc, char **argv, bool admin, 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 (cfg.opcode & 0x01) + cfg.write = true; + + if (cfg.opcode & 0x02) + cfg.read = true; + + if (cfg.write) { + flags = O_RDONLY; + dfd = mfd = STDIN_FILENO; + } + + if (cfg.read) { + flags = O_WRONLY | O_CREAT; + dfd = mfd = STDOUT_FILENO; + } + if (strlen(cfg.input_file)) { dfd = open(cfg.input_file, flags, mode); if (dfd < 0) { @@ -7357,14 +7506,6 @@ static int passthru(int argc, char **argv, bool admin, goto free_metadata; } - if (cfg.write && !(cfg.opcode & 0x01)) { - fprintf(stderr, "warning: write flag set but write direction bit is not set in the opcode\n"); - } - - if (cfg.read && !(cfg.opcode & 0x02)) { - fprintf(stderr, "warning: read flag set but read direction bit is not set in the opcode\n"); - } - memset(data, cfg.prefill, cfg.data_len); if (!cfg.read && !cfg.write) { fprintf(stderr, "data direction not given\n"); @@ -7434,7 +7575,7 @@ static int passthru(int argc, char **argv, bool admin, admin ? "Admin": "IO", strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific", result); - if (cfg.read && cfg.input_file) { + if (cfg.read && strlen(cfg.input_file)) { if (write(dfd, (void *)data, cfg.data_len) < 0) perror("failed to write data buffer"); if (cfg.metadata_len && cfg.metadata) |