]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
NVMe: Add reservation support
authorKeith Busch <keith.busch@intel.com>
Mon, 1 Dec 2014 20:51:11 +0000 (13:51 -0700)
committerKeith Busch <keith.busch@intel.com>
Mon, 1 Dec 2014 20:51:11 +0000 (13:51 -0700)
Signed-off-by: Keith Busch <keith.busch@intel.com>
Documentation/nvme-resv-acquire.txt [new file with mode: 0644]
Documentation/nvme-resv-register.txt [new file with mode: 0644]
Documentation/nvme-resv-release.txt [new file with mode: 0644]
Documentation/nvme-resv-report.txt [new file with mode: 0644]
nvme.c

diff --git a/Documentation/nvme-resv-acquire.txt b/Documentation/nvme-resv-acquire.txt
new file mode 100644 (file)
index 0000000..56f8786
--- /dev/null
@@ -0,0 +1,87 @@
+nvme-help(1)
+=============
+
+NAME
+----
+nvme-resv-acquire - Acquire an nvme reservation
+
+SYNOPSIS
+--------
+[verse]
+'nvme resv-acquire' <device>
+
+DESCRIPTION
+-----------
+The Reservation Acquire command is used to acquire a reservation on
+a namespace, preempt a reservation held on a namespace, and abort a
+reservation held on a namespace.
+
+OPTIONS
+-------
+-n <nsid>::
+--namespace-id=<nsid>::
+       Override the nsid field. If using the admin character device,
+       this paramter is required.
+
+-c <crkey>::
+--crkey=<crkey>::
+       Current Reservation Key: The field specifies the current
+       reservation key associated with the host. If the IEKEY bit is
+       set to ‘1’ in the command, then the CRKEY check succeeds
+       regardless of the value in this field.
+
+-p <prkey>::
+--prkey=<prkey>::
+       Preempt Reservation Key: If the Reservation Acquire Action is set
+       to 001b (i.e., Preempt) or 010b (i.e., Preempt and Abort), then
+       this field specifies the reservation key to be unregistered from
+       the namespace. For all other Reservation Acquire Action values,
+       this field is reserved.
+
+-t <rtype>::
+--rtyep=<rtype>::
+       Reservation Type: This field specifies the type of reservation
+       to be created.
++
+[]
+|=================
+|Value|Definition
+|0h|Reserved 
+|1h|Write Exclusive Reservation
+|2h|Exclusive Access Reservation
+|3h|Write Exclusive - Registrants Only Reservation
+|4h|Exclusive Access - Registrants Only Reservation
+|5h|Write Exclusive - All Registrants Reservation
+|6h|Exclusive Access - All Registrants Reservation
+|07h-FFh|Reserved
+|=================
+
+-a <racqa>::
+--racqa=<racqa>::
+       Reservation Acquire Action: This field specifies the action that
+       is performed by the command.
++
+[]
+|=================
+|Value|Definition
+|0|Acquire
+|1|Preempt
+|2|Preempt and Abort
+|3-7|Reserved
+|=================
+
+-i::
+--iekey::
+       Ignore Existing Key: If this bit is set to a '1', then the
+       Current Reservation Key (CRKEY) check is disabled and the command
+       shall succeed regardless of the CRKEY field  value.
++
+Indicator option, defaults to '0'.
+
+EXAMPLES
+--------
+No examples yet
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/Documentation/nvme-resv-register.txt b/Documentation/nvme-resv-register.txt
new file mode 100644 (file)
index 0000000..420c329
--- /dev/null
@@ -0,0 +1,88 @@
+nvme-help(1)
+=============
+
+NAME
+----
+nvme-resv-register - Register an nvme reservation
+
+SYNOPSIS
+--------
+[verse]
+'nvme resv-register' <device>
+
+DESCRIPTION
+-----------
+The Reservation Register command is used to register, unregister, or
+replace a reservation key.
+
+OPTIONS
+-------
+-n <nsid>::
+--namespace-id=<nsid>::
+       Override the nsid field. If using the admin character device,
+       this paramter is required.
+
+-c <crkey>::
+--crkey=<crkey>::
+       Current Reservation Key: If the Reservation Register Action is
+       001b (i.e., Unregister Reservation Key) or 010b (i.e., Replace
+       Reservation Key), then this field contains the current reservation
+       key associated with the host. For all other Reservation Register
+       Action values, this field is reserved. The controller ignores
+       the value of this field when the Ignore Existing Key (IEKEY)
+       bit is set to ‘1’.
+
+-k <nrkey>::
+--nrkey=<nrkey>::
+       New Reservation Key: If the Reservation Register Action is
+       000b (i.e., Register Reservation Key) or 010b (i.e., Replace
+       Reservation Key), then this field contains the new reservation
+       key associated with the host. For all other Reservation Register
+       Action values, this field is reserved.
+
+-p <cptpl>::
+--cptpl=<cptpl>::
+       Change Persist Through Power Loss State: This field allows the
+       Persist Through Power Loss state associated with the namespace
+       to be modified as a side effect of processing this command.
++
+[]
+|=================
+|Value|Definition
+|0|No change to PTPL state
+|1|Reserved
+|2|Set PTPL state to ‘0’. Reservations are released and registrants
+are cleared on a power on.
+|3|Set PTPL state to ‘1’. Reservations and registrants persist across
+a power loss.
+|=================
+
+-a <rrega>::
+--rrega=<rrega>::
+       Reservation Register Action: This field specifies the registration
+       action that is performed by the command.
++
+[]
+|=================
+|Value|Definition
+|0|Register Reservation Key
+|1|Unregister Reservation Key
+|2|Replace Reservation Key
+|3-7|Reserved
+|=================
+
+-i::
+--iekey::
+       Ignore Existing Key: If this bit is set to a '1', then the
+       Current Reservation Key (CRKEY) check is disabled and the command
+       shall succeed regardless of the CRKEY field value.
++
+Indicator option, defaults to '0'.
+
+EXAMPLES
+--------
+No examples yet
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/Documentation/nvme-resv-release.txt b/Documentation/nvme-resv-release.txt
new file mode 100644 (file)
index 0000000..00747da
--- /dev/null
@@ -0,0 +1,80 @@
+nvme-help(1)
+=============
+
+NAME
+----
+nvme-resv-release - Release an nvme reservation
+
+SYNOPSIS
+--------
+[verse]
+'nvme resv-release' <device>
+
+DESCRIPTION
+-----------
+The Reservation Release command is used to release or clear a reservation
+held on a namespace.
+
+OPTIONS
+-------
+-n <nsid>::
+--namespace-id=<nsid>::
+       Override the nsid field. If using the admin character device,
+       this paramter is required.
+
+-c <crkey>::
+--crkey=<crkey>::
+       Current Reservation Key: If the Reservation Register Action is
+       001b (i.e., Unregister Reservation Key) or 010b (i.e., Replace
+       Reservation Key), then this field contains the current reservation
+       key associated with the host. For all other Reservation Register
+       Action values, this field is reserved. The controller ignores
+       the value of this field when the Ignore Existing Key (IEKEY)
+       bit is set to ‘1’.
+
+-t <rtype>::
+--rtyep=<rtype>::
+       Reservation Type: This field specifies the type of reservation
+       to be created.
++
+[]
+|=================
+|Value|Definition
+|0h|Reserved 
+|1h|Write Exclusive Reservation
+|2h|Exclusive Access Reservation
+|3h|Write Exclusive - Registrants Only Reservation
+|4h|Exclusive Access - Registrants Only Reservation
+|5h|Write Exclusive - All Registrants Reservation
+|6h|Exclusive Access - All Registrants Reservation
+|07h-FFh|Reserved
+|=================
+
+-a <rrela>::
+--rrela=<rrela>::
+       Reservation Release Action: This field specifies the registration
+       action that is performed by the command.
++
+[]
+|=================
+|Value|Definition
+|0|Release
+|1|Clear
+|2-7|Reserved
+|=================
+
+-i::
+--iekey::
+       Ignore Existing Key: If this bit is set to a '1', then the
+       Current Reservation Key (CRKEY) check is disabled and the command
+       shall succeed regardless of the CRKEY field value.
++
+Indicator option, defaults to '0'.
+
+EXAMPLES
+--------
+No examples yet
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/Documentation/nvme-resv-report.txt b/Documentation/nvme-resv-report.txt
new file mode 100644 (file)
index 0000000..fd93b87
--- /dev/null
@@ -0,0 +1,50 @@
+nvme-id-ns(1)
+=============
+
+NAME
+----
+nvme-resv-report - Send NVMe Reservation Report, parse the result
+
+SYNOPSIS
+--------
+[verse]
+'nvme resv-report' <device> [--namespace-id=<nsid> | -n <nsid>]
+                       [--numd=<num-dwords> | -d <num-dwords>]
+                       [-b | --raw-binary]
+
+DESCRIPTION
+-----------
+The Reservation Report command returns a Reservation Status data structure
+to host memory that describes the registration and reservation status
+of a namespace.
+
+The size of the Reservation Status data structure is a function of the
+number of controllers in the NVM Subsystem that are associated with
+hosts that are registrants of the namespace (i.e., there is a Registered
+Controller data structure for each such controller).
+
+OPTIONS
+-------
+-n <nsid>::
+--namespace-id=<nsid>::
+       Retrieve the reservation report structure for the given nsid. This
+       is required for the character devices, or overrides the block nsid
+       if given.
+
+-d <num-dwords>::
+--numd=<num-dwords>::
+       Specify the number of Dwords of the Reservation Status structure
+       to transfer. Defaults to 4k.
+
+-b::
+--raw-binary::
+       Print the raw buffer to stdout. Structure is not parsed by
+       program.
+
+EXAMPLES
+--------
+No examples yet.
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/nvme.c b/nvme.c
index dc068f8f1e428d59c092296962fa623a2463dab5..f92856e211e27b2b0c1ea060872fb4ff0cbdc7e5 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <getopt.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,6 +60,10 @@ static const char *devicename;
        ENTRY(IO_PASSTHRU, "io-passthru", "Submit an arbitrary IO command, return results", io_passthru) \
        ENTRY(SECURITY_SEND, "security-send", "Submit a Security Send command, return results", sec_send) \
        ENTRY(SECURITY_RECV, "security-recv", "Submit a Security Receive command, return results", sec_recv) \
