From 93e08015a49b2e3b9fbfa03b8727d3e2fc5db5b7 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 23 Oct 2015 16:56:07 -0600 Subject: [PATCH] Add data-set management command By popular demand... Signed-off-by: Keith Busch --- Documentation/cmds-main.txt | 3 + Documentation/nvme-dsm.1 | 97 ++++ Documentation/nvme-dsm.html | 902 ++++++++++++++++++++++++++++++++++++ Documentation/nvme-dsm.txt | 82 ++++ nvme.c | 142 +++++- src/argconfig.c | 41 +- src/argconfig.h | 2 + 7 files changed, 1262 insertions(+), 7 deletions(-) create mode 100644 Documentation/nvme-dsm.1 create mode 100644 Documentation/nvme-dsm.html create mode 100644 Documentation/nvme-dsm.txt diff --git a/Documentation/cmds-main.txt b/Documentation/cmds-main.txt index e05b35de..ed031269 100644 --- a/Documentation/cmds-main.txt +++ b/Documentation/cmds-main.txt @@ -10,6 +10,9 @@ linknvme:nvme-error-log[1]:: linknvme:nvme-flush[1]:: Submit flush +linknvme:nvme-dms[1]:: + Submit Data Set Management + linknvme:nvme-format[1]:: Format namespace(s) diff --git a/Documentation/nvme-dsm.1 b/Documentation/nvme-dsm.1 new file mode 100644 index 00000000..e29f0d5d --- /dev/null +++ b/Documentation/nvme-dsm.1 @@ -0,0 +1,97 @@ +'\" t +.\" Title: nvme-dsm +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: 10/23/2015 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-DSM" "1" "10/23/2015" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-dsm \- Send NVMe Data Set Management, return results +.SH "SYNOPSIS" +.sp +.nf +\fInvme dsm\fR [ \-\-namespace\-id= | \-n ] + [ \-\-ctx\-attrs= | \-a ] + [ \-\-blocks= | \-b ] + [ \-\-slbs= | \-s ] + [ \-\-ad | \-d ] [ \-\-idw | \-w ] [ \-\-idr | \-r ] + [ \-\-cdw11= | \-c ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends an Data Set Management command and provides the result and returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. If the character device is given, the \*(Aq\-\-namespace\-id\*(Aq option is mandatory, otherwise it will use the ns\-id of the namespace for the block device you opened\&. For block devices, the ns\-id used can be overridden with the same option\&. +.sp +You must specify at least one of the values for range list\&. If the range lists provided do not list the same number of elements, the default values for the remaining in the range will be set to 0\&. +.sp +The command dword 11 may be provided at the command line\&. For convenience, the current defined attributes (discard, integral read/write) for a data\-set management have flags\&. If cdw11 is specified, this will override any settings from the flags may have provided\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Sends the command with the requested nsid\&. This is required for the character devices, or overrides the block nsid if given\&. +.RE +.PP +\-a , \-\-ctx\-attrs= +.RS 4 +Comma separated list of the context attributes in each range +.RE +.PP +\-b , \-\-blocks= +.RS 4 +Comma separated list of the number of blocks in each range +.RE +.PP +\-s , \-\-slbs= +.RS 4 +Comma separated list of the starting block in each range +.RE +.PP +\-d , \-\-ad= +.RS 4 +Attribute Deallocate\&. +.RE +.PP +\-w , \-\-idw= +.RS 4 +Attribute Integral Dataset for Write\&. +.RE +.PP +\-r , \-\-idr= +.RS 4 +Attribute Integral Dataset for Read\&. +.RE +.PP +\-c , \-\-cdw11= +.RS 4 +All the command command dword 11 attribuets\&. Use exclusive from speficying individual attributes +.RE +.SH "EXAMPLES" +.sp +No examples yet +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-dsm.html b/Documentation/nvme-dsm.html new file mode 100644 index 00000000..9c6d42f2 --- /dev/null +++ b/Documentation/nvme-dsm.html @@ -0,0 +1,902 @@ + + + + + +nvme-dsm(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme dsm <device>  [ --namespace-id=<nsid> | -n <nsid> ]
+                        [ --ctx-attrs=<attribute-list,> | -a <attribute-list,> ]
+                        [ --blocks=<nlb-list,> | -b <nlb-list,> ]
+                        [ --slbs=<slba-list,> | -s <slba-list,> ]
+                        [ --ad | -d ] [ --idw | -w ] [ --idr | -r ]
+                        [ --cdw11=<cdw11> | -c <cdw11> ]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends an Data Set Management command and +provides the result and returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). +If the character device is given, the '--namespace-id' option is +mandatory, otherwise it will use the ns-id of the namespace for the block +device you opened. For block devices, the ns-id used can be overridden +with the same option.

