nvdimm.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [ndctl PATCH 0/2] Be kind to humans
@ 2017-07-07 20:37 Dan Williams
  2017-07-07 20:37 ` [ndctl PATCH 1/2] ndctl, list: convert json control flags to bit-flags Dan Williams
  2017-07-07 20:37 ` [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default Dan Williams
  0 siblings, 2 replies; 11+ messages in thread
From: Dan Williams @ 2017-07-07 20:37 UTC (permalink / raw)
  To: linux-nvdimm; +Cc: Yasunori Goto

Prompted by Yasunori's patch to add a few more fields to the "ndctl list
--dimm" output we realized it would be nice to have listings be more
user-friendly by default. These patches take advantage of json-c's
ability to specify a custom object serializer so that we can select
'human' vs 'machine' friendly output.

This of course opens the door to things like colorized output to reflect
the health state of a dimm.

---

Dan Williams (2):
      ndctl, list: convert json control flags to bit-flags
      ndctl, daxctl, list: make terminal output human readable by default


 Documentation/daxctl/daxctl-list.txt |   21 ++++
 Documentation/ndctl/ndctl-list.txt   |   30 ++++++
 daxctl/list.c                        |   36 +++++++-
 ndctl/list.c                         |   59 ++++++++++--
 ndctl/namespace.c                    |    6 +
 util/json.c                          |  162 ++++++++++++++++++++++++++--------
 util/json.h                          |   25 ++++-
 7 files changed, 279 insertions(+), 60 deletions(-)
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [ndctl PATCH 1/2] ndctl, list: convert json control flags to bit-flags
  2017-07-07 20:37 [ndctl PATCH 0/2] Be kind to humans Dan Williams
@ 2017-07-07 20:37 ` Dan Williams
  2017-07-07 20:37 ` [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default Dan Williams
  1 sibling, 0 replies; 11+ messages in thread
From: Dan Williams @ 2017-07-07 20:37 UTC (permalink / raw)
  To: linux-nvdimm

Before we go to add new flags to pass through the json helpers, convert the
existing control flags to bit flags.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 daxctl/list.c     |   17 ++++++++++++++---
 ndctl/list.c      |   25 ++++++++++++++++++-------
 ndctl/namespace.c |    3 ++-
 util/json.c       |   54 ++++++++++++++++++++++++++---------------------------
 util/json.h       |   14 ++++++++++----
 5 files changed, 70 insertions(+), 43 deletions(-)

diff --git a/daxctl/list.c b/daxctl/list.c
index 3e3f822b0ea1..e213df138f07 100644
--- a/daxctl/list.c
+++ b/daxctl/list.c
@@ -28,6 +28,17 @@ static struct {
 	bool idle;
 } list;
 
+static unsigned long listopts_to_flags(void)
+{
+	unsigned long flags = 0;
+
+	if (list.devs)
+		flags |= UTIL_JSON_DAX;
+	if (list.idle)
+		flags |= UTIL_JSON_IDLE;
+	return flags;
+}
+
 static struct {
 	const char *dev;
 	int region_id;
@@ -102,15 +113,15 @@ int cmd_list(int argc, const char **argv, void *ctx)
 			}
 
 			jregion = util_daxctl_region_to_json(region,
-					list.devs, param.dev, list.idle);
+					param.dev, listopts_to_flags());
 			if (!jregion) {
 				fail("\n");
 				continue;
 			}
 			json_object_array_add(jregions, jregion);
 		} else if (list.devs)
-			jdevs = util_daxctl_devs_to_list(region,
-					jdevs, param.dev, list.idle);
+			jdevs = util_daxctl_devs_to_list(region, jdevs,
+					param.dev, listopts_to_flags());
 	}
 
 	if (jregions)
diff --git a/ndctl/list.c b/ndctl/list.c
index ad710ed70de5..7ecfcc91b0ec 100644
--- a/ndctl/list.c
+++ b/ndctl/list.c
@@ -39,6 +39,19 @@ static struct {
 	bool media_errors;
 } list;
 
+static unsigned long listopts_to_flags(void)
+{
+	unsigned long flags = 0;
+
+	if (list.idle)
+		flags |= UTIL_JSON_IDLE;
+	if (list.media_errors)
+		flags |= UTIL_JSON_MEDIA_ERRORS;
+	if (list.dax)
+		flags |= UTIL_JSON_DAX;
+	return flags;
+}
+
 static struct {
 	const char *bus;
 	const char *region;
@@ -112,8 +125,7 @@ static struct json_object *list_namespaces(struct ndctl_region *region,
 						jnamespaces);
 		}
 
-		jndns = util_namespace_to_json(ndns, list.idle, list.dax,
-				list.media_errors);
+		jndns = util_namespace_to_json(ndns, listopts_to_flags());
 		if (!jndns) {
 			fail("\n");
 			continue;
@@ -132,7 +144,7 @@ static struct json_object *list_namespaces(struct ndctl_region *region,
 }
 
 static struct json_object *region_to_json(struct ndctl_region *region,
-		bool include_media_errors)
+		unsigned long flags)
 {
 	struct json_object *jregion = json_object_new_object();
 	struct json_object *jobj, *jbbs, *jmappings = NULL;
@@ -219,8 +231,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 		json_object_object_add(jregion, "state", jobj);
 	}
 
-	jbbs = util_region_badblocks_to_json(region, include_media_errors,
-			&bb_count);
+	jbbs = util_region_badblocks_to_json(region, &bb_count, flags);
 	if (bb_count) {
 		jobj = json_object_new_int(bb_count);
 		if (!jobj) {
@@ -229,7 +240,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 		}
 		json_object_object_add(jregion, "badblock_count", jobj);
 	}
-	if (include_media_errors && jbbs)
+	if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs)
 		json_object_object_add(jregion, "badblocks", jbbs);
 
 	list_namespaces(region, jregion, NULL, false);
@@ -435,7 +446,7 @@ int cmd_list(int argc, const char **argv, void *ctx)
 							jregions);
 			}
 
-			jregion = region_to_json(region, list.media_errors);
+			jregion = region_to_json(region, listopts_to_flags());
 			if (!jregion) {
 				fail("\n");
 				continue;
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index 778a65d02c2a..b28936cfd3ec 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -397,7 +397,8 @@ static int setup_namespace(struct ndctl_region *region,
 		error("%s: failed to enable\n",
 				ndctl_namespace_get_devname(ndns));
 	} else {
-		struct json_object *jndns = util_namespace_to_json(ndns, 0, 1, 0);
+		struct json_object *jndns = util_namespace_to_json(ndns,
+				UTIL_JSON_DAX);
 
 		if (jndns)
 			printf("%s\n", json_object_to_json_string_ext(jndns,
diff --git a/util/json.c b/util/json.c
index a9b733a76f05..1863bca10121 100644
--- a/util/json.c
+++ b/util/json.c
@@ -161,7 +161,8 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
 }
 
 struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
-		struct json_object *jdevs, const char *ident, bool include_idle)
+		struct json_object *jdevs, const char *ident,
+		unsigned long flags)
 {
 	struct daxctl_dev *dev;
 
@@ -171,7 +172,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 		if (!util_daxctl_dev_filter(dev, ident))
 			continue;
 
-		if (!include_idle && !daxctl_dev_get_size(dev))
+		if (!(flags & UTIL_JSON_IDLE) && !daxctl_dev_get_size(dev))
 			continue;
 
 		if (!jdevs) {
@@ -193,7 +194,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 }
 
 struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
-		bool include_devs, const char *ident, bool include_idle)
+		const char *ident, unsigned long flags)
 {
 	unsigned long align;
 	struct json_object *jregion, *jobj;
@@ -232,10 +233,10 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 		json_object_object_add(jregion, "align", jobj);
 	}
 
-	if (!include_devs)
+	if (!(flags & UTIL_JSON_DAX))
 		return jregion;
 
-	jobj = util_daxctl_devs_to_list(region, NULL, ident, include_idle);
+	jobj = util_daxctl_devs_to_list(region, NULL, ident, flags);
 	if (jobj)
 		json_object_object_add(jregion, "devices", jobj);
 
@@ -246,20 +247,20 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 }
 
 struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
-		bool include_media_errors, unsigned int *bb_count)
+		unsigned int *bb_count, unsigned long flags)
 {
 	struct json_object *jbb = NULL, *jbbs = NULL, *jobj;
 	struct badblock *bb;
 	int bbs = 0;
 
-	if (include_media_errors) {
+	if (flags & UTIL_JSON_MEDIA_ERRORS) {
 		jbbs = json_object_new_array();
 		if (!jbbs)
 			return NULL;
 	}
 
 	ndctl_region_badblock_foreach(region, bb) {
-		if (include_media_errors) {
+		if (flags & UTIL_JSON_MEDIA_ERRORS) {
 			jbb = json_object_new_object();
 			if (!jbb)
 				goto err_array;
@@ -294,7 +295,7 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
 
 static struct json_object *dev_badblocks_to_json(struct ndctl_region *region,
 		unsigned long long dev_begin, unsigned long long dev_size,
-		bool include_media_errors, unsigned int *bb_count)
+		unsigned int *bb_count, unsigned long flags)
 {
 	struct json_object *jbb = NULL, *jbbs = NULL, *jobj;
 	unsigned long long region_begin, dev_end, offset;
@@ -307,7 +308,7 @@ static struct json_object *dev_badblocks_to_json(struct ndctl_region *region,
 
 	dev_end = dev_begin + dev_size - 1;
 
-	if (include_media_errors) {
+	if (flags & UTIL_JSON_MEDIA_ERRORS) {
 		jbbs = json_object_new_array();
 		if (!jbbs)
 			return NULL;
@@ -335,7 +336,7 @@ static struct json_object *dev_badblocks_to_json(struct ndctl_region *region,
 		offset = (begin - dev_begin) >> 9;
 		len = (end - begin + 1) >> 9;
 
-		if (include_media_errors) {
+		if (flags & UTIL_JSON_MEDIA_ERRORS) {
 			/* add to json */
 			jbb = json_object_new_object();
 			if (!jbb)
@@ -369,7 +370,7 @@ static struct json_object *dev_badblocks_to_json(struct ndctl_region *region,
 }
 
 static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn,
-		bool include_media_errors, unsigned int *bb_count)
+		unsigned int *bb_count, unsigned long flags)
 {
 	struct ndctl_region *region = ndctl_pfn_get_region(pfn);
 	unsigned long long pfn_begin, pfn_size;
@@ -383,7 +384,7 @@ static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn,
 		return NULL;
 
 	return dev_badblocks_to_json(region, pfn_begin, pfn_size,
-			include_media_errors, bb_count);
+			bb_count, flags);
 }
 
 static void util_btt_badblocks_to_json(struct ndctl_btt *btt,
@@ -409,11 +410,11 @@ static void util_btt_badblocks_to_json(struct ndctl_btt *btt,
 	 * FIXME: switch to native BTT badblocks representation
 	 * when / if the kernel provides it.
 	 */
-	dev_badblocks_to_json(region, begin, size, false, bb_count);
+	dev_badblocks_to_json(region, begin, size, bb_count, 0);
 }
 
 static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax,
-		bool include_media_errors, unsigned int *bb_count)
+		unsigned int *bb_count, unsigned long flags)
 {
 	struct ndctl_region *region = ndctl_dax_get_region(dax);
 	unsigned long long dax_begin, dax_size;
@@ -427,12 +428,11 @@ static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax,
 		return NULL;
 
 	return dev_badblocks_to_json(region, dax_begin, dax_size,
-			include_media_errors, bb_count);
+			bb_count, flags);
 }
 
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
-		bool include_idle, bool include_dax,
-		bool include_media_errors)
+		unsigned long flags)
 {
 	struct json_object *jndns = json_object_new_object();
 	struct json_object *jobj, *jbbs = NULL;
@@ -526,10 +526,10 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		if (!jobj)
 			goto err;
 		json_object_object_add(jndns, "uuid", jobj);
-		if (include_dax) {
+		if (flags & UTIL_JSON_DAX) {
 			dax_region = ndctl_dax_get_daxctl_region(dax);
-			jobj = util_daxctl_region_to_json(dax_region,
-					true, NULL, include_idle);
+			jobj = util_daxctl_region_to_json(dax_region, NULL,
+					flags);
 			if (jobj)
 				json_object_object_add(jndns, "daxregion", jobj);
 		}
@@ -576,17 +576,15 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 	}
 
 	if (pfn)
-		jbbs = util_pfn_badblocks_to_json(pfn, include_media_errors,
-				&bb_count);
+		jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags);
 	else if (dax)
-		jbbs = util_dax_badblocks_to_json(dax, include_media_errors,
-				&bb_count);
+		jbbs = util_dax_badblocks_to_json(dax, &bb_count, flags);
 	else if (btt)
 		util_btt_badblocks_to_json(btt, &bb_count);
 	else
 		jbbs = util_region_badblocks_to_json(
-				ndctl_namespace_get_region(ndns),
-				include_media_errors, &bb_count);
+				ndctl_namespace_get_region(ndns), &bb_count,
+				flags);
 
 	if (bb_count) {
 		jobj = json_object_new_int(bb_count);
@@ -597,7 +595,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		json_object_object_add(jndns, "badblock_count", jobj);
 	}
 
-	if (include_media_errors && jbbs)
+	if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs)
 		json_object_object_add(jndns, "badblocks", jbbs);
 
 	return jndns;
diff --git a/util/json.h b/util/json.h
index 966478466313..19d5ffc4376e 100644
--- a/util/json.h
+++ b/util/json.h
@@ -16,23 +16,29 @@
 #include <stdbool.h>
 #include <ndctl/libndctl.h>
 
+enum util_json_flags {
+	UTIL_JSON_IDLE = (1 << 0),
+	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
+	UTIL_JSON_DAX = (1 << 2),
+};
+
 struct json_object;
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
 struct json_object *util_bus_to_json(struct ndctl_bus *bus);
 struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
 struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
-		bool include_idle, bool include_dax, bool include_media_errs);
+		unsigned long flags);
 struct daxctl_region;
 struct daxctl_dev;
 struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
