nvme-cli: Extended Data Structure in resv-report

Schremmer, Steven Steve.Schremmer at netapp.com
Fri Jun 23 09:24:47 PDT 2017


Allow user to specify cdw11 value to request Extended Data
Structure. Update resv_report show and json functions to decode
the returned data.

Signed-off-by: Steve Schremmer <steve.schremmer at netapp.com>
---
 Documentation/nvme-resv-report.txt |  5 ++
 linux/nvme.h                       | 20 +++++++-
 nvme-ioctl.c                       |  3 +-
 nvme-ioctl.h                       |  2 +-
 nvme-print.c                       | 96 +++++++++++++++++++++++++++++---------
 nvme-print.h                       |  4 +-
 nvme.c                             | 13 ++++--
 7 files changed, 114 insertions(+), 29 deletions(-)

diff --git a/Documentation/nvme-resv-report.txt b/Documentation/nvme-resv-report.txt
index a7def3a..e207102 100644
--- a/Documentation/nvme-resv-report.txt
+++ b/Documentation/nvme-resv-report.txt
@@ -37,6 +37,11 @@ OPTIONS
 	Specify the number of Dwords of the Reservation Status structure
 	to transfer. Defaults to 4k.
 
+-c <cdw11>::
+--cdw11=<cdw11>::
+	The value for command dword 11. Setting bit 0 specifies that the 
+	controller returns the Extended Data Structure.
+
 -b::
 --raw-binary::
 	Print the raw buffer to stdout. Structure is not parsed by
