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 <[email protected]>
> Reported-by: Linda Knippers <[email protected]>
> Reported-by: Yasunori Goto <[email protected]>
> Signed-off-by: Dan Williams <[email protected]>
> ---
> 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
> [email protected]
> https://lists.01.org/mailman/listinfo/linux-nvdimm


_______________________________________________
Linux-nvdimm mailing list
[email protected]
https://lists.01.org/mailman/listinfo/linux-nvdimm

Reply via email to