From e67e56018c77a865ad29680afca172c99f1e3bcd Mon Sep 17 00:00:00 2001 From: yjin Date: Mon, 22 May 2023 18:05:57 +0800 Subject: [PATCH] plugins/sfx: sfx-nvme add expand-cap & dump evtlog cmd Signed-off-by: yjin --- plugins/scaleflux/sfx-nvme.c | 487 ++++++++++++++++++++++++++++++++++- plugins/scaleflux/sfx-nvme.h | 2 + 2 files changed, 488 insertions(+), 1 deletion(-) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 4bcfbf6a..0ebc5b64 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -3,19 +3,23 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include +#include +#include #include "common.h" #include "nvme.h" #include "libnvme.h" #include "plugin.h" #include "linux/types.h" +#include "nvme-wrap.h" #include "nvme-print.h" #define CREATE_CMD @@ -66,6 +70,10 @@ struct sfx_freespace_ctx __u64 hw_used; /* hw space used in 4K */ __u64 app_written; /* app data written in 4K */ __u64 out_of_space; + __u64 map_unit; + __u64 max_user_space; + __u64 extendible_user_cap_lba_count; + __u64 friendly_change_cap_support; }; struct nvme_capacity_info { @@ -816,6 +824,7 @@ static void show_cap_info(struct sfx_freespace_ctx *ctx) printf("used provisioned capacity:%5lluGB(0x%"PRIx64")\n", IDEMA_CAP2GB(ctx->phy_space) - IDEMA_CAP2GB(ctx->free_space), (uint64_t)(ctx->phy_space - ctx->free_space)); + printf("map_unit :0x%"PRIx64"K\n", (uint64_t)(ctx->map_unit * 4)); } static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1211,3 +1220,479 @@ static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct pl return err; } + +static int nvme_parse_evtlog(void *pevent_log_info, __u32 log_len, char* output) +{ + __u32 offset = 0; + __u32 length = log_len; + __u16 fw_core; + __u64 fw_time; + __u8 code_level; + __u8 code_type; + char str_buffer[512]; + __u32 str_pos; + FILE* fd; + int err = 0; + + enum sfx_evtlog_level { + sfx_evtlog_level_warning, + sfx_evtlog_level_error, + }; + + const char* sfx_evtlog_warning[4] = { + "RESERVED", + "TOO_MANY_BB", + "LOW_SPACE", + "HIGH_TEMPERATURE" + }; + + const char* sfx_evtlog_error[14] = { + "RESERVED", + "HAS_ASSERT", + "HAS_PANIC_DUMP", + "INVALID_FORMAT_CAPACITY", + "MAT_FAILED", + "FREEZE_DUE_TO_RECOVERY_FAILED", + "RFS_BROKEN", + "MEDIA_ERR_ON_PAGE_IN", + "MEDIA_ERR_ON_MPAGE_HEADER", + "CAPACITOR_BROKEN", + "READONLY_DUE_TO_RECOVERY_FAILED", + "RD_ERR_IN_GSD_RECOVERY", + "RD_ERR_ON_PF_RECOVERY", + "MEDIA_ERR_ON_FULL_RECOVERY" + }; + + struct sfx_nvme_evtlog_info { + __u16 time_stamp[4]; + __u64 magic1; + __u8 reverse[10]; + char evt_name[32]; + __u64 magic2; + char fw_ver[24]; + char bl2_ver[32]; + __u16 code; + __u16 assert_id; + } __attribute__((packed)); + + struct sfx_nvme_evtlog_info* info = NULL; + + if ((fd = fopen(output, "w+")) == NULL) { + fprintf(stderr, "Failed to open %s file to write\n", output); + err = ENOENT; + goto ret; + } + + while(length > 0) { + info = (struct sfx_nvme_evtlog_info*)(pevent_log_info + offset); + + if ((info->magic1 == 0x474F4C545645) && + (info->magic2 == 0x38B0B3ABA9BA)) { + + memset(str_buffer, 0, 512); + str_pos = 0; + + fw_core = info->time_stamp[3]; + snprintf(str_buffer + str_pos, 16, "[%d-", fw_core); + str_pos = strlen(str_buffer); + + fw_time = ((__u64)info->time_stamp[2] << 32) + ((__u64)info->time_stamp[1] << 16) + (__u64)info->time_stamp[0]; + convert_ts(fw_time, str_buffer + str_pos); + str_pos = strlen(str_buffer); + + strcpy(str_buffer + str_pos, "] event-log:\n"); + str_pos = strlen(str_buffer); + + snprintf(str_buffer + str_pos, 128, \ + " > fw_version: %s\n > bl2_version: %s\n",\ + info->fw_ver, info->bl2_ver); + str_pos = strlen(str_buffer); + + code_level = (info->code & 0x100) >> 8; + code_type = (info->code % 0x100); + if (code_level == sfx_evtlog_level_warning) { + snprintf(str_buffer + str_pos, 128,\ + " > error_str: [WARNING][%s]\n\n",\ + sfx_evtlog_warning[code_type]); + } else { + if (info->assert_id) { + snprintf(str_buffer + str_pos, 128,\ + " > error_str: [ERROR][%s]\n > assert_id: %d\n\n",\ + sfx_evtlog_error[code_type], info->assert_id); + } else { + snprintf(str_buffer + str_pos, 128,\ + " > error_str: [ERROR][%s]\n\n",\ + sfx_evtlog_error[code_type]); + } + } + str_pos = strlen(str_buffer); + + if (fwrite(str_buffer, 1, str_pos, fd) != str_pos) { + fprintf(stderr, "Failed to write parse result to output file\n"); + goto close_fd; + } + } + + offset++; + length--; + + if (!(offset % (log_len / 100)) || (offset == log_len)) { + util_spinner("Parse", (float) (offset) / (float) (log_len)); + } + } + + printf("\nParse-evtlog: Success\n"); + +close_fd: + fclose(fd); +ret: + return err; +} + +static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id,\ + __u32 storage_medium, char* file, bool parse, char*output) +{ + struct nvme_persistent_event_log *pevent; + void *pevent_log_info; + __u8 lsp_base; + __u32 offset = 0; + __u32 length = 0; + __u32 log_len; + __u32 single_len; + bool huge; + int err = 0; + FILE *fd = NULL; + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .lid = NVME_LOG_LID_PERSISTENT_EVENT, + .nsid = namespace_id, + .lpo = NVME_LOG_LPO_NONE, + .lsp = NVME_LOG_LSP_NONE, + .lsi = NVME_LOG_LSI_NONE, + .rae = false, + .uuidx = NVME_UUID_NONE, + .csi = NVME_CSI_NVM, + .ot = false, + .len = 0, + .log = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + + if (storage_medium == 0) { + lsp_base = 0; + single_len = 64 * 1024 - 4; + } else { + lsp_base = 4; + single_len = 32 * 1024; + } + + pevent = calloc(sizeof(*pevent), sizeof(__u8)); + if (!pevent) { + err = -ENOMEM; + goto ret; + } + + args.lsp = lsp_base + NVME_PEVENT_LOG_RELEASE_CTX; + args.log = pevent; + args.len = sizeof(*pevent); + + err = nvme_get_log(&args); + if (err) { + fprintf(stderr, "Unable to get evtlog lsp=0x%x, ret = 0x%x\n", args.lsp, err); + goto free_pevent; + } + + args.lsp = lsp_base + NVME_PEVENT_LOG_EST_CTX_AND_READ; + err = nvme_get_log(&args); + if (err) { + fprintf(stderr, "Unable to get evtlog lsp=0x%x, ret = 0x%x\n", args.lsp, err); + goto free_pevent; + } + + log_len = le64_to_cpu(pevent->tll); + if (log_len % 4 != 0) { + log_len = (log_len / 4 + 1) * 4; + } + + pevent_log_info = nvme_alloc(single_len, &huge); + if (!pevent_log_info) { + err = -ENOMEM; + goto free_pevent; + } + + if ((fd = fopen(file, "wb+")) == NULL) { + fprintf(stderr, "Failed to open %s file to write\n", file); + err = ENOENT; + goto free; + } + + args.lsp = lsp_base + NVME_PEVENT_LOG_READ; + args.log = pevent_log_info; + length = log_len; + while(length > 0) { + args.lpo = offset; + if (length > single_len) { + args.len = single_len; + } else { + memset(args.log, 0, args.len); + args.len = length; + } + err = nvme_get_log(&args); + if (err) { + fprintf(stderr, "Unable to get evtlog offset=0x%x len 0x%x ret = 0x%x\n", offset, args.len, err); + goto close_fd; + } + + if (fwrite(args.log, 1, args.len, fd) != args.len) { + fprintf(stderr, "Failed to write evtlog to file\n"); + goto close_fd; + } + + offset += args.len; + length -= args.len; + util_spinner("Parse", (float) (offset) / (float) (log_len)); + } + + printf("\nDump-evtlog: Success\n"); + + if (parse) { + nvme_free(pevent_log_info, huge); + pevent_log_info = nvme_alloc(log_len, &huge); + if (!pevent_log_info) { + fprintf(stderr, "Failed to alloc enough memory 0x%x to parse evtlog\n", log_len); + err = -ENOMEM; + goto close_fd; + } + + fclose(fd); + if ((fd = fopen(file, "rb")) == NULL) { + fprintf(stderr, "Failed to open %s file to read\n", file); + err = ENOENT; + goto free; + } + if (fread(pevent_log_info, 1, log_len, fd) != log_len) { + fprintf(stderr, "Failed to read evtlog to buffer\n"); + goto close_fd; + } + + err = nvme_parse_evtlog(pevent_log_info, log_len, output); + } + +close_fd: + fclose(fd); +free: + nvme_free(pevent_log_info, huge); +free_pevent: + free(pevent); +ret: + return err; +} + +static int sfx_dump_evtlog(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + char* desc = "dump evtlog into file and parse"; + const char* file = "evtlog file(required)"; + const char* namespace_id = "desired namespace"; + const char* storage_medium = "evtlog storage medium\n" + "0: nand(default) 1: nor"; + const char* parse = "parse error & warning evtlog from evtlog file"; + const char* output = "parse result output file"; + struct nvme_dev *dev; + int err = 0; + + struct config { + char* file; + __u32 namespace_id; + __u32 storage_medium; + bool parse; + char* output; + }; + struct config cfg = { + .file = NULL, + .namespace_id = 0xffffffff, + .storage_medium = 0, + .parse = false, + .output = NULL, + }; + + OPT_ARGS(opts) = { + OPT_FILE("file", 'f', &cfg.file, file), + OPT_UINT("namespace_id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("storage_medium", 's', &cfg.storage_medium, storage_medium), + OPT_FLAG("parse", 'p', &cfg.parse, parse), + OPT_FILE("output", 'o', &cfg.output, output), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + goto ret; + + if (!cfg.file) { + fprintf(stderr, "file required param\n"); + err = EINVAL; + goto close_dev; + } + + if (cfg.parse && !cfg.output) { + fprintf(stderr, "output file required if evtlog need be parsed\n"); + err = EINVAL; + goto close_dev; + } + + err = nvme_dump_evtlog(dev, cfg.namespace_id, cfg.storage_medium, cfg.file, cfg.parse, cfg.output); + if (err) { + goto close_dev; + } + +close_dev: + dev_close(dev); +ret: + return err; +} + +static int nvme_expand_cap(struct nvme_dev *dev, __u32 namespace_id,\ + __u64 namespace_size, __u64 namespace_cap, __u32 lbaf, __u32 units) +{ + struct dirent **devices; + char dev_name[32] = ""; + int i = 0; + int num = 0; + int err = 0; + + struct sfx_expand_cap_info { + __u64 namespace_size; + __u64 namespace_cap; + __u8 reserve[10]; + __u8 lbaf; + __u8 reserve1[5]; + } __attribute__((packed)); + + if (S_ISCHR(dev->direct.stat.st_mode)) { + snprintf(dev_name, 32, "%sn%u", dev->name, namespace_id); + } else { + strcpy(dev_name, dev->name); + } + + num = scandir("/dev", &devices, nvme_namespace_filter, alphasort); + if (num <= 0) { + err = num; + goto ret; + } + + if (strcmp(dev_name, devices[num-1]->d_name)) { + fprintf(stderr, "Expand namespace not the last one\n"); + err = EINVAL; + goto free_devices; + } + + if (units == 0) { + namespace_size = IDEMA_CAP(namespace_size) / (1 << (lbaf * 3)); + namespace_cap = IDEMA_CAP(namespace_cap) / (1 << (lbaf * 3)); + } + + struct sfx_expand_cap_info info = { + .namespace_size = namespace_size, + .namespace_cap = namespace_cap, + .lbaf = lbaf, + }; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_ns_mgmt, + .nsid = namespace_id, + .addr = (__u64)(uintptr_t)&info, + .data_len = sizeof(info), + .cdw10 = 0x0e, + }; + + err = nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL); + if (err) { + fprintf(stderr, "Create ns failed\n"); + nvme_show_status(err); + goto free_devices; + } + +free_devices: + for (i=0; idirect.stat.st_mode)) { + fprintf(stderr, "namespace_id or blk device required\n"); + err = EINVAL; + goto ret; + } else { + cfg.namespace_id = atoi(&dev->name[strlen(dev->name) - 1]); + } + } + + if (!cfg.namespace_size) { + fprintf(stderr, "namespace_size required param\n"); + err = EINVAL; + goto close_dev; + } + + if (!cfg.namespace_cap) { + fprintf(stderr, "namespace_cap required param\n"); + err = EINVAL; + goto close_dev; + } + + err = nvme_expand_cap(dev, cfg.namespace_id, cfg.namespace_size, cfg.namespace_cap, cfg.lbaf, cfg.units); + if (err) { + goto close_dev; + } + + printf("%s: Success, create nsid:%d\n", cmd->name, cfg.namespace_id); + +close_dev: + dev_close(dev); +ret: + return err; +} diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h index 0b95d92d..53e22179 100644 --- a/plugins/scaleflux/sfx-nvme.h +++ b/plugins/scaleflux/sfx-nvme.h @@ -16,6 +16,8 @@ PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions", NVME_VERSION), ENTRY("change-cap", "Dynamic change capacity", change_cap) ENTRY("set-feature", "Set a feature", sfx_set_feature) ENTRY("get-feature", "Get a feature", sfx_get_feature) + ENTRY("dump-evtlog", "dump evtlog into file and parse warning & error log", sfx_dump_evtlog) + ENTRY("expand-cap", "expand the last namespace capacity lossless", sfx_expand_cap) ) ); -- 2.49.0