diff --git a/linux/nvme.h b/linux/nvme.h
index 08bf0b1..3f4bb8d 100644
--- a/linux/nvme.h
+++ b/linux/nvme.h
@@ -369,7 +369,7 @@ struct nvme_reservation_status {
 	__u8	regctl[2];
 	__u8	resv5[2];
 	__u8	ptpls;
-	__u8	resv10[13];
+	__u8	resv10[14];
 	struct {
 		__le16	cntlid;
 		__u8	rcsts;
@@ -379,6 +379,24 @@ struct nvme_reservation_status {
 	} regctl_ds[];
 };
 
+struct nvme_reservation_status_ext {
+	__le32	gen;
+	__u8	rtype;
+	__u8	regctl[2];
+	__u8	resv5[2];
+	__u8	ptpls;
+	__u8	resv10[14];
+	__u8	resv24[40];
+	struct {
+		__le16	cntlid;
+		__u8	rcsts;
+		__u8	resv3[5];
+		__le64	rkey;
+		__u8	hostid[16];
+		__u8	resv32[32];
+	} regctl_eds[];
+};
+
 enum nvme_async_event_type {
 	NVME_AER_TYPE_ERROR	= 0,
 	NVME_AER_TYPE_SMART	= 1,
diff --git a/nvme-ioctl.c b/nvme-ioctl.c
index a22399a..d7c5911 100644
--- a/nvme-ioctl.c
+++ b/nvme-ioctl.c
@@ -300,12 +300,13 @@ int nvme_resv_release(int fd, __u32 nsid, __u8 rtype, __u8 rrela,
 	return nvme_submit_io_passthru(fd, &cmd);
 }
 
-int nvme_resv_report(int fd, __u32 nsid, __u32 numd, void *data)
+int nvme_resv_report(int fd, __u32 nsid, __u32 numd, __u32 cdw11, void *data)
 {
 	struct nvme_passthru_cmd cmd = {
 		.opcode		= nvme_cmd_resv_report,
 		.nsid		= nsid,
 		.cdw10		= numd,
+		.cdw11		= cdw11,
 		.addr		= (__u64)(uintptr_t) data,
 		.data_len	= numd << 2,
 	};
diff --git a/nvme-ioctl.h b/nvme-ioctl.h
index 3beddf8..f11756d 100644
--- a/nvme-ioctl.h
+++ b/nvme-ioctl.h
@@ -62,7 +62,7 @@ int nvme_resv_register(int fd, __u32 nsid, __u8 rrega, __u8 cptpl,
 		       bool iekey, __u64 crkey, __u64 nrkey);
 int nvme_resv_release(int fd, __u32 nsid, __u8 rtype, __u8 rrela,
 		      bool iekey, __u64 crkey);
-int nvme_resv_report(int fd, __u32 nsid, __u32 numd, void *data);
+int nvme_resv_report(int fd, __u32 nsid, __u32 numd, __u32 cdw11, void *data);
 
 /* NVME_ADMIN_CMD */
 int nvme_passthru_admin(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
diff --git a/nvme-print.c b/nvme-print.c
index c08f312..4992a95 100644
--- a/nvme-print.c
+++ b/nvme-print.c
@@ -799,29 +799,53 @@ void show_error_log(struct nvme_error_log_page *err_log, int entries, const char
 	}
 }
 
-void show_nvme_resv_report(struct nvme_reservation_status *status)
+void show_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __u32 cdw11)
 {
-	int i, regctl;
+	int i, j, regctl, entries;
 
 	regctl = status->regctl[0] | (status->regctl[1] << 8);
 
 	printf("\nNVME Reservation status:\n\n");
 	printf("gen       : %d\n", le32_to_cpu(status->gen));
-	printf("regctl    : %d\n", regctl);
 	printf("rtype     : %d\n", status->rtype);
+	printf("regctl    : %d\n", regctl);
 	printf("ptpls     : %d\n", status->ptpls);
 
-	for (i = 0; i < regctl; i++) {
-		printf("regctl[%d] :\n", i);
-		printf("  cntlid  : %x\n", le16_to_cpu(status->regctl_ds[i].cntlid));
-		printf("  rcsts   : %x\n", status->regctl_ds[i].rcsts);
-		printf("  hostid  : %"PRIx64"\n", (uint64_t)le64_to_cpu(status->regctl_ds[i].hostid));
-		printf("  rkey    : %"PRIx64"\n", (uint64_t)le64_to_cpu(status->regctl_ds[i].rkey));
+	/* check Extended Data Structure bit */
+	if ((cdw11 & 0x1) == 0) {
+		/* if status buffer was too small, don't loop past the end of the buffer */
+		entries = (bytes - 24) / 24;
+		if (entries < regctl)
+			regctl = entries;
+
+		for (i = 0; i < regctl; i++) {
+			printf("regctl[%d] :\n", i);
+			printf("  cntlid  : %x\n", le16_to_cpu(status->regctl_ds[i].cntlid));
+			printf("  rcsts   : %x\n", status->regctl_ds[i].rcsts);
+			printf("  hostid  : %"PRIx64"\n", (uint64_t)le64_to_cpu(status->regctl_ds[i].hostid));
+			printf("  rkey    : %"PRIx64"\n", (uint64_t)le64_to_cpu(status->regctl_ds[i].rkey));
+		}
+	} else {
+		struct nvme_reservation_status_ext *ext_status = (struct nvme_reservation_status_ext *)status;
+		/* if status buffer was too small, don't loop past the end of the buffer */
+		entries = (bytes - 64) / 64;
+		if (entries < regctl)
+			regctl = entries;
+
+		for (i = 0; i < regctl; i++) {
+			printf("regctlext[%d] :\n", i);
+			printf("  cntlid     : %x\n", le16_to_cpu(ext_status->regctl_eds[i].cntlid));
+			printf("  rcsts      : %x\n", ext_status->regctl_eds[i].rcsts);
+			printf("  rkey       : %"PRIx64"\n", (uint64_t)le64_to_cpu(ext_status->regctl_eds[i].rkey));
+			printf("  hostid     : ");
+			for (j = 0; j < 16; j++)
+				printf("%x", ext_status->regctl_eds[i].hostid[j]);
+			printf("\n");
+		}
 	}
 	printf("\n");
 }
 
-
 static char *fw_to_string(__u64 fw)
 {
 	static char ret[9];
@@ -1456,33 +1480,63 @@ void json_error_log(struct nvme_error_log_page *err_log, int entries, const char
 	json_free_object(root);
 }
 
-void json_nvme_resv_report(struct nvme_reservation_status *status)
+void json_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __u32 cdw11)
 {
 	struct json_object *root;
 	struct json_array *rcs;
-	int i, regctl;
+	int i, j, regctl, entries;
 
 	regctl = status->regctl[0] | (status->regctl[1] << 8);
 
 	root = json_create_object();
 
 	json_object_add_value_int(root, "gen", le32_to_cpu(status->gen));
-	json_object_add_value_int(root, "regctl", regctl);
 	json_object_add_value_int(root, "rtype", status->rtype);
+	json_object_add_value_int(root, "regctl", regctl);
 	json_object_add_value_int(root, "ptpls", status->ptpls);
 
 	rcs = json_create_array();
-	json_object_add_value_array(root, "regctls", rcs);
+        /* check Extended Data Structure bit */
+        if ((cdw11 & 0x1) == 0) {
+                /* if status buffer was too small, don't loop past the end of the buffer */
+                entries = (bytes - 24) / 24;
+                if (entries < regctl)
+                        regctl = entries;
+
+		json_object_add_value_array(root, "regctls", rcs);
+		for (i = 0; i < regctl; i++) {
+			struct json_object *rc = json_create_object();
+
+			json_object_add_value_int(rc, "cntlid", le16_to_cpu(status->regctl_ds[i].cntlid));
+			json_object_add_value_int(rc, "rcsts", status->regctl_ds[i].rcsts);
+			json_object_add_value_int(rc, "hostid", (uint64_t)le64_to_cpu(status->regctl_ds[i].hostid));
+			json_object_add_value_int(rc, "rkey", (uint64_t)le64_to_cpu(status->regctl_ds[i].rkey));
+
+			json_array_add_value_object(rcs, rc);
+		}
+	} else {
+		struct nvme_reservation_status_ext *ext_status = (struct nvme_reservation_status_ext *)status;
+		char	hostid[33];
 
-	for (i = 0; i < regctl; i++) {
-		struct json_object *rc = json_create_object();
+                /* if status buffer was too small, don't loop past the end of the buffer */
+                entries = (bytes - 64) / 64;
+                if (entries < regctl)
+                        regctl = entries;
 
-		json_object_add_value_int(rc, "cntlid", le16_to_cpu(status->regctl_ds[i].cntlid));
-		json_object_add_value_int(rc, "rcsts", status->regctl_ds[i].rcsts);
-		json_object_add_value_int(rc, "hostid", (uint64_t)le64_to_cpu(status->regctl_ds[i].hostid));
-		json_object_add_value_int(rc, "rkey", (uint64_t)le64_to_cpu(status->regctl_ds[i].rkey));
+		json_object_add_value_array(root, "regctlext", rcs);
+		for (i = 0; i < regctl; i++) {
+			struct json_object *rc = json_create_object();
 
-		json_array_add_value_object(rcs, rc);
+			json_object_add_value_int(rc, "cntlid", le16_to_cpu(ext_status->regctl_eds[i].cntlid));
+			json_object_add_value_int(rc, "rcsts", ext_status->regctl_eds[i].rcsts);
+			json_object_add_value_int(rc, "rkey", (uint64_t)le64_to_cpu(ext_status->regctl_eds[i].rkey));
+			for (j = 0; j < 16; j++)
+				sprintf(hostid + j * 2, "%02x", ext_status->regctl_eds[i].hostid[j]);
+
+			json_object_add_value_string(rc, "hostid", hostid);
+
+			json_array_add_value_object(rcs, rc);
+		}
 	}
 
 	json_print_object(root, NULL);
diff --git a/nvme-print.h b/nvme-print.h
index 0502d0d..ce19876 100644
--- a/nvme-print.h
+++ b/nvme-print.h
@@ -20,7 +20,7 @@ uint64_t int48_to_long(__u8 *data);
 void __show_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, void (*vendor_show)(__u8 *vs, struct json_object *root));
 void show_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode);
 void show_nvme_id_ns(struct nvme_id_ns *ns, unsigned int flags);
-void show_nvme_resv_report(struct nvme_reservation_status *status);
+void show_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __u32 cdw11);
 void show_lba_range(struct nvme_lba_range_type *lbrt, int nr_ranges);
 void show_error_log(struct nvme_error_log_page *err_log, int entries, const char *devname);
 void show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname);
@@ -34,7 +34,7 @@ char *nvme_feature_to_string(int feature);
 
 void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, void (*vendor_show)(__u8 *vs, struct json_object *root));
 void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int flags);
