#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 {
__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;
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_change_cap,
.nsid = nsid,
- .cdw10 = (capacity & 0xffffffff),
- .cdw11 = (capacity >> 32),
+ .cdw10 = (capacity & 0xffffffff),
+ .cdw11 = (capacity >> 32),
};
return err;
}
-
struct sfx_lat_stats {
__u16 maj;
__u16 min;
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;
}
}
/**
- * @brief "hooks of sfx get-bad-block"
+ * @brief "hooks of sfx get-bad-block"
*
* @param argc
* @param argv
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)
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;
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;
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),
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";
}
}
{
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()
};
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) {
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;
}
* 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",