+

You must specify at least one of the values for range list. If the range +lists provided do not list the same number of elements, the default +values for the remaining in the range will be set to 0.

+

The command dword 11 may be provided at the command line. For convenience, +the current defined attributes (discard, integral read/write) for a +data-set management have flags. If cdw11 is specified, this will override +any settings from the flags may have provided.

+
+
+
+

OPTIONS

+
+
+
+-n <nsid> +
+
+--namespace-id=<nsid> +
+
+

+ Sends the command with the requested nsid. This is required for the + character devices, or overrides the block nsid if given. +

+
+
+-a <attribute-list,> +
+
+--ctx-attrs=<attribute-list> +
+
+

+ Comma separated list of the context attributes in each range +

+
+
+-b <nlb-list,> +
+
+--blocks=<nlb-list,> +
+
+

+ Comma separated list of the number of blocks in each range +

+
+
+-s <slba-list,> +
+
+--slbs=<slba-list,> +
+
+

+ Comma separated list of the starting block in each range +

+
+
+-d <deallocate> +
+
+--ad=<deallocate> +
+
+

+ Attribute Deallocate. +

+
+
+-w <write> +
+
+--idw=<write> +
+
+

+ Attribute Integral Dataset for Write. +

+
+
+-r <read> +
+
+--idr=<read> +
+
+

+ Attribute Integral Dataset for Read. +

+
+
+-c <cdw11> +
+
+--cdw11=<cdw11> +
+
+

+ All the command command dword 11 attribuets. Use exclusive from + speficying individual attributes +

+
+
+
+
+
+

EXAMPLES

+
+

No examples yet