+       ENTRY(RESV_ACQUIRE, "resv-acquire", "Submit a Reservation Acquire, return results", resv_acquire) \
+       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(HELP, "help", "Display this help", help)
 
 #define ENTRY(i, n, h, f) \
@@ -124,6 +129,14 @@ static void get_dev(int optind, int argc, char **argv)
        open_dev((const char *)argv[optind]);
 }
 
+static void get_long(char *optarg, __u64 *val)
+{
+       if (sscanf(optarg, "%lli", val) == 1)
+               return;
+       fprintf(stderr, "bad param for command value:%s\n", optarg);
+       exit(EINVAL);
+}
+
 static void get_int(char *optarg, __u32 *val)
 {
        if (sscanf(optarg, "%i", val) == 1)
@@ -170,6 +183,28 @@ static void show_error_log(struct nvme_error_log_page *err_log, int entries)
        }
 }
 
+static void show_nvme_resv_report(struct nvme_reservation_status *status)
+{
+       int i, regctl;
+
+       regctl = status->regctl[0] | (status->regctl[1] << 8);
+
+       printf("\nNVME Reservatation status:\n\n");
+       printf("gen       : %d\n", le32toh(status->gen));
+       printf("regctl    : %d\n", regctl);
+       printf("rtype     : %d\n", status->rtype);
+       printf("ptpls     : %d\n", status->ptpls);
+
+       for (i = 0; i < regctl; i++) {
+               printf("regctl[%d] :\n", i);
+               printf("  cntlid  : %x\n", le16toh(status->regctl_ds[i].cntlid));
+               printf("  rcsts   : %x\n", status->regctl_ds[i].rcsts);
+               printf("  hostid  : %llx\n", le64toh(status->regctl_ds[i].hostid));
+               printf("  rkey    : %llx\n", le64toh(status->regctl_ds[i].rkey));
+       }
+       printf("\n");
+}
+
 static char *fw_to_string(__u64 fw)
 {
        static char ret[9];
@@ -1201,6 +1236,299 @@ static int sec_send(int argc, char **argv)
         return err;
 }
 
