From 727de41af97db70722cfc1898ebb027b9e339969 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 20 Nov 2021 08:04:31 +0100 Subject: Adding upstream version 1.16. Signed-off-by: Daniel Baumann --- nvme.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 501 insertions(+), 11 deletions(-) (limited to 'nvme.c') diff --git a/nvme.c b/nvme.c index c10932f..862f9b6 100644 --- a/nvme.c +++ b/nvme.c @@ -365,18 +365,25 @@ ret: return nvme_status_to_errno(err, false); } +__u16 get_feat_buf_len(unsigned short feature) { + return nvme_feat_buf_len[feature]; +} + static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve telemetry log and write to binary file"; const char *fname = "File name to save raw binary, includes header"; const char *hgen = "Have the host tell the controller to generate the report"; const char *cgen = "Gather report generated by the controller."; - const char *dgen = "Pick which telemetry data area to report. Default is all. Valid options are 1, 2, 3."; + const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4."; const size_t bs = 512; struct nvme_telemetry_log_page_hdr *hdr; + struct nvme_id_ctrl ctrl; size_t full_size, offset = bs; int err = -1, fd, output; void *page_log; + __u32 result; + void *buf = NULL; struct config { char *file_name; @@ -454,6 +461,46 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct case 3: full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset; break; + case 4: + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + perror("identify-ctrl"); + goto close_output; + } + + if (posix_memalign(&buf, getpagesize(), nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR])) { + fprintf(stderr, "can not allocate feature payload\n"); + errno = ENOMEM; + err = -1; + goto close_output; + } + memset(buf, 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR]); + + err = nvme_get_feature(fd, NVME_NSID_ALL, NVME_FEAT_HOST_BEHAVIOR, 0, 0, + 0, nvme_feat_buf_len[NVME_FEAT_HOST_BEHAVIOR], buf, &result); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + perror("get-feature"); + } else { + if ((ctrl.lpa & 0x40)) { + if (((unsigned char *)buf)[1] == 1) + full_size = (le32_to_cpu(hdr->dalb4) * bs) + offset; + else { + fprintf(stderr, "Data area 4 unsupported, Host Behavior Support ETDAS not set to 1\n"); + errno = EINVAL; + err = -1; + } + } else { + fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n"); + errno = EINVAL; + err = -1; + } + } + free(buf); + if (err) + goto close_output; + break; default: fprintf(stderr, "Invalid data area requested\n"); errno = EINVAL; @@ -593,6 +640,56 @@ ret: return nvme_status_to_errno(err, false); } +static int get_supported_log_pages(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve supported logs and print the table."; + const char *verbose = "Increase output verbosity"; + struct nvme_support_log_pages supports; + + int err = -1, fd; + enum nvme_print_flags flags; + + struct config { + int verbose; + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (cfg.verbose) + flags |= VERBOSE; + + err = nvme_supported_log(fd, &supports); + if (!err) + nvme_show_supported_log(&supports, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("supported log pages"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int get_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve specified number of "\ @@ -929,7 +1026,7 @@ static int get_persistent_event_log(int argc, char **argv, const char *log_len = "number of bytes to retrieve"; const char *raw = "use binary output"; void *pevent_log_info; - struct nvme_persistent_event_log_head *pevent_log_head = NULL; + struct nvme_persistent_event_log_head *pevent_log_head, *collected_head; enum nvme_print_flags flags; int err = -1, fd; bool huge; @@ -985,22 +1082,22 @@ static int get_persistent_event_log(int argc, char **argv, sizeof(*pevent_log_head), pevent_log_head); if (err < 0) { perror("persistent event log"); - goto close_fd; + goto free_head; } else if (err) { nvme_show_status(err); - goto close_fd; + goto free_head; } if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) { printf("Releasing Persistent Event Log Context\n"); - goto close_fd; + goto free_head; } if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { cfg.log_len = le64_to_cpu(pevent_log_head->tll); } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { printf("Establishing Persistent Event Log Context\n"); - goto close_fd; + goto free_head; } /* @@ -1018,22 +1115,39 @@ static int get_persistent_event_log(int argc, char **argv, perror("could not alloc buffer for persistent event log page\n"); errno = ENOMEM; err = -1; - goto close_fd; + goto free_head; } err = nvme_persistent_event_log(fd, cfg.action, cfg.log_len, pevent_log_info); - if (!err) + if (!err) { + err = nvme_persistent_event_log(fd, cfg.action, + sizeof(*pevent_log_head), pevent_log_head); + if (err < 0) { + perror("persistent event log"); + goto free; + } else if (err) { + nvme_show_status(err); + goto free; + } + collected_head = pevent_log_info; + if(collected_head->gen_number != pevent_log_head->gen_number) { + printf("Collected Persistent Event Log may be invalid, "\ + "Re-read the log is reiquired\n"); + goto free; + } nvme_show_persistent_event_log(pevent_log_info, cfg.action, cfg.log_len, devicename, flags); + } else if (err > 0) nvme_show_status(err); else perror("persistent event log"); +free: nvme_free(pevent_log_info, huge); - -close_fd: +free_head: free(pevent_log_head); +close_fd: close(fd); ret: return nvme_status_to_errno(err, false); @@ -1249,6 +1363,113 @@ ret: } +static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Boot Partition " \ + "log page and prints it, for the given " \ + "device in either decoded format(default), " \ + "json or binary."; + const char *lsp = "log specific field"; + const char *fname = "boot partition data output file name"; + struct nvme_boot_part_hdr boot; + __u8 *bp_log; + enum nvme_print_flags flags; + int err = -1, fd = 0, output = 0; + __u32 bpsz = 0; + + struct config { + __u8 lsp; + char *output_format; + char *file_name; + }; + + struct config cfg = { + .lsp = 0, + .file_name = NULL, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_BYTE("lsp", 's', &cfg.lsp, lsp), + OPT_FILE("output-file", 'f', &cfg.file_name, fname), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.file_name) { + fprintf(stderr, "Please provide an output file!\n"); + errno = EINVAL; + err = -1; + goto close_fd; + } + + if (cfg.lsp > 128) { + fprintf(stderr, "invalid lsp param: %u\n", cfg.lsp); + errno = EINVAL; + err = -1; + goto close_fd; + } + + output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "Failed to open output file %s: %s!\n", + cfg.file_name, strerror(errno)); + err = output; + goto close_fd; + } + + err = nvme_boot_part_log(fd, cfg.lsp, &boot, sizeof(boot)); + if (err < 0) { + perror("boot partition log"); + goto close_output; + } else if (err) { + nvme_show_status(err); + goto close_output; + } + + bpsz = (boot.bpinfo & 0x7fff) * 128 * 1024; + bp_log = calloc(sizeof(boot) + bpsz, 1); + if (!bp_log) { + perror("could not alloc buffer for boot partition log"); + errno = ENOMEM; + err = -1; + goto close_output; + } + + err = nvme_boot_part_log(fd, cfg.lsp, &bp_log, sizeof(boot) + bpsz); + if (!err) + nvme_show_boot_part_log(&bp_log, devicename, flags, sizeof(boot) + bpsz); + else if (err > 0) + nvme_show_status(err); + else + perror("boot partition log"); + + err = write(output, (void *) bp_log + sizeof(boot), bpsz); + if (err != bpsz) { + fprintf(stderr, "Failed to flush all data to file!\n"); + } else { + printf("Data flushed into file %s\n", cfg.file_name); + } + err = 0; + + free(bp_log); + +close_output: + close(output); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve desired number of bytes "\ @@ -1428,6 +1649,53 @@ ret: return nvme_status_to_errno(err, false); } +static int get_fid_support_effects_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve FID Support and Effects log and show it."; + const char *human_readable = "show log in readable format"; + struct nvme_fid_support_effects fid_support_log; + enum nvme_print_flags flags; + int fd, err = -1; + + struct config { + int human_readable; + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.human_readable) + flags |= VERBOSE; + + err = nvme_fid_support_effects_log(fd, &fid_support_log); + if (!err) + nvme_show_fid_support_effects_log(&fid_support_log, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("fid support effects log"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Show controller list information for the subsystem the "\ @@ -1554,6 +1822,67 @@ ret: return nvme_status_to_errno(err, false); } +static int id_endurance_grp_list(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Show endurance group list information for the given endurance "\ + "group id"; + const char *endurance_grp_id = "Endurance Group ID"; + int err = -1, fd; + struct nvme_endurance_group_list *endgrp_list; + enum nvme_print_flags flags; + + struct config { + __u16 endgrp_id; + char *output_format; + }; + + struct config cfg = { + .endgrp_id = 0, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_SHRT("endgrp-id", 'i', &cfg.endgrp_id, endurance_grp_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (flags != JSON && flags != NORMAL) { + err = -EINVAL; + fprintf(stderr, "invalid output format\n"); + goto close_fd; + } + + if (posix_memalign((void *)&endgrp_list, getpagesize(), 0x1000)) { + fprintf(stderr, "can not allocate memory for endurance gropu list\n"); + errno = ENOMEM; + err = -1; + goto close_fd; + } + + err = nvme_identify_endurance_group_list(fd, cfg.endgrp_id, endgrp_list); + if (!err) + nvme_show_endurance_group_list(endgrp_list, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("id endurance group list"); + + free(endgrp_list); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Delete the given namespace by "\ @@ -2224,6 +2553,73 @@ ret: return nvme_status_to_errno(err, false); } +static int cmd_set_independent_id_ns(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an I/O Command Set Independent Identify "\ + "Namespace command to the given device, returns properties of the "\ + "specified namespace in human-readable or binary or json format."; + const char *raw = "show identify in binary format"; + const char *human_readable = "show identify in readable format"; + const char *namespace_id = "identifier of desired namespace"; + + enum nvme_print_flags flags; + struct nvme_cmd_set_independent_id_ns ns; + int err = -1, fd; + + struct config { + __u32 namespace_id; + int raw_binary; + int human_readable; + char *output_format; + }; + + struct config cfg = { + .namespace_id = 0, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + if (cfg.human_readable) + 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_cmd_set_independent_identify_ns(fd, cfg.namespace_id, &ns); + if (!err) + nvme_show_cmd_set_independent_id_ns(&ns, cfg.namespace_id, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("I/O command set independent identify namespace"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Send an Identify Namespace Granularity List command to the "\ @@ -5244,7 +5640,7 @@ static int submit_io(int opcode, char *command, const char *desc, gettimeofday(&start_time, NULL); err = nvme_io(fd, opcode, cfg.namespace_id, cfg.start_block, cfg.block_count, control, dsmgmt, cfg.ref_tag, cfg.app_tag, cfg.app_tag_mask, - cfg.storage_tag, buffer, mbuffer); + cfg.storage_tag, buffer, buffer_size, mbuffer, mbuffer_size); gettimeofday(&end_time, NULL); if (cfg.latency) printf(" latency: %s: %llu us\n", @@ -5788,6 +6184,100 @@ static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *p return rpmb_cmd_option(argc, argv, cmd, plugin); } +static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The Lockdown command is used to control the "\ + "Command and Feature Lockdown capability which configures the "\ + "prohibition or allowance of execution of the specified command "\ + "or Set Features command targeting a specific Feature Identifier."; + const char *ofi_desc = "Opcode or Feature Identifier(OFI) "\ + "specifies the command opcode or Set Features Feature Identifier "\ + "identified by the Scope field."; + const char *ifc_desc = "[0-3] Interface (INF) field identifies the "\ + "interfaces affected by this command."; + const char *prhbt_desc = "[0-1]Prohibit(PRHBT) bit specifies whether "\ + "to prohibit or allow the command opcode or Set Features Feature "\ + "Identifier specified by this command."; + const char *scp_desc = "[0-15]Scope(SCP) field specifies the contents "\ + "of the Opcode or Feature Identifier field."; + const char *uuid_desc = "UUID Index - If this field is set to a non-zero "\ + "value, then the value of this field is the index of a UUID in the UUID "\ + "List that is used by the command.If this field is cleared to 0h,"\ + "then no UUID index is specified"; + + int fd, err = -1; + + struct config { + __u8 ofi; + __u8 ifc; + __u8 prhbt; + __u8 scp; + __u8 uuid; + }; + + struct config cfg = { + .ofi = 0, + .ifc = 0, + .prhbt = 0, + .scp = 0, + .uuid = 0, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("ofi", 'o', &cfg.ofi, ofi_desc), + OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc), + OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc), + OPT_BYTE("scp", 's', &cfg.scp, scp_desc), + OPT_BYTE("uuid", 'U', &cfg.uuid, uuid_desc), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + /* check for input arguement limit */ + if (cfg.ifc > 3) { + fprintf(stderr, "invalid interface settings:%d\n", cfg.ifc); + errno = EINVAL; + err = -1; + goto close_fd; + } + if (cfg.prhbt > 1) { + fprintf(stderr, "invalid prohibit settings:%d\n", cfg.prhbt); + errno = EINVAL; + err = -1; + goto close_fd; + } + if (cfg.scp > 15) { + fprintf(stderr, "invalid scope settings:%d\n", cfg.scp); + errno = EINVAL; + err = -1; + goto close_fd; + } + if (cfg.uuid > 127) { + fprintf(stderr, "invalid UUID index settings:%d\n", cfg.uuid); + errno = EINVAL; + err = -1; + goto close_fd; + } + + err = nvme_lockdown(fd, cfg.scp,cfg.prhbt,cfg.ifc,cfg.ofi, + cfg.uuid); + + if (err < 0) + perror("lockdown"); + else if (err > 0) + nvme_show_status(err); + else + printf("Lockdown Command is Successful\n"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, const char *desc, struct command *cmd) { -- cgit v1.2.3