+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-dsm.txt b/Documentation/nvme-dsm.txt new file mode 100644 index 00000000..9afedc6b --- /dev/null +++ b/Documentation/nvme-dsm.txt @@ -0,0 +1,82 @@ +nvme-dsm(1) +=========== + +NAME +---- +nvme-dsm - Send NVMe Data Set Management, return results + +SYNOPSIS +-------- +[verse] +'nvme dsm' [ --namespace-id= | -n ] + [ --ctx-attrs= | -a ] + [ --blocks= | -b ] + [ --slbs= | -s ] + [ --ad | -d ] [ --idw | -w ] [ --idr | -r ] + [ --cdw11= | -c ] + + +DESCRIPTION +----------- +For the NVMe device given, sends an Data Set Management command and +provides the result and returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). +If the character device is given, the `'--namespace-id'` option is +mandatory, otherwise it will use the ns-id of the namespace for the block +device you opened. For block devices, the ns-id used can be overridden +with the same option. + +You must specify at least one of the values for range list. If the range +lists provided do not list the same number of elements, the default +values for the remaining in the range will be set to 0. + +The command dword 11 may be provided at the command line. For convenience, +the current defined attributes (discard, integral read/write) for a +data-set management have flags. If cdw11 is specified, this will override +any settings from the flags may have provided. + +OPTIONS +------- +-n :: +--namespace-id=:: + Sends the command with the requested nsid. This is required for the + character devices, or overrides the block nsid if given. + +-a :: +--ctx-attrs=:: + Comma separated list of the context attributes in each range + +-b :: +--blocks=:: + Comma separated list of the number of blocks in each range + +-s :: +--slbs=:: + Comma separated list of the starting block in each range + +-d :: +--ad=:: + Attribute Deallocate. + +-w :: +--idw=:: + Attribute Integral Dataset for Write. + +-r :: +--idr=:: + Attribute Integral Dataset for Read. + +-c :: +--cdw11=:: + All the command command dword 11 attribuets. Use exclusive from + speficying individual attributes + +EXAMPLES +-------- +No examples yet + +NVME +---- +Part of the nvme-user suite diff --git a/nvme.c b/nvme.c index 60a9a24b..97d9f960 100644 --- a/nvme.c +++ b/nvme.c @@ -52,6 +52,7 @@ #include "src/suffix.h" #define min(x, y) (x) > (y) ? (y) : (x) +#define max(x, y) (x) > (y) ? (x) : (y) static int fd; static struct stat nvme_stat; @@ -88,6 +89,7 @@ static const char nvme_version_string[] = NVME_VERSION; ENTRY(RESV_REGISTER, "resv-register", "Submit a Reservation Register, return results", resv_register) \ ENTRY(RESV_RELEASE, "resv-release", "Submit a Reservation Release, return results", resv_release) \ ENTRY(RESV_REPORT, "resv-report", "Submit a Reservation Report, return results", resv_report) \ + ENTRY(DSM, "dsm", "Submit a Data Set Management command, return results", dsm) \ ENTRY(FLUSH, "flush", "Submit a Flush command, return results", flush) \ ENTRY(COMPARE, "compare", "Submit a Comapre command, return results", compare) \ ENTRY(READ_CMD, "read", "Submit a read command, return results", read_cmd) \ @@ -360,7 +362,7 @@ char* nvme_feature_to_string(int feature) case NVME_FEAT_AUTO_PST: return "Autonomous Power State Transition"; case NVME_FEAT_HOST_MEM_BUF: return "Host Memory Buffer"; case NVME_FEAT_SW_PROGRESS: return "Software Progress"; - case NVME_FEAT_HOST_ID: return "Host Identifier"; + case NVME_FEAT_HOST_ID: return "Host Identifier"; case NVME_FEAT_RESV_MASK: return "Reservation Notification Mask"; case NVME_FEAT_RESV_PERSIST: return "Reservation Persistence"; default: return "Unknown"; @@ -2178,8 +2180,8 @@ static int get_feature(int argc, char **argv) {"raw-binary", "", CFG_NONE, &defaults.raw_binary, no_argument, raw_binary}, {"b", "", CFG_NONE, &defaults.raw_binary, no_argument, raw_binary}, {"cdw11", "NUM", CFG_POSITIVE, &defaults.cdw11, required_argument, cdw11}, - {"human-readable", "", CFG_NONE, &defaults.human_readable, no_argument, human_readable}, - {"H", "", CFG_NONE, &defaults.human_readable, no_argument, human_readable}, + {"human-readable", "", CFG_NONE, &defaults.human_readable, no_argument, human_readable}, + {"H", "", CFG_NONE, &defaults.human_readable, no_argument, human_readable}, {0} }; @@ -2721,6 +2723,140 @@ static int sec_send(int argc, char **argv) return err; } +static int dsm(int argc, char **argv) +{ + struct nvme_passthru_cmd cmd; + const char *desc = "dsm: The Dataset Management command is used by the host to "\ + "indicate attributes for ranges of logical blocks. This includes attributes "\ + "like frequency that data is read or written, access size, and other "\ + "information that may be used to optimize performance and reliability."; + const char *namespace_id = "name of desired namespace"; + const char *blocks = "Comma separated list of the number of blocks in each range"; + const char *starting_blocks = "Comma separated list of the starting block in each range"; + const char *context_attrs = "Comma separated list of the context attributes in each range"; + const char *ad = "Attribute Deallocate"; + const char *idw = "Attribute Integral Dataset for Write"; + const char *idr = "Attribute Integral Dataset for Read"; + const char *cdw11 = "All the command command dword 11 attribuets. Use instead of specifying individual attributes"; + + int i, err; + uint8_t nr, nc, nb, ns; + __u32 ctx_attrs[256]; + __u32 nlbs[256]; + __u64 slbas[256]; + void *buffer; + struct nvme_dsm_range *dsm; + + struct config { + char *ctx_attrs; + char *blocks; + char *slbas; + int ad; + int idw; + int idr; + __u32 cdw11; + __u32 namespace_id; + }; + struct config cfg; + + const struct config defaults = { + .ctx_attrs = "", + .blocks = "", + .slbas = "", + .namespace_id = 0, + .ad = 0, + .idw = 0, + .idr = 0, + .cdw11 = 0, + }; + + const struct argconfig_commandline_options command_line_options[] = { + {"namespace-id", "NUM", CFG_POSITIVE, &defaults.namespace_id, required_argument, namespace_id}, + {"n", "NUM", CFG_POSITIVE, &defaults.namespace_id, required_argument, namespace_id}, + {"ctx-attrs", "LIST", CFG_STRING, &defaults.ctx_attrs, required_argument, context_attrs}, + {"a", "LIST", CFG_STRING, &defaults.ctx_attrs, required_argument, context_attrs}, + {"blocks", "LIST", CFG_STRING, &defaults.blocks, required_argument, blocks}, + {"b", "LIST", CFG_STRING, &defaults.blocks, required_argument, blocks}, + {"slbs", "LIST", CFG_STRING, &defaults.slbas, required_argument, starting_blocks}, + {"s", "LIST", CFG_STRING, &defaults.slbas, required_argument, starting_blocks}, + {"ad", "FLAG", CFG_NONE, &defaults.ad, no_argument, ad}, + {"d", "FLAG", CFG_NONE, &defaults.ad, no_argument, ad}, + {"idw", "FLAG", CFG_NONE, &defaults.idw, no_argument, idw}, + {"w", "FLAG", CFG_NONE, &defaults.idw, no_argument, idw}, + {"idr", "FLAG", CFG_NONE, &defaults.idr, no_argument, idr}, + {"r", "FLAG", CFG_NONE, &defaults.idr, no_argument, idr}, + {"cdw11", "NUM", CFG_POSITIVE, &defaults.namespace_id, required_argument, cdw11}, + {"c", "NUM", CFG_POSITIVE, &defaults.namespace_id, required_argument, cdw11}, + {0} + }; + + memset(ctx_attrs, 0, sizeof(ctx_attrs)); + memset(slbas, 0, sizeof(slbas)); + memset(nlbs, 0, sizeof(nlbs)); + + argconfig_parse(argc, argv, desc, command_line_options, + &defaults, &cfg, sizeof(cfg)); + get_dev(1, argc, argv); + + nc = argconfig_parse_comma_sep_array(cfg.ctx_attrs, (int *)ctx_attrs, 256); + nb = argconfig_parse_comma_sep_array(cfg.blocks, (int *)nlbs, 256); + ns = argconfig_parse_comma_sep_array_long(cfg.slbas, slbas, 256); + nr = max(nc, max(nb, ns)); + if (!nr) { + fprintf(stderr, "No range definition provided\n"); + return EINVAL; + } + + if (!cfg.namespace_id) { + if (!S_ISBLK(nvme_stat.st_mode)) { + fprintf(stderr, + "%s: non-block device requires namespace-id param\n", + devicename); + exit(ENOTBLK); + } + cfg.namespace_id = ioctl(fd, NVME_IOCTL_ID); + if (cfg.namespace_id <= 0) { + fprintf(stderr, + "%s: failed to return namespace id\n", + devicename); + return errno; + } + } + + if (!cfg.cdw11) + cfg.cdw11 = (cfg.ad << 2) | (cfg.idw << 1) | (cfg.idr << 0); + + buffer = malloc(nr * sizeof (struct nvme_dsm_range)); + if (!buffer) + return ENOMEM; + + dsm = buffer; + for (i = 0; i < nr; i++) { + dsm[i].cattr = htole32(ctx_attrs[i]); + dsm[i].nlb = htole32(nlbs[i]); + dsm[i].slba = htole64(slbas[i]); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.nsid = cfg.namespace_id; + cmd.opcode = nvme_cmd_dsm; + cmd.addr = (__u64)((unsigned long)buffer); + cmd.data_len = nr * sizeof(struct nvme_dsm_range); + cmd.cdw10 = nr; + cmd.cdw11 = cfg.cdw11; + + err = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd); + if (err < 0) { + fprintf(stderr, "error:%x\n", err); + return errno; + } else if (err != 0) + fprintf(stderr, "NVME IO command error:%s(%x)\n", + nvme_status_to_string(err), err); + else + printf("NVMe DSM: success\n"); + return 0; +} + static int flush(int argc, char **argv) { struct nvme_passthru_cmd cmd; diff --git a/src/argconfig.c b/src/argconfig.c index a9d883fb..3641d715 100644 --- a/src/argconfig.c +++ b/src/argconfig.c @@ -491,13 +491,12 @@ unsigned argconfig_parse_comma_sep_array(char *string, int *val, char *p; if (!strlen(string) || string == NULL) - exit(1); + return 0; - tmp = malloc(strlen(string) + 1); tmp = strtok(string, ","); + if (!tmp) + return 0; - if (tmp==NULL) - exit(1); val[ret] = strtol(tmp, &p, 0); @@ -527,6 +526,40 @@ unsigned argconfig_parse_comma_sep_array(char *string, int *val, } +unsigned argconfig_parse_comma_sep_array_long(char *string, unsigned long long *val, + unsigned max_length) +{ + unsigned ret = 0; + char *tmp; + char *p; + + if (!strlen(string) || string == NULL) + return 0; + + tmp = strtok(string, ","); + if (tmp==NULL) + return 0; + + val[ret] = strtoll(tmp, &p, 0); + if (*p != 0) + exit(1); + ret++; + while(1) { + tmp = strtok(NULL, ","); + + if (tmp == NULL) + return ret; + + if (ret >= max_length) + exit(1); + + val[ret] = strtoll(tmp, &p, 0); + if (*p != 0) + exit(1); + ret++; + } +} + void argconfig_register_help_func(argconfig_help_func *f) { int i; for (i = 0; i < MAX_HELP_FUNC; i++) { diff --git a/src/argconfig.h b/src/argconfig.h index 6e779311..a6af2fd9 100644 --- a/src/argconfig.h +++ b/src/argconfig.h @@ -103,6 +103,8 @@ int argconfig_parse_subopt_string (char *string, char **options, size_t max_options); unsigned argconfig_parse_comma_sep_array(char *string,int *ret, unsigned max_length); +unsigned argconfig_parse_comma_sep_array_long(char *string, unsigned long long *ret, + unsigned max_length); void argconfig_register_help_func(argconfig_help_func * f); void argconfig_print_subopt_help(const struct argconfig_sub_options * options, -- 2.50.1