-		bool include_media_errors, unsigned int *bb_count);
+		unsigned int *bb_count, unsigned long flags);
 struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
-		bool include_devs, const char *ident, bool include_idle);
+		const char *ident, unsigned long flags);
 struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
 struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 		struct json_object *jdevs, const char *ident,
-		bool include_idle);
+		unsigned long flags);
 #ifdef HAVE_NDCTL_SMART
 struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
 #else

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default
  2017-07-07 20:37 [ndctl PATCH 0/2] Be kind to humans Dan Williams
  2017-07-07 20:37 ` [ndctl PATCH 1/2] ndctl, list: convert json control flags to bit-flags Dan Williams
@ 2017-07-07 20:37 ` Dan Williams
  2017-07-10  6:56   ` Yasunori Goto
  1 sibling, 1 reply; 11+ messages in thread
From: Dan Williams @ 2017-07-07 20:37 UTC (permalink / raw)
  To: linux-nvdimm; +Cc: Yasunori Goto

The json output format of the 'list' commands is meant to make it easy
to ingest the data into other tools. However, for direct administrator
use of the utility it now offers human friendly formatting by default.

    Before:
    # ndctl list --region=7
    {
      "dev":"region7",
      "size":67108864,
      "available_size":67108864,
      "type":"pmem",
      "iset_id":-6382611090938810793,
      "badblock_count":8
    }

    After:
    # ndctl list --region=7
    {
      "dev":"region7",
      "size":"64.00 MiB",
      "available_size":"64.00 MiB",
      "type":"pmem",
      "iset_id":"0xa76c6907811fae57",
      "badblock_count":8
    }

The old behavior is still the default when stdout is a pipe and the
output can be forced into that mode with the --format=machine option.

Reported-by: Linda Knippers <linda.knippers@hpe.com>
Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 Documentation/daxctl/daxctl-list.txt |   21 +++++++
 Documentation/ndctl/ndctl-list.txt   |   30 +++++++++
 daxctl/list.c                        |   19 ++++++
 ndctl/list.c                         |   34 +++++++++--
 ndctl/namespace.c                    |    7 ++
 util/json.c                          |  108 +++++++++++++++++++++++++++++++---
 util/json.h                          |   11 +++
 7 files changed, 211 insertions(+), 19 deletions(-)

diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
index 6de8d828de27..8cae0cd050fe 100644
--- a/Documentation/daxctl/daxctl-list.txt
+++ b/Documentation/daxctl/daxctl-list.txt
@@ -72,6 +72,27 @@ OPTIONS
 -i::
 --idle::
 	Include idle (not enabled / zero-sized) devices in the listing
+-f::
+--format=::
+	By default 'daxctl list' will detect if it is being run
+	interactively (stdout is a tty) and convert some JSON integer
+	values to more human readable strings. The --format=machine
+	option overrides that default and prints the raw decimal values
+	for all numbers, as would be the case if stdout was a pipe.
+	Specifying --format=human allow piping human friendly output to
+	a file.  Example:
+
+[verse]
+# daxctl list
+{
+  "chardev":"dax1.0",
+  "size":"30.57 GiB"
+}
+# daxctl list --format=machine
+{
+  "chardev":"dax1.0",
+  "size":32828817408
+}
 
 COPYRIGHT
 ---------
diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
index fd67d2b3e0ba..e9e9bcee54a8 100644
--- a/Documentation/ndctl/ndctl-list.txt
+++ b/Documentation/ndctl/ndctl-list.txt
@@ -180,6 +180,36 @@ include::xable-region-options.txt[]
   ]
 }
 
+-f::
+--format=::
+	By default 'ndctl list' will detect if it is being run
+	interactively (stdout is a tty) and convert some JSON integer
+	values to more human readable strings. The --format=machine
+	option overrides that default and prints the raw decimal values
+	for all numbers, as would be the case if stdout was a pipe.
+	Specifying --format=human allows piping human friendly output to
+	a file.  Example:
+
+[verse]
+# ndctl list --region=7
+{
+  "dev":"region7",
+  "size":"64.00 MiB",
+  "available_size":"64.00 MiB",
+  "type":"pmem",
+  "iset_id":"0xa76c6907811fae57",
+  "badblock_count":8
+}
+# ndctl list --format=machine --region=7
+{
+  "dev":"region7",
+  "size":67108864,
+  "available_size":67108864,
+  "type":"pmem",
+  "iset_id":-6382611090938810793,
+  "badblock_count":8
+}
+
 COPYRIGHT
 ---------
 Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
diff --git a/daxctl/list.c b/daxctl/list.c
index e213df138f07..5db216def63d 100644
--- a/daxctl/list.c
+++ b/daxctl/list.c
@@ -26,6 +26,7 @@ static struct {
 	bool devs;
 	bool regions;
 	bool idle;
+	bool tty;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -36,14 +37,18 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_DAX;
 	if (list.idle)
 		flags |= UTIL_JSON_IDLE;
+	if (list.tty)
+		flags |= UTIL_JSON_TTY;
 	return flags;
 }
 
 static struct {
 	const char *dev;
+	const char *format;
 	int region_id;
 } param = {
 	.region_id = -1,
+	.format = "auto",
 };
 
 static int did_fail;
@@ -70,6 +75,9 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
 		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
+		OPT_STRING('f', "format", &param.format, "format-mode",
+				"select \'human\' vs \'machine\' friendly number formats "
+				" (default: \'auto\')"),
 		OPT_END(),
 	};
 	const char * const u[] = {
@@ -96,6 +104,17 @@ int cmd_list(int argc, const char **argv, void *ctx)
 	if (num_list_flags() == 0)
 		list.devs = true;
 
+	if (strcmp(param.format, "auto") == 0)
+		list.tty = isatty(1);
+	else if (strcmp(param.format, "human") == 0)
+		list.tty = true;
+	else if (strcmp(param.format, "machine") == 0)
+		list.tty = false;
+	else {
+		error("unknown format type \"%s\"\n", param.format);
+		usage_with_options(u, options);
+	}
+
 	daxctl_region_foreach(ctx, region) {
 		struct json_object *jregion = NULL;
 
diff --git a/ndctl/list.c b/ndctl/list.c
index 7ecfcc91b0ec..fe1bed3c3a24 100644
--- a/ndctl/list.c
+++ b/ndctl/list.c
@@ -37,6 +37,7 @@ static struct {
 	bool health;
 	bool dax;
 	bool media_errors;
+	bool tty;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_MEDIA_ERRORS;
 	if (list.dax)
 		flags |= UTIL_JSON_DAX;
+	if (list.tty)
+		flags |= UTIL_JSON_TTY;
 	return flags;
 }
 
@@ -59,7 +62,10 @@ static struct {
 	const char *dimm;
 	const char *mode;
 	const char *namespace;
-} param;
+	const char *format;
+} param = {
+	.format = "auto",
+};
 
 static int did_fail;
 static int jflag = JSON_C_TO_STRING_PRETTY;
@@ -160,12 +166,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 		goto err;
 	json_object_object_add(jregion, "dev", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_size(region));
+	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "size", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
+	jobj = util_json_object_size(ndctl_region_get_available_size(region),
+			flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "available_size", jobj);
@@ -186,8 +193,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 
 	iset = ndctl_region_get_interleave_set(region);
 	if (iset) {
-		jobj = json_object_new_int64(
-				ndctl_interleave_set_get_cookie(iset));
+		jobj = util_json_object_hex(
+				ndctl_interleave_set_get_cookie(iset), flags);
 		if (!jobj)
 			fail("\n");
 		else
@@ -216,7 +223,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 			json_object_object_add(jregion, "mappings", jmappings);
 		}
 
-		jmapping = util_mapping_to_json(mapping);
+		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
 		if (!jmapping) {
 			fail("\n");
 			continue;
@@ -282,6 +289,9 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
 		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
 				"include media errors"),
+		OPT_STRING('f', "format", &param.format, "format-mode",
+				"select \'human\' vs \'machine\' friendly number formats "
+				" (default: \'auto\')"),
 		OPT_END(),
 	};
 	const char * const u[] = {
@@ -332,6 +342,18 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		return -EINVAL;
 	}
 
+	if (strcmp(param.format, "auto") == 0)
+		list.tty = isatty(1);
+	else if (strcmp(param.format, "human") == 0)
+		list.tty = true;
+	else if (strcmp(param.format, "machine") == 0)
+		list.tty = false;
+	else {
+		error("unknown format type \"%s\"\n", param.format);
+		usage_with_options(u, options);
+	}
+
+
 	ndctl_bus_foreach(ctx, bus) {
 		struct json_object *jbus = NULL;
 		struct ndctl_region *region;
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index b28936cfd3ec..a0c2b95e7d38 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
 		error("%s: failed to enable\n",
 				ndctl_namespace_get_devname(ndns));
 	} else {
-		struct json_object *jndns = util_namespace_to_json(ndns,
-				UTIL_JSON_DAX);
+		unsigned long flags = UTIL_JSON_DAX;
+		struct json_object *jndns;
 
+		if (isatty(1))
+			flags |= UTIL_JSON_TTY;
+		jndns = util_namespace_to_json(ndns, flags);
 		if (jndns)
 			printf("%s\n", json_object_to_json_string_ext(jndns,
 						JSON_C_TO_STRING_PRETTY));
diff --git a/util/json.c b/util/json.c
index 1863bca10121..0a2053e1d1eb 100644
--- a/util/json.c
+++ b/util/json.c
@@ -11,10 +11,12 @@
  * General Public License for more details.
  */
 #include <limits.h>
+#include <string.h>
 #include <util/json.h>
 #include <util/filter.h>
 #include <uuid/uuid.h>
 #include <json-c/json.h>
+#include <json-c/printbuf.h>
 #include <ndctl/libndctl.h>
 #include <daxctl/libdaxctl.h>
 #include <ccan/array_size/array_size.h>
@@ -25,6 +27,92 @@
 #include <ndctl.h>
 #endif
 
+enum {
+	IEC,
+	JEDEC,
+};
+
+/* stolen from mdadm::human_size_brief() */
+static int display_size(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long bytes = json_object_get_int64(jobj);
+	static const int prefix = IEC;
+	static char buf[32];
+
+	/*
+	 * We convert bytes to either centi-M{ega,ibi}bytes or
+	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
+	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
+	 * converting to gigabytes, as that shows more precision and isn't too
+	 * large a number.  Terabytes are not yet handled.
+	 *
+	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
+	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
+	 */
+
+	if (bytes < 5000*1024)
+		snprintf(buf, sizeof(buf), "%lld", bytes);
+	else if (prefix == IEC) {
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
+
+			snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB\"",
+					cMiB/100 , cMiB % 100);
+		} else {
+			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
+
+			snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB\"",
+					cGiB/100 , cGiB % 100);
+		}
+	} else if (prefix == JEDEC) {
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
+
+			snprintf(buf, sizeof(buf), "\"%ld.%02ld MB\"",
+					cMB/100, cMB % 100);
+		} else {
+			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
+
+			snprintf(buf, sizeof(buf), "\"%ld.%02ld GB\"",
+					cGB/100 , cGB % 100);
+		}
+	} else
+		buf[0] = 0;
+
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long val = json_object_get_int64(jobj);
+	static char buf[32];
+
+	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(size);
+
+	if (jobj && (flags & UTIL_JSON_TTY))
+		json_object_set_serializer(jobj, display_size, NULL, NULL);
+	return jobj;
+}
+
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(val);
+
+	if (jobj && (flags & UTIL_JSON_TTY))
+		json_object_set_serializer(jobj, display_hex, NULL, NULL);
+	return jobj;
+}
+
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
 {
 	int len = json_object_array_length(jarray);
@@ -140,7 +228,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
 	return NULL;
 }
 
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags)
 {
 	const char *devname = daxctl_dev_get_devname(dev);
 	struct json_object *jdev, *jobj;
@@ -153,7 +242,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
 	if (jobj)
 		json_object_object_add(jdev, "chardev", jobj);
 
-	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
+	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
 	if (jobj)
 		json_object_object_add(jdev, "size", jobj);
 
@@ -181,7 +270,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 				return NULL;
 		}
 
-		jdev = util_daxctl_dev_to_json(dev);
+		jdev = util_daxctl_dev_to_json(dev, flags);
 		if (!jdev) {
 			json_object_put(jdevs);
 			return NULL;
@@ -211,7 +300,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	size = daxctl_region_get_size(region);
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "size", jobj);
@@ -219,7 +308,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	available_size = daxctl_region_get_available_size(region);
 	if (available_size) {
-		jobj = json_object_new_int64(available_size);
+		jobj = util_json_object_size(available_size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "available_size", jobj);
@@ -490,7 +579,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		json_object_object_add(jndns, "mode", jobj);
 
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (jobj)
 			json_object_object_add(jndns, "size", jobj);
 	}
@@ -604,7 +693,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 	return NULL;
 }
 
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags)
 {
 	struct json_object *jmapping = json_object_new_object();
 	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
@@ -618,12 +708,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
 		goto err;
 	json_object_object_add(jmapping, "dimm", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "offset", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "length", jobj);
diff --git a/util/json.h b/util/json.h
index 19d5ffc4376e..a46ac4fa92ce 100644
--- a/util/json.h
+++ b/util/json.h
@@ -20,13 +20,15 @@ enum util_json_flags {
 	UTIL_JSON_IDLE = (1 << 0),
 	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
 	UTIL_JSON_DAX = (1 << 2),
+	UTIL_JSON_TTY = (1 << 3),
 };
 
 struct json_object;
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
 struct json_object *util_bus_to_json(struct ndctl_bus *bus);
 struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags);
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		unsigned long flags);
 struct daxctl_region;
@@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
 		unsigned int *bb_count, unsigned long flags);
 struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 		const char *ident, unsigned long flags);
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags);
 struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 		struct json_object *jdevs, const char *ident,
 		unsigned long flags);
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags);
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags);
 #ifdef HAVE_NDCTL_SMART
 struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
 #else

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default
  2017-07-07 20:37 ` [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default Dan Williams
@ 2017-07-10  6:56   ` Yasunori Goto
  2017-07-10 18:26     ` [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting Dan Williams
  0 siblings, 1 reply; 11+ messages in thread
From: Yasunori Goto @ 2017-07-10  6:56 UTC (permalink / raw)
  To: Dan Williams; +Cc: linux-nvdimm

Dan-san,

First of all, thank you for your patch.
I have some comments.

- I suppose that users may be confused a bit due to
  the difference of output between stdout and via pipe.
  (Users will use "ndctl | less" command to read all of output of ndctl list,
   but its output will differ from stdout output....)
  Probably, same style output is easy for users to understand.

- In my first impression, I thought -f option means "--force"....

I think "du" command is good sample.
du command has -h (--human-readable) option.
  Default           : output raw value. 
  With -h option: output values in human readable format.

So, this way looks good and enough for ndctl.

Bye,


> The json output format of the 'list' commands is meant to make it easy
> to ingest the data into other tools. However, for direct administrator
> use of the utility it now offers human friendly formatting by default.
> 
>     Before:
>     # ndctl list --region=7
>     {
>       "dev":"region7",
>       "size":67108864,
>       "available_size":67108864,
>       "type":"pmem",
>       "iset_id":-6382611090938810793,
>       "badblock_count":8
>     }
> 
>     After:
>     # ndctl list --region=7
>     {
>       "dev":"region7",
>       "size":"64.00 MiB",
>       "available_size":"64.00 MiB",
>       "type":"pmem",
>       "iset_id":"0xa76c6907811fae57",
>       "badblock_count":8
>     }
> 
> The old behavior is still the default when stdout is a pipe and the
> output can be forced into that mode with the --format=machine option.
> 
> Reported-by: Linda Knippers <linda.knippers@hpe.com>
> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  Documentation/daxctl/daxctl-list.txt |   21 +++++++
>  Documentation/ndctl/ndctl-list.txt   |   30 +++++++++
>  daxctl/list.c                        |   19 ++++++
>  ndctl/list.c                         |   34 +++++++++--
>  ndctl/namespace.c                    |    7 ++
>  util/json.c                          |  108 +++++++++++++++++++++++++++++++---
>  util/json.h                          |   11 +++
>  7 files changed, 211 insertions(+), 19 deletions(-)
> 
> diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
> index 6de8d828de27..8cae0cd050fe 100644
> --- a/Documentation/daxctl/daxctl-list.txt
> +++ b/Documentation/daxctl/daxctl-list.txt
> @@ -72,6 +72,27 @@ OPTIONS
>  -i::
>  --idle::
>  	Include idle (not enabled / zero-sized) devices in the listing
> +-f::
> +--format=::
> +	By default 'daxctl list' will detect if it is being run
> +	interactively (stdout is a tty) and convert some JSON integer
> +	values to more human readable strings. The --format=machine
> +	option overrides that default and prints the raw decimal values
> +	for all numbers, as would be the case if stdout was a pipe.
> +	Specifying --format=human allow piping human friendly output to
> +	a file.  Example:
> +
> +[verse]
> +# daxctl list
> +{
> +  "chardev":"dax1.0",
> +  "size":"30.57 GiB"
> +}
> +# daxctl list --format=machine
> +{
> +  "chardev":"dax1.0",
> +  "size":32828817408
> +}
>  
>  COPYRIGHT
>  ---------
> diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
> index fd67d2b3e0ba..e9e9bcee54a8 100644
> --- a/Documentation/ndctl/ndctl-list.txt
> +++ b/Documentation/ndctl/ndctl-list.txt
> @@ -180,6 +180,36 @@ include::xable-region-options.txt[]
>    ]
>  }
>  
> +-f::
> +--format=::
> +	By default 'ndctl list' will detect if it is being run
> +	interactively (stdout is a tty) and convert some JSON integer
> +	values to more human readable strings. The --format=machine
> +	option overrides that default and prints the raw decimal values
> +	for all numbers, as would be the case if stdout was a pipe.
> +	Specifying --format=human allows piping human friendly output to
> +	a file.  Example:
> +
> +[verse]
> +# ndctl list --region=7
> +{
> +  "dev":"region7",
> +  "size":"64.00 MiB",
> +  "available_size":"64.00 MiB",
> +  "type":"pmem",
> +  "iset_id":"0xa76c6907811fae57",
> +  "badblock_count":8
> +}
> +# ndctl list --format=machine --region=7
> +{
> +  "dev":"region7",
> +  "size":67108864,
> +  "available_size":67108864,
> +  "type":"pmem",
> +  "iset_id":-6382611090938810793,
> +  "badblock_count":8
> +}
> +
>  COPYRIGHT
>  ---------
>  Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> diff --git a/daxctl/list.c b/daxctl/list.c
> index e213df138f07..5db216def63d 100644
> --- a/daxctl/list.c
> +++ b/daxctl/list.c
> @@ -26,6 +26,7 @@ static struct {
>  	bool devs;
>  	bool regions;
>  	bool idle;
> +	bool tty;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -36,14 +37,18 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_DAX;
>  	if (list.idle)
>  		flags |= UTIL_JSON_IDLE;
> +	if (list.tty)
> +		flags |= UTIL_JSON_TTY;
>  	return flags;
>  }
>  
>  static struct {
>  	const char *dev;
> +	const char *format;
>  	int region_id;
>  } param = {
>  	.region_id = -1,
> +	.format = "auto",
>  };
>  
>  static int did_fail;
> @@ -70,6 +75,9 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
>  		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
> +		OPT_STRING('f', "format", &param.format, "format-mode",
> +				"select \'human\' vs \'machine\' friendly number formats "
> +				" (default: \'auto\')"),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> @@ -96,6 +104,17 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  	if (num_list_flags() == 0)
>  		list.devs = true;
>  
> +	if (strcmp(param.format, "auto") == 0)
> +		list.tty = isatty(1);
> +	else if (strcmp(param.format, "human") == 0)
> +		list.tty = true;
> +	else if (strcmp(param.format, "machine") == 0)
> +		list.tty = false;
> +	else {
> +		error("unknown format type \"%s\"\n", param.format);
> +		usage_with_options(u, options);
> +	}
> +
>  	daxctl_region_foreach(ctx, region) {
>  		struct json_object *jregion = NULL;
>  
> diff --git a/ndctl/list.c b/ndctl/list.c
> index 7ecfcc91b0ec..fe1bed3c3a24 100644
> --- a/ndctl/list.c
> +++ b/ndctl/list.c
> @@ -37,6 +37,7 @@ static struct {
>  	bool health;
>  	bool dax;
>  	bool media_errors;
> +	bool tty;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_MEDIA_ERRORS;
>  	if (list.dax)
>  		flags |= UTIL_JSON_DAX;
> +	if (list.tty)
> +		flags |= UTIL_JSON_TTY;
>  	return flags;
>  }
>  
> @@ -59,7 +62,10 @@ static struct {
>  	const char *dimm;
>  	const char *mode;
>  	const char *namespace;
> -} param;
> +	const char *format;
> +} param = {
> +	.format = "auto",
> +};
>  
>  static int did_fail;
>  static int jflag = JSON_C_TO_STRING_PRETTY;
> @@ -160,12 +166,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  		goto err;
>  	json_object_object_add(jregion, "dev", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "size", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_available_size(region),
> +			flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "available_size", jobj);
> @@ -186,8 +193,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  
>  	iset = ndctl_region_get_interleave_set(region);
>  	if (iset) {
> -		jobj = json_object_new_int64(
> -				ndctl_interleave_set_get_cookie(iset));
> +		jobj = util_json_object_hex(
> +				ndctl_interleave_set_get_cookie(iset), flags);
>  		if (!jobj)
>  			fail("\n");
>  		else
> @@ -216,7 +223,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  			json_object_object_add(jregion, "mappings", jmappings);
>  		}
>  
> -		jmapping = util_mapping_to_json(mapping);
> +		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
>  		if (!jmapping) {
>  			fail("\n");
>  			continue;
> @@ -282,6 +289,9 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
>  		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
>  				"include media errors"),
> +		OPT_STRING('f', "format", &param.format, "format-mode",
> +				"select \'human\' vs \'machine\' friendly number formats "
> +				" (default: \'auto\')"),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> @@ -332,6 +342,18 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		return -EINVAL;
>  	}
>  
> +	if (strcmp(param.format, "auto") == 0)
> +		list.tty = isatty(1);
> +	else if (strcmp(param.format, "human") == 0)
> +		list.tty = true;
> +	else if (strcmp(param.format, "machine") == 0)
> +		list.tty = false;
> +	else {
> +		error("unknown format type \"%s\"\n", param.format);
> +		usage_with_options(u, options);
> +	}
> +
> +
>  	ndctl_bus_foreach(ctx, bus) {
>  		struct json_object *jbus = NULL;
>  		struct ndctl_region *region;
> diff --git a/ndctl/namespace.c b/ndctl/namespace.c
> index b28936cfd3ec..a0c2b95e7d38 100644
> --- a/ndctl/namespace.c
> +++ b/ndctl/namespace.c
> @@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
>  		error("%s: failed to enable\n",
>  				ndctl_namespace_get_devname(ndns));
>  	} else {
> -		struct json_object *jndns = util_namespace_to_json(ndns,
> -				UTIL_JSON_DAX);
> +		unsigned long flags = UTIL_JSON_DAX;
> +		struct json_object *jndns;
>  
> +		if (isatty(1))
> +			flags |= UTIL_JSON_TTY;
> +		jndns = util_namespace_to_json(ndns, flags);
>  		if (jndns)
>  			printf("%s\n", json_object_to_json_string_ext(jndns,
>  						JSON_C_TO_STRING_PRETTY));
> diff --git a/util/json.c b/util/json.c
> index 1863bca10121..0a2053e1d1eb 100644
> --- a/util/json.c
> +++ b/util/json.c
> @@ -11,10 +11,12 @@
>   * General Public License for more details.
>   */
>  #include <limits.h>
> +#include <string.h>
>  #include <util/json.h>
>  #include <util/filter.h>
>  #include <uuid/uuid.h>
>  #include <json-c/json.h>
> +#include <json-c/printbuf.h>
>  #include <ndctl/libndctl.h>
>  #include <daxctl/libdaxctl.h>
>  #include <ccan/array_size/array_size.h>
> @@ -25,6 +27,92 @@
>  #include <ndctl.h>
>  #endif
>  
> +enum {
> +	IEC,
> +	JEDEC,
> +};
> +
> +/* stolen from mdadm::human_size_brief() */
> +static int display_size(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long bytes = json_object_get_int64(jobj);
> +	static const int prefix = IEC;
> +	static char buf[32];
> +
> +	/*
> +	 * We convert bytes to either centi-M{ega,ibi}bytes or
> +	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
> +	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
> +	 * converting to gigabytes, as that shows more precision and isn't too
> +	 * large a number.  Terabytes are not yet handled.
> +	 *
> +	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
> +	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
> +	 */
> +
> +	if (bytes < 5000*1024)
> +		snprintf(buf, sizeof(buf), "%lld", bytes);
> +	else if (prefix == IEC) {
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
> +
> +			snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB\"",
> +					cMiB/100 , cMiB % 100);
> +		} else {
> +			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
> +
> +			snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB\"",
> +					cGiB/100 , cGiB % 100);
> +		}
> +	} else if (prefix == JEDEC) {
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
> +
> +			snprintf(buf, sizeof(buf), "\"%ld.%02ld MB\"",
> +					cMB/100, cMB % 100);
> +		} else {
> +			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
> +
> +			snprintf(buf, sizeof(buf), "\"%ld.%02ld GB\"",
> +					cGB/100 , cGB % 100);
> +		}
> +	} else
> +		buf[0] = 0;
> +
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long val = json_object_get_int64(jobj);
> +	static char buf[32];
> +
> +	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(size);
> +
> +	if (jobj && (flags & UTIL_JSON_TTY))
> +		json_object_set_serializer(jobj, display_size, NULL, NULL);
> +	return jobj;
> +}
> +
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(val);
> +
> +	if (jobj && (flags & UTIL_JSON_TTY))
> +		json_object_set_serializer(jobj, display_hex, NULL, NULL);
> +	return jobj;
> +}
> +
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
>  {
>  	int len = json_object_array_length(jarray);
> @@ -140,7 +228,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
>  	return NULL;
>  }
>  
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags)
>  {
>  	const char *devname = daxctl_dev_get_devname(dev);
>  	struct json_object *jdev, *jobj;
> @@ -153,7 +242,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
>  	if (jobj)
>  		json_object_object_add(jdev, "chardev", jobj);
>  
> -	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
> +	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
>  	if (jobj)
>  		json_object_object_add(jdev, "size", jobj);
>  
> @@ -181,7 +270,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  				return NULL;
>  		}
>  
> -		jdev = util_daxctl_dev_to_json(dev);
> +		jdev = util_daxctl_dev_to_json(dev, flags);
>  		if (!jdev) {
>  			json_object_put(jdevs);
>  			return NULL;
> @@ -211,7 +300,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	size = daxctl_region_get_size(region);
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "size", jobj);
> @@ -219,7 +308,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	available_size = daxctl_region_get_available_size(region);
>  	if (available_size) {
> -		jobj = json_object_new_int64(available_size);
> +		jobj = util_json_object_size(available_size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "available_size", jobj);
> @@ -490,7 +579,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		json_object_object_add(jndns, "mode", jobj);
>  
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (jobj)
>  			json_object_object_add(jndns, "size", jobj);
>  	}
> @@ -604,7 +693,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  	return NULL;
>  }
>  
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags)
>  {
>  	struct json_object *jmapping = json_object_new_object();
>  	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
> @@ -618,12 +708,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
>  		goto err;
>  	json_object_object_add(jmapping, "dimm", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "offset", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "length", jobj);
> diff --git a/util/json.h b/util/json.h
> index 19d5ffc4376e..a46ac4fa92ce 100644
> --- a/util/json.h
> +++ b/util/json.h
> @@ -20,13 +20,15 @@ enum util_json_flags {
>  	UTIL_JSON_IDLE = (1 << 0),
>  	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
>  	UTIL_JSON_DAX = (1 << 2),
> +	UTIL_JSON_TTY = (1 << 3),
>  };
>  
>  struct json_object;
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
>  struct json_object *util_bus_to_json(struct ndctl_bus *bus);
>  struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags);
>  struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		unsigned long flags);
>  struct daxctl_region;
> @@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
>  		unsigned int *bb_count, unsigned long flags);
>  struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  		const char *ident, unsigned long flags);
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags);
>  struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  		struct json_object *jdevs, const char *ident,
>  		unsigned long flags);
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags);
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags);
>  #ifdef HAVE_NDCTL_SMART
>  struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
>  #else
> 
> _______________________________________________
> Linux-nvdimm mailing list
> Linux-nvdimm@lists.01.org
> https://lists.01.org/mailman/listinfo/linux-nvdimm