-void json_nvme_resv_report(struct nvme_reservation_status *status);
+void json_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __u32 cdw11);
 void json_error_log(struct nvme_error_log_page *err_log, int entries, const char *devname);
 void json_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname);
 void json_fw_log(struct nvme_firmware_log_page *fw_log, const char *devname);
diff --git a/nvme.c b/nvme.c
index 515d135..14886fc 100644
--- a/nvme.c
+++ b/nvme.c
@@ -2144,6 +2144,7 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin
 		"namespace.";
 	const char *namespace_id = "identifier of desired namespace";
 	const char *numd = "number of dwords to transfer";
+	const char *cdw11 = "command dword 11 value";
 	const char *raw_binary = "dump output in binary format";
 
 	int err, fmt, fd;
@@ -2152,6 +2153,7 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin
 	struct config {
 		__u32 namespace_id;
 		__u32 numd;
+		__u32 cdw11;
 		int   raw_binary;
 		char *output_format;
 	};
@@ -2159,12 +2161,14 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin
 	struct config cfg = {
 		.namespace_id = 0,
 		.numd         = 0,
+		.cdw11	      = 0,
 		.output_format = "normal",
 	};
 
 	const struct argconfig_commandline_options command_line_options[] = {
 		{"namespace-id",  'n', "NUM", CFG_POSITIVE, &cfg.namespace_id,  required_argument, namespace_id},
 		{"numd",          'd', "NUM", CFG_POSITIVE, &cfg.numd,          required_argument, numd},
+		{"cdw11",         'c', "NUM", CFG_POSITIVE, &cfg.cdw11,         required_argument, cdw11},
 		{"raw-binary",    'b', "",    CFG_NONE,     &cfg.raw_binary,    no_argument,       raw_binary},
 		{"output-format", 'o', "FMT", CFG_STRING,   &cfg.output_format, required_argument, output_format },
 		{NULL}
@@ -2184,13 +2188,16 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin
 		cfg.namespace_id = get_nsid(fd);
 	if (!cfg.numd || cfg.numd > (0x1000 >> 2))
 		cfg.numd = 0x1000 >> 2;
+	if (cfg.numd < 3)
+		cfg.numd = 3; /* get the header fields at least */
 
 	if (posix_memalign((void **)&status, getpagesize(), cfg.numd << 2)) {
 		fprintf(stderr, "No memory for resv report:%d\n", cfg.numd << 2);
 		return ENOMEM;
 	}
+	memset(status, 0, cfg.numd << 2);
 
-	err = nvme_resv_report(fd, cfg.namespace_id, cfg.numd, status);
+	err = nvme_resv_report(fd, cfg.namespace_id, cfg.numd, cfg.cdw11, status);
 	if (err < 0)
 		perror("reservation report");
 	else if (err != 0)
@@ -2199,10 +2206,10 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin
 		if (fmt == BINARY)
 			d_raw((unsigned char *)status, cfg.numd << 2);
 		else if (fmt == JSON)
-			json_nvme_resv_report(status);
+			json_nvme_resv_report(status, cfg.numd << 2, cfg.cdw11);
 		else {
 			printf("NVME Reservation Report success\n");
-			show_nvme_resv_report(status);
+			show_nvme_resv_report(status, cfg.numd << 2, cfg.cdw11);
 		}
 	}
 	free(status);
-- 
2.12.2




More information about the Linux-nvme mailing list