+static int resv_acquire(int argc, char **argv)
+{
+       struct nvme_passthru_cmd cmd;
+        int err, opt, long_index = 0;
+       unsigned int nsid = 0;
+       unsigned char rtype = 0, racqa = 0, iekey = 0;
+       static struct option opts[] = {
+               {"namespace-id", required_argument, 0, 'n'},
+               {"crkey", required_argument, 0, 'c'},
+               {"prkey", required_argument, 0, 'p'},
+               {"rtype", required_argument, 0, 't'},
+               {"racqa", required_argument, 0, 'a'},
+               {"iekey", no_argument, 0, 'i'},
+               { 0, 0, 0, 0}
+       };
+       __u64 prkey= 0, crkey = 0, payload[2];
+
+       while ((opt = getopt_long(argc, (char **)argv, "n:c:p:t:a:i", opts,
+                                                       &long_index)) != -1) {
+               switch(opt) {
+               case 'n': get_int(optarg, &nsid); break;
+               case 'c': get_long(optarg, &crkey); break;
+               case 'p': get_long(optarg, &prkey); break;
+               case 't': get_byte(optarg, &rtype); break;
+               case 'a': get_byte(optarg, &racqa); break;
+               case 'i': iekey = 1; break;
+               default:
+                       return EINVAL;
+               }
+       }
+       get_dev(optind, argc, argv);
+
+       if (!nsid) {
+               if (!S_ISBLK(nvme_stat.st_mode)) {
+                       fprintf(stderr,
+                               "%s: non-block device requires namespace-id param\n",
+                               devicename);
+                       exit(ENOTBLK);
+               }
+               nsid = ioctl(fd, NVME_IOCTL_ID);
+               if (nsid <= 0) {
+                       fprintf(stderr,
+                               "%s: failed to return namespace id\n",
+                               devicename);
+                       return errno;
+               }
+       }
+       if (racqa > 7) {
+               fprintf(stderr, "invalid racqa:%d\n", racqa);
+               return EINVAL;
+       }
+
+       payload[0] = crkey;
+       payload[1] = prkey;
+
+       memset(&cmd, 0, sizeof(cmd));
+        cmd.opcode = nvme_cmd_resv_acquire;
+        cmd.nsid = nsid;
+        cmd.cdw10 = rtype << 8 | iekey << 3 | racqa;
+
+        cmd.addr = (__u64)payload;
+        cmd.data_len = sizeof(payload);
+
+        err = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+        if (err < 0)
+                return errno;
+        else if (err != 0)
+                fprintf(stderr, "NVME IO command error:%04x\n", err);
+        else
+                printf("NVME Reservation Acquire success\n");
+       return 0;
+}
+
+static int resv_register(int argc, char **argv)
+{
+       struct nvme_passthru_cmd cmd;
+        int err, opt, long_index = 0;
+       unsigned int nsid = 0;
+       unsigned char rrega = 0, iekey = 0, cptpl = 0;
+       static struct option opts[] = {
+               {"namespace-id", required_argument, 0, 'n'},
+               {"crkey", required_argument, 0, 'c'},
+               {"nrkey", required_argument, 0, 'k'},
+               {"rrega", required_argument, 0, 'r'},
+               {"cptpl", required_argument, 0, 'p'},
+               {"iekey", required_argument, 0, 'i'},
+               { 0, 0, 0, 0}
+       };
+       __u64 nrkey= 0, crkey = 0, payload[2];
+
+       while ((opt = getopt_long(argc, (char **)argv, "n:c:p:t:i:k:", opts,
+                                                       &long_index)) != -1) {
+               switch(opt) {
+               case 'n': get_int(optarg, &nsid); break;
+               case 'c': get_long(optarg, &crkey); break;
+               case 'k': get_long(optarg, &nrkey); break;
+               case 'r': get_byte(optarg, &rrega); break;
+               case 'p': get_byte(optarg, &cptpl); break;
+               case 'i': get_byte(optarg, &iekey); break;
+               default:
+                       return EINVAL;
+               }
+       }
+       get_dev(optind, argc, argv);
+
+       if (!nsid) {
+               if (!S_ISBLK(nvme_stat.st_mode)) {
+                       fprintf(stderr,
+                               "%s: non-block device requires namespace-id param\n",
+                               devicename);
+                       exit(ENOTBLK);
+               }
+               nsid = ioctl(fd, NVME_IOCTL_ID);
+               if (nsid <= 0) {
+                       fprintf(stderr,
+                               "%s: failed to return namespace id\n",
+                               devicename);
+                       return errno;
+               }
+       }
+       if (iekey > 1) {
+               fprintf(stderr, "invalid iekey:%d\n", iekey);
+               return EINVAL;
+       }
+       if (cptpl > 3) {
+               fprintf(stderr, "invalid cptpl:%d\n", cptpl);
+               return EINVAL;
+       }
+
+       payload[0] = crkey;
+       payload[1] = nrkey;
+
+       memset(&cmd, 0, sizeof(cmd));
+        cmd.opcode = nvme_cmd_resv_register;
+        cmd.nsid = nsid;
+       cmd.cdw10 = cptpl << 30 | iekey << 3 | rrega;
+
+        cmd.addr = (__u64)payload;
+        cmd.data_len = sizeof(payload);
+
+        err = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+        if (err < 0)
+                return errno;
+        else if (err != 0)
+                fprintf(stderr, "NVME IO command error:%04x\n", err);
+        else
+                printf("NVME Reservation  success\n");
+       return 0;
+}
+
+static int resv_release(int argc, char **argv)
+{
+       struct nvme_passthru_cmd cmd;
+        int err, opt, long_index = 0;
+       unsigned int nsid = 0;
+       unsigned char rtype = 0, rrela = 0, iekey = 0;
+       static struct option opts[] = {
+               {"namespace-id", required_argument, 0, 'n'},
+               {"crkey", required_argument, 0, 'c'},
+               {"rtype", required_argument, 0, 't'},
+               {"rrela", required_argument, 0, 'a'},
+               {"iekey", required_argument, 0, 'i'},
+               { 0, 0, 0, 0}
+       };
+       __u64 crkey = 0;
+
+       while ((opt = getopt_long(argc, (char **)argv, "n:c:p:t:a:i:", opts,
+                                                       &long_index)) != -1) {
+               switch(opt) {
+               case 'n': get_int(optarg, &nsid); break;
+               case 'c': get_long(optarg, &crkey); break;
+               case 't': get_byte(optarg, &rtype); break;
+               case 'a': get_byte(optarg, &rrela); break;
+               case 'i': get_byte(optarg, &iekey); break;
+               default:
+                       return EINVAL;
+               }
+       }
+       get_dev(optind, argc, argv);
+
+       if (!nsid) {
+               if (!S_ISBLK(nvme_stat.st_mode)) {
+                       fprintf(stderr,
+                               "%s: non-block device requires namespace-id param\n",
+                               devicename);
+                       exit(ENOTBLK);
+               }
+               nsid = ioctl(fd, NVME_IOCTL_ID);
+               if (nsid <= 0) {
+                       fprintf(stderr,
+                               "%s: failed to return namespace id\n",
+                               devicename);
+                       return errno;
+               }
+       }
+       if (iekey > 1) {
+               fprintf(stderr, "invalid iekey:%d\n", iekey);
+               return EINVAL;
+       }
+       if (rrela > 7) {
+               fprintf(stderr, "invalid rrela:%d\n", rrela);
+               return EINVAL;
+       }
+
+       memset(&cmd, 0, sizeof(cmd));
+        cmd.opcode = nvme_cmd_resv_release;
+        cmd.nsid = nsid;
+       cmd.cdw10 = rtype << 8 | iekey << 3 | rrela;
+
+        cmd.addr = (__u64)&crkey;
+        cmd.data_len = sizeof(crkey);
+
+        err = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+        if (err < 0)
+                return errno;
+        else if (err != 0)
+                fprintf(stderr, "NVME IO command error:%04x\n", err);
+        else
+                printf("NVME Reservation Register success\n");
+       return 0;
+}
+
+static int resv_report(int argc, char **argv)
+{
+       struct nvme_passthru_cmd cmd;
+        int err, opt, long_index = 0, raw = 0;
+       unsigned int nsid = 0, numd = 0x1000 > 2;
+       struct nvme_reservation_status *status;
+       static struct option opts[] = {
+               {"namespace-id", required_argument, 0, 'n'},
+               {"numd", required_argument, 0, 'd'},
+               {"raw-binary", no_argument, 0, 'b'},
+               { 0, 0, 0, 0}
+       };
+
+       while ((opt = getopt_long(argc, (char **)argv, "n:d:r", opts,
+                                                       &long_index)) != -1) {
+               switch(opt) {
+               case 'n': get_int(optarg, &nsid); break;
+               case 'd': get_int(optarg, &numd); break;
+               case 'r': raw = 0; break;
+               default:
+                       return EINVAL;
+               }
+       }
+       get_dev(optind, argc, argv);
+
+       if (!nsid) {
+               if (!S_ISBLK(nvme_stat.st_mode)) {
+                       fprintf(stderr,
+                               "%s: non-block device requires namespace-id param\n",
+                               devicename);
+                       exit(ENOTBLK);
+               }
+               nsid = ioctl(fd, NVME_IOCTL_ID);
+               if (nsid <= 0) {
+                       fprintf(stderr,
+                               "%s: failed to return namespace id\n",
+                               devicename);
+                       return errno;
+               }
+       }
+       if (!numd || numd > (0x1000 >> 2))
+               numd = 0x1000 >> 2;
+
+       if (posix_memalign((void **)&status, getpagesize(), numd << 2)) {
+               fprintf(stderr, "No memory for resv report:%d\n", numd << 2);
+               return ENOMEM;
+       }
+
+       memset(&cmd, 0, sizeof(cmd));
+        cmd.opcode = nvme_cmd_resv_report;
+        cmd.nsid = nsid;
+        cmd.cdw10 = numd;
+
+        cmd.addr = (__u64)status;
+        cmd.data_len = numd << 2;
+
+        err = ioctl(fd, NVME_IOCTL_IO_CMD, &cmd);
+        if (err < 0)
+                return errno;
+        else if (err != 0)
+                fprintf(stderr, "NVME IO command error:%04x\n", err);
+        else {
+               if (!raw) {
+                       printf("NVME Reservation Report success\n");
+                       show_nvme_resv_report(status);
+               } else
+                       d_raw(status, numd << 2);
+       }
+       return 0;
+}
+
 static int sec_recv(int argc, char **argv)
 {
        struct nvme_admin_cmd cmd;