]> www.infradead.org Git - users/hch/nvme-cli.git/commitdiff
sfx: add set/get-feature sub cmd
authorWei Hou <wei.hou@scaleflux.com>
Thu, 11 Jun 2020 05:46:29 +0000 (13:46 +0800)
committerKeith Busch <kbusch@kernel.org>
Tue, 30 Jun 2020 17:31:36 +0000 (11:31 -0600)
Signed-off-by: Wei Hou <wei.hou@scaleflux.com>
plugins/scaleflux/sfx-nvme.c
plugins/scaleflux/sfx-nvme.h

index 846ca77dc28f2c6b5d5ba8960aa47fa2186f2b8c..182804a696a20f1d47354056683f2ab2b7a5a0bb 100644 (file)
@@ -8,6 +8,8 @@
 #include <asm/byteorder.h>
 #include <sys/ioctl.h>
 #include <sys/sysinfo.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include "linux/nvme_ioctl.h"
 
 #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",
index daf9c338f20aea4d83243742f207c2e7380d3cce..8f145019037a0aa92b9a3a516551df59e199ed2c 100644 (file)
@@ -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"
-
-