_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10  6:56   ` Yasunori Goto
@ 2017-07-10 18:26     ` Dan Williams
  2017-07-10 18:42       ` Linda Knippers
  0 siblings, 1 reply; 11+ messages in thread
From: Dan Williams @ 2017-07-10 18:26 UTC (permalink / raw)
  To: linux-nvdimm; +Cc: Yasunori Goto

The json output format of the 'list' commands is meant to make it easy
to ingest the data into other tools. However, for direct administrator
use of the utility provide an option to format some numbers for easier
human consumption, similar to the "-h" to du(1). Note that the short
option is "-u" since "-h" is already established as the short option for
"--help".

    Before:
    # ndctl list --region=7
    {
      "dev":"region7",
      "size":67108864,
      "available_size":67108864,
      "type":"pmem",
      "iset_id":-6382611090938810793,
      "badblock_count":8
    }

    After:
    # ndctl list --region=7 --human
    {
      "dev":"region7",
      "size":"64.00 MiB / 67.11 MB",
      "available_size":"64.00 MiB / 67.11 MB",
      "type":"pmem",
      "iset_id":"0xa76c6907811fae57",
      "badblock_count":8
    }

Cc: Dave Jiang <dave.jiang@intel.com>
Reported-by: Linda Knippers <linda.knippers@hpe.com>
Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---

Changes since v1:
* drop the auto-detect and just require the "--human" option for the
  different format of the output. This prevents any confusion with
  default output behavior (Yasunori)

* include the JEDEC size in addition to the IEC size (i.e. MiB vs MB
  etc) (Dave).

 Documentation/daxctl/daxctl-list.txt |   20 ++++++
 Documentation/ndctl/ndctl-list.txt   |   28 +++++++++
 daxctl/list.c                        |    5 ++
 ndctl/list.c                         |   16 ++++-
 ndctl/namespace.c                    |    7 ++
 util/json.c                          |  106 +++++++++++++++++++++++++++++++---
 util/json.h                          |   11 +++-
 7 files changed, 175 insertions(+), 18 deletions(-)

diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
index 6de8d828de27..75dbcc4a07ef 100644
--- a/Documentation/daxctl/daxctl-list.txt
+++ b/Documentation/daxctl/daxctl-list.txt
@@ -73,6 +73,26 @@ OPTIONS
 --idle::
 	Include idle (not enabled / zero-sized) devices in the listing
 
+-u::
+--human::
+	By default 'daxctl list' will output machine-friendly raw-integer
+	data. Instead, with this flag, numbers representing storage size
+	will be formatted as human readable strings with units, other
+	fields are converted to hexadecimal strings.  Example:
+
+[verse]
+# daxctl list
+{
+  "chardev":"dax1.0",
+  "size":32828817408
+}
+
+# daxctl list --human
+{
+  "chardev":"dax1.0",
+  "size":"30.57 GiB / 32.83 GB"
+}
+
 COPYRIGHT
 ---------
 Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
index fd67d2b3e0ba..cc2d87cbb295 100644
--- a/Documentation/ndctl/ndctl-list.txt
+++ b/Documentation/ndctl/ndctl-list.txt
@@ -180,6 +180,34 @@ include::xable-region-options.txt[]
   ]
 }
 
+-u::
+--human::
+	By default 'ndctl list' will output machine-friendly raw-integer
+	data. Instead, with this flag, numbers representing storage size
+	will be formatted as human readable strings with units, other
+	fields are converted to hexadecimal strings.  Example:
+
+[verse]
+# ndctl list --region=7
+{
+  "dev":"region7",
+  "size":67108864,
+  "available_size":67108864,
+  "type":"pmem",
+  "iset_id":-6382611090938810793,
+  "badblock_count":8
+}
+
+# ndctl list --human --region=7
+{
+  "dev":"region7",
+  "size":"64.00 MiB / 67.11 MB",
+  "available_size":"64.00 MiB / 67.11 MB",
+  "type":"pmem",
+  "iset_id":"0xa76c6907811fae57",
+  "badblock_count":8
+}
+
 COPYRIGHT
 ---------
 Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
diff --git a/daxctl/list.c b/daxctl/list.c
index e213df138f07..678ef6ce4c13 100644
--- a/daxctl/list.c
+++ b/daxctl/list.c
@@ -26,6 +26,7 @@ static struct {
 	bool devs;
 	bool regions;
 	bool idle;
+	bool human;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -36,6 +37,8 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_DAX;
 	if (list.idle)
 		flags |= UTIL_JSON_IDLE;
+	if (list.human)
+		flags |= UTIL_JSON_HUMAN;
 	return flags;
 }
 
@@ -70,6 +73,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
 		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
+		OPT_BOOLEAN('u', "human", &list.human,
+				"use human friendly number formats "),
 		OPT_END(),
 	};
 	const char * const u[] = {
diff --git a/ndctl/list.c b/ndctl/list.c
index 7ecfcc91b0ec..d81d6464ebb1 100644
--- a/ndctl/list.c
+++ b/ndctl/list.c
@@ -37,6 +37,7 @@ static struct {
 	bool health;
 	bool dax;
 	bool media_errors;
+	bool human;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_MEDIA_ERRORS;
 	if (list.dax)
 		flags |= UTIL_JSON_DAX;
+	if (list.human)
+		flags |= UTIL_JSON_HUMAN;
 	return flags;
 }
 
@@ -160,12 +163,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 		goto err;
 	json_object_object_add(jregion, "dev", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_size(region));
+	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "size", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
+	jobj = util_json_object_size(ndctl_region_get_available_size(region),
+			flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "available_size", jobj);
@@ -186,8 +190,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 
 	iset = ndctl_region_get_interleave_set(region);
 	if (iset) {
-		jobj = json_object_new_int64(
-				ndctl_interleave_set_get_cookie(iset));
+		jobj = util_json_object_hex(
+				ndctl_interleave_set_get_cookie(iset), flags);
 		if (!jobj)
 			fail("\n");
 		else
@@ -216,7 +220,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 			json_object_object_add(jregion, "mappings", jmappings);
 		}
 
-		jmapping = util_mapping_to_json(mapping);
+		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
 		if (!jmapping) {
 			fail("\n");
 			continue;
@@ -282,6 +286,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
 		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
 				"include media errors"),
+		OPT_BOOLEAN('u', "human", &list.human,
+				"use human friendly number formats "),
 		OPT_END(),
 	};
 	const char * const u[] = {
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index b28936cfd3ec..7b0198f11cad 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
 		error("%s: failed to enable\n",
 				ndctl_namespace_get_devname(ndns));
 	} else {
-		struct json_object *jndns = util_namespace_to_json(ndns,
-				UTIL_JSON_DAX);
+		unsigned long flags = UTIL_JSON_DAX;
+		struct json_object *jndns;
 
+		if (isatty(1))
+			flags |= UTIL_JSON_HUMAN;
+		jndns = util_namespace_to_json(ndns, flags);
 		if (jndns)
 			printf("%s\n", json_object_to_json_string_ext(jndns,
 						JSON_C_TO_STRING_PRETTY));
diff --git a/util/json.c b/util/json.c
index 1863bca10121..30217326c0b4 100644
--- a/util/json.c
+++ b/util/json.c
@@ -11,10 +11,12 @@
  * General Public License for more details.
  */
 #include <limits.h>
+#include <string.h>
 #include <util/json.h>
 #include <util/filter.h>
 #include <uuid/uuid.h>
 #include <json-c/json.h>
+#include <json-c/printbuf.h>
 #include <ndctl/libndctl.h>
 #include <daxctl/libdaxctl.h>
 #include <ccan/array_size/array_size.h>
@@ -25,6 +27,90 @@
 #include <ndctl.h>
 #endif
 
+/* adapted from mdadm::human_size_brief() */
+static int display_size(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long bytes = json_object_get_int64(jobj);
+	static char buf[128];
+	int c;
+
+	/*
+	 * We convert bytes to either centi-M{ega,ibi}bytes or
+	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
+	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
+	 * converting to gigabytes, as that shows more precision and isn't too
+	 * large a number.  Terabytes are not yet handled.
+	 *
+	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
+	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
+	 */
+
+	if (bytes < 5000*1024)
+		snprintf(buf, sizeof(buf), "%lld", bytes);
+	else {
+		/* IEC */
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
+
+			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB",
+					cMiB/100 , cMiB % 100);
+		} else {
+			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
+
+			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB",
+					cGiB/100 , cGiB % 100);
+		}
+
+		c += snprintf(buf + c, sizeof(buf) - c, " / ");
+
+		/* JEDEC */
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
+
+			snprintf(buf + c, sizeof(buf) - c, "%ld.%02ld MB\"",
+					cMB/100, cMB % 100);
+		} else {
+			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
+
+			snprintf(buf + c, sizeof(buf) - c, "%ld.%02ld GB\"",
+					cGB/100 , cGB % 100);
+		}
+	}
+
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long val = json_object_get_int64(jobj);
+	static char buf[32];
+
+	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(size);
+
+	if (jobj && (flags & UTIL_JSON_HUMAN))
+		json_object_set_serializer(jobj, display_size, NULL, NULL);
+	return jobj;
+}
+
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(val);
+
+	if (jobj && (flags & UTIL_JSON_HUMAN))
+		json_object_set_serializer(jobj, display_hex, NULL, NULL);
+	return jobj;
+}
+
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
 {
 	int len = json_object_array_length(jarray);
@@ -140,7 +226,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
 	return NULL;
 }
 
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags)
 {
 	const char *devname = daxctl_dev_get_devname(dev);
 	struct json_object *jdev, *jobj;
@@ -153,7 +240,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
 	if (jobj)
 		json_object_object_add(jdev, "chardev", jobj);
 
-	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
+	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
 	if (jobj)
 		json_object_object_add(jdev, "size", jobj);
 
@@ -181,7 +268,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 				return NULL;
 		}
 
-		jdev = util_daxctl_dev_to_json(dev);
+		jdev = util_daxctl_dev_to_json(dev, flags);
 		if (!jdev) {
 			json_object_put(jdevs);
 			return NULL;
@@ -211,7 +298,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	size = daxctl_region_get_size(region);
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "size", jobj);
@@ -219,7 +306,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	available_size = daxctl_region_get_available_size(region);
 	if (available_size) {
-		jobj = json_object_new_int64(available_size);
+		jobj = util_json_object_size(available_size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "available_size", jobj);
@@ -490,7 +577,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		json_object_object_add(jndns, "mode", jobj);
 
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (jobj)
 			json_object_object_add(jndns, "size", jobj);
 	}
@@ -604,7 +691,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 	return NULL;
 }
 
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags)
 {
 	struct json_object *jmapping = json_object_new_object();
 	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
@@ -618,12 +706,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
 		goto err;
 	json_object_object_add(jmapping, "dimm", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "offset", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "length", jobj);
diff --git a/util/json.h b/util/json.h
index 19d5ffc4376e..ec394b636cff 100644
--- a/util/json.h
+++ b/util/json.h
@@ -20,13 +20,15 @@ enum util_json_flags {
 	UTIL_JSON_IDLE = (1 << 0),
 	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
 	UTIL_JSON_DAX = (1 << 2),
+	UTIL_JSON_HUMAN = (1 << 3),
 };
 
 struct json_object;
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
 struct json_object *util_bus_to_json(struct ndctl_bus *bus);
 struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags);
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		unsigned long flags);
 struct daxctl_region;
@@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
 		unsigned int *bb_count, unsigned long flags);
 struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 		const char *ident, unsigned long flags);
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags);
 struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 		struct json_object *jdevs, const char *ident,
 		unsigned long flags);
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags);
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags);
 #ifdef HAVE_NDCTL_SMART
 struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
 #else

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 18:26     ` [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting Dan Williams
@ 2017-07-10 18:42       ` Linda Knippers
  2017-07-10 19:47         ` Dan Williams
  0 siblings, 1 reply; 11+ messages in thread
From: Linda Knippers @ 2017-07-10 18:42 UTC (permalink / raw)
  To: Dan Williams, linux-nvdimm; +Cc: Yasunori Goto

On 07/10/2017 02:26 PM, Dan Williams wrote:
> The json output format of the 'list' commands is meant to make it easy
> to ingest the data into other tools. However, for direct administrator
> use of the utility provide an option to format some numbers for easier
> human consumption, similar to the "-h" to du(1). Note that the short
> option is "-u" since "-h" is already established as the short option for
> "--help".
> 
>     Before:
>     # ndctl list --region=7
>     {
>       "dev":"region7",
>       "size":67108864,
>       "available_size":67108864,
>       "type":"pmem",
>       "iset_id":-6382611090938810793,
>       "badblock_count":8
>     }
> 
>     After:
>     # ndctl list --region=7 --human
>     {
>       "dev":"region7",
>       "size":"64.00 MiB / 67.11 MB",
>       "available_size":"64.00 MiB / 67.11 MB",
>       "type":"pmem",
>       "iset_id":"0xa76c6907811fae57",
>       "badblock_count":8
>     }
> 
> Cc: Dave Jiang <dave.jiang@intel.com>
> Reported-by: Linda Knippers <linda.knippers@hpe.com>
> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
> 
> Changes since v1:
> * drop the auto-detect and just require the "--human" option for the
>   different format of the output. This prevents any confusion with
>   default output behavior (Yasunori)
> 
> * include the JEDEC size in addition to the IEC size (i.e. MiB vs MB
>   etc) (Dave).

I'd rather see us pick one or the other and not provide two values.
Even though this is the human friendly output, it's possible that it could
be used by other programs that know what MB means and know what MiB means
but now would need to know that / doesn't mean to divide one by the other.
I think as long as the units are clear, either one is ok.

-- ljk
> 
>  Documentation/daxctl/daxctl-list.txt |   20 ++++++
>  Documentation/ndctl/ndctl-list.txt   |   28 +++++++++
>  daxctl/list.c                        |    5 ++
>  ndctl/list.c                         |   16 ++++-
>  ndctl/namespace.c                    |    7 ++
>  util/json.c                          |  106 +++++++++++++++++++++++++++++++---
>  util/json.h                          |   11 +++-
>  7 files changed, 175 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
> index 6de8d828de27..75dbcc4a07ef 100644
> --- a/Documentation/daxctl/daxctl-list.txt
> +++ b/Documentation/daxctl/daxctl-list.txt
> @@ -73,6 +73,26 @@ OPTIONS
>  --idle::
>  	Include idle (not enabled / zero-sized) devices in the listing
>  
> +-u::
> +--human::
> +	By default 'daxctl list' will output machine-friendly raw-integer
> +	data. Instead, with this flag, numbers representing storage size
> +	will be formatted as human readable strings with units, other
> +	fields are converted to hexadecimal strings.  Example:
> +
> +[verse]
> +# daxctl list
> +{
> +  "chardev":"dax1.0",
> +  "size":32828817408
> +}
> +
> +# daxctl list --human
> +{
> +  "chardev":"dax1.0",
> +  "size":"30.57 GiB / 32.83 GB"
> +}
> +
>  COPYRIGHT
>  ---------
>  Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
> index fd67d2b3e0ba..cc2d87cbb295 100644
> --- a/Documentation/ndctl/ndctl-list.txt
> +++ b/Documentation/ndctl/ndctl-list.txt
> @@ -180,6 +180,34 @@ include::xable-region-options.txt[]
>    ]
>  }
>  
> +-u::
> +--human::
> +	By default 'ndctl list' will output machine-friendly raw-integer
> +	data. Instead, with this flag, numbers representing storage size
> +	will be formatted as human readable strings with units, other
> +	fields are converted to hexadecimal strings.  Example:
> +
> +[verse]
> +# ndctl list --region=7
> +{
> +  "dev":"region7",
> +  "size":67108864,
> +  "available_size":67108864,
> +  "type":"pmem",
> +  "iset_id":-6382611090938810793,
> +  "badblock_count":8
> +}
> +
> +# ndctl list --human --region=7
> +{
> +  "dev":"region7",
> +  "size":"64.00 MiB / 67.11 MB",
> +  "available_size":"64.00 MiB / 67.11 MB",
> +  "type":"pmem",
> +  "iset_id":"0xa76c6907811fae57",
> +  "badblock_count":8
> +}
> +
>  COPYRIGHT
>  ---------
>  Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> diff --git a/daxctl/list.c b/daxctl/list.c
> index e213df138f07..678ef6ce4c13 100644
> --- a/daxctl/list.c
> +++ b/daxctl/list.c
> @@ -26,6 +26,7 @@ static struct {
>  	bool devs;
>  	bool regions;
>  	bool idle;
> +	bool human;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -36,6 +37,8 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_DAX;
>  	if (list.idle)
>  		flags |= UTIL_JSON_IDLE;
> +	if (list.human)
> +		flags |= UTIL_JSON_HUMAN;
>  	return flags;
>  }
>  
> @@ -70,6 +73,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
>  		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
> +		OPT_BOOLEAN('u', "human", &list.human,
> +				"use human friendly number formats "),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> diff --git a/ndctl/list.c b/ndctl/list.c
> index 7ecfcc91b0ec..d81d6464ebb1 100644
> --- a/ndctl/list.c
> +++ b/ndctl/list.c
> @@ -37,6 +37,7 @@ static struct {
>  	bool health;
>  	bool dax;
>  	bool media_errors;
> +	bool human;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_MEDIA_ERRORS;
>  	if (list.dax)
>  		flags |= UTIL_JSON_DAX;
> +	if (list.human)
> +		flags |= UTIL_JSON_HUMAN;
>  	return flags;
>  }
>  
> @@ -160,12 +163,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  		goto err;
>  	json_object_object_add(jregion, "dev", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "size", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_available_size(region),
> +			flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "available_size", jobj);
> @@ -186,8 +190,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  
>  	iset = ndctl_region_get_interleave_set(region);
>  	if (iset) {
> -		jobj = json_object_new_int64(
> -				ndctl_interleave_set_get_cookie(iset));
> +		jobj = util_json_object_hex(
> +				ndctl_interleave_set_get_cookie(iset), flags);
>  		if (!jobj)
>  			fail("\n");
>  		else
> @@ -216,7 +220,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  			json_object_object_add(jregion, "mappings", jmappings);
>  		}
>  
> -		jmapping = util_mapping_to_json(mapping);
> +		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
>  		if (!jmapping) {
>  			fail("\n");
>  			continue;
> @@ -282,6 +286,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
>  		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
>  				"include media errors"),
> +		OPT_BOOLEAN('u', "human", &list.human,
> +				"use human friendly number formats "),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> diff --git a/ndctl/namespace.c b/ndctl/namespace.c
> index b28936cfd3ec..7b0198f11cad 100644
> --- a/ndctl/namespace.c
> +++ b/ndctl/namespace.c
> @@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
>  		error("%s: failed to enable\n",
>  				ndctl_namespace_get_devname(ndns));
>  	} else {
> -		struct json_object *jndns = util_namespace_to_json(ndns,
> -				UTIL_JSON_DAX);
> +		unsigned long flags = UTIL_JSON_DAX;
> +		struct json_object *jndns;
>  
> +		if (isatty(1))
> +			flags |= UTIL_JSON_HUMAN;
> +		jndns = util_namespace_to_json(ndns, flags);
>  		if (jndns)
>  			printf("%s\n", json_object_to_json_string_ext(jndns,
>  						JSON_C_TO_STRING_PRETTY));
> diff --git a/util/json.c b/util/json.c
> index 1863bca10121..30217326c0b4 100644
> --- a/util/json.c
> +++ b/util/json.c
> @@ -11,10 +11,12 @@
>   * General Public License for more details.
>   */
>  #include <limits.h>
> +#include <string.h>
>  #include <util/json.h>
>  #include <util/filter.h>
>  #include <uuid/uuid.h>
>  #include <json-c/json.h>
> +#include <json-c/printbuf.h>
>  #include <ndctl/libndctl.h>
>  #include <daxctl/libdaxctl.h>
>  #include <ccan/array_size/array_size.h>
> @@ -25,6 +27,90 @@
>  #include <ndctl.h>
>  #endif
>  
> +/* adapted from mdadm::human_size_brief() */
> +static int display_size(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long bytes = json_object_get_int64(jobj);
> +	static char buf[128];
> +	int c;
> +
> +	/*
> +	 * We convert bytes to either centi-M{ega,ibi}bytes or
> +	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
> +	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
> +	 * converting to gigabytes, as that shows more precision and isn't too
> +	 * large a number.  Terabytes are not yet handled.
> +	 *
> +	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
> +	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
> +	 */
> +
> +	if (bytes < 5000*1024)
> +		snprintf(buf, sizeof(buf), "%lld", bytes);
> +	else {
> +		/* IEC */
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
> +
> +			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB",
> +					cMiB/100 , cMiB % 100);
> +		} else {
> +			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
> +
> +			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB",
> +					cGiB/100 , cGiB % 100);
> +		}
> +
> +		c += snprintf(buf + c, sizeof(buf) - c, " / ");
> +
> +		/* JEDEC */
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
> +
> +			snprintf(buf + c, sizeof(buf) - c, "%ld.%02ld MB\"",
> +					cMB/100, cMB % 100);
> +		} else {
> +			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
> +
> +			snprintf(buf + c, sizeof(buf) - c, "%ld.%02ld GB\"",
> +					cGB/100 , cGB % 100);
> +		}
> +	}
> +
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long val = json_object_get_int64(jobj);
> +	static char buf[32];
> +
> +	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(size);
> +
> +	if (jobj && (flags & UTIL_JSON_HUMAN))
> +		json_object_set_serializer(jobj, display_size, NULL, NULL);
> +	return jobj;
> +}
> +
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(val);
> +
> +	if (jobj && (flags & UTIL_JSON_HUMAN))
> +		json_object_set_serializer(jobj, display_hex, NULL, NULL);
> +	return jobj;
> +}
> +
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
>  {
>  	int len = json_object_array_length(jarray);
> @@ -140,7 +226,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
>  	return NULL;
>  }
>  
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags)
>  {
>  	const char *devname = daxctl_dev_get_devname(dev);
>  	struct json_object *jdev, *jobj;
> @@ -153,7 +240,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
>  	if (jobj)
>  		json_object_object_add(jdev, "chardev", jobj);
>  
> -	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
> +	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
>  	if (jobj)
>  		json_object_object_add(jdev, "size", jobj);
>  
> @@ -181,7 +268,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  				return NULL;
>  		}
>  
> -		jdev = util_daxctl_dev_to_json(dev);
> +		jdev = util_daxctl_dev_to_json(dev, flags);
>  		if (!jdev) {
>  			json_object_put(jdevs);
>  			return NULL;
> @@ -211,7 +298,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	size = daxctl_region_get_size(region);
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "size", jobj);
> @@ -219,7 +306,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	available_size = daxctl_region_get_available_size(region);
>  	if (available_size) {
> -		jobj = json_object_new_int64(available_size);
> +		jobj = util_json_object_size(available_size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "available_size", jobj);
> @@ -490,7 +577,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		json_object_object_add(jndns, "mode", jobj);
>  
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (jobj)
>  			json_object_object_add(jndns, "size", jobj);
>  	}
> @@ -604,7 +691,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  	return NULL;
>  }
>  
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags)
>  {
>  	struct json_object *jmapping = json_object_new_object();
>  	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
> @@ -618,12 +706,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
>  		goto err;
>  	json_object_object_add(jmapping, "dimm", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "offset", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "length", jobj);
> diff --git a/util/json.h b/util/json.h
> index 19d5ffc4376e..ec394b636cff 100644
> --- a/util/json.h
> +++ b/util/json.h
> @@ -20,13 +20,15 @@ enum util_json_flags {
>  	UTIL_JSON_IDLE = (1 << 0),
>  	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
>  	UTIL_JSON_DAX = (1 << 2),
> +	UTIL_JSON_HUMAN = (1 << 3),
>  };
>  
>  struct json_object;
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
>  struct json_object *util_bus_to_json(struct ndctl_bus *bus);
>  struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags);
>  struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		unsigned long flags);
>  struct daxctl_region;
> @@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
>  		unsigned int *bb_count, unsigned long flags);
>  struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  		const char *ident, unsigned long flags);
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags);
>  struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  		struct json_object *jdevs, const char *ident,
>  		unsigned long flags);
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags);
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags);
>  #ifdef HAVE_NDCTL_SMART
>  struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
>  #else
> 

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 18:42       ` Linda Knippers
@ 2017-07-10 19:47         ` Dan Williams
  2017-07-10 19:56           ` Dan Williams
  0 siblings, 1 reply; 11+ messages in thread
From: Dan Williams @ 2017-07-10 19:47 UTC (permalink / raw)
  To: Linda Knippers; +Cc: Yasunori Goto, linux-nvdimm

On Mon, Jul 10, 2017 at 11:42 AM, Linda Knippers <linda.knippers@hpe.com> wrote:
> On 07/10/2017 02:26 PM, Dan Williams wrote:
>> The json output format of the 'list' commands is meant to make it easy
>> to ingest the data into other tools. However, for direct administrator
>> use of the utility provide an option to format some numbers for easier
>> human consumption, similar to the "-h" to du(1). Note that the short
>> option is "-u" since "-h" is already established as the short option for
>> "--help".
>>
>>     Before:
>>     # ndctl list --region=7
>>     {
>>       "dev":"region7",
>>       "size":67108864,
>>       "available_size":67108864,
>>       "type":"pmem",
>>       "iset_id":-6382611090938810793,
>>       "badblock_count":8
>>     }
>>
>>     After:
>>     # ndctl list --region=7 --human
>>     {
>>       "dev":"region7",
>>       "size":"64.00 MiB / 67.11 MB",
>>       "available_size":"64.00 MiB / 67.11 MB",
>>       "type":"pmem",
>>       "iset_id":"0xa76c6907811fae57",
>>       "badblock_count":8
>>     }
>>
>> Cc: Dave Jiang <dave.jiang@intel.com>
>> Reported-by: Linda Knippers <linda.knippers@hpe.com>
>> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
>> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>> ---
>>
>> Changes since v1:
>> * drop the auto-detect and just require the "--human" option for the
>>   different format of the output. This prevents any confusion with
>>   default output behavior (Yasunori)
>>
>> * include the JEDEC size in addition to the IEC size (i.e. MiB vs MB
>>   etc) (Dave).
>
> I'd rather see us pick one or the other and not provide two values.
> Even though this is the human friendly output, it's possible that it could
> be used by other programs that know what MB means and know what MiB means
> but now would need to know that / doesn't mean to divide one by the other.
> I think as long as the units are clear, either one is ok.

I started there, the thing that tipped it for me is that fio is
currently outputting both formats when it displays size data. A script
or another program should not be using the "--human" option, that
should eliminate the opportunity for confusion.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 19:47         ` Dan Williams
@ 2017-07-10 19:56           ` Dan Williams
  2017-07-10 20:11             ` Linda Knippers
  0 siblings, 1 reply; 11+ messages in thread
