From 622adf9f188273ce35648ebeac7948b04cf78814 Mon Sep 17 00:00:00 2001 From: Wei Hou Date: Thu, 11 Jun 2020 13:46:29 +0800 Subject: [PATCH] sfx: add set/get-feature sub cmd Signed-off-by: Wei Hou --- plugins/scaleflux/sfx-nvme.c | 232 ++++++++++++++++++++++++++--------- plugins/scaleflux/sfx-nvme.h | 4 +- 2 files changed, 175 insertions(+), 61 deletions(-) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 846ca77..182804a 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "linux/nvme_ioctl.h" @@ -28,36 +30,40 @@ #define SECTOR_SHIFT 9 #define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx) -#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define NVME_IOCTL_CLR_CARD _IO('N', 0x47) +#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL) enum { SFX_LOG_LATENCY_READ_STATS = 0xc1, SFX_LOG_SMART = 0xc2, - SFX_LOG_LATENCY_WRITE_STATS = 0xc3, + SFX_LOG_LATENCY_WRITE_STATS = 0xc3, SFX_LOG_QUAL = 0xc4, SFX_LOG_MISMATCHLBA = 0xc5, SFX_LOG_MEDIA = 0xc6, SFX_LOG_BBT = 0xc7, SFX_LOG_IDENTIFY = 0xcc, SFX_FEAT_ATOMIC = 0x01, + SFX_FEAT_UP_P_CAP = 0xac, + SFX_FEAT_CLR_CARD = 0xdc, }; enum sfx_nvme_admin_opcode { nvme_admin_query_cap_info = 0xd3, nvme_admin_change_cap = 0xd4, - nvme_admin_sfx_set_features = 0xd5, - nvme_admin_sfx_get_features = 0xd6, + nvme_admin_sfx_set_features = 0xd5, + nvme_admin_sfx_get_features = 0xd6, }; struct sfx_freespace_ctx { __u64 free_space; - __u64 phy_cap; /* physical capacity, in unit of sector */ - __u64 phy_space; /* physical space considering OP, in unit of sector */ - __u64 user_space; /* user required space, in unit of sector*/ - __u64 hw_used; /* hw space used in 4K */ - __u64 app_written; /* app data written in 4K */ + __u64 phy_cap; /* physical capacity, in unit of sector */ + __u64 phy_space; /* physical space considering OP, in unit of sector */ + __u64 user_space; /* user required space, in unit of sector*/ + __u64 hw_used; /* hw space used in 4K */ + __u64 app_written; /* app data written in 4K */ }; struct nvme_capacity_info { @@ -66,7 +72,7 @@ struct nvme_capacity_info { __u64 used_space; __u64 free_space; }; -struct __attribute__((packed)) nvme_additional_smart_log_item { +struct __attribute__((packed)) nvme_additional_smart_log_item { uint8_t key; uint8_t _kp[2]; uint8_t norm; @@ -112,8 +118,8 @@ int nvme_change_cap(int fd, __u32 nsid, __u64 capacity) struct nvme_admin_cmd cmd = { .opcode = nvme_admin_change_cap, .nsid = nsid, - .cdw10 = (capacity & 0xffffffff), - .cdw11 = (capacity >> 32), + .cdw10 = (capacity & 0xffffffff), + .cdw11 = (capacity >> 32), }; @@ -373,7 +379,6 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return err; } - struct sfx_lat_stats { __u16 maj; __u16 min; @@ -448,7 +453,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); + nvme_status_to_string(err), err); return err; } @@ -541,7 +546,7 @@ static void bd_table_show(unsigned char *bd_table, __u64 table_size) } /** - * @brief "hooks of sfx get-bad-block" + * @brief "hooks of sfx get-bad-block" * * @param argc * @param argv @@ -592,10 +597,16 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct static void show_cap_info(struct sfx_freespace_ctx *ctx) { - printf("user sectors: %#llx\n", ctx->user_space); - printf("totl physical sectors: %#llx\n", ctx->phy_space); - printf("free physical sectors: %#llx\n", ctx->free_space); - printf("used physical sectors: %#llx\n", ctx->phy_space - ctx->free_space); + + printf("logic capacity:%5lluGB(0x%llx)\n", + IDEMA_CAP2GB(ctx->user_space), ctx->user_space); + printf("provisioned capacity:%5lluGB(0x%llx)\n", + IDEMA_CAP2GB(ctx->phy_space), ctx->phy_space); + printf("free provisioned capacity:%5lluGB(0x%llx)\n", + IDEMA_CAP2GB(ctx->free_space), ctx->free_space); + printf("used provisioned capacity:%5lluGB(0x%llx)\n", + IDEMA_CAP2GB(ctx->phy_space) - IDEMA_CAP2GB(ctx->free_space), + ctx->phy_space - ctx->free_space); } static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -631,42 +642,83 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu return err; } -static int change_cap_mem_check(int fd, __u64 trg_in_4k) +static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) { struct sfx_freespace_ctx freespace_ctx = { 0 }; struct sysinfo s_info; __u64 mem_need = 0; __u64 cur_in_4k = 0; + __u64 provisoned_cap_4k = 0; __u32 cnt_ms = 0; + int extend = 0; while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) { if (cnt_ms++ > 600) {//1min - fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno); return -1; } usleep(100000); } - cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); - if (cur_in_4k > trg_in_4k) { - return 0; + /* + * capacity illegal check + */ + provisoned_cap_4k = freespace_ctx.phy_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); + if (trg_in_4k < ((__u64)provisoned_cap_4k/2)) { + fprintf(stderr, "WARNING: the target capacity is less than" + "0.5 provisioned capacity, please make it larger\n"); + return -1; } - - if (sysinfo(&s_info) < 0) { - printf("change-cap query mem info fail\n"); + if (trg_in_4k > ((__u64)provisoned_cap_4k*4)) { + fprintf(stderr, "WARNING: the target capacity is too large\n"); return -1; } - mem_need = (trg_in_4k - cur_in_4k) * 8; - if (s_info.freeram <= 10 || mem_need > s_info.freeram) { - fprintf(stderr, "WARNING: mem needed is %llu, free mem is %lu\n" - "Insufficient memory, please drop cache or add free memory and retry\n", - mem_need, s_info.freeram); - return -1; + /* + * check whether mem enough if extend + * */ + cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); + extend = (cur_in_4k <= trg_in_4k); + if (extend) { + if (sysinfo(&s_info) < 0) { + printf("change-cap query mem info fail\n"); + return -1; + } + mem_need = (trg_in_4k - cur_in_4k) * 8; + if (s_info.freeram <= 10 || mem_need > s_info.freeram) { + fprintf(stderr, "WARNING: mem needed is %llu, freemem is %lu\n" + "Insufficient memory, please drop cache or add free memory and retry\n", + mem_need, s_info.freeram); + return -1; + } } + *shrink = !extend; + return 0; } +/** + * @brief prompt and get user confirm input + * + * @param str, prompt string + * + * @return 0, cancled; 1 confirmed + */ +static int sfx_confirm_change(const char *str) +{ + char confirm; + fprintf(stderr, "WARNING: %s.\n" + "Use the force [--force] option to suppress this warning.\n", str); + + fprintf(stderr, "Confirm Y/y, Others cancel:\n"); + confirm = fgetc(stdin); + if (confirm != 'y' && confirm != 'Y') { + fprintf(stderr, "Cancled.\n"); + return 0; + } + fprintf(stderr, "Sending operation ... \n"); + return 1; +} + static int change_cap(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = -1, fd; @@ -678,6 +730,8 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; __u64 cap_in_4k = 0; __u64 cap_in_sec = 0; + int shrink = 0; + struct config { __u64 cap_in_byte; __u32 capacity_in_gb; @@ -694,7 +748,7 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin OPT_ARGS(opts) = { OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), - OPT_UINT("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), + OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), OPT_FLAG("force", 'f', &cfg.force, force), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG("json", 'j', &cfg.json, json), @@ -706,45 +760,79 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin return fd; } - if (!cfg.force) { - fprintf(stderr, "WARNING: Changing capacity may irrevocably delete user data.\n" - "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n" - "Use the force [--force|-f] option to suppress this warning.\n"); - sleep(10); - fprintf(stderr, "Sending operation ... \n"); - } - cap_in_sec = IDEMA_CAP(cfg.capacity_in_gb); cap_in_4k = cap_in_sec >> 3; if (cfg.cap_in_byte) cap_in_4k = cfg.cap_in_byte >> 12; printf("%dG %lluB %llu 4K\n", cfg.capacity_in_gb, cfg.cap_in_byte, cap_in_4k); - if (change_cap_mem_check(fd, cap_in_4k)) + + if (change_sanity_check(fd, cap_in_4k, &shrink)) { + printf("Scaleflux change-capacity: fail\n"); return err; + } + + + if (!cfg.force && shrink && !sfx_confirm_change("Changing Cap may irrevocably delete this device's data")) { + return 0; + } err = nvme_change_cap(fd, 0xffffffff, cap_in_4k); if (err < 0) perror("sfx-change-cap"); else if (err != 0) - fprintf(stderr, "NVMe IO command error:%s(%x)\n", + fprintf(stderr, "NVME IO command error:%s(%x)\n", nvme_status_to_string(err), err); else { - printf("ScaleFlux change-capacity: success\n"); - if(ioctl(fd, BLKRRPART) < 0) { - fprintf(stderr, "failed to re-read partition table\n"); - err = EFAULT; - } + printf("Scaleflux change-capacity: success\n"); + ioctl(fd, BLKRRPART); } return err; } +static int sfx_verify_chr(int fd) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) { + perror("fstat"); + return errno; + } + if (!S_ISCHR(nvme_stat.st_mode)) { + fprintf(stderr, + "Error: requesting clean card on non-controller handle\n"); + return ENOTBLK; + } + return 0; +} + +static int sfx_clean_card(int fd) +{ + int ret; + + ret = sfx_verify_chr(fd); + if (ret) + return ret; + ret = ioctl(fd, NVME_IOCTL_CLR_CARD); + if (ret) + perror("Ioctl Fail."); + else + printf("ScaleFlux clean card success\n"); + + return ret; +} + char *sfx_feature_to_string(int feature) { switch (feature) { - case SFX_FEAT_ATOMIC: return "ATOMIC"; + case SFX_FEAT_ATOMIC: + return "ATOMIC"; + case SFX_FEAT_UP_P_CAP: + return "UPDATE_PROVISION_CAPACITY"; - default: return "Unknown"; + default: + return "Unknown"; } } @@ -752,27 +840,34 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl { int err = 0, fd; char *desc = "ScaleFlux internal set features\n" - "feature id 1: ATOMIC"; + "feature id 1: ATOMIC\n" + "value 0: Disable atomic write\n" + " 1: Enable atomic write"; const char *value = "new value of feature (required)"; const char *feature_id = "hex feature name (required)"; const char *namespace_id = "desired namespace"; + const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; + struct nvme_id_ns ns; struct config { __u32 namespace_id; __u32 feature_id; __u32 value; + __u32 force; }; struct config cfg = { .namespace_id = 1, .feature_id = 0, .value = 0, + .force = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), + OPT_UINT("value", 'v', &cfg.value, value), + OPT_FLAG("force", 's', &cfg.force, force), OPT_END() }; @@ -786,7 +881,17 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl return EINVAL; } - if (cfg.feature_id == SFX_FEAT_ATOMIC) { + if (cfg.feature_id == SFX_FEAT_CLR_CARD) { + /*Warning for clean card*/ + if (!cfg.force && !sfx_confirm_change("Going to clean device's data, confirm umount fs and try again")) { + return 0; + } else { + return sfx_clean_card(fd); + } + + } + + if (cfg.feature_id == SFX_FEAT_ATOMIC && cfg.value != 0) { if (cfg.namespace_id != 0xffffffff) { err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns); if (err) { @@ -794,7 +899,7 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl perror("identify-namespace"); else fprintf(stderr, - "NVMe Admin command error:%s(%x)\n", + "NVME Admin command error:%s(%x)\n", nvme_status_to_string(err), err); return err; } @@ -802,18 +907,29 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl * atomic only support with sector-size = 4k now */ if ((ns.flbas & 0xf) != 1) { - printf("Please change-sector size to 4K, then retry\n"); + printf("Please change-sector size to 4K, then retry!!!\n"); return EFAULT; } } + } else if (cfg.feature_id == SFX_FEAT_UP_P_CAP) { + if (cfg.value <= 0) { + fprintf(stderr, "Invalid Param\n"); + return EINVAL; + } + + /*Warning for change pacp by GB*/ + if (!cfg.force && !sfx_confirm_change("Changing physical capacity may irrevocably delete this device's data")) { + return 0; + } } err = nvme_sfx_set_features(fd, cfg.namespace_id, cfg.feature_id, cfg.value); + if (err < 0) { - perror("ScaleFlux-set-feature"); + perror("Scaleflux-set-feature"); return errno; } else if (!err) { - printf("ScaleFlux set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + printf("Scaleflux set-feature:%#02x (%s), value:%d\n", cfg.feature_id, sfx_feature_to_string(cfg.feature_id), cfg.value); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h index daf9c33..8f14501 100644 --- a/plugins/scaleflux/sfx-nvme.h +++ b/plugins/scaleflux/sfx-nvme.h @@ -14,12 +14,10 @@ PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions"), ENTRY("query-cap", "Query current capacity info", query_cap_info) 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("get-feature", "Get a feature", sfx_get_feature) ) ); #endif #include "define_cmd.h" - - -- 2.49.0