From: Dan Williams @ 2017-07-10 19:56 UTC (permalink / raw)
  To: Linda Knippers; +Cc: Yasunori Goto, linux-nvdimm

On Mon, Jul 10, 2017 at 12:47 PM, Dan Williams <dan.j.williams@intel.com> wrote:
> On Mon, Jul 10, 2017 at 11:42 AM, Linda Knippers <linda.knippers@hpe.com> wrote:
>> On 07/10/2017 02:26 PM, Dan Williams wrote:
>>> The json output format of the 'list' commands is meant to make it easy
>>> to ingest the data into other tools. However, for direct administrator
>>> use of the utility provide an option to format some numbers for easier
>>> human consumption, similar to the "-h" to du(1). Note that the short
>>> option is "-u" since "-h" is already established as the short option for
>>> "--help".
>>>
>>>     Before:
>>>     # ndctl list --region=7
>>>     {
>>>       "dev":"region7",
>>>       "size":67108864,
>>>       "available_size":67108864,
>>>       "type":"pmem",
>>>       "iset_id":-6382611090938810793,
>>>       "badblock_count":8
>>>     }
>>>
>>>     After:
>>>     # ndctl list --region=7 --human
>>>     {
>>>       "dev":"region7",
>>>       "size":"64.00 MiB / 67.11 MB",
>>>       "available_size":"64.00 MiB / 67.11 MB",
>>>       "type":"pmem",
>>>       "iset_id":"0xa76c6907811fae57",
>>>       "badblock_count":8
>>>     }
>>>
>>> Cc: Dave Jiang <dave.jiang@intel.com>
>>> Reported-by: Linda Knippers <linda.knippers@hpe.com>
>>> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
>>> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>>> ---
>>>
>>> Changes since v1:
>>> * drop the auto-detect and just require the "--human" option for the
>>>   different format of the output. This prevents any confusion with
>>>   default output behavior (Yasunori)
>>>
>>> * include the JEDEC size in addition to the IEC size (i.e. MiB vs MB
>>>   etc) (Dave).
>>
>> I'd rather see us pick one or the other and not provide two values.
>> Even though this is the human friendly output, it's possible that it could
>> be used by other programs that know what MB means and know what MiB means
>> but now would need to know that / doesn't mean to divide one by the other.
>> I think as long as the units are clear, either one is ok.
>
> I started there, the thing that tipped it for me is that fio is
> currently outputting both formats when it displays size data. A script
> or another program should not be using the "--human" option, that
> should eliminate the opportunity for confusion.

How about this to get rid of the "/" character and more closely match fio?

{
  "dev":"region7",
  "size":"64.00 MiB (67.11 MB)",
  "available_size":"64.00 MiB (67.11 MB)",
  "type":"pmem",
  "iset_id":"0xa76c6907811fae57",
  "badblock_count":8
}
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 19:56           ` Dan Williams
@ 2017-07-10 20:11             ` Linda Knippers
  2017-07-10 20:14               ` [ndctl PATCH v3] " Dan Williams
  0 siblings, 1 reply; 11+ messages in thread
From: Linda Knippers @ 2017-07-10 20:11 UTC (permalink / raw)
  To: Dan Williams; +Cc: Yasunori Goto, linux-nvdimm



On 7/10/2017 3:56 PM, Dan Williams wrote:
> On Mon, Jul 10, 2017 at 12:47 PM, Dan Williams <dan.j.williams@intel.com> wrote:
>> On Mon, Jul 10, 2017 at 11:42 AM, Linda Knippers <linda.knippers@hpe.com> wrote:
>>> On 07/10/2017 02:26 PM, Dan Williams wrote:
>>>> The json output format of the 'list' commands is meant to make it easy
>>>> to ingest the data into other tools. However, for direct administrator
>>>> use of the utility provide an option to format some numbers for easier
>>>> human consumption, similar to the "-h" to du(1). Note that the short
>>>> option is "-u" since "-h" is already established as the short option for
>>>> "--help".
>>>>
>>>>     Before:
>>>>     # ndctl list --region=7
>>>>     {
>>>>       "dev":"region7",
>>>>       "size":67108864,
>>>>       "available_size":67108864,
>>>>       "type":"pmem",
>>>>       "iset_id":-6382611090938810793,
>>>>       "badblock_count":8
>>>>     }
>>>>
>>>>     After:
>>>>     # ndctl list --region=7 --human
>>>>     {
>>>>       "dev":"region7",
>>>>       "size":"64.00 MiB / 67.11 MB",
>>>>       "available_size":"64.00 MiB / 67.11 MB",
>>>>       "type":"pmem",
>>>>       "iset_id":"0xa76c6907811fae57",
>>>>       "badblock_count":8
>>>>     }
>>>>
>>>> Cc: Dave Jiang <dave.jiang@intel.com>
>>>> Reported-by: Linda Knippers <linda.knippers@hpe.com>
>>>> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
>>>> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>>>> ---
>>>>
>>>> Changes since v1:
>>>> * drop the auto-detect and just require the "--human" option for the
>>>>   different format of the output. This prevents any confusion with
>>>>   default output behavior (Yasunori)
>>>>
>>>> * include the JEDEC size in addition to the IEC size (i.e. MiB vs MB
>>>>   etc) (Dave).
>>>
>>> I'd rather see us pick one or the other and not provide two values.
>>> Even though this is the human friendly output, it's possible that it could
>>> be used by other programs that know what MB means and know what MiB means
>>> but now would need to know that / doesn't mean to divide one by the other.
>>> I think as long as the units are clear, either one is ok.
>>
>> I started there, the thing that tipped it for me is that fio is
>> currently outputting both formats when it displays size data. A script
>> or another program should not be using the "--human" option, that
>> should eliminate the opportunity for confusion.
>
> How about this to get rid of the "/" character and more closely match fio?
>
> {
>   "dev":"region7",
>   "size":"64.00 MiB (67.11 MB)",
>   "available_size":"64.00 MiB (67.11 MB)",
>   "type":"pmem",
>   "iset_id":"0xa76c6907811fae57",
>   "badblock_count":8
> }
>

That looks better to this human.

Thanks,

-- ljk
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [ndctl PATCH v3] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 20:11             ` Linda Knippers
@ 2017-07-10 20:14               ` Dan Williams
  2017-07-11  5:06                 ` Yasunori Goto
  0 siblings, 1 reply; 11+ messages in thread
From: Dan Williams @ 2017-07-10 20:14 UTC (permalink / raw)
  To: linux-nvdimm; +Cc: Yasunori Goto

The json output format of the 'list' commands is meant to make it easy
to ingest the data into other tools. However, for direct administrator
use of the utility provide an option to format some numbers for easier
human consumption, similar to the "-h" to du(1). Note that the short
option is "-u" since "-h" is already established as the short option for
"--help".

    Before:
    # ndctl list --region=7
    {
      "dev":"region7",
      "size":67108864,
      "available_size":67108864,
      "type":"pmem",
      "iset_id":-6382611090938810793,
      "badblock_count":8
    }

    After:
    # ndctl list --region=7 --human
    {
      "dev":"region7",
      "size":"64.00 MiB (67.11 MB)",
      "available_size":"64.00 MiB (67.11 MB)",
      "type":"pmem",
      "iset_id":"0xa76c6907811fae57",
      "badblock_count":8
    }

Cc: Dave Jiang <dave.jiang@intel.com>
Reported-by: Linda Knippers <linda.knippers@hpe.com>
Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
Changes in v3:
* Switch the size format from "x MiB / y MB" to "x MIB (y MB)" so it
  does not look like a divide operation. (Linda)

 Documentation/daxctl/daxctl-list.txt |   20 +++++++
 Documentation/ndctl/ndctl-list.txt   |   28 +++++++++
 daxctl/list.c                        |    5 ++
 ndctl/list.c                         |   16 ++++-
 ndctl/namespace.c                    |    7 ++
 util/json.c                          |  104 +++++++++++++++++++++++++++++++---
 util/json.h                          |   11 +++-
 7 files changed, 173 insertions(+), 18 deletions(-)

diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
index 6de8d828de27..6249645f4bbd 100644
--- a/Documentation/daxctl/daxctl-list.txt
+++ b/Documentation/daxctl/daxctl-list.txt
@@ -73,6 +73,26 @@ OPTIONS
 --idle::
 	Include idle (not enabled / zero-sized) devices in the listing
 
+-u::
+--human::
+	By default 'daxctl list' will output machine-friendly raw-integer
+	data. Instead, with this flag, numbers representing storage size
+	will be formatted as human readable strings with units, other
+	fields are converted to hexadecimal strings.  Example:
+
+[verse]
+# daxctl list
+{
+  "chardev":"dax1.0",
+  "size":32828817408
+}
+
+# daxctl list --human
+{
+  "chardev":"dax1.0",
+  "size":"30.57 GiB (32.83 GB)"
+}
+
 COPYRIGHT
 ---------
 Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
index fd67d2b3e0ba..cdcf238bcb99 100644
--- a/Documentation/ndctl/ndctl-list.txt
+++ b/Documentation/ndctl/ndctl-list.txt
@@ -180,6 +180,34 @@ include::xable-region-options.txt[]
   ]
 }
 
+-u::
+--human::
+	By default 'ndctl list' will output machine-friendly raw-integer
+	data. Instead, with this flag, numbers representing storage size
+	will be formatted as human readable strings with units, other
+	fields are converted to hexadecimal strings.  Example:
+
+[verse]
+# ndctl list --region=7
+{
+  "dev":"region7",
+  "size":67108864,
+  "available_size":67108864,
+  "type":"pmem",
+  "iset_id":-6382611090938810793,
+  "badblock_count":8
+}
+
+# ndctl list --human --region=7
+{
+  "dev":"region7",
+  "size":"64.00 MiB (67.11 MB)",
+  "available_size":"64.00 MiB (67.11 MB)",
+  "type":"pmem",
+  "iset_id":"0xa76c6907811fae57",
+  "badblock_count":8
+}
+
 COPYRIGHT
 ---------
 Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
diff --git a/daxctl/list.c b/daxctl/list.c
index e213df138f07..678ef6ce4c13 100644
--- a/daxctl/list.c
+++ b/daxctl/list.c
@@ -26,6 +26,7 @@ static struct {
 	bool devs;
 	bool regions;
 	bool idle;
+	bool human;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -36,6 +37,8 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_DAX;
 	if (list.idle)
 		flags |= UTIL_JSON_IDLE;
+	if (list.human)
+		flags |= UTIL_JSON_HUMAN;
 	return flags;
 }
 
@@ -70,6 +73,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
 		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
+		OPT_BOOLEAN('u', "human", &list.human,
+				"use human friendly number formats "),
 		OPT_END(),
 	};
 	const char * const u[] = {
diff --git a/ndctl/list.c b/ndctl/list.c
index 7ecfcc91b0ec..d81d6464ebb1 100644
--- a/ndctl/list.c
+++ b/ndctl/list.c
@@ -37,6 +37,7 @@ static struct {
 	bool health;
 	bool dax;
 	bool media_errors;
+	bool human;
 } list;
 
 static unsigned long listopts_to_flags(void)
@@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
 		flags |= UTIL_JSON_MEDIA_ERRORS;
 	if (list.dax)
 		flags |= UTIL_JSON_DAX;
+	if (list.human)
+		flags |= UTIL_JSON_HUMAN;
 	return flags;
 }
 
@@ -160,12 +163,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 		goto err;
 	json_object_object_add(jregion, "dev", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_size(region));
+	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "size", jobj);
 
-	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
+	jobj = util_json_object_size(ndctl_region_get_available_size(region),
+			flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jregion, "available_size", jobj);
@@ -186,8 +190,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 
 	iset = ndctl_region_get_interleave_set(region);
 	if (iset) {
-		jobj = json_object_new_int64(
-				ndctl_interleave_set_get_cookie(iset));
+		jobj = util_json_object_hex(
+				ndctl_interleave_set_get_cookie(iset), flags);
 		if (!jobj)
 			fail("\n");
 		else
@@ -216,7 +220,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
 			json_object_object_add(jregion, "mappings", jmappings);
 		}
 
-		jmapping = util_mapping_to_json(mapping);
+		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
 		if (!jmapping) {
 			fail("\n");
 			continue;
@@ -282,6 +286,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
 		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
 		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
 				"include media errors"),
+		OPT_BOOLEAN('u', "human", &list.human,
+				"use human friendly number formats "),
 		OPT_END(),
 	};
 	const char * const u[] = {
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index b28936cfd3ec..7b0198f11cad 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
 		error("%s: failed to enable\n",
 				ndctl_namespace_get_devname(ndns));
 	} else {
-		struct json_object *jndns = util_namespace_to_json(ndns,
-				UTIL_JSON_DAX);
+		unsigned long flags = UTIL_JSON_DAX;
+		struct json_object *jndns;
 
+		if (isatty(1))
+			flags |= UTIL_JSON_HUMAN;
+		jndns = util_namespace_to_json(ndns, flags);
 		if (jndns)
 			printf("%s\n", json_object_to_json_string_ext(jndns,
 						JSON_C_TO_STRING_PRETTY));
diff --git a/util/json.c b/util/json.c
index 1863bca10121..0878979bb8de 100644
--- a/util/json.c
+++ b/util/json.c
@@ -11,10 +11,12 @@
  * General Public License for more details.
  */
 #include <limits.h>
+#include <string.h>
 #include <util/json.h>
 #include <util/filter.h>
 #include <uuid/uuid.h>
 #include <json-c/json.h>
+#include <json-c/printbuf.h>
 #include <ndctl/libndctl.h>
 #include <daxctl/libdaxctl.h>
 #include <ccan/array_size/array_size.h>
@@ -25,6 +27,88 @@
 #include <ndctl.h>
 #endif
 
+/* adapted from mdadm::human_size_brief() */
+static int display_size(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long bytes = json_object_get_int64(jobj);
+	static char buf[128];
+	int c;
+
+	/*
+	 * We convert bytes to either centi-M{ega,ibi}bytes or
+	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
+	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
+	 * converting to gigabytes, as that shows more precision and isn't too
+	 * large a number.  Terabytes are not yet handled.
+	 *
+	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
+	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
+	 */
+
+	if (bytes < 5000*1024)
+		snprintf(buf, sizeof(buf), "%lld", bytes);
+	else {
+		/* IEC */
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
+
+			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB",
+					cMiB/100 , cMiB % 100);
+		} else {
+			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
+
+			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB",
+					cGiB/100 , cGiB % 100);
+		}
+
+		/* JEDEC */
+		if (bytes < 2*1024LL*1024LL*1024LL) {
+			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
+
+			snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld MB)\"",
+					cMB/100, cMB % 100);
+		} else {
+			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
+
+			snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld GB)\"",
+					cGB/100 , cGB % 100);
+		}
+	}
+
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
+		int level, int flags)
+{
+	unsigned long long val = json_object_get_int64(jobj);
+	static char buf[32];
+
+	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
+	return printbuf_memappend(pbuf, buf, strlen(buf));
+}
+
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(size);
+
+	if (jobj && (flags & UTIL_JSON_HUMAN))
+		json_object_set_serializer(jobj, display_size, NULL, NULL);
+	return jobj;
+}
+
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags)
+{
+	struct json_object *jobj = json_object_new_int64(val);
+
+	if (jobj && (flags & UTIL_JSON_HUMAN))
+		json_object_set_serializer(jobj, display_hex, NULL, NULL);
+	return jobj;
+}
+
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
 {
 	int len = json_object_array_length(jarray);
@@ -140,7 +224,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
 	return NULL;
 }
 
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags)
 {
 	const char *devname = daxctl_dev_get_devname(dev);
 	struct json_object *jdev, *jobj;
@@ -153,7 +238,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
 	if (jobj)
 		json_object_object_add(jdev, "chardev", jobj);
 
-	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
+	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
 	if (jobj)
 		json_object_object_add(jdev, "size", jobj);
 
@@ -181,7 +266,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 				return NULL;
 		}
 
-		jdev = util_daxctl_dev_to_json(dev);
+		jdev = util_daxctl_dev_to_json(dev, flags);
 		if (!jdev) {
 			json_object_put(jdevs);
 			return NULL;
@@ -211,7 +296,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	size = daxctl_region_get_size(region);
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "size", jobj);
@@ -219,7 +304,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 
 	available_size = daxctl_region_get_available_size(region);
 	if (available_size) {
-		jobj = json_object_new_int64(available_size);
+		jobj = util_json_object_size(available_size, flags);
 		if (!jobj)
 			goto err;
 		json_object_object_add(jregion, "available_size", jobj);
@@ -490,7 +575,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		json_object_object_add(jndns, "mode", jobj);
 
 	if (size < ULLONG_MAX) {
-		jobj = json_object_new_int64(size);
+		jobj = util_json_object_size(size, flags);
 		if (jobj)
 			json_object_object_add(jndns, "size", jobj);
 	}
@@ -604,7 +689,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 	return NULL;
 }
 
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags)
 {
 	struct json_object *jmapping = json_object_new_object();
 	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
@@ -618,12 +704,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
 		goto err;
 	json_object_object_add(jmapping, "dimm", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "offset", jobj);
 
-	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
+	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
 	if (!jobj)
 		goto err;
 	json_object_object_add(jmapping, "length", jobj);
diff --git a/util/json.h b/util/json.h
index 19d5ffc4376e..ec394b636cff 100644
--- a/util/json.h
+++ b/util/json.h
@@ -20,13 +20,15 @@ enum util_json_flags {
 	UTIL_JSON_IDLE = (1 << 0),
 	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
 	UTIL_JSON_DAX = (1 << 2),
+	UTIL_JSON_HUMAN = (1 << 3),
 };
 
 struct json_object;
 void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
 struct json_object *util_bus_to_json(struct ndctl_bus *bus);
 struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
+struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
+		unsigned long flags);
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		unsigned long flags);
 struct daxctl_region;
@@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
 		unsigned int *bb_count, unsigned long flags);
 struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
 		const char *ident, unsigned long flags);
-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
+struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
+		unsigned long flags);
 struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
 		struct json_object *jdevs, const char *ident,
 		unsigned long flags);
+struct json_object *util_json_object_size(unsigned long long size,
+		unsigned long flags);
+struct json_object *util_json_object_hex(unsigned long long val,
+		unsigned long flags);
 #ifdef HAVE_NDCTL_SMART
 struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
 #else

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [ndctl PATCH v3] ndctl, daxctl, list: add --human option for number formatting
  2017-07-10 20:14               ` [ndctl PATCH v3] " Dan Williams
@ 2017-07-11  5:06                 ` Yasunori Goto
  0 siblings, 0 replies; 11+ messages in thread
From: Yasunori Goto @ 2017-07-11  5:06 UTC (permalink / raw)
  To: Dan Williams; +Cc: linux-nvdimm


Looks good to me. Thanks.


> The json output format of the 'list' commands is meant to make it easy
> to ingest the data into other tools. However, for direct administrator
> use of the utility provide an option to format some numbers for easier
> human consumption, similar to the "-h" to du(1). Note that the short
> option is "-u" since "-h" is already established as the short option for
> "--help".
> 
>     Before:
>     # ndctl list --region=7
>     {
>       "dev":"region7",
>       "size":67108864,
>       "available_size":67108864,
>       "type":"pmem",
>       "iset_id":-6382611090938810793,
>       "badblock_count":8
>     }
> 
>     After:
>     # ndctl list --region=7 --human
>     {
>       "dev":"region7",
>       "size":"64.00 MiB (67.11 MB)",
>       "available_size":"64.00 MiB (67.11 MB)",
>       "type":"pmem",
>       "iset_id":"0xa76c6907811fae57",
>       "badblock_count":8
>     }
> 
> Cc: Dave Jiang <dave.jiang@intel.com>
> Reported-by: Linda Knippers <linda.knippers@hpe.com>
> Reported-by: Yasunori Goto <y-goto@jp.fujitsu.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
> Changes in v3:
> * Switch the size format from "x MiB / y MB" to "x MIB (y MB)" so it
>   does not look like a divide operation. (Linda)
> 
>  Documentation/daxctl/daxctl-list.txt |   20 +++++++
>  Documentation/ndctl/ndctl-list.txt   |   28 +++++++++
>  daxctl/list.c                        |    5 ++
>  ndctl/list.c                         |   16 ++++-
>  ndctl/namespace.c                    |    7 ++
>  util/json.c                          |  104 +++++++++++++++++++++++++++++++---
>  util/json.h                          |   11 +++-
>  7 files changed, 173 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/daxctl/daxctl-list.txt b/Documentation/daxctl/daxctl-list.txt
> index 6de8d828de27..6249645f4bbd 100644
> --- a/Documentation/daxctl/daxctl-list.txt
> +++ b/Documentation/daxctl/daxctl-list.txt
> @@ -73,6 +73,26 @@ OPTIONS
>  --idle::
>  	Include idle (not enabled / zero-sized) devices in the listing
>  
> +-u::
> +--human::
> +	By default 'daxctl list' will output machine-friendly raw-integer
> +	data. Instead, with this flag, numbers representing storage size
> +	will be formatted as human readable strings with units, other
> +	fields are converted to hexadecimal strings.  Example:
> +
> +[verse]
> +# daxctl list
> +{
> +  "chardev":"dax1.0",
> +  "size":32828817408
> +}
> +
> +# daxctl list --human
> +{
> +  "chardev":"dax1.0",
> +  "size":"30.57 GiB (32.83 GB)"
> +}
> +
>  COPYRIGHT
>  ---------
>  Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
> index fd67d2b3e0ba..cdcf238bcb99 100644
> --- a/Documentation/ndctl/ndctl-list.txt
> +++ b/Documentation/ndctl/ndctl-list.txt
> @@ -180,6 +180,34 @@ include::xable-region-options.txt[]
>    ]
>  }
>  
> +-u::
> +--human::
> +	By default 'ndctl list' will output machine-friendly raw-integer
> +	data. Instead, with this flag, numbers representing storage size
> +	will be formatted as human readable strings with units, other
> +	fields are converted to hexadecimal strings.  Example:
> +
> +[verse]
> +# ndctl list --region=7
> +{
> +  "dev":"region7",
> +  "size":67108864,
> +  "available_size":67108864,
> +  "type":"pmem",
> +  "iset_id":-6382611090938810793,
> +  "badblock_count":8
> +}
> +
> +# ndctl list --human --region=7
> +{
> +  "dev":"region7",
> +  "size":"64.00 MiB (67.11 MB)",
> +  "available_size":"64.00 MiB (67.11 MB)",
> +  "type":"pmem",
> +  "iset_id":"0xa76c6907811fae57",
> +  "badblock_count":8
> +}
> +
>  COPYRIGHT
>  ---------
>  Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> diff --git a/daxctl/list.c b/daxctl/list.c
> index e213df138f07..678ef6ce4c13 100644
> --- a/daxctl/list.c
> +++ b/daxctl/list.c
> @@ -26,6 +26,7 @@ static struct {
>  	bool devs;
>  	bool regions;
>  	bool idle;
> +	bool human;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -36,6 +37,8 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_DAX;
>  	if (list.idle)
>  		flags |= UTIL_JSON_IDLE;
> +	if (list.human)
> +		flags |= UTIL_JSON_HUMAN;
>  	return flags;
>  }
>  
> @@ -70,6 +73,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"),
>  		OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"),
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
> +		OPT_BOOLEAN('u', "human", &list.human,
> +				"use human friendly number formats "),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> diff --git a/ndctl/list.c b/ndctl/list.c
> index 7ecfcc91b0ec..d81d6464ebb1 100644
> --- a/ndctl/list.c
> +++ b/ndctl/list.c
> @@ -37,6 +37,7 @@ static struct {
>  	bool health;
>  	bool dax;
>  	bool media_errors;
> +	bool human;
>  } list;
>  
>  static unsigned long listopts_to_flags(void)
> @@ -49,6 +50,8 @@ static unsigned long listopts_to_flags(void)
>  		flags |= UTIL_JSON_MEDIA_ERRORS;
>  	if (list.dax)
>  		flags |= UTIL_JSON_DAX;
> +	if (list.human)
> +		flags |= UTIL_JSON_HUMAN;
>  	return flags;
>  }
>  
> @@ -160,12 +163,13 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  		goto err;
>  	json_object_object_add(jregion, "dev", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_size(region), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "size", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_region_get_available_size(region));
> +	jobj = util_json_object_size(ndctl_region_get_available_size(region),
> +			flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jregion, "available_size", jobj);
> @@ -186,8 +190,8 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  
>  	iset = ndctl_region_get_interleave_set(region);
>  	if (iset) {
> -		jobj = json_object_new_int64(
> -				ndctl_interleave_set_get_cookie(iset));
> +		jobj = util_json_object_hex(
> +				ndctl_interleave_set_get_cookie(iset), flags);
>  		if (!jobj)
>  			fail("\n");
>  		else
> @@ -216,7 +220,7 @@ static struct json_object *region_to_json(struct ndctl_region *region,
>  			json_object_object_add(jregion, "mappings", jmappings);
>  		}
>  
> -		jmapping = util_mapping_to_json(mapping);
> +		jmapping = util_mapping_to_json(mapping, listopts_to_flags());
>  		if (!jmapping) {
>  			fail("\n");
>  			continue;
> @@ -282,6 +286,8 @@ int cmd_list(int argc, const char **argv, void *ctx)
>  		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
>  		OPT_BOOLEAN('M', "media-errors", &list.media_errors,
>  				"include media errors"),
> +		OPT_BOOLEAN('u', "human", &list.human,
> +				"use human friendly number formats "),
>  		OPT_END(),
>  	};
>  	const char * const u[] = {
> diff --git a/ndctl/namespace.c b/ndctl/namespace.c
> index b28936cfd3ec..7b0198f11cad 100644
> --- a/ndctl/namespace.c
> +++ b/ndctl/namespace.c
> @@ -397,9 +397,12 @@ static int setup_namespace(struct ndctl_region *region,
>  		error("%s: failed to enable\n",
>  				ndctl_namespace_get_devname(ndns));
>  	} else {
> -		struct json_object *jndns = util_namespace_to_json(ndns,
> -				UTIL_JSON_DAX);
> +		unsigned long flags = UTIL_JSON_DAX;
> +		struct json_object *jndns;
>  
> +		if (isatty(1))
> +			flags |= UTIL_JSON_HUMAN;
> +		jndns = util_namespace_to_json(ndns, flags);
>  		if (jndns)
>  			printf("%s\n", json_object_to_json_string_ext(jndns,
>  						JSON_C_TO_STRING_PRETTY));
> diff --git a/util/json.c b/util/json.c
> index 1863bca10121..0878979bb8de 100644
> --- a/util/json.c
> +++ b/util/json.c
> @@ -11,10 +11,12 @@
>   * General Public License for more details.
>   */
>  #include <limits.h>
> +#include <string.h>
>  #include <util/json.h>
>  #include <util/filter.h>
>  #include <uuid/uuid.h>
>  #include <json-c/json.h>
> +#include <json-c/printbuf.h>
>  #include <ndctl/libndctl.h>
>  #include <daxctl/libdaxctl.h>
>  #include <ccan/array_size/array_size.h>
> @@ -25,6 +27,88 @@
>  #include <ndctl.h>
>  #endif
>  
> +/* adapted from mdadm::human_size_brief() */
> +static int display_size(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long bytes = json_object_get_int64(jobj);
> +	static char buf[128];
> +	int c;
> +
> +	/*
> +	 * We convert bytes to either centi-M{ega,ibi}bytes or
> +	 * centi-G{igi,ibi}bytes, with appropriate rounding, and then print
> +	 * 1/100th of those as a decimal.  We allow upto 2048Megabytes before
> +	 * converting to gigabytes, as that shows more precision and isn't too
> +	 * large a number.  Terabytes are not yet handled.
> +	 *
> +	 * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
> +	 * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
> +	 */
> +
> +	if (bytes < 5000*1024)
> +		snprintf(buf, sizeof(buf), "%lld", bytes);
> +	else {
> +		/* IEC */
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
> +
> +			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB",
> +					cMiB/100 , cMiB % 100);
> +		} else {
> +			long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
> +
> +			c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB",
> +					cGiB/100 , cGiB % 100);
> +		}
> +
> +		/* JEDEC */
> +		if (bytes < 2*1024LL*1024LL*1024LL) {
> +			long cMB  = (bytes / (1000000LL / 200LL) + 1) / 2;
> +
> +			snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld MB)\"",
> +					cMB/100, cMB % 100);
> +		} else {
> +			long cGB  = (bytes / (1000000000LL/200LL) + 1) / 2;
> +
> +			snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld GB)\"",
> +					cGB/100 , cGB % 100);
> +		}
> +	}
> +
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +static int display_hex(struct json_object *jobj, struct printbuf *pbuf,
> +		int level, int flags)
> +{
> +	unsigned long long val = json_object_get_int64(jobj);
> +	static char buf[32];
> +
> +	snprintf(buf, sizeof(buf), "\"%#llx\"", val);
> +	return printbuf_memappend(pbuf, buf, strlen(buf));
> +}
> +
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(size);
> +
> +	if (jobj && (flags & UTIL_JSON_HUMAN))
> +		json_object_set_serializer(jobj, display_size, NULL, NULL);
> +	return jobj;
> +}
> +
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags)
> +{
> +	struct json_object *jobj = json_object_new_int64(val);
> +
> +	if (jobj && (flags & UTIL_JSON_HUMAN))
> +		json_object_set_serializer(jobj, display_hex, NULL, NULL);
> +	return jobj;
> +}
> +
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag)
>  {
>  	int len = json_object_array_length(jarray);
> @@ -140,7 +224,8 @@ struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm)
>  	return NULL;
>  }
>  
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags)
>  {
>  	const char *devname = daxctl_dev_get_devname(dev);
>  	struct json_object *jdev, *jobj;
> @@ -153,7 +238,7 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev)
>  	if (jobj)
>  		json_object_object_add(jdev, "chardev", jobj);
>  
> -	jobj = json_object_new_int64(daxctl_dev_get_size(dev));
> +	jobj = util_json_object_size(daxctl_dev_get_size(dev), flags);
>  	if (jobj)
>  		json_object_object_add(jdev, "size", jobj);
>  
> @@ -181,7 +266,7 @@ struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  				return NULL;
>  		}
>  
> -		jdev = util_daxctl_dev_to_json(dev);
> +		jdev = util_daxctl_dev_to_json(dev, flags);
>  		if (!jdev) {
>  			json_object_put(jdevs);
>  			return NULL;
> @@ -211,7 +296,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	size = daxctl_region_get_size(region);
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "size", jobj);
> @@ -219,7 +304,7 @@ struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  
>  	available_size = daxctl_region_get_available_size(region);
>  	if (available_size) {
> -		jobj = json_object_new_int64(available_size);
> +		jobj = util_json_object_size(available_size, flags);
>  		if (!jobj)
>  			goto err;
>  		json_object_object_add(jregion, "available_size", jobj);
> @@ -490,7 +575,7 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		json_object_object_add(jndns, "mode", jobj);
>  
>  	if (size < ULLONG_MAX) {
> -		jobj = json_object_new_int64(size);
> +		jobj = util_json_object_size(size, flags);
>  		if (jobj)
>  			json_object_object_add(jndns, "size", jobj);
>  	}
> @@ -604,7 +689,8 @@ struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  	return NULL;
>  }
>  
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags)
>  {
>  	struct json_object *jmapping = json_object_new_object();
>  	struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping);
> @@ -618,12 +704,12 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping)
>  		goto err;
>  	json_object_object_add(jmapping, "dimm", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_offset(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "offset", jobj);
>  
> -	jobj = json_object_new_int64(ndctl_mapping_get_length(mapping));
> +	jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags);
>  	if (!jobj)
>  		goto err;
>  	json_object_object_add(jmapping, "length", jobj);
> diff --git a/util/json.h b/util/json.h
> index 19d5ffc4376e..ec394b636cff 100644
> --- a/util/json.h
> +++ b/util/json.h
> @@ -20,13 +20,15 @@ enum util_json_flags {
>  	UTIL_JSON_IDLE = (1 << 0),
>  	UTIL_JSON_MEDIA_ERRORS = (1 << 1),
>  	UTIL_JSON_DAX = (1 << 2),
> +	UTIL_JSON_HUMAN = (1 << 3),
>  };
>  
>  struct json_object;
>  void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag);
>  struct json_object *util_bus_to_json(struct ndctl_bus *bus);
>  struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm);
> -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping);
> +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
> +		unsigned long flags);
>  struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
>  		unsigned long flags);
>  struct daxctl_region;
> @@ -35,10 +37,15 @@ struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
>  		unsigned int *bb_count, unsigned long flags);
>  struct json_object *util_daxctl_region_to_json(struct daxctl_region *region,
>  		const char *ident, unsigned long flags);
> -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev);
> +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev,
> +		unsigned long flags);
>  struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region,
>  		struct json_object *jdevs, const char *ident,
>  		unsigned long flags);
> +struct json_object *util_json_object_size(unsigned long long size,
> +		unsigned long flags);
> +struct json_object *util_json_object_hex(unsigned long long val,
> +		unsigned long flags);
>  #ifdef HAVE_NDCTL_SMART
>  struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
>  #else
> 
> _______________________________________________
> Linux-nvdimm mailing list
> Linux-nvdimm@lists.01.org
> https://lists.01.org/mailman/listinfo/linux-nvdimm


_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2017-07-11  5:04 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-07 20:37 [ndctl PATCH 0/2] Be kind to humans Dan Williams
2017-07-07 20:37 ` [ndctl PATCH 1/2] ndctl, list: convert json control flags to bit-flags Dan Williams
2017-07-07 20:37 ` [ndctl PATCH 2/2] ndctl, daxctl, list: make terminal output human readable by default Dan Williams
2017-07-10  6:56   ` Yasunori Goto
2017-07-10 18:26     ` [ndctl PATCH v2] ndctl, daxctl, list: add --human option for number formatting Dan Williams
2017-07-10 18:42       ` Linda Knippers
2017-07-10 19:47         ` Dan Williams
2017-07-10 19:56           ` Dan Williams
2017-07-10 20:11             ` Linda Knippers
2017-07-10 20:14               ` [ndctl PATCH v3] " Dan Williams
2017-07-11  5:06                 ` Yasunori Goto

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).