From 965fa02e372fe3a05acc9ae8379f6dd796b7346d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 5 Jan 2022 13:32:26 -0800 Subject: [PATCH 077/217] util: Distribute 'filter' and 'json' helpers to per-tool objects In preparation for switching build systems, fix the long standing wart of mixing ndctl, daxctl, and cxl 'filter' and 'json' utilities in the top-level util/filter.[ch]. Distribute them to their respective {ndctl,daxctl,cxl}/filter.{c,h} locations. This also removes the naming collisions for util/json.h between util/ and ndct/util/. I.e. is no longer ambiguous or subject to being shadowed by the tool local "util" directory. Unfortunately unwinding this caused a lot of code to move all at once. The benefit is that now it is clear that ndctl is the only tool that reaches across into the 'filter' and 'json' functionality of another tool (daxctl). Link: https://lore.kernel.org/r/164141834691.3990253.16681105368577841032.stgit@dwillia2-desk3.amr.corp.intel.com Tested-by: Alison Schofield Tested-by: Vaibhav Jain Signed-off-by: Dan Williams Signed-off-by: Vishal Verma --- Makefile.am | 1 - Makefile.am.in | 3 - cxl/Makefile.am | 3 + cxl/filter.c | 25 + cxl/filter.h | 7 + cxl/json.c | 214 +++++ cxl/json.h | 8 + cxl/list.c | 4 +- cxl/memdev.c | 3 +- daxctl/Makefile.am | 5 + daxctl/device.c | 4 +- daxctl/filter.c | 43 + daxctl/filter.h | 12 + daxctl/json.c | 245 ++++++ daxctl/json.h | 18 + daxctl/list.c | 4 +- ndctl/Makefile.am | 16 +- ndctl/bus.c | 4 +- ndctl/check.c | 2 +- ndctl/dimm.c | 6 +- {util => ndctl}/filter.c | 60 +- {util => ndctl}/filter.h | 12 +- ndctl/inject-error.c | 6 +- ndctl/inject-smart.c | 6 +- ndctl/{util => }/json-smart.c | 5 +- ndctl/json.c | 1114 ++++++++++++++++++++++++ ndctl/json.h | 24 + ndctl/{util => }/keys.c | 5 +- ndctl/{util => }/keys.h | 0 ndctl/lib/libndctl.c | 2 +- ndctl/lib/papr.c | 4 +- ndctl/lib/private.h | 4 +- ndctl/list.c | 5 +- ndctl/load-keys.c | 7 +- ndctl/monitor.c | 4 +- ndctl/namespace.c | 6 +- ndctl/region.c | 3 +- test/Makefile.am | 22 +- test/ack-shutdown-count-set.c | 2 +- test/daxdev-errors.c | 2 +- test/device-dax.c | 2 +- test/dsm-fail.c | 4 +- test/libndctl.c | 2 +- test/list-smart-dimm.c | 6 +- test/pmem_namespaces.c | 2 +- test/revoke-devmem.c | 2 +- util/help.c | 2 +- util/json.c | 1542 +-------------------------------- util/json.h | 39 +- 49 files changed, 1820 insertions(+), 1701 deletions(-) create mode 100644 cxl/filter.c create mode 100644 cxl/filter.h create mode 100644 cxl/json.c create mode 100644 cxl/json.h create mode 100644 daxctl/filter.c create mode 100644 daxctl/filter.h create mode 100644 daxctl/json.c create mode 100644 daxctl/json.h rename {util => ndctl}/filter.c (88%) rename {util => ndctl}/filter.h (89%) rename ndctl/{util => }/json-smart.c (99%) create mode 100644 ndctl/json.c create mode 100644 ndctl/json.h rename ndctl/{util => }/keys.c (99%) rename ndctl/{util => }/keys.h (100%) diff -up ndctl-71.1/Makefile.am.in.orig ndctl-71.1/Makefile.am.in --- ndctl-71.1/Makefile.am.in.orig 2022-10-07 15:50:40.071454550 -0400 +++ ndctl-71.1/Makefile.am.in 2022-10-07 15:51:03.900535680 -0400 @@ -9,9 +9,6 @@ AM_CPPFLAGS = \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DPREFIX=\""$(prefix)"\" \ -DNDCTL_MAN_PATH=\""$(mandir)"\" \ - -I${top_srcdir}/ndctl/lib \ - -I${top_srcdir}/ndctl \ - -I${top_srcdir}/cxl \ -I${top_srcdir}/ \ $(KMOD_CFLAGS) \ $(UDEV_CFLAGS) \ diff -up ndctl-71.1/Makefile.am.orig ndctl-71.1/Makefile.am --- ndctl-71.1/Makefile.am.orig 2022-10-07 15:50:40.071454550 -0400 +++ ndctl-71.1/Makefile.am 2022-10-07 15:51:03.900535680 -0400 @@ -84,7 +84,6 @@ libutil_a_SOURCES = \ util/help.c \ util/strbuf.c \ util/wrapper.c \ - util/filter.c \ util/bitmap.c \ util/abspath.c \ util/iomem.c \ diff -up ndctl-71.1/cxl/Makefile.am.orig ndctl-71.1/cxl/Makefile.am --- ndctl-71.1/cxl/Makefile.am.orig 2022-10-07 15:50:40.071454550 -0400 +++ ndctl-71.1/cxl/Makefile.am 2022-10-07 15:51:03.900535680 -0400 @@ -12,6 +12,9 @@ cxl_SOURCES =\ list.c \ memdev.c \ ../util/json.c \ + json.c \ + filter.c \ + filter.h \ builtin.h cxl_LDADD =\ diff -up ndctl-71.1/cxl/filter.c.orig ndctl-71.1/cxl/filter.c --- ndctl-71.1/cxl/filter.c.orig 2022-10-07 15:51:03.900535680 -0400 +++ ndctl-71.1/cxl/filter.c 2022-10-07 15:51:03.900535680 -0400 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include "filter.h" + +struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + const char *ident) +{ + int memdev_id; + + if (!ident || strcmp(ident, "all") == 0) + return memdev; + + if (strcmp(ident, cxl_memdev_get_devname(memdev)) == 0) + return memdev; + + if ((sscanf(ident, "%d", &memdev_id) == 1 + || sscanf(ident, "mem%d", &memdev_id) == 1) + && cxl_memdev_get_id(memdev) == memdev_id) + return memdev; + + return NULL; +} diff -up ndctl-71.1/cxl/filter.h.orig ndctl-71.1/cxl/filter.h --- ndctl-71.1/cxl/filter.h.orig 2022-10-07 15:51:03.901535684 -0400 +++ ndctl-71.1/cxl/filter.h 2022-10-07 15:51:03.901535684 -0400 @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef _CXL_UTIL_FILTER_H_ +#define _CXL_UTIL_FILTER_H_ +struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + const char *ident); +#endif /* _CXL_UTIL_FILTER_H_ */ diff -up ndctl-71.1/cxl/json.c.orig ndctl-71.1/cxl/json.c --- ndctl-71.1/cxl/json.c.orig 2022-10-07 15:51:03.901535684 -0400 +++ ndctl-71.1/cxl/json.c 2022-10-07 15:51:03.901535684 -0400 @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include +#include +#include + +#include "json.h" + +static struct json_object *util_cxl_memdev_health_to_json( + struct cxl_memdev *memdev, unsigned long flags) +{ + struct json_object *jhealth; + struct json_object *jobj; + struct cxl_cmd *cmd; + u32 field; + int rc; + + jhealth = json_object_new_object(); + if (!jhealth) + return NULL; + if (!memdev) + goto err_jobj; + + cmd = cxl_cmd_new_get_health_info(memdev); + if (!cmd) + goto err_jobj; + + rc = cxl_cmd_submit(cmd); + if (rc < 0) + goto err_cmd; + rc = cxl_cmd_get_mbox_status(cmd); + if (rc != 0) + goto err_cmd; + + /* health_status fields */ + rc = cxl_cmd_health_info_get_maintenance_needed(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "maintenance_needed", jobj); + + rc = cxl_cmd_health_info_get_performance_degraded(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "performance_degraded", jobj); + + rc = cxl_cmd_health_info_get_hw_replacement_needed(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "hw_replacement_needed", jobj); + + /* media_status fields */ + rc = cxl_cmd_health_info_get_media_normal(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_normal", jobj); + + rc = cxl_cmd_health_info_get_media_not_ready(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_not_ready", jobj); + + rc = cxl_cmd_health_info_get_media_persistence_lost(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_persistence_lost", jobj); + + rc = cxl_cmd_health_info_get_media_data_lost(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_data_lost", jobj); + + rc = cxl_cmd_health_info_get_media_powerloss_persistence_loss(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_powerloss_persistence_loss", jobj); + + rc = cxl_cmd_health_info_get_media_shutdown_persistence_loss(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_shutdown_persistence_loss", jobj); + + rc = cxl_cmd_health_info_get_media_persistence_loss_imminent(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_persistence_loss_imminent", jobj); + + rc = cxl_cmd_health_info_get_media_powerloss_data_loss(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_powerloss_data_loss", jobj); + + rc = cxl_cmd_health_info_get_media_shutdown_data_loss(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_shutdown_data_loss", jobj); + + rc = cxl_cmd_health_info_get_media_data_loss_imminent(cmd); + jobj = json_object_new_boolean(rc); + if (jobj) + json_object_object_add(jhealth, "media_data_loss_imminent", jobj); + + /* ext_status fields */ + if (cxl_cmd_health_info_get_ext_life_used_normal(cmd)) + jobj = json_object_new_string("normal"); + else if (cxl_cmd_health_info_get_ext_life_used_warning(cmd)) + jobj = json_object_new_string("warning"); + else if (cxl_cmd_health_info_get_ext_life_used_critical(cmd)) + jobj = json_object_new_string("critical"); + else + jobj = json_object_new_string("unknown"); + if (jobj) + json_object_object_add(jhealth, "ext_life_used", jobj); + + if (cxl_cmd_health_info_get_ext_temperature_normal(cmd)) + jobj = json_object_new_string("normal"); + else if (cxl_cmd_health_info_get_ext_temperature_warning(cmd)) + jobj = json_object_new_string("warning"); + else if (cxl_cmd_health_info_get_ext_temperature_critical(cmd)) + jobj = json_object_new_string("critical"); + else + jobj = json_object_new_string("unknown"); + if (jobj) + json_object_object_add(jhealth, "ext_temperature", jobj); + + if (cxl_cmd_health_info_get_ext_corrected_volatile_normal(cmd)) + jobj = json_object_new_string("normal"); + else if (cxl_cmd_health_info_get_ext_corrected_volatile_warning(cmd)) + jobj = json_object_new_string("warning"); + else + jobj = json_object_new_string("unknown"); + if (jobj) + json_object_object_add(jhealth, "ext_corrected_volatile", jobj); + + if (cxl_cmd_health_info_get_ext_corrected_persistent_normal(cmd)) + jobj = json_object_new_string("normal"); + else if (cxl_cmd_health_info_get_ext_corrected_persistent_warning(cmd)) + jobj = json_object_new_string("warning"); + else + jobj = json_object_new_string("unknown"); + if (jobj) + json_object_object_add(jhealth, "ext_corrected_persistent", jobj); + + /* other fields */ + field = cxl_cmd_health_info_get_life_used(cmd); + if (field != 0xff) { + jobj = json_object_new_int(field); + if (jobj) + json_object_object_add(jhealth, "life_used_percent", jobj); + } + + field = cxl_cmd_health_info_get_temperature(cmd); + if (field != 0xffff) { + jobj = json_object_new_int(field); + if (jobj) + json_object_object_add(jhealth, "temperature", jobj); + } + + field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); + jobj = json_object_new_int64(field); + if (jobj) + json_object_object_add(jhealth, "dirty_shutdowns", jobj); + + field = cxl_cmd_health_info_get_volatile_errors(cmd); + jobj = json_object_new_int64(field); + if (jobj) + json_object_object_add(jhealth, "volatile_errors", jobj); + + field = cxl_cmd_health_info_get_pmem_errors(cmd); + jobj = json_object_new_int64(field); + if (jobj) + json_object_object_add(jhealth, "pmem_errors", jobj); + + cxl_cmd_unref(cmd); + return jhealth; + +err_cmd: + cxl_cmd_unref(cmd); +err_jobj: + json_object_put(jhealth); + return NULL; +} + +struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + unsigned long flags) +{ + const char *devname = cxl_memdev_get_devname(memdev); + struct json_object *jdev, *jobj; + + jdev = json_object_new_object(); + if (!devname || !jdev) + return NULL; + + jobj = json_object_new_string(devname); + if (jobj) + json_object_object_add(jdev, "memdev", jobj); + + jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags); + if (jobj) + json_object_object_add(jdev, "pmem_size", jobj); + + jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags); + if (jobj) + json_object_object_add(jdev, "ram_size", jobj); + + if (flags & UTIL_JSON_HEALTH) { + jobj = util_cxl_memdev_health_to_json(memdev, flags); + if (jobj) + json_object_object_add(jdev, "health", jobj); + } + return jdev; +} diff -up ndctl-71.1/cxl/json.h.orig ndctl-71.1/cxl/json.h --- ndctl-71.1/cxl/json.h.orig 2022-10-07 15:51:03.901535684 -0400 +++ ndctl-71.1/cxl/json.h 2022-10-07 15:51:03.901535684 -0400 @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef __CXL_UTIL_JSON_H__ +#define __CXL_UTIL_JSON_H__ +struct cxl_memdev; +struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + unsigned long flags); +#endif /* __CXL_UTIL_JSON_H__ */ diff -up ndctl-71.1/cxl/list.c.orig ndctl-71.1/cxl/list.c --- ndctl-71.1/cxl/list.c.orig 2022-10-07 15:50:40.072454553 -0400 +++ ndctl-71.1/cxl/list.c 2022-10-07 15:51:03.901535684 -0400 @@ -6,12 +6,14 @@ #include #include #include -#include #include #include #include #include +#include "json.h" +#include "filter.h" + static struct { bool memdevs; bool idle; diff -up ndctl-71.1/cxl/memdev.c.orig ndctl-71.1/cxl/memdev.c --- ndctl-71.1/cxl/memdev.c.orig 2022-10-07 15:50:40.072454553 -0400 +++ ndctl-71.1/cxl/memdev.c 2022-10-07 15:51:03.901535684 -0400 @@ -6,12 +6,13 @@ #include #include #include -#include #include #include #include #include +#include "filter.h" + struct action_context { FILE *f_out; FILE *f_in; diff -up ndctl-71.1/daxctl/Makefile.am.orig ndctl-71.1/daxctl/Makefile.am --- ndctl-71.1/daxctl/Makefile.am.orig 2022-10-07 15:50:40.073454556 -0400 +++ ndctl-71.1/daxctl/Makefile.am 2022-10-07 15:51:03.902535687 -0400 @@ -18,6 +18,11 @@ daxctl_SOURCES =\ migrate.c \ device.c \ ../util/json.c \ + ../util/json.h \ + json.c \ + json.h \ + filter.c \ + filter.h \ builtin.h daxctl_LDADD =\ diff -up ndctl-71.1/daxctl/device.c.orig ndctl-71.1/daxctl/device.c --- ndctl-71.1/daxctl/device.c.orig 2022-10-07 15:50:40.074454560 -0400 +++ ndctl-71.1/daxctl/device.c 2022-10-07 15:51:03.902535687 -0400 @@ -11,13 +11,15 @@ #include #include #include -#include #include #include #include #include #include +#include "filter.h" +#include "json.h" + static struct { const char *dev; const char *mode; diff -up ndctl-71.1/daxctl/filter.c.orig ndctl-71.1/daxctl/filter.c --- ndctl-71.1/daxctl/filter.c.orig 2022-10-07 15:51:03.902535687 -0400 +++ ndctl-71.1/daxctl/filter.c 2022-10-07 15:51:03.902535687 -0400 @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include + +#include "filter.h" + +struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, + const char *ident) +{ + struct daxctl_region *region = daxctl_dev_get_region(dev); + int region_id, dev_id; + + if (!ident || strcmp(ident, "all") == 0) + return dev; + + if (strcmp(ident, daxctl_dev_get_devname(dev)) == 0) + return dev; + + if (sscanf(ident, "%d.%d", ®ion_id, &dev_id) == 2 && + daxctl_region_get_id(region) == region_id && + daxctl_dev_get_id(dev) == dev_id) + return dev; + + return NULL; +} + +struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, + const char *ident) +{ + int region_id; + + if (!ident || strcmp(ident, "all") == 0) + return region; + + if ((sscanf(ident, "%d", ®ion_id) == 1 || + sscanf(ident, "region%d", ®ion_id) == 1) && + daxctl_region_get_id(region) == region_id) + return region; + + return NULL; +} diff -up ndctl-71.1/daxctl/filter.h.orig ndctl-71.1/daxctl/filter.h --- ndctl-71.1/daxctl/filter.h.orig 2022-10-07 15:51:03.902535687 -0400 +++ ndctl-71.1/daxctl/filter.h 2022-10-07 15:51:03.902535687 -0400 @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef _DAXCTL_UTIL_FILTER_H_ +#define _DAXCTL_UTIL_FILTER_H_ +#include +#include + +struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, + const char *ident); +struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, + const char *ident); +#endif /* _DAXCTL_UTIL_FILTER_H_ */ diff -up ndctl-71.1/daxctl/json.c.orig ndctl-71.1/daxctl/json.c --- ndctl-71.1/daxctl/json.c.orig 2022-10-07 15:51:03.903535690 -0400 +++ ndctl-71.1/daxctl/json.c 2022-10-07 15:51:03.902535687 -0400 @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" +#include "json.h" + +struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, + unsigned long flags) +{ + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); + const char *devname = daxctl_dev_get_devname(dev); + struct json_object *jdev, *jobj, *jmappings = NULL; + struct daxctl_mapping *mapping = NULL; + int node, movable, align; + + jdev = json_object_new_object(); + if (!devname || !jdev) + return NULL; + + jobj = json_object_new_string(devname); + if (jobj) + json_object_object_add(jdev, "chardev", jobj); + + jobj = util_json_object_size(daxctl_dev_get_size(dev), flags); + if (jobj) + json_object_object_add(jdev, "size", jobj); + + node = daxctl_dev_get_target_node(dev); + if (node >= 0) { + jobj = json_object_new_int(node); + if (jobj) + json_object_object_add(jdev, "target_node", jobj); + } + + align = daxctl_dev_get_align(dev); + if (align > 0) { + jobj = util_json_object_size(daxctl_dev_get_align(dev), flags); + if (jobj) + json_object_object_add(jdev, "align", jobj); + } + + if (mem) + jobj = json_object_new_string("system-ram"); + else + jobj = json_object_new_string("devdax"); + if (jobj) + json_object_object_add(jdev, "mode", jobj); + + if (mem && daxctl_dev_get_resource(dev) != 0) { + int num_sections = daxctl_memory_num_sections(mem); + int num_online = daxctl_memory_is_online(mem); + + jobj = json_object_new_int(num_online); + if (jobj) + json_object_object_add(jdev, "online_memblocks", jobj); + + jobj = json_object_new_int(num_sections); + if (jobj) + json_object_object_add(jdev, "total_memblocks", jobj); + + movable = daxctl_memory_is_movable(mem); + if (movable == 1) + jobj = json_object_new_boolean(true); + else if (movable == 0) + jobj = json_object_new_boolean(false); + else + jobj = NULL; + if (jobj) + json_object_object_add(jdev, "movable", jobj); + } + + if (!daxctl_dev_is_enabled(dev)) { + jobj = json_object_new_string("disabled"); + if (jobj) + json_object_object_add(jdev, "state", jobj); + } + + if (!(flags & UTIL_JSON_DAX_MAPPINGS)) + return jdev; + + daxctl_mapping_foreach(dev, mapping) { + struct json_object *jmapping; + + if (!jmappings) { + jmappings = json_object_new_array(); + if (!jmappings) + continue; + + json_object_object_add(jdev, "mappings", jmappings); + } + + jmapping = util_daxctl_mapping_to_json(mapping, flags); + if (!jmapping) + continue; + json_object_array_add(jmappings, jmapping); + } + return jdev; +} + +struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, + struct json_object *jdevs, const char *ident, + unsigned long flags) +{ + struct daxctl_dev *dev; + + daxctl_dev_foreach(region, dev) { + struct json_object *jdev; + + if (!util_daxctl_dev_filter(dev, ident)) + continue; + + if (!(flags & (UTIL_JSON_IDLE|UTIL_JSON_CONFIGURED)) + && !daxctl_dev_get_size(dev)) + continue; + + if (!jdevs) { + jdevs = json_object_new_array(); + if (!jdevs) + return NULL; + } + + jdev = util_daxctl_dev_to_json(dev, flags); + if (!jdev) { + json_object_put(jdevs); + return NULL; + } + + json_object_array_add(jdevs, jdev); + } + + return jdevs; +} + +struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, + const char *ident, unsigned long flags) +{ + unsigned long align; + struct json_object *jregion, *jobj; + unsigned long long available_size, size; + + jregion = json_object_new_object(); + if (!jregion) + return NULL; + + /* + * The flag indicates when we are being called by an agent that + * already knows about the parent device information. + */ + if (!(flags & UTIL_JSON_DAX)) { + /* trim off the redundant /sys/devices prefix */ + const char *path = daxctl_region_get_path(region); + int len = strlen("/sys/devices"); + const char *trim = &path[len]; + + if (strncmp(path, "/sys/devices", len) != 0) + goto err; + jobj = json_object_new_string(trim); + if (!jobj) + goto err; + json_object_object_add(jregion, "path", jobj); + } + + jobj = json_object_new_int(daxctl_region_get_id(region)); + if (!jobj) + goto err; + json_object_object_add(jregion, "id", jobj); + + size = daxctl_region_get_size(region); + if (size < ULLONG_MAX) { + jobj = util_json_object_size(size, flags); + if (!jobj) + goto err; + json_object_object_add(jregion, "size", jobj); + } + + available_size = daxctl_region_get_available_size(region); + if (available_size) { + jobj = util_json_object_size(available_size, flags); + if (!jobj) + goto err; + json_object_object_add(jregion, "available_size", jobj); + } + + align = daxctl_region_get_align(region); + if (align < ULONG_MAX) { + jobj = json_object_new_int64(align); + if (!jobj) + goto err; + json_object_object_add(jregion, "align", jobj); + } + + if (!(flags & UTIL_JSON_DAX_DEVS)) + return jregion; + + jobj = util_daxctl_devs_to_list(region, NULL, ident, flags); + if (jobj) + json_object_object_add(jregion, "devices", jobj); + + return jregion; + err: + json_object_put(jregion); + return NULL; +} + +struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, + unsigned long flags) +{ + struct json_object *jmapping = json_object_new_object(); + struct json_object *jobj; + + if (!jmapping) + return NULL; + + jobj = util_json_object_hex(daxctl_mapping_get_offset(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "page_offset", jobj); + + jobj = util_json_object_hex(daxctl_mapping_get_start(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "start", jobj); + + jobj = util_json_object_hex(daxctl_mapping_get_end(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "end", jobj); + + jobj = util_json_object_size(daxctl_mapping_get_size(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "size", jobj); + + return jmapping; + err: + json_object_put(jmapping); + return NULL; +} diff -up ndctl-71.1/daxctl/json.h.orig ndctl-71.1/daxctl/json.h --- ndctl-71.1/daxctl/json.h.orig 2022-10-07 15:51:03.905535697 -0400 +++ ndctl-71.1/daxctl/json.h 2022-10-07 15:51:03.904535694 -0400 @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef __DAXCTL_JSON_H__ +#define __DAXCTL_JSON_H__ +#include + +struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, + unsigned long flags); +struct daxctl_region; +struct daxctl_dev; +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, + 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); +#endif /* __CXL_UTIL_JSON_H__ */ diff -up ndctl-71.1/daxctl/list.c.orig ndctl-71.1/daxctl/list.c --- ndctl-71.1/daxctl/list.c.orig 2022-10-07 15:50:40.075454563 -0400 +++ ndctl-71.1/daxctl/list.c 2022-10-07 15:51:03.905535697 -0400 @@ -6,12 +6,14 @@ #include #include #include -#include #include #include #include #include +#include "filter.h" +#include "json.h" + static struct { bool devs; bool regions; diff -up ndctl-71.1/ndctl/Makefile.am.orig ndctl-71.1/ndctl/Makefile.am --- ndctl-71.1/ndctl/Makefile.am.orig 2022-10-07 15:50:40.076454567 -0400 +++ ndctl-71.1/ndctl/Makefile.am 2022-10-07 15:51:03.905535697 -0400 @@ -19,13 +19,19 @@ ndctl_SOURCES = ndctl.c \ region.c \ dimm.c \ ../util/log.c \ - ../util/filter.c \ - ../util/filter.h \ + ../daxctl/filter.c \ + ../daxctl/filter.h \ + filter.c \ + filter.h \ list.c \ ../util/json.c \ ../util/json.h \ - util/json-smart.c \ - util/keys.h \ + ../daxctl/json.c \ + ../daxctl/json.h \ + json.c \ + json.h \ + json-smart.c \ + keys.h \ inject-error.c \ inject-smart.c \ monitor.c \ @@ -36,7 +42,7 @@ ndctl_SOURCES = ndctl.c \ firmware-update.h if ENABLE_KEYUTILS -ndctl_SOURCES += util/keys.c \ +ndctl_SOURCES += keys.c \ load-keys.c keys_configdir = $(ndctl_keysdir) keys_config_DATA = $(ndctl_keysreadme) diff -up ndctl-71.1/ndctl/bus.c.orig ndctl-71.1/ndctl/bus.c --- ndctl-71.1/ndctl/bus.c.orig 2022-10-07 15:50:40.077454570 -0400 +++ ndctl-71.1/ndctl/bus.c 2022-10-07 15:51:03.905535697 -0400 @@ -8,12 +8,14 @@ #include #include #include -#include #include #include #include #include +#include "filter.h" +#include "json.h" + static struct { bool verbose; bool force; diff -up ndctl-71.1/ndctl/check.c.orig ndctl-71.1/ndctl/check.c --- ndctl-71.1/ndctl/check.c.orig 2022-10-07 15:50:40.078454573 -0400 +++ ndctl-71.1/ndctl/check.c 2022-10-07 15:51:03.906535700 -0400 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include #include #include +#include #include #include #include diff -up ndctl-71.1/ndctl/dimm.c.orig ndctl-71.1/ndctl/dimm.c --- ndctl-71.1/ndctl/dimm.c.orig 2022-10-07 15:50:40.078454573 -0400 +++ ndctl-71.1/ndctl/dimm.c 2022-10-07 15:51:03.907535704 -0400 @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,10 @@ #include #include #include -#include + +#include "filter.h" +#include "json.h" +#include "keys.h" static const char *cmd_name = "dimm"; static int err_count; diff -up ndctl-71.1/ndctl/filter.c.orig ndctl-71.1/ndctl/filter.c --- ndctl-71.1/ndctl/filter.c.orig 2022-10-07 15:51:03.928535775 -0400 +++ ndctl-71.1/ndctl/filter.c 2022-10-07 15:51:03.908535707 -0400 @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" + +struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *__ident) +{ + char *end = NULL, *ident, *save; + unsigned long bus_id, id; + const char *provider, *devname, *name; + + if (!__ident) + return bus; + + ident = strdup(__ident); + if (!ident) + return NULL; + + for (name = strtok_r(ident, " ", &save); name; + name = strtok_r(NULL, " ", &save)) { + if (strcmp(name, "all") == 0) + break; + + bus_id = strtoul(ident, &end, 0); + if (end == ident || end[0]) + bus_id = ULONG_MAX; + + provider = ndctl_bus_get_provider(bus); + devname = ndctl_bus_get_devname(bus); + id = ndctl_bus_get_id(bus); + + if (bus_id < ULONG_MAX && bus_id == id) + break; + + if (bus_id == ULONG_MAX && (strcmp(provider, name) == 0 + || strcmp(devname, name) == 0)) + break; + } + free(ident); + + if (name) + return bus; + return NULL; +} + +struct ndctl_region *util_region_filter(struct ndctl_region *region, + const char *__ident) +{ + char *end = NULL, *ident, *save; + const char *name, *region_name; + unsigned long region_id, id; + + if (!__ident) + return region; + + ident = strdup(__ident); + if (!ident) + return NULL; + + for (name = strtok_r(ident, " ", &save); name; + name = strtok_r(NULL, " ", &save)) { + if (strcmp(name, "all") == 0) + break; + + region_id = strtoul(ident, &end, 0); + if (end == ident || end[0]) + region_id = ULONG_MAX; + + region_name = ndctl_region_get_devname(region); + id = ndctl_region_get_id(region); + + if (region_id < ULONG_MAX && region_id == id) + break; + + if (region_id == ULONG_MAX && strcmp(region_name, name) == 0) + break; + } + free(ident); + + if (name) + return region; + return NULL; +} + +struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, + const char *__ident) +{ + struct ndctl_region *region = ndctl_namespace_get_region(ndns); + unsigned long region_id, ndns_id; + const char *name; + char *ident, *save; + + if (!__ident) + return ndns; + + ident = strdup(__ident); + if (!ident) + return NULL; + + for (name = strtok_r(ident, " ", &save); name; + name = strtok_r(NULL, " ", &save)) { + if (strcmp(name, "all") == 0) + break; + + if (strcmp(name, ndctl_namespace_get_devname(ndns)) == 0) + break; + + if (sscanf(name, "%ld.%ld", ®ion_id, &ndns_id) == 2 + && ndctl_region_get_id(region) == region_id + && ndctl_namespace_get_id(ndns) == ndns_id) + break; + } + free(ident); + + if (name) + return ndns; + return NULL; +} + +struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, + const char *__ident) +{ + char *end = NULL, *ident, *save; + const char *name, *dimm_name; + unsigned long dimm_id, id; + + if (!__ident) + return dimm; + + ident = strdup(__ident); + if (!ident) + return NULL; + + for (name = strtok_r(ident, " ", &save); name; + name = strtok_r(NULL, " ", &save)) { + if (strcmp(name, "all") == 0) + break; + + dimm_id = strtoul(ident, &end, 0); + if (end == ident || end[0]) + dimm_id = ULONG_MAX; + + dimm_name = ndctl_dimm_get_devname(dimm); + id = ndctl_dimm_get_id(dimm); + + if (dimm_id < ULONG_MAX && dimm_id == id) + break; + + if (dimm_id == ULONG_MAX && strcmp(dimm_name, name) == 0) + break; + } + free(ident); + + if (name) + return dimm; + return NULL; +} + +struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, + const char *ident) +{ + struct ndctl_dimm *dimm; + + if (!ident || strcmp(ident, "all") == 0) + return bus; + + ndctl_dimm_foreach(bus, dimm) + if (util_dimm_filter(dimm, ident)) + return bus; + return NULL; +} + +struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, + const char *ident) +{ + struct ndctl_region *region; + + if (!ident || strcmp(ident, "all") == 0) + return bus; + + ndctl_region_foreach(bus, region) + if (util_region_filter(region, ident)) + return bus; + return NULL; +} + +struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, + const char *ident) +{ + struct ndctl_region *region; + struct ndctl_namespace *ndns; + + if (!ident || strcmp(ident, "all") == 0) + return bus; + + ndctl_region_foreach(bus, region) + ndctl_namespace_foreach(region, ndns) + if (util_namespace_filter(ndns, ident)) + return bus; + return NULL; +} + +struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, + const char *ident) +{ + struct ndctl_dimm *dimm; + + if (!ident || strcmp(ident, "all") == 0) + return region; + + ndctl_dimm_foreach_in_region(region, dimm) + if (util_dimm_filter(dimm, ident)) + return region; + + return NULL; +} + +struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, + const char *ident) +{ + struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); + struct ndctl_region *region; + struct ndctl_dimm *check; + + if (!ident || strcmp(ident, "all") == 0) + return dimm; + + ndctl_region_foreach(bus, region) { + if (!util_region_filter(region, ident)) + continue; + ndctl_dimm_foreach_in_region(region, check) + if (check == dimm) + return dimm; + } + + return NULL; +} + +struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, + const char *ident) +{ + struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); + struct ndctl_namespace *ndns; + struct ndctl_region *region; + struct ndctl_dimm *check; + + if (!ident || strcmp(ident, "all") == 0) + return dimm; + + ndctl_region_foreach(bus, region) { + ndctl_namespace_foreach(region, ndns) { + if (!util_namespace_filter(ndns, ident)) + continue; + ndctl_dimm_foreach_in_region(region, check) + if (check == dimm) + return dimm; + } + } + + return NULL; +} + +struct ndctl_dimm *util_dimm_filter_by_numa_node(struct ndctl_dimm *dimm, + int numa_node) +{ + struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); + struct ndctl_region *region; + struct ndctl_dimm *check; + + if (numa_node == NUMA_NO_NODE) + return dimm; + + ndctl_region_foreach(bus, region) + ndctl_dimm_foreach_in_region(region, check) + if (check == dimm && + ndctl_region_get_numa_node(region) == numa_node) + return dimm; + + return NULL; +} + +struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, + const char *ident) +{ + struct ndctl_namespace *ndns; + + if (!ident || strcmp(ident, "all") == 0) + return region; + + ndctl_namespace_foreach(region, ndns) + if (util_namespace_filter(ndns, ident)) + return region; + return NULL; +} + +enum ndctl_namespace_mode util_nsmode(const char *mode) +{ + if (!mode) + return NDCTL_NS_MODE_UNKNOWN; + if (strcasecmp(mode, "memory") == 0) + return NDCTL_NS_MODE_FSDAX; + if (strcasecmp(mode, "fsdax") == 0) + return NDCTL_NS_MODE_FSDAX; + if (strcasecmp(mode, "sector") == 0) + return NDCTL_NS_MODE_SECTOR; + if (strcasecmp(mode, "safe") == 0) + return NDCTL_NS_MODE_SECTOR; + if (strcasecmp(mode, "dax") == 0) + return NDCTL_NS_MODE_DEVDAX; + if (strcasecmp(mode, "devdax") == 0) + return NDCTL_NS_MODE_DEVDAX; + if (strcasecmp(mode, "raw") == 0) + return NDCTL_NS_MODE_RAW; + + return NDCTL_NS_MODE_UNKNOWN; +} + +const char *util_nsmode_name(enum ndctl_namespace_mode mode) +{ + static const char * const modes[] = { + [NDCTL_NS_MODE_FSDAX] = "fsdax", + [NDCTL_NS_MODE_DEVDAX] = "devdax", + [NDCTL_NS_MODE_RAW] = "raw", + [NDCTL_NS_MODE_SECTOR] = "sector", + [NDCTL_NS_MODE_UNKNOWN] = "unknown", + }; + + return modes[mode]; +} + +int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, + struct util_filter_params *param) +{ + struct ndctl_bus *bus; + unsigned int type = 0; + int numa_node = NUMA_NO_NODE; + char *end = NULL; + + if (param->type && (strcmp(param->type, "pmem") != 0 + && strcmp(param->type, "blk") != 0)) { + error("unknown type \"%s\" must be \"pmem\" or \"blk\"\n", + param->type); + return -EINVAL; + } + + if (param->type) { + if (strcmp(param->type, "pmem") == 0) + type = ND_DEVICE_REGION_PMEM; + else + type = ND_DEVICE_REGION_BLK; + } + + if (param->mode && util_nsmode(param->mode) == NDCTL_NS_MODE_UNKNOWN) { + error("invalid mode: '%s'\n", param->mode); + return -EINVAL; + } + + if (param->numa_node && strcmp(param->numa_node, "all") != 0) { + struct stat st; + + if (stat("/sys/devices/system/node", &st) != 0) { + error("This system does not support NUMA"); + return -EINVAL; + } + + numa_node = strtol(param->numa_node, &end, 0); + if (end == param->numa_node || end[0]) { + error("invalid numa_node: '%s'\n", param->numa_node); + return -EINVAL; + } + } + + ndctl_bus_foreach(ctx, bus) { + struct ndctl_region *region; + struct ndctl_dimm *dimm; + + if (!util_bus_filter(bus, param->bus) + || !util_bus_filter_by_dimm(bus, param->dimm) + || !util_bus_filter_by_region(bus, param->region) + || !util_bus_filter_by_namespace(bus, param->namespace)) + continue; + + if (!fctx->filter_bus(bus, fctx)) + continue; + + ndctl_dimm_foreach(bus, dimm) { + if (!fctx->filter_dimm) + break; + + if (!util_dimm_filter(dimm, param->dimm) + || !util_dimm_filter_by_region(dimm, + param->region) + || !util_dimm_filter_by_namespace(dimm, + param->namespace) + || !util_dimm_filter_by_numa_node(dimm, + numa_node)) + continue; + + fctx->filter_dimm(dimm, fctx); + } + + ndctl_region_foreach(bus, region) { + struct ndctl_namespace *ndns; + + if (!util_region_filter(region, param->region) + || !util_region_filter_by_dimm(region, + param->dimm) + || !util_region_filter_by_namespace(region, + param->namespace)) + continue; + + /* + * if numa_node attribute is not available for regions + * (which is true for pre 5.4 kernels), don't skip the + * region if namespace is also requested, let the + * namespace filter handle the NUMA node filtering. + */ + if (numa_node != NUMA_NO_NODE && + !ndctl_region_has_numa(region) && + !fctx->filter_namespace) { + fprintf(stderr, + "This kernel does not provide NUMA node information per-region\n"); + continue; + } + + if (ndctl_region_has_numa(region) && + numa_node != NUMA_NO_NODE && + ndctl_region_get_numa_node(region) != numa_node) + continue; + + if (type && ndctl_region_get_type(region) != type) + continue; + + if (!fctx->filter_region(region, fctx)) + continue; + + ndctl_namespace_foreach(region, ndns) { + enum ndctl_namespace_mode mode; + + if (!fctx->filter_namespace) + break; + if (!util_namespace_filter(ndns, param->namespace)) + continue; + + mode = ndctl_namespace_get_mode(ndns); + if (param->mode && util_nsmode(param->mode) != mode) + continue; + + if (numa_node != NUMA_NO_NODE && + ndctl_namespace_get_numa_node(ndns) != numa_node) + continue; + + fctx->filter_namespace(ndns, fctx); + } + } + } + return 0; +} diff -up ndctl-71.1/ndctl/filter.h.orig ndctl-71.1/ndctl/filter.h --- ndctl-71.1/ndctl/filter.h.orig 2022-10-07 15:51:03.929535779 -0400 +++ ndctl-71.1/ndctl/filter.h 2022-10-07 15:51:03.908535707 -0400 @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef _NDCTL_UTIL_FILTER_H_ +#define _NDCTL_UTIL_FILTER_H_ +#include +#include + +struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *ident); +struct ndctl_region *util_region_filter(struct ndctl_region *region, + const char *ident); +struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, + const char *ident); +struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, const char *ident); +struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, + const char *ident); +struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, + const char *ident); +struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, + const char *ident); +struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, + const char *ident); +struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, + const char *ident); +struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, + const char *ident); +struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, + const char *ident); + +enum ndctl_namespace_mode util_nsmode(const char *mode); +const char *util_nsmode_name(enum ndctl_namespace_mode mode); + +struct json_object; + +/* json object hierarchy for the util_filter_walk() performed by cmd_list() */ +struct list_filter_arg { + struct json_object *jnamespaces; + struct json_object *jregions; + struct json_object *jdimms; + struct json_object *jbuses; + struct json_object *jregion; + struct json_object *jbus; + unsigned long flags; +}; + +struct monitor_filter_arg { + struct list_head dimms; + int maxfd_dimm; + int num_dimm; + unsigned long flags; +}; + +/* + * struct util_filter_ctx - control and callbacks for util_filter_walk() + * ->filter_bus() and ->filter_region() return bool because the + * child-object filter routines can not be called if the parent context + * is not established. ->filter_dimm() and ->filter_namespace() are leaf + * objects, so no child dependencies to check. + */ +struct util_filter_ctx { + bool (*filter_bus)(struct ndctl_bus *bus, struct util_filter_ctx *ctx); + void (*filter_dimm)(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx); + bool (*filter_region)(struct ndctl_region *region, + struct util_filter_ctx *ctx); + void (*filter_namespace)(struct ndctl_namespace *ndns, + struct util_filter_ctx *ctx); + union { + void *arg; + struct list_filter_arg *list; + struct monitor_filter_arg *monitor; + }; +}; + +struct util_filter_params { + const char *bus; + const char *region; + const char *type; + const char *dimm; + const char *mode; + const char *namespace; + const char *numa_node; +}; + +struct ndctl_ctx; +int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, + struct util_filter_params *param); +#endif /* _NDCTL_UTIL_FILTER_H_ */ diff -up ndctl-71.1/ndctl/inject-error.c.orig ndctl-71.1/ndctl/inject-error.c --- ndctl-71.1/ndctl/inject-error.c.orig 2022-10-07 15:50:40.081454584 -0400 +++ ndctl-71.1/ndctl/inject-error.c 2022-10-07 15:51:03.909535711 -0400 @@ -12,12 +12,11 @@ #include #include -#include #include #include #include #include -#include +#include #include #include #include @@ -26,6 +25,9 @@ #include #include +#include "filter.h" +#include "json.h" + static bool verbose; static struct parameters { const char *bus; diff -up ndctl-71.1/ndctl/inject-smart.c.orig ndctl-71.1/ndctl/inject-smart.c --- ndctl-71.1/ndctl/inject-smart.c.orig 2022-10-07 15:50:40.081454584 -0400 +++ ndctl-71.1/ndctl/inject-smart.c 2022-10-07 15:51:03.910535714 -0400 @@ -13,12 +13,11 @@ #include #include -#include #include #include #include #include -#include +#include #include #include #include @@ -27,6 +26,9 @@ #include #include +#include "filter.h" +#include "json.h" + static struct parameters { const char *bus; const char *dimm; diff -up ndctl-71.1/ndctl/json-smart.c.orig ndctl-71.1/ndctl/json-smart.c --- ndctl-71.1/ndctl/json-smart.c.orig 2022-10-07 15:51:03.929535779 -0400 +++ ndctl-71.1/ndctl/json-smart.c 2022-10-07 15:51:03.910535714 -0400 @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include +#include +#include + +#include "json.h" + +static void smart_threshold_to_json(struct ndctl_dimm *dimm, + struct json_object *jhealth) +{ + unsigned int alarm_control; + struct json_object *jobj; + struct ndctl_cmd *cmd; + int rc; + + cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); + if (!cmd) + return; + + rc = ndctl_cmd_submit_xlat(cmd); + if (rc < 0) + goto out; + + alarm_control = ndctl_cmd_smart_threshold_get_alarm_control(cmd); + if (alarm_control & ND_SMART_TEMP_TRIP) { + unsigned int temp; + double t; + + jobj = json_object_new_boolean(true); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_media_temperature", jobj); + temp = ndctl_cmd_smart_threshold_get_temperature(cmd); + t = ndctl_decode_smart_temperature(temp); + jobj = json_object_new_double(t); + if (jobj) + json_object_object_add(jhealth, + "temperature_threshold", jobj); + } else { + jobj = json_object_new_boolean(false); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_media_temperature", jobj); + } + + if (alarm_control & ND_SMART_CTEMP_TRIP) { + unsigned int temp; + double t; + + jobj = json_object_new_boolean(true); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_ctrl_temperature", jobj); + temp = ndctl_cmd_smart_threshold_get_ctrl_temperature(cmd); + t = ndctl_decode_smart_temperature(temp); + jobj = json_object_new_double(t); + if (jobj) + json_object_object_add(jhealth, + "controller_temperature_threshold", jobj); + } else { + jobj = json_object_new_boolean(false); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_ctrl_temperature", jobj); + } + + if (alarm_control & ND_SMART_SPARE_TRIP) { + unsigned int spares; + + jobj = json_object_new_boolean(true); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_spares", jobj); + spares = ndctl_cmd_smart_threshold_get_spares(cmd); + jobj = json_object_new_int(spares); + if (jobj) + json_object_object_add(jhealth, + "spares_threshold", jobj); + } else { + jobj = json_object_new_boolean(false); + if (jobj) + json_object_object_add(jhealth, + "alarm_enabled_spares", jobj); + } + + out: + ndctl_cmd_unref(cmd); +} + +struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm) +{ + struct json_object *jhealth = json_object_new_object(); + struct json_object *jobj; + struct ndctl_cmd *cmd; + unsigned int flags; + int rc; + + if (!jhealth) + return NULL; + + cmd = ndctl_dimm_cmd_new_smart(dimm); + if (!cmd) + goto err; + + rc = ndctl_cmd_submit_xlat(cmd); + if (rc < 0) { + jobj = json_object_new_string("unknown"); + if (jobj) + json_object_object_add(jhealth, "health_state", jobj); + goto out; + } + + flags = ndctl_cmd_smart_get_flags(cmd); + if (flags & ND_SMART_HEALTH_VALID) { + unsigned int health = ndctl_cmd_smart_get_health(cmd); + + if (health & ND_SMART_FATAL_HEALTH) + jobj = json_object_new_string("fatal"); + else if (health & ND_SMART_CRITICAL_HEALTH) + jobj = json_object_new_string("critical"); + else if (health & ND_SMART_NON_CRITICAL_HEALTH) + jobj = json_object_new_string("non-critical"); + else + jobj = json_object_new_string("ok"); + if (jobj) + json_object_object_add(jhealth, "health_state", jobj); + } + + if (flags & ND_SMART_TEMP_VALID) { + unsigned int temp = ndctl_cmd_smart_get_temperature(cmd); + double t = ndctl_decode_smart_temperature(temp); + + jobj = json_object_new_double(t); + if (jobj) + json_object_object_add(jhealth, "temperature_celsius", jobj); + } + + if (flags & ND_SMART_CTEMP_VALID) { + unsigned int temp = ndctl_cmd_smart_get_ctrl_temperature(cmd); + double t = ndctl_decode_smart_temperature(temp); + + jobj = json_object_new_double(t); + if (jobj) + json_object_object_add(jhealth, + "controller_temperature_celsius", jobj); + } + + if (flags & ND_SMART_SPARES_VALID) { + unsigned int spares = ndctl_cmd_smart_get_spares(cmd); + + jobj = json_object_new_int(spares); + if (jobj) + json_object_object_add(jhealth, "spares_percentage", jobj); + } + + if (flags & ND_SMART_ALARM_VALID) { + unsigned int alarm_flags = ndctl_cmd_smart_get_alarm_flags(cmd); + bool temp_flag = !!(alarm_flags & ND_SMART_TEMP_TRIP); + bool ctrl_temp_flag = !!(alarm_flags & ND_SMART_CTEMP_TRIP); + bool spares_flag = !!(alarm_flags & ND_SMART_SPARE_TRIP); + + jobj = json_object_new_boolean(temp_flag); + if (jobj) + json_object_object_add(jhealth, "alarm_temperature", jobj); + + jobj = json_object_new_boolean(ctrl_temp_flag); + if (jobj) + json_object_object_add(jhealth, "alarm_controller_temperature", jobj); + + jobj = json_object_new_boolean(spares_flag); + if (jobj) + json_object_object_add(jhealth, "alarm_spares", jobj); + } + + smart_threshold_to_json(dimm, jhealth); + + if (flags & ND_SMART_USED_VALID) { + unsigned int life_used = ndctl_cmd_smart_get_life_used(cmd); + + jobj = json_object_new_int(life_used); + if (jobj) + json_object_object_add(jhealth, "life_used_percentage", jobj); + } + + if (flags & ND_SMART_SHUTDOWN_VALID) { + unsigned int shutdown = ndctl_cmd_smart_get_shutdown_state(cmd); + + jobj = json_object_new_string(shutdown ? "dirty" : "clean"); + if (jobj) + json_object_object_add(jhealth, "shutdown_state", jobj); + } + + if (flags & ND_SMART_SHUTDOWN_COUNT_VALID) { + unsigned int shutdown = ndctl_cmd_smart_get_shutdown_count(cmd); + + jobj = json_object_new_int(shutdown); + if (jobj) + json_object_object_add(jhealth, "shutdown_count", jobj); + } + + ndctl_cmd_unref(cmd); + return jhealth; + err: + json_object_put(jhealth); + jhealth = NULL; + out: + if (cmd) + ndctl_cmd_unref(cmd); + return jhealth; +} diff -up ndctl-71.1/ndctl/json.c.orig ndctl-71.1/ndctl/json.c --- ndctl-71.1/ndctl/json.c.orig 2022-10-07 15:51:03.912535721 -0400 +++ ndctl-71.1/ndctl/json.c 2022-10-07 15:51:03.912535721 -0400 @@ -0,0 +1,1114 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include +#include +#include +#include +#include +#include +#include + +#include "json.h" +#include "ndctl.h" +#include "../daxctl/json.h" + +struct json_object *util_bus_to_json(struct ndctl_bus *bus, unsigned long flags) +{ + struct json_object *jbus = json_object_new_object(); + struct json_object *jobj, *fw_obj = NULL; + int scrub; + + if (!jbus) + return NULL; + + jobj = json_object_new_string(ndctl_bus_get_provider(bus)); + if (!jobj) + goto err; + json_object_object_add(jbus, "provider", jobj); + + jobj = json_object_new_string(ndctl_bus_get_devname(bus)); + if (!jobj) + goto err; + json_object_object_add(jbus, "dev", jobj); + + scrub = ndctl_bus_get_scrub_state(bus); + if (scrub < 0) + return jbus; + + jobj = json_object_new_string(scrub ? "active" : "idle"); + if (!jobj) + goto err; + json_object_object_add(jbus, "scrub_state", jobj); + + if (flags & UTIL_JSON_FIRMWARE) { + struct ndctl_dimm *dimm; + + /* + * Skip displaying firmware activation capability if no + * DIMMs support firmware update. + */ + ndctl_dimm_foreach(bus, dimm) + if (ndctl_dimm_fw_update_supported(dimm) == 0) { + fw_obj = json_object_new_object(); + break; + } + } + + if (fw_obj) { + enum ndctl_fwa_state state; + enum ndctl_fwa_method method; + + jobj = NULL; + method = ndctl_bus_get_fw_activate_method(bus); + if (method == NDCTL_FWA_METHOD_RESET) + jobj = json_object_new_string("reset"); + if (method == NDCTL_FWA_METHOD_SUSPEND) + jobj = json_object_new_string("suspend"); + if (method == NDCTL_FWA_METHOD_LIVE) + jobj = json_object_new_string("live"); + if (jobj) + json_object_object_add(fw_obj, "activate_method", jobj); + + jobj = NULL; + state = ndctl_bus_get_fw_activate_state(bus); + if (state == NDCTL_FWA_ARMED) + jobj = json_object_new_string("armed"); + if (state == NDCTL_FWA_IDLE) + jobj = json_object_new_string("idle"); + if (state == NDCTL_FWA_ARM_OVERFLOW) + jobj = json_object_new_string("overflow"); + if (jobj) + json_object_object_add(fw_obj, "activate_state", jobj); + + json_object_object_add(jbus, "firmware", fw_obj); + } + + return jbus; + err: + json_object_put(jbus); + return NULL; +} + + + +struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, + unsigned long flags) +{ + struct json_object *jfirmware = json_object_new_object(); + bool can_update, need_powercycle; + enum ndctl_fwa_result result; + enum ndctl_fwa_state state; + struct json_object *jobj; + struct ndctl_cmd *cmd; + uint64_t run, next; + int rc; + + if (!jfirmware) + return NULL; + + cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); + if (!cmd) + goto err; + + rc = ndctl_cmd_submit(cmd); + if ((rc < 0) || ndctl_cmd_fw_xlat_firmware_status(cmd) != FW_SUCCESS) { + jobj = util_json_object_hex(-1, flags); + if (jobj) + json_object_object_add(jfirmware, "current_version", + jobj); + goto out; + } + + run = ndctl_cmd_fw_info_get_run_version(cmd); + if (run == ULLONG_MAX) { + jobj = util_json_object_hex(-1, flags); + if (jobj) + json_object_object_add(jfirmware, "current_version", + jobj); + goto out; + } + + jobj = util_json_object_hex(run, flags); + if (jobj) + json_object_object_add(jfirmware, "current_version", jobj); + + rc = ndctl_dimm_fw_update_supported(dimm); + can_update = rc == 0; + jobj = json_object_new_boolean(can_update); + if (jobj) + json_object_object_add(jfirmware, "can_update", jobj); + + + next = ndctl_cmd_fw_info_get_updated_version(cmd); + if (next == ULLONG_MAX) { + jobj = util_json_object_hex(-1, flags); + if (jobj) + json_object_object_add(jfirmware, "next_version", + jobj); + goto out; + } + + if (!next) + goto out; + + jobj = util_json_object_hex(next, flags); + if (jobj) + json_object_object_add(jfirmware, + "next_version", jobj); + + state = ndctl_dimm_get_fw_activate_state(dimm); + switch (state) { + case NDCTL_FWA_IDLE: + jobj = json_object_new_string("idle"); + break; + case NDCTL_FWA_ARMED: + jobj = json_object_new_string("armed"); + break; + case NDCTL_FWA_BUSY: + jobj = json_object_new_string("busy"); + break; + default: + jobj = NULL; + break; + } + if (jobj) + json_object_object_add(jfirmware, "activate_state", jobj); + + result = ndctl_dimm_get_fw_activate_result(dimm); + switch (result) { + case NDCTL_FWA_RESULT_NONE: + case NDCTL_FWA_RESULT_SUCCESS: + case NDCTL_FWA_RESULT_NOTSTAGED: + /* + * If a 'next' firmware version is staged then this + * result is stale, if the activation succeeds that is + * indicated by not finding a 'next' entry. + */ + need_powercycle = false; + break; + case NDCTL_FWA_RESULT_NEEDRESET: + case NDCTL_FWA_RESULT_FAIL: + default: + /* + * If the last activation failed, or if the activation + * result is unavailable it is always the case that the + * only remediation is powercycle. + */ + need_powercycle = true; + break; + } + + if (need_powercycle) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto out; + json_object_object_add(jfirmware, "need_powercycle", jobj); + } + + ndctl_cmd_unref(cmd); + return jfirmware; + +err: + json_object_put(jfirmware); + jfirmware = NULL; +out: + if (cmd) + ndctl_cmd_unref(cmd); + return jfirmware; +} + + + +struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, + unsigned long flags) +{ + struct json_object *jdimm = json_object_new_object(); + const char *id = ndctl_dimm_get_unique_id(dimm); + unsigned int handle = ndctl_dimm_get_handle(dimm); + unsigned short phys_id = ndctl_dimm_get_phys_id(dimm); + struct json_object *jobj; + enum ndctl_security_state sstate; + + if (!jdimm) + return NULL; + + jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); + if (!jobj) + goto err; + json_object_object_add(jdimm, "dev", jobj); + + if (id) { + jobj = json_object_new_string(id); + if (!jobj) + goto err; + json_object_object_add(jdimm, "id", jobj); + } + + if (handle < UINT_MAX) { + jobj = util_json_object_hex(handle, flags); + if (!jobj) + goto err; + json_object_object_add(jdimm, "handle", jobj); + } + + if (phys_id < USHRT_MAX) { + jobj = util_json_object_hex(phys_id, flags); + if (!jobj) + goto err; + json_object_object_add(jdimm, "phys_id", jobj); + } + + if (!ndctl_dimm_is_enabled(dimm)) { + jobj = json_object_new_string("disabled"); + if (!jobj) + goto err; + json_object_object_add(jdimm, "state", jobj); + } + + if (ndctl_dimm_failed_map(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_failed_map", jobj); + } + + if (ndctl_dimm_failed_save(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_failed_save", jobj); + } + + if (ndctl_dimm_failed_arm(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_failed_arm", jobj); + } + + if (ndctl_dimm_failed_restore(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_failed_restore", jobj); + } + + if (ndctl_dimm_failed_flush(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_failed_flush", jobj); + } + + if (ndctl_dimm_smart_pending(dimm)) { + jobj = json_object_new_boolean(true); + if (!jobj) + goto err; + json_object_object_add(jdimm, "flag_smart_event", jobj); + } + + sstate = ndctl_dimm_get_security(dimm); + if (sstate == NDCTL_SECURITY_DISABLED) + jobj = json_object_new_string("disabled"); + else if (sstate == NDCTL_SECURITY_UNLOCKED) + jobj = json_object_new_string("unlocked"); + else if (sstate == NDCTL_SECURITY_LOCKED) + jobj = json_object_new_string("locked"); + else if (sstate == NDCTL_SECURITY_FROZEN) + jobj = json_object_new_string("frozen"); + else if (sstate == NDCTL_SECURITY_OVERWRITE) + jobj = json_object_new_string("overwrite"); + else + jobj = NULL; + if (jobj) + json_object_object_add(jdimm, "security", jobj); + + if (ndctl_dimm_security_is_frozen(dimm)) { + jobj = json_object_new_boolean(true); + if (jobj) + json_object_object_add(jdimm, "security_frozen", jobj); + } + + if (flags & UTIL_JSON_FIRMWARE) { + struct json_object *jfirmware; + + jfirmware = util_dimm_firmware_to_json(dimm, flags); + if (jfirmware) + json_object_object_add(jdimm, "firmware", jfirmware); + } + + return jdimm; + err: + json_object_put(jdimm); + return NULL; +} + +#define _SZ(get_max, get_elem, type) \ +static struct json_object *util_##type##_build_size_array(struct ndctl_##type *arg) \ +{ \ + struct json_object *arr = json_object_new_array(); \ + int i; \ + \ + if (!arr) \ + return NULL; \ + \ + for (i = 0; i < get_max(arg); i++) { \ + struct json_object *jobj; \ + int64_t align; \ + \ + align = get_elem(arg, i); \ + jobj = json_object_new_int64(align); \ + if (!jobj) \ + goto err; \ + json_object_array_add(arr, jobj); \ + } \ + \ + return arr; \ +err: \ + json_object_put(arr); \ + return NULL; \ +} +#define SZ(type, kind) _SZ(ndctl_##type##_get_num_##kind##s, \ + ndctl_##type##_get_supported_##kind, type) +SZ(pfn, alignment) +SZ(dax, alignment) +SZ(btt, sector_size) + +struct json_object *util_region_capabilities_to_json(struct ndctl_region *region) +{ + struct json_object *jcaps, *jcap, *jobj; + struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); + struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); + struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); + + if (!btt || !pfn || !dax) + return NULL; + + jcaps = json_object_new_array(); + if (!jcaps) + return NULL; + + if (btt) { + jcap = json_object_new_object(); + if (!jcap) + goto err; + json_object_array_add(jcaps, jcap); + + jobj = json_object_new_string("sector"); + if (!jobj) + goto err; + json_object_object_add(jcap, "mode", jobj); + jobj = util_btt_build_size_array(btt); + if (!jobj) + goto err; + json_object_object_add(jcap, "sector_sizes", jobj); + } + + if (pfn) { + jcap = json_object_new_object(); + if (!jcap) + goto err; + json_object_array_add(jcaps, jcap); + + jobj = json_object_new_string("fsdax"); + if (!jobj) + goto err; + json_object_object_add(jcap, "mode", jobj); + jobj = util_pfn_build_size_array(pfn); + if (!jobj) + goto err; + json_object_object_add(jcap, "alignments", jobj); + } + + if (dax) { + jcap = json_object_new_object(); + if (!jcap) + goto err; + json_object_array_add(jcaps, jcap); + + jobj = json_object_new_string("devdax"); + if (!jobj) + goto err; + json_object_object_add(jcap, "mode", jobj); + jobj = util_dax_build_size_array(dax); + if (!jobj) + goto err; + json_object_object_add(jcap, "alignments", jobj); + } + + return jcaps; +err: + json_object_put(jcaps); + return NULL; +} + + +static int compare_dimm_number(const void *p1, const void *p2) +{ + struct ndctl_dimm *dimm1 = *(struct ndctl_dimm **)p1; + struct ndctl_dimm *dimm2 = *(struct ndctl_dimm **)p2; + const char *dimm1_name = ndctl_dimm_get_devname(dimm1); + const char *dimm2_name = ndctl_dimm_get_devname(dimm2); + int num1, num2; + + if (sscanf(dimm1_name, "nmem%d", &num1) != 1) + num1 = 0; + if (sscanf(dimm2_name, "nmem%d", &num2) != 1) + num2 = 0; + + return num1 - num2; +} + +static struct json_object *badblocks_to_jdimms(struct ndctl_region *region, + unsigned long long addr, unsigned long len) +{ + struct ndctl_bus *bus = ndctl_region_get_bus(region); + int count = ndctl_region_get_interleave_ways(region); + unsigned long long end = addr + len; + struct json_object *jdimms, *jobj; + struct ndctl_dimm **dimms, *dimm; + int found, i; + + jdimms = json_object_new_array(); + if (!jdimms) + return NULL; + + dimms = calloc(count, sizeof(struct ndctl_dimm *)); + if (!dimms) + goto err_dimms; + + for (found = 0; found < count && addr < end; addr += 512) { + dimm = ndctl_bus_get_dimm_by_physical_address(bus, addr); + if (!dimm) + continue; + + for (i = 0; i < count; i++) + if (dimms[i] == dimm) + break; + if (i >= count) + dimms[found++] = dimm; + } + + if (!found) + goto err_found; + + qsort(dimms, found, sizeof(dimm), compare_dimm_number); + + for (i = 0; i < found; i++) { + const char *devname = ndctl_dimm_get_devname(dimms[i]); + + jobj = json_object_new_string(devname); + if (!jobj) + break; + json_object_array_add(jdimms, jobj); + } + + if (!i) + goto err_found; + free(dimms); + return jdimms; + +err_found: + free(dimms); +err_dimms: + json_object_put(jdimms); + return NULL; +} + +struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, + unsigned int *bb_count, unsigned long flags) +{ + struct json_object *jbb = NULL, *jbbs = NULL, *jobj; + struct badblock *bb; + int bbs = 0; + + if (flags & UTIL_JSON_MEDIA_ERRORS) { + jbbs = json_object_new_array(); + if (!jbbs) + return NULL; + } + + ndctl_region_badblock_foreach(region, bb) { + struct json_object *jdimms; + unsigned long long addr; + + bbs += bb->len; + + /* recheck so we can still get the badblocks_count from above */ + if (!(flags & UTIL_JSON_MEDIA_ERRORS)) + continue; + + /* get start address of region */ + addr = ndctl_region_get_resource(region); + if (addr == ULLONG_MAX) + goto err_array; + + /* get address of bad block */ + addr += bb->offset << 9; + + jbb = json_object_new_object(); + if (!jbb) + goto err_array; + + jobj = json_object_new_int64(bb->offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); + + jobj = json_object_new_int(bb->len); + if (!jobj) + goto err; + json_object_object_add(jbb, "length", jobj); + + jdimms = badblocks_to_jdimms(region, addr, bb->len << 9); + if (jdimms) + json_object_object_add(jbb, "dimms", jdimms); + json_object_array_add(jbbs, jbb); + } + + *bb_count = bbs; + + if (bbs) + return jbbs; + + err: + json_object_put(jbb); + err_array: + json_object_put(jbbs); + return NULL; +} + +static struct json_object *util_namespace_badblocks_to_json( + struct ndctl_namespace *ndns, + unsigned int *bb_count, unsigned long flags) +{ + struct json_object *jbb = NULL, *jbbs = NULL, *jobj; + struct badblock *bb; + int bbs = 0; + + if (flags & UTIL_JSON_MEDIA_ERRORS) { + jbbs = json_object_new_array(); + if (!jbbs) + return NULL; + } else + return NULL; + + ndctl_namespace_badblock_foreach(ndns, bb) { + bbs += bb->len; + + /* recheck so we can still get the badblocks_count from above */ + if (!(flags & UTIL_JSON_MEDIA_ERRORS)) + continue; + + jbb = json_object_new_object(); + if (!jbb) + goto err_array; + + jobj = json_object_new_int64(bb->offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); + + jobj = json_object_new_int(bb->len); + if (!jobj) + goto err; + json_object_object_add(jbb, "length", jobj); + json_object_array_add(jbbs, jbb); + } + + *bb_count = bbs; + + if (bbs) + return jbbs; + + err: + json_object_put(jbb); + err_array: + json_object_put(jbbs); + return NULL; +} + +static struct json_object *dev_badblocks_to_json(struct ndctl_region *region, + unsigned long long dev_begin, unsigned long long dev_size, + unsigned int *bb_count, unsigned long flags) +{ + struct json_object *jbb = NULL, *jbbs = NULL, *jobj; + unsigned long long region_begin, dev_end, offset; + unsigned int len, bbs = 0; + struct badblock *bb; + + region_begin = ndctl_region_get_resource(region); + if (region_begin == ULLONG_MAX) + return NULL; + + dev_end = dev_begin + dev_size - 1; + + if (flags & UTIL_JSON_MEDIA_ERRORS) { + jbbs = json_object_new_array(); + if (!jbbs) + return NULL; + } + + ndctl_region_badblock_foreach(region, bb) { + unsigned long long bb_begin, bb_end, begin, end; + struct json_object *jdimms; + + bb_begin = region_begin + (bb->offset << 9); + bb_end = bb_begin + (bb->len << 9) - 1; + + if (bb_end <= dev_begin || bb_begin >= dev_end) + continue; + + if (bb_begin < dev_begin) + begin = dev_begin; + else + begin = bb_begin; + + if (bb_end > dev_end) + end = dev_end; + else + end = bb_end; + + offset = (begin - dev_begin) >> 9; + len = (end - begin + 1) >> 9; + + bbs += len; + + /* recheck so we can still get the badblocks_count from above */ + if (!(flags & UTIL_JSON_MEDIA_ERRORS)) + continue; + + jbb = json_object_new_object(); + if (!jbb) + goto err_array; + + jobj = json_object_new_int64(offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); + + jobj = json_object_new_int(len); + if (!jobj) + goto err; + json_object_object_add(jbb, "length", jobj); + + jdimms = badblocks_to_jdimms(region, begin, len << 9); + if (jdimms) + json_object_object_add(jbb, "dimms", jdimms); + + json_object_array_add(jbbs, jbb); + } + + *bb_count = bbs; + + if (bbs) + return jbbs; + + err: + json_object_put(jbb); + err_array: + json_object_put(jbbs); + return NULL; +} + +static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn, + unsigned int *bb_count, unsigned long flags) +{ + struct ndctl_region *region = ndctl_pfn_get_region(pfn); + unsigned long long pfn_begin, pfn_size; + + pfn_begin = ndctl_pfn_get_resource(pfn); + if (pfn_begin == ULLONG_MAX) { + struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); + + return util_namespace_badblocks_to_json(ndns, bb_count, flags); + } + + pfn_size = ndctl_pfn_get_size(pfn); + if (pfn_size == ULLONG_MAX) + return NULL; + + return dev_badblocks_to_json(region, pfn_begin, pfn_size, + bb_count, flags); +} + +static void util_btt_badblocks_to_json(struct ndctl_btt *btt, + unsigned int *bb_count) +{ + struct ndctl_region *region = ndctl_btt_get_region(btt); + struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); + unsigned long long begin, size; + + if (!ndns) + return; + + begin = ndctl_namespace_get_resource(ndns); + if (begin == ULLONG_MAX) + return; + + size = ndctl_namespace_get_size(ndns); + if (size == ULLONG_MAX) + return; + + /* + * The dev_badblocks_to_json() for BTT is not accurate with + * respect to data vs metadata badblocks, and is only useful for + * a potential bb_count. + * + * FIXME: switch to native BTT badblocks representation + * when / if the kernel provides it. + */ + dev_badblocks_to_json(region, begin, size, bb_count, 0); +} +static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax, + unsigned int *bb_count, unsigned long flags) +{ + struct ndctl_region *region = ndctl_dax_get_region(dax); + unsigned long long dax_begin, dax_size; + + dax_begin = ndctl_dax_get_resource(dax); + if (dax_begin == ULLONG_MAX) + return NULL; + + dax_size = ndctl_dax_get_size(dax); + if (dax_size == ULLONG_MAX) + return NULL; + + return dev_badblocks_to_json(region, dax_begin, dax_size, + bb_count, flags); +} + +static struct json_object *util_raw_uuid(struct ndctl_namespace *ndns) +{ + char buf[40]; + uuid_t raw_uuid; + + ndctl_namespace_get_uuid(ndns, raw_uuid); + if (uuid_is_null(raw_uuid)) + return NULL; + uuid_unparse(raw_uuid, buf); + return json_object_new_string(buf); +} + +static void util_raw_uuid_to_json(struct ndctl_namespace *ndns, + unsigned long flags, + struct json_object *jndns) +{ + struct json_object *jobj; + + if (!(flags & UTIL_JSON_VERBOSE)) + return; + + jobj = util_raw_uuid(ndns); + if (!jobj) + return; + json_object_object_add(jndns, "raw_uuid", jobj); +} + +struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, + unsigned long flags) +{ + struct json_object *jndns = json_object_new_object(); + enum ndctl_pfn_loc loc = NDCTL_PFN_LOC_NONE; + struct json_object *jobj, *jbbs = NULL; + const char *locations[] = { + [NDCTL_PFN_LOC_NONE] = "none", + [NDCTL_PFN_LOC_RAM] = "mem", + [NDCTL_PFN_LOC_PMEM] = "dev", + }; + unsigned long long size = ULLONG_MAX; + unsigned int sector_size = UINT_MAX; + enum ndctl_namespace_mode mode; + const char *bdev = NULL, *name; + unsigned int bb_count = 0; + struct ndctl_btt *btt; + struct ndctl_pfn *pfn; + struct ndctl_dax *dax; + unsigned long align = 0; + char buf[40]; + uuid_t uuid; + int numa, target; + + if (!jndns) + return NULL; + + jobj = json_object_new_string(ndctl_namespace_get_devname(ndns)); + if (!jobj) + goto err; + json_object_object_add(jndns, "dev", jobj); + + btt = ndctl_namespace_get_btt(ndns); + dax = ndctl_namespace_get_dax(ndns); + pfn = ndctl_namespace_get_pfn(ndns); + mode = ndctl_namespace_get_mode(ndns); + switch (mode) { + case NDCTL_NS_MODE_MEMORY: + if (pfn) { /* dynamic memory mode */ + size = ndctl_pfn_get_size(pfn); + loc = ndctl_pfn_get_location(pfn); + } else { /* native/static memory mode */ + size = ndctl_namespace_get_size(ndns); + loc = NDCTL_PFN_LOC_RAM; + } + jobj = json_object_new_string("fsdax"); + break; + case NDCTL_NS_MODE_DAX: + if (!dax) + goto err; + size = ndctl_dax_get_size(dax); + jobj = json_object_new_string("devdax"); + loc = ndctl_dax_get_location(dax); + break; + case NDCTL_NS_MODE_SECTOR: + if (!btt) + goto err; + jobj = json_object_new_string("sector"); + size = ndctl_btt_get_size(btt); + break; + case NDCTL_NS_MODE_RAW: + size = ndctl_namespace_get_size(ndns); + jobj = json_object_new_string("raw"); + break; + default: + jobj = NULL; + } + if (jobj) + json_object_object_add(jndns, "mode", jobj); + + if ((mode != NDCTL_NS_MODE_SECTOR) && (mode != NDCTL_NS_MODE_RAW)) { + jobj = json_object_new_string(locations[loc]); + if (jobj) + json_object_object_add(jndns, "map", jobj); + } + + if (size < ULLONG_MAX) { + jobj = util_json_object_size(size, flags); + if (jobj) + json_object_object_add(jndns, "size", jobj); + } + + if (btt) { + ndctl_btt_get_uuid(btt, uuid); + uuid_unparse(uuid, buf); + jobj = json_object_new_string(buf); + if (!jobj) + goto err; + json_object_object_add(jndns, "uuid", jobj); + util_raw_uuid_to_json(ndns, flags, jndns); + bdev = ndctl_btt_get_block_device(btt); + } else if (pfn) { + align = ndctl_pfn_get_align(pfn); + ndctl_pfn_get_uuid(pfn, uuid); + uuid_unparse(uuid, buf); + jobj = json_object_new_string(buf); + if (!jobj) + goto err; + json_object_object_add(jndns, "uuid", jobj); + util_raw_uuid_to_json(ndns, flags, jndns); + bdev = ndctl_pfn_get_block_device(pfn); + } else if (dax) { + struct daxctl_region *dax_region; + + dax_region = ndctl_dax_get_daxctl_region(dax); + align = ndctl_dax_get_align(dax); + ndctl_dax_get_uuid(dax, uuid); + uuid_unparse(uuid, buf); + jobj = json_object_new_string(buf); + if (!jobj) + goto err; + json_object_object_add(jndns, "uuid", jobj); + util_raw_uuid_to_json(ndns, flags, jndns); + if ((flags & UTIL_JSON_DAX) && dax_region) { + jobj = util_daxctl_region_to_json(dax_region, NULL, + flags); + if (jobj) + json_object_object_add(jndns, "daxregion", jobj); + } else if (dax_region) { + struct daxctl_dev *dev; + + /* + * We can only find/list these device-dax + * details when the instance is enabled. + */ + dev = daxctl_dev_get_first(dax_region); + if (dev) { + name = daxctl_dev_get_devname(dev); + jobj = json_object_new_string(name); + if (!jobj) + goto err; + json_object_object_add(jndns, "chardev", jobj); + } + } + } else if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { + ndctl_namespace_get_uuid(ndns, uuid); + uuid_unparse(uuid, buf); + jobj = json_object_new_string(buf); + if (!jobj) + goto err; + json_object_object_add(jndns, "uuid", jobj); + bdev = ndctl_namespace_get_block_device(ndns); + } else + bdev = ndctl_namespace_get_block_device(ndns); + + if (btt) + sector_size = ndctl_btt_get_sector_size(btt); + else if (!dax) { + sector_size = ndctl_namespace_get_sector_size(ndns); + if (!sector_size || sector_size == UINT_MAX) + sector_size = 512; + } + + /* + * The kernel will default to a 512 byte sector size on PMEM + * namespaces that don't explicitly have a sector size. This + * happens because they use pre-v1.2 labels or because they + * don't have a label space (devtype=nd_namespace_io). + */ + if (sector_size < UINT_MAX) { + jobj = json_object_new_int(sector_size); + if (!jobj) + goto err; + json_object_object_add(jndns, "sector_size", jobj); + } + + if (align) { + jobj = json_object_new_int64(align); + if (!jobj) + goto err; + json_object_object_add(jndns, "align", jobj); + } + + if (bdev && bdev[0]) { + jobj = json_object_new_string(bdev); + if (!jobj) + goto err; + json_object_object_add(jndns, "blockdev", jobj); + } + + if (!ndctl_namespace_is_active(ndns)) { + jobj = json_object_new_string("disabled"); + if (!jobj) + goto err; + json_object_object_add(jndns, "state", jobj); + } + + name = ndctl_namespace_get_alt_name(ndns); + if (name && name[0]) { + jobj = json_object_new_string(name); + if (!jobj) + goto err; + json_object_object_add(jndns, "name", jobj); + } + + numa = ndctl_namespace_get_numa_node(ndns); + if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { + jobj = json_object_new_int(numa); + if (jobj) + json_object_object_add(jndns, "numa_node", jobj); + } + + target = ndctl_namespace_get_target_node(ndns); + if (target >= 0 && flags & UTIL_JSON_VERBOSE) { + jobj = json_object_new_int(target); + if (jobj) + json_object_object_add(jndns, "target_node", jobj); + } + + if (pfn) + jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags); + else if (dax) + 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), &bb_count, + flags); + if (!jbbs) + jbbs = util_namespace_badblocks_to_json(ndns, &bb_count, + flags); + } + + if (bb_count) { + jobj = json_object_new_int(bb_count); + if (!jobj) { + json_object_put(jbbs); + goto err; + } + json_object_object_add(jndns, "badblock_count", jobj); + } + + if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) + json_object_object_add(jndns, "badblocks", jbbs); + + return jndns; + err: + json_object_put(jndns); + return NULL; +} + + +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); + struct json_object *jobj; + int position; + + if (!jmapping) + return NULL; + + jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); + if (!jobj) + goto err; + json_object_object_add(jmapping, "dimm", jobj); + + jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "offset", jobj); + + jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags); + if (!jobj) + goto err; + json_object_object_add(jmapping, "length", jobj); + + position = ndctl_mapping_get_position(mapping); + if (position >= 0) { + jobj = json_object_new_int(position); + if (!jobj) + goto err; + json_object_object_add(jmapping, "position", jobj); + } + + return jmapping; + err: + json_object_put(jmapping); + return NULL; +} + +struct json_object *util_badblock_rec_to_json(u64 block, u64 count, + unsigned long flags) +{ + struct json_object *jerr = json_object_new_object(); + struct json_object *jobj; + + if (!jerr) + return NULL; + + jobj = util_json_object_hex(block, flags); + if (!jobj) + goto err; + json_object_object_add(jerr, "block", jobj); + + jobj = util_json_object_hex(count, flags); + if (!jobj) + goto err; + json_object_object_add(jerr, "count", jobj); + + return jerr; + err: + json_object_put(jerr); + return NULL; +} diff -up ndctl-71.1/ndctl/json.h.orig ndctl-71.1/ndctl/json.h --- ndctl-71.1/ndctl/json.h.orig 2022-10-07 15:51:03.913535724 -0400 +++ ndctl-71.1/ndctl/json.h 2022-10-07 15:51:03.913535724 -0400 @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +#ifndef __NDCTL_UTIL_JSON_H__ +#define __NDCTL_UTIL_JSON_H__ +#include +#include + +struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, + unsigned long flags); +struct json_object *util_badblock_rec_to_json(u64 block, u64 count, + unsigned long flags); +struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, + unsigned int *bb_count, unsigned long flags); +struct json_object *util_bus_to_json(struct ndctl_bus *bus, + unsigned long flags); +struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, + unsigned long flags); +struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, + unsigned long flags); +struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); +struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, + unsigned long flags); +struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); +#endif /* __NDCTL_UTIL_JSON_H__ */ diff -up ndctl-71.1/ndctl/keys.c.orig ndctl-71.1/ndctl/keys.c --- ndctl-71.1/ndctl/keys.c.orig 2022-10-07 15:51:03.930535782 -0400 +++ ndctl-71.1/ndctl/keys.c 2022-10-07 15:51:03.913535724 -0400 @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2018-2020 Intel Corporation. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "keys.h" + +static int get_key_path(struct ndctl_dimm *dimm, char *path, + enum ndctl_key_type key_type) +{ + char hostname[HOST_NAME_MAX]; + int rc; + + rc = gethostname(hostname, HOST_NAME_MAX); + if (rc < 0) { + fprintf(stderr, "gethostname: %s\n", strerror(errno)); + return -errno; + } + + switch (key_type) { + case ND_USER_OLD_KEY: + rc = sprintf(path, "%s/nvdimm-old_%s_%s.blob", + NDCTL_KEYS_DIR, + ndctl_dimm_get_unique_id(dimm), + hostname); + break; + case ND_USER_KEY: + rc = sprintf(path, "%s/nvdimm_%s_%s.blob", + NDCTL_KEYS_DIR, + ndctl_dimm_get_unique_id(dimm), + hostname); + break; + case ND_MASTER_OLD_KEY: + rc = sprintf(path, "%s/nvdimm-master-old_%s_%s.blob", + NDCTL_KEYS_DIR, + ndctl_dimm_get_unique_id(dimm), + hostname); + break; + case ND_MASTER_KEY: + rc = sprintf(path, "%s/nvdimm-master_%s_%s.blob", + NDCTL_KEYS_DIR, + ndctl_dimm_get_unique_id(dimm), + hostname); + break; + default: + return -EINVAL; + } + + if (rc < 0) { + fprintf(stderr, "error setting path: %s\n", strerror(errno)); + return -errno; + } + + return 0; +} + +static int get_key_desc(struct ndctl_dimm *dimm, char *desc, + enum ndctl_key_type key_type) +{ + int rc; + + switch (key_type) { + case ND_USER_OLD_KEY: + rc = sprintf(desc, "nvdimm-old:%s", + ndctl_dimm_get_unique_id(dimm)); + break; + case ND_USER_KEY: + rc = sprintf(desc, "nvdimm:%s", + ndctl_dimm_get_unique_id(dimm)); + break; + case ND_MASTER_OLD_KEY: + rc = sprintf(desc, "nvdimm-master-old:%s", + ndctl_dimm_get_unique_id(dimm)); + break; + case ND_MASTER_KEY: + rc = sprintf(desc, "nvdimm-master:%s", + ndctl_dimm_get_unique_id(dimm)); + break; + default: + return -EINVAL; + } + + if (rc < 0) { + fprintf(stderr, "error setting key description: %s\n", + strerror(errno)); + return -errno; + } + + return 0; +} + +char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, + int dirfd, enum key_type key_type) +{ + struct stat st; + ssize_t read_bytes = 0; + int rc, fd; + char *blob, *pl, *rdptr; + char prefix[] = "load "; + bool need_prefix = false; + + if (key_type == KEY_ENCRYPTED || key_type == KEY_TRUSTED) + need_prefix = true; + + fd = openat(dirfd, path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open file %s: %s\n", + path, strerror(errno)); + return NULL; + } + + rc = fstat(fd, &st); + if (rc < 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + return NULL; + } + if ((st.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "%s not a regular file\n", path); + return NULL; + } + + if (st.st_size == 0 || st.st_size > 4096) { + fprintf(stderr, "Invalid blob file size\n"); + return NULL; + } + + *size = st.st_size; + if (need_prefix) + *size += strlen(prefix); + + /* + * We need to increment postfix and space. + * "keyhandle=" is 10 bytes, plus null termination. + */ + if (postfix) + *size += strlen(postfix) + 10 + 1; + blob = malloc(*size); + if (!blob) { + fprintf(stderr, "Unable to allocate memory for blob\n"); + return NULL; + } + + if (need_prefix) { + memcpy(blob, prefix, strlen(prefix)); + pl = blob + strlen(prefix); + } else + pl = blob; + + rdptr = pl; + do { + rc = read(fd, rdptr, st.st_size - read_bytes); + if (rc < 0) { + fprintf(stderr, "Failed to read from blob file: %s\n", + strerror(errno)); + free(blob); + close(fd); + return NULL; + } + read_bytes += rc; + rdptr += rc; + } while (read_bytes != st.st_size); + + close(fd); + + if (postfix) { + pl += read_bytes; + *pl = ' '; + pl++; + rc = sprintf(pl, "keyhandle=%s", postfix); + } + + return blob; +} + +static key_serial_t dimm_check_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + char desc[ND_KEY_DESC_SIZE]; + int rc; + + rc = get_key_desc(dimm, desc, key_type); + if (rc < 0) + return rc; + + return keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); +} + +static key_serial_t dimm_create_key(struct ndctl_dimm *dimm, + const char *kek, enum ndctl_key_type key_type) +{ + char desc[ND_KEY_DESC_SIZE]; + char path[PATH_MAX]; + char cmd[ND_KEY_CMD_SIZE]; + key_serial_t key; + void *buffer; + int rc; + ssize_t size; + FILE *fp; + ssize_t wrote; + struct stat st; + + if (ndctl_dimm_is_active(dimm)) { + fprintf(stderr, "regions active on %s, op failed\n", + ndctl_dimm_get_devname(dimm)); + return -EBUSY; + } + + rc = get_key_desc(dimm, desc, key_type); + if (rc < 0) + return rc; + + /* make sure it's not already in the key ring */ + key = keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); + if (key > 0) { + fprintf(stderr, "Error: key already present in user keyring\n"); + return -EEXIST; + } + + rc = get_key_path(dimm, path, key_type); + if (rc < 0) + return rc; + + rc = stat(path, &st); + if (rc == 0) { + fprintf(stderr, "%s already exists!\n", path); + return -EEXIST; + } + + rc = sprintf(cmd, "new enc32 %s 32", kek); + if (rc < 0) { + fprintf(stderr, "sprintf: %s\n", strerror(errno)); + return -errno; + } + + key = add_key("encrypted", desc, cmd, strlen(cmd), + KEY_SPEC_USER_KEYRING); + if (key < 0) { + fprintf(stderr, "add_key failed: %s\n", strerror(errno)); + return -errno; + } + + size = keyctl_read_alloc(key, &buffer); + if (size < 0) { + fprintf(stderr, "keyctl_read_alloc failed: %s\n", strerror(errno)); + keyctl_unlink(key, KEY_SPEC_USER_KEYRING); + return rc; + } + + fp = fopen(path, "w"); + if (!fp) { + rc = -errno; + fprintf(stderr, "Unable to open file %s: %s\n", + path, strerror(errno)); + free(buffer); + return rc; + } + + wrote = fwrite(buffer, 1, size, fp); + if (wrote != size) { + if (wrote == -1) + rc = -errno; + else + rc = -EIO; + fprintf(stderr, "Failed to write to %s: %s\n", + path, strerror(-rc)); + fclose(fp); + free(buffer); + return rc; + } + + fclose(fp); + free(buffer); + return key; +} + +static key_serial_t dimm_load_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + key_serial_t key; + char desc[ND_KEY_DESC_SIZE]; + char path[PATH_MAX]; + int rc; + char *blob; + int size; + + if (ndctl_dimm_is_active(dimm)) { + fprintf(stderr, "regions active on %s, op failed\n", + ndctl_dimm_get_devname(dimm)); + return -EBUSY; + } + + rc = get_key_desc(dimm, desc, key_type); + if (rc < 0) + return rc; + + rc = get_key_path(dimm, path, key_type); + if (rc < 0) + return rc; + + blob = ndctl_load_key_blob(path, &size, NULL, -1, KEY_ENCRYPTED); + if (!blob) + return -ENOMEM; + + key = add_key("encrypted", desc, blob, size, KEY_SPEC_USER_KEYRING); + free(blob); + if (key < 0) { + fprintf(stderr, "add_key failed: %s\n", strerror(errno)); + return -errno; + } + + return key; +} + +/* + * The function will check to see if the existing key is there and remove + * from user key ring if it is. Rename the existing key blob to old key + * blob, and then attempt to inject the key as old key into the user key + * ring. + */ +static key_serial_t move_key_to_old(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + int rc; + key_serial_t key; + char old_path[PATH_MAX]; + char new_path[PATH_MAX]; + enum ndctl_key_type okey_type; + + if (ndctl_dimm_is_active(dimm)) { + fprintf(stderr, "regions active on %s, op failed\n", + ndctl_dimm_get_devname(dimm)); + return -EBUSY; + } + + key = dimm_check_key(dimm, key_type); + if (key > 0) + keyctl_unlink(key, KEY_SPEC_USER_KEYRING); + + if (key_type == ND_USER_KEY) + okey_type = ND_USER_OLD_KEY; + else if (key_type == ND_MASTER_KEY) + okey_type = ND_MASTER_OLD_KEY; + else + return -EINVAL; + + rc = get_key_path(dimm, old_path, key_type); + if (rc < 0) + return rc; + + rc = get_key_path(dimm, new_path, okey_type); + if (rc < 0) + return rc; + + rc = rename(old_path, new_path); + if (rc < 0) { + fprintf(stderr, "rename failed from %s to %s: %s\n", + old_path, new_path, strerror(errno)); + return -errno; + } + + return dimm_load_key(dimm, okey_type); +} + +static int dimm_remove_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + key_serial_t key; + char path[PATH_MAX]; + int rc; + + key = dimm_check_key(dimm, key_type); + if (key > 0) + keyctl_unlink(key, KEY_SPEC_USER_KEYRING); + + rc = get_key_path(dimm, path, key_type); + if (rc < 0) + return rc; + + rc = unlink(path); + if (rc < 0) { + fprintf(stderr, "delete file %s failed: %s\n", + path, strerror(errno)); + return -errno; + } + + return 0; +} + +static int verify_kek(struct ndctl_dimm *dimm, const char *kek) +{ + char *type, *desc, *key_handle; + key_serial_t key; + int rc = 0; + + key_handle = strdup(kek); + if (!key_handle) + return -ENOMEM; + + type = strtok(key_handle, ":"); + if (!type) { + fprintf(stderr, "No key type found for kek handle\n"); + rc = -EINVAL; + goto out; + } + + if (strcmp(type, "trusted") != 0 && + strcmp(type, "user") != 0) { + fprintf(stderr, "No such key type: %s", type); + rc = -EINVAL; + goto out; + } + + desc = strtok(NULL, ":"); + if (!desc) { + fprintf(stderr, "No description found for kek handle\n"); + rc = -EINVAL; + goto out; + } + + key = keyctl_search(KEY_SPEC_USER_KEYRING, type, desc, 0); + if (key < 0) { + fprintf(stderr, "No key encryption key found\n"); + rc = key; + goto out; + } + +out: + free(key_handle); + return rc; +} + +int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, + enum ndctl_key_type key_type) +{ + key_serial_t key; + int rc; + + rc = verify_kek(dimm, kek); + if (rc < 0) + return rc; + + key = dimm_create_key(dimm, kek, key_type); + if (key < 0) + return key; + + if (key_type == ND_MASTER_KEY) + rc = ndctl_dimm_update_master_passphrase(dimm, 0, key); + else + rc = ndctl_dimm_update_passphrase(dimm, 0, key); + if (rc < 0) { + dimm_remove_key(dimm, key_type); + return rc; + } + + return 0; +} + +static char *get_current_kek(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + key_serial_t key; + char *key_buf; + long rc; + char *type, *desc; + + key = dimm_check_key(dimm, key_type); + if (key < 0) + return NULL; + + rc = keyctl_read_alloc(key, (void **)&key_buf); + if (rc < 0) + return NULL; + + rc = sscanf(key_buf, "%ms %ms", &type, &desc); + if (rc < 0) + return NULL; + + free(key_buf); + free(type); + + return desc; +} + +int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, + enum ndctl_key_type key_type) +{ + int rc; + key_serial_t old_key, new_key; + char *current_kek = NULL; + enum ndctl_key_type okey_type; + + if (kek) { + rc = verify_kek(dimm, kek); + if (rc < 0) + return rc; + } else { /* find current kek */ + current_kek = get_current_kek(dimm, key_type); + if (!current_kek) + return -ENOKEY; + } + + if (key_type == ND_USER_KEY) + okey_type = ND_USER_OLD_KEY; + else if (key_type == ND_MASTER_KEY) + okey_type = ND_MASTER_OLD_KEY; + else + return -EINVAL; + + /* + * 1. check if current key is loaded and remove + * 2. move current key blob to old key blob + * 3. load old key blob + * 4. trigger change key with old and new key + * 5. remove old key + * 6. remove old key blob + */ + old_key = move_key_to_old(dimm, key_type); + if (old_key < 0) + return old_key; + + new_key = dimm_create_key(dimm, current_kek ? current_kek : kek, + key_type); + free(current_kek); + /* need to create new key here */ + if (new_key < 0) { + new_key = dimm_load_key(dimm, key_type); + if (new_key < 0) + return new_key; + } + + if (key_type == ND_MASTER_KEY) + rc = ndctl_dimm_update_master_passphrase(dimm, + old_key, new_key); + else + rc = ndctl_dimm_update_passphrase(dimm, old_key, new_key); + if (rc < 0) + return rc; + + rc = dimm_remove_key(dimm, okey_type); + if (rc < 0) + return rc; + + return 0; +} + +static key_serial_t check_dimm_key(struct ndctl_dimm *dimm, bool need_key, + enum ndctl_key_type key_type) +{ + key_serial_t key; + + key = dimm_check_key(dimm, key_type); + if (key < 0) { + key = dimm_load_key(dimm, key_type); + if (key < 0 && need_key) { + fprintf(stderr, "Unable to load key\n"); + return -ENOKEY; + } else + key = 0; + } + return key; +} + +static int run_key_op(struct ndctl_dimm *dimm, + key_serial_t key, + int (*run_op)(struct ndctl_dimm *, long), const char *name) +{ + int rc; + + rc = run_op(dimm, key); + if (rc < 0) { + fprintf(stderr, "Failed %s for %s\n", name, + ndctl_dimm_get_devname(dimm)); + return rc; + } + + return 0; +} + +static int discard_key(struct ndctl_dimm *dimm) +{ + int rc; + + rc = dimm_remove_key(dimm, ND_USER_KEY); + if (rc < 0) { + fprintf(stderr, "Unable to cleanup key.\n"); + return rc; + } + + return 0; +} + +int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) +{ + key_serial_t key; + int rc; + + key = check_dimm_key(dimm, true, ND_USER_KEY); + if (key < 0) + return key; + + rc = run_key_op(dimm, key, ndctl_dimm_disable_passphrase, + "remove passphrase"); + if (rc < 0) + return rc; + + return discard_key(dimm); +} + +int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + key_serial_t key = 0; + int rc; + + if (key_type != ND_ZERO_KEY) { + key = check_dimm_key(dimm, true, key_type); + if (key < 0) + return key; + } + + if (key_type == ND_MASTER_KEY) + rc = run_key_op(dimm, key, ndctl_dimm_master_secure_erase, + "master crypto erase"); + else if (key_type == ND_USER_KEY || key_type == ND_ZERO_KEY) + rc = run_key_op(dimm, key, ndctl_dimm_secure_erase, + "crypto erase"); + else + rc = -EINVAL; + if (rc < 0) + return rc; + + if (key_type == ND_USER_KEY) + return discard_key(dimm); + + return 0; +} + +int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) +{ + key_serial_t key; + int rc; + + key = check_dimm_key(dimm, false, ND_USER_KEY); + if (key < 0) + return key; + + rc = run_key_op(dimm, key, ndctl_dimm_overwrite, + "overwrite"); + if (rc < 0) + return rc; + + return 0; +} diff -up ndctl-71.1/ndctl/keys.h.orig ndctl-71.1/ndctl/keys.h --- ndctl-71.1/ndctl/keys.h.orig 2022-10-07 15:51:03.930535782 -0400 +++ ndctl-71.1/ndctl/keys.h 2022-10-07 15:51:03.914535728 -0400 @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2020 Intel Corporation. All rights reserved. */ + +#ifndef _NDCTL_UTIL_KEYS_H_ +#define _NDCTL_UTIL_KEYS_H_ + +enum ndctl_key_type { + ND_USER_KEY, + ND_USER_OLD_KEY, + ND_MASTER_KEY, + ND_MASTER_OLD_KEY, + ND_ZERO_KEY, +}; + +enum key_type { + KEY_USER = 0, + KEY_TRUSTED, + KEY_ENCRYPTED, +}; + +#ifdef ENABLE_KEYUTILS +char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, + int dirfd, enum key_type key_type); +int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, + enum ndctl_key_type key_type); +int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, + enum ndctl_key_type key_type); +int ndctl_dimm_remove_key(struct ndctl_dimm *dimm); +int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type); +int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm); +#else +char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, + int dirfd, enum key_type key_type) +{ + return NULL; +} +static inline int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, + const char *kek, enum ndctl_key_type key_type) +{ + return -EOPNOTSUPP; +} + +static inline int ndctl_dimm_update_key(struct ndctl_dimm *dimm, + const char *kek, enum ndctl_key_type key_type) +{ + return -EOPNOTSUPP; +} + +static inline int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) +{ + return -EOPNOTSUPP; +} + +static inline int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, + enum ndctl_key_type key_type) +{ + return -EOPNOTSUPP; +} + +static inline int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) +{ + return -EOPNOTSUPP; +} +#endif /* ENABLE_KEYUTILS */ + +#endif diff -up ndctl-71.1/ndctl/lib/libndctl.c.orig ndctl-71.1/ndctl/lib/libndctl.c --- ndctl-71.1/ndctl/lib/libndctl.c.orig 2022-10-07 15:50:40.085454597 -0400 +++ ndctl-71.1/ndctl/lib/libndctl.c 2022-10-07 15:51:03.915535731 -0400 @@ -20,10 +20,10 @@ #include #include -#include #include #include #include +#include #include #include #include diff -up ndctl-71.1/ndctl/lib/papr.c.orig ndctl-71.1/ndctl/lib/papr.c --- ndctl-71.1/ndctl/lib/papr.c.orig 2022-10-07 15:50:40.086454601 -0400 +++ ndctl-71.1/ndctl/lib/papr.c 2022-10-07 15:51:03.916535735 -0400 @@ -10,9 +10,9 @@ #include #include #include -#include +#include #include -#include +#include "private.h" #include "papr.h" /* Utility logging maros for simplify logging */ diff -up ndctl-71.1/ndctl/lib/private.h.orig ndctl-71.1/ndctl/lib/private.h --- ndctl-71.1/ndctl/lib/private.h.orig 2022-10-07 15:50:40.086454601 -0400 +++ ndctl-71.1/ndctl/lib/private.h 2022-10-07 15:51:03.917535738 -0400 @@ -14,8 +14,9 @@ #include #include -#include +#include #include +#include #include #include #include "intel.h" @@ -23,7 +24,6 @@ #include "msft.h" #include "hyperv.h" #include "papr.h" -#include "libndctl-nfit.h" struct nvdimm_data { struct ndctl_cmd *cmd_read; diff -up ndctl-71.1/ndctl/list.c.orig ndctl-71.1/ndctl/list.c --- ndctl-71.1/ndctl/list.c.orig 2022-10-07 15:50:40.087454604 -0400 +++ ndctl-71.1/ndctl/list.c 2022-10-07 15:51:03.917535738 -0400 @@ -7,13 +7,14 @@ #include #include -#include #include #include #include #include -#include +#include "ndctl.h" +#include "filter.h" +#include "json.h" static struct { bool buses; diff -up ndctl-71.1/ndctl/load-keys.c.orig ndctl-71.1/ndctl/load-keys.c --- ndctl-71.1/ndctl/load-keys.c.orig 2022-10-07 15:50:40.087454604 -0400 +++ ndctl-71.1/ndctl/load-keys.c 2022-10-07 15:51:03.917535738 -0400 @@ -12,13 +12,14 @@ #include #include #include -#include #include +#include #include #include #include -#include -#include + +#include "filter.h" +#include "keys.h" static struct parameters { const char *key_path; diff -up ndctl-71.1/ndctl/monitor.c.orig ndctl-71.1/ndctl/monitor.c --- ndctl-71.1/ndctl/monitor.c.orig 2022-10-07 15:50:40.087454604 -0400 +++ ndctl-71.1/ndctl/monitor.c 2022-10-07 15:51:03.917535738 -0400 @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,9 @@ #endif #include +#include "filter.h" +#include "json.h" + static struct monitor { const char *log; const char *config_file; diff -up ndctl-71.1/ndctl/namespace.c.orig ndctl-71.1/ndctl/namespace.c --- ndctl-71.1/ndctl/namespace.c.orig 2022-10-07 15:50:40.088454607 -0400 +++ ndctl-71.1/ndctl/namespace.c 2022-10-07 15:51:03.918535741 -0400 @@ -9,7 +9,6 @@ #include #include -#include #include "action.h" #include "namespace.h" #include @@ -20,11 +19,14 @@ #include #include #include -#include +#include #include #include #include +#include "filter.h" +#include "json.h" + static bool verbose; static bool force; static bool repair; diff -up ndctl-71.1/ndctl/region.c.orig ndctl-71.1/ndctl/region.c --- ndctl-71.1/ndctl/region.c.orig 2022-10-07 15:50:40.088454607 -0400 +++ ndctl-71.1/ndctl/region.c 2022-10-07 15:51:03.918535741 -0400 @@ -5,10 +5,11 @@ #include #include #include "action.h" -#include #include #include +#include "filter.h" + static struct { const char *bus; const char *type; diff -up ndctl-71.1/ndctl/util/json-smart.c.orig ndctl-71.1/ndctl/util/json-smart.c diff -up ndctl-71.1/ndctl/util/keys.c.orig ndctl-71.1/ndctl/util/keys.c diff -up ndctl-71.1/ndctl/util/keys.h.orig ndctl-71.1/ndctl/util/keys.h diff -up ndctl-71.1/test/Makefile.am.orig ndctl-71.1/test/Makefile.am --- ndctl-71.1/test/Makefile.am.orig 2022-10-07 15:50:40.088454607 -0400 +++ ndctl-71.1/test/Makefile.am 2022-10-07 15:51:03.919535745 -0400 @@ -80,12 +80,19 @@ testcore =\ libndctl_SOURCES = libndctl.c $(testcore) libndctl_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) +namespace_core =\ + ../ndctl/namespace.c \ + ../ndctl/filter.c \ + ../ndctl/check.c \ + ../util/json.c \ + ../ndctl/json.c \ + ../daxctl/filter.c \ + ../daxctl/json.c + dsm_fail_SOURCES =\ dsm-fail.c \ $(testcore) \ - ../ndctl/namespace.c \ - ../ndctl/check.c \ - ../util/json.c + $(namespace_core) dsm_fail_LDADD = $(LIBNDCTL_LIB) \ $(KMOD_LIBS) \ @@ -122,9 +129,7 @@ device_dax_SOURCES = \ dax-dev.c \ dax-pmd.c \ $(testcore) \ - ../ndctl/namespace.c \ - ../ndctl/check.c \ - ../util/json.c + $(namespace_core) if ENABLE_POISON dax_pmd_SOURCES += dax-poison.c @@ -153,7 +158,10 @@ smart_listen_LDADD = $(LIBNDCTL_LIB) list_smart_dimm_SOURCES = \ list-smart-dimm.c \ - ../util/json.c + ../ndctl/filter.c \ + ../util/json.c \ + ../ndctl/json.c + list_smart_dimm_LDADD = \ $(LIBNDCTL_LIB) \ $(JSON_LIBS) \ diff -up ndctl-71.1/test/ack-shutdown-count-set.c.orig ndctl-71.1/test/ack-shutdown-count-set.c --- ndctl-71.1/test/ack-shutdown-count-set.c.orig 2022-10-07 15:50:40.089454611 -0400 +++ ndctl-71.1/test/ack-shutdown-count-set.c 2022-10-07 15:51:03.919535745 -0400 @@ -15,7 +15,7 @@ #include #include -#include +#include #include static int test_dimm(struct ndctl_dimm *dimm) diff -up ndctl-71.1/test/daxdev-errors.c.orig ndctl-71.1/test/daxdev-errors.c --- ndctl-71.1/test/daxdev-errors.c.orig 2022-10-07 15:50:40.090454614 -0400 +++ ndctl-71.1/test/daxdev-errors.c 2022-10-07 15:51:03.920535748 -0400 @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define fail() fprintf(stderr, "%s: failed at: %d\n", __func__, __LINE__) diff -up ndctl-71.1/test/device-dax.c.orig ndctl-71.1/test/device-dax.c --- ndctl-71.1/test/device-dax.c.orig 2022-10-07 15:50:40.090454614 -0400 +++ ndctl-71.1/test/device-dax.c 2022-10-07 15:51:03.921535752 -0400 @@ -20,7 +20,7 @@ #include #include -#include +#include #include static sigjmp_buf sj_env; diff -up ndctl-71.1/test/dsm-fail.c.orig ndctl-71.1/test/dsm-fail.c --- ndctl-71.1/test/dsm-fail.c.orig 2022-10-07 15:50:40.091454617 -0400 +++ ndctl-71.1/test/dsm-fail.c 2022-10-07 15:51:03.921535752 -0400 @@ -14,8 +14,8 @@ #include #include -#include -#include +#include +#include #include #define DIMM_PATH "/sys/devices/platform/nfit_test.0/nfit_test_dimm/test_dimm0" diff -up ndctl-71.1/test/libndctl.c.orig ndctl-71.1/test/libndctl.c --- ndctl-71.1/test/libndctl.c.orig 2022-10-07 15:50:40.092454621 -0400 +++ ndctl-71.1/test/libndctl.c 2022-10-07 15:51:03.922535755 -0400 @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */ diff -up ndctl-71.1/test/list-smart-dimm.c.orig ndctl-71.1/test/list-smart-dimm.c --- ndctl-71.1/test/list-smart-dimm.c.orig 2022-10-07 15:50:40.093454624 -0400 +++ ndctl-71.1/test/list-smart-dimm.c 2022-10-07 15:51:03.923535758 -0400 @@ -3,11 +3,13 @@ #include #include #include -#include #include #include #include -#include + +#include +#include +#include struct util_filter_params param; static int did_fail; diff -up ndctl-71.1/test/pmem_namespaces.c.orig ndctl-71.1/test/pmem_namespaces.c --- ndctl-71.1/test/pmem_namespaces.c.orig 2022-10-07 15:50:40.093454624 -0400 +++ ndctl-71.1/test/pmem_namespaces.c 2022-10-07 15:51:03.923535758 -0400 @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,6 @@ #include #include -#include #define err(msg)\ fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno)) diff -up ndctl-71.1/test/revoke-devmem.c.orig ndctl-71.1/test/revoke-devmem.c --- ndctl-71.1/test/revoke-devmem.c.orig 2022-10-07 15:50:40.094454628 -0400 +++ ndctl-71.1/test/revoke-devmem.c 2022-10-07 15:51:03.924535762 -0400 @@ -19,7 +19,7 @@ #include #include -#include +#include #include static sigjmp_buf sj_env; diff -up ndctl-71.1/util/filter.c.orig ndctl-71.1/util/filter.c diff -up ndctl-71.1/util/filter.h.orig ndctl-71.1/util/filter.h diff -up ndctl-71.1/util/help.c.orig ndctl-71.1/util/help.c --- ndctl-71.1/util/help.c.orig 2022-10-07 15:50:40.095454631 -0400 +++ ndctl-71.1/util/help.c 2022-10-07 15:51:03.925535765 -0400 @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff -up ndctl-71.1/util/json.c.orig ndctl-71.1/util/json.c --- ndctl-71.1/util/json.c.orig 2022-10-07 15:50:40.096454635 -0400 +++ ndctl-71.1/util/json.c 2022-10-07 15:51:03.926535769 -0400 @@ -2,17 +2,10 @@ // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. #include #include +#include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include /* adapted from mdadm::human_size_brief() */ static int display_size(struct json_object *jobj, struct printbuf *pbuf, @@ -112,1536 +105,3 @@ void util_display_json_array(FILE *f_out } json_object_put(jarray); } - -struct json_object *util_bus_to_json(struct ndctl_bus *bus, unsigned long flags) -{ - struct json_object *jbus = json_object_new_object(); - struct json_object *jobj, *fw_obj = NULL; - int scrub; - - if (!jbus) - return NULL; - - jobj = json_object_new_string(ndctl_bus_get_provider(bus)); - if (!jobj) - goto err; - json_object_object_add(jbus, "provider", jobj); - - jobj = json_object_new_string(ndctl_bus_get_devname(bus)); - if (!jobj) - goto err; - json_object_object_add(jbus, "dev", jobj); - - scrub = ndctl_bus_get_scrub_state(bus); - if (scrub < 0) - return jbus; - - jobj = json_object_new_string(scrub ? "active" : "idle"); - if (!jobj) - goto err; - json_object_object_add(jbus, "scrub_state", jobj); - - if (flags & UTIL_JSON_FIRMWARE) { - struct ndctl_dimm *dimm; - - /* - * Skip displaying firmware activation capability if no - * DIMMs support firmware update. - */ - ndctl_dimm_foreach(bus, dimm) - if (ndctl_dimm_fw_update_supported(dimm) == 0) { - fw_obj = json_object_new_object(); - break; - } - } - - if (fw_obj) { - enum ndctl_fwa_state state; - enum ndctl_fwa_method method; - - jobj = NULL; - method = ndctl_bus_get_fw_activate_method(bus); - if (method == NDCTL_FWA_METHOD_RESET) - jobj = json_object_new_string("reset"); - if (method == NDCTL_FWA_METHOD_SUSPEND) - jobj = json_object_new_string("suspend"); - if (method == NDCTL_FWA_METHOD_LIVE) - jobj = json_object_new_string("live"); - if (jobj) - json_object_object_add(fw_obj, "activate_method", jobj); - - jobj = NULL; - state = ndctl_bus_get_fw_activate_state(bus); - if (state == NDCTL_FWA_ARMED) - jobj = json_object_new_string("armed"); - if (state == NDCTL_FWA_IDLE) - jobj = json_object_new_string("idle"); - if (state == NDCTL_FWA_ARM_OVERFLOW) - jobj = json_object_new_string("overflow"); - if (jobj) - json_object_object_add(fw_obj, "activate_state", jobj); - - json_object_object_add(jbus, "firmware", fw_obj); - } - - return jbus; - err: - json_object_put(jbus); - return NULL; -} - -struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, - unsigned long flags) -{ - struct json_object *jfirmware = json_object_new_object(); - bool can_update, need_powercycle; - enum ndctl_fwa_result result; - enum ndctl_fwa_state state; - struct json_object *jobj; - struct ndctl_cmd *cmd; - uint64_t run, next; - int rc; - - if (!jfirmware) - return NULL; - - cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); - if (!cmd) - goto err; - - rc = ndctl_cmd_submit(cmd); - if ((rc < 0) || ndctl_cmd_fw_xlat_firmware_status(cmd) != FW_SUCCESS) { - jobj = util_json_object_hex(-1, flags); - if (jobj) - json_object_object_add(jfirmware, "current_version", - jobj); - goto out; - } - - run = ndctl_cmd_fw_info_get_run_version(cmd); - if (run == ULLONG_MAX) { - jobj = util_json_object_hex(-1, flags); - if (jobj) - json_object_object_add(jfirmware, "current_version", - jobj); - goto out; - } - - jobj = util_json_object_hex(run, flags); - if (jobj) - json_object_object_add(jfirmware, "current_version", jobj); - - rc = ndctl_dimm_fw_update_supported(dimm); - can_update = rc == 0; - jobj = json_object_new_boolean(can_update); - if (jobj) - json_object_object_add(jfirmware, "can_update", jobj); - - - next = ndctl_cmd_fw_info_get_updated_version(cmd); - if (next == ULLONG_MAX) { - jobj = util_json_object_hex(-1, flags); - if (jobj) - json_object_object_add(jfirmware, "next_version", - jobj); - goto out; - } - - if (!next) - goto out; - - jobj = util_json_object_hex(next, flags); - if (jobj) - json_object_object_add(jfirmware, - "next_version", jobj); - - state = ndctl_dimm_get_fw_activate_state(dimm); - switch (state) { - case NDCTL_FWA_IDLE: - jobj = json_object_new_string("idle"); - break; - case NDCTL_FWA_ARMED: - jobj = json_object_new_string("armed"); - break; - case NDCTL_FWA_BUSY: - jobj = json_object_new_string("busy"); - break; - default: - jobj = NULL; - break; - } - if (jobj) - json_object_object_add(jfirmware, "activate_state", jobj); - - result = ndctl_dimm_get_fw_activate_result(dimm); - switch (result) { - case NDCTL_FWA_RESULT_NONE: - case NDCTL_FWA_RESULT_SUCCESS: - case NDCTL_FWA_RESULT_NOTSTAGED: - /* - * If a 'next' firmware version is staged then this - * result is stale, if the activation succeeds that is - * indicated by not finding a 'next' entry. - */ - need_powercycle = false; - break; - case NDCTL_FWA_RESULT_NEEDRESET: - case NDCTL_FWA_RESULT_FAIL: - default: - /* - * If the last activation failed, or if the activation - * result is unavailable it is always the case that the - * only remediation is powercycle. - */ - need_powercycle = true; - break; - } - - if (need_powercycle) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto out; - json_object_object_add(jfirmware, "need_powercycle", jobj); - } - - ndctl_cmd_unref(cmd); - return jfirmware; - -err: - json_object_put(jfirmware); - jfirmware = NULL; -out: - if (cmd) - ndctl_cmd_unref(cmd); - return jfirmware; -} - -struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, - unsigned long flags) -{ - struct json_object *jdimm = json_object_new_object(); - const char *id = ndctl_dimm_get_unique_id(dimm); - unsigned int handle = ndctl_dimm_get_handle(dimm); - unsigned short phys_id = ndctl_dimm_get_phys_id(dimm); - struct json_object *jobj; - enum ndctl_security_state sstate; - - if (!jdimm) - return NULL; - - jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); - if (!jobj) - goto err; - json_object_object_add(jdimm, "dev", jobj); - - if (id) { - jobj = json_object_new_string(id); - if (!jobj) - goto err; - json_object_object_add(jdimm, "id", jobj); - } - - if (handle < UINT_MAX) { - jobj = util_json_object_hex(handle, flags); - if (!jobj) - goto err; - json_object_object_add(jdimm, "handle", jobj); - } - - if (phys_id < USHRT_MAX) { - jobj = util_json_object_hex(phys_id, flags); - if (!jobj) - goto err; - json_object_object_add(jdimm, "phys_id", jobj); - } - - if (!ndctl_dimm_is_enabled(dimm)) { - jobj = json_object_new_string("disabled"); - if (!jobj) - goto err; - json_object_object_add(jdimm, "state", jobj); - } - - if (ndctl_dimm_failed_map(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_failed_map", jobj); - } - - if (ndctl_dimm_failed_save(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_failed_save", jobj); - } - - if (ndctl_dimm_failed_arm(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_failed_arm", jobj); - } - - if (ndctl_dimm_failed_restore(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_failed_restore", jobj); - } - - if (ndctl_dimm_failed_flush(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_failed_flush", jobj); - } - - if (ndctl_dimm_smart_pending(dimm)) { - jobj = json_object_new_boolean(true); - if (!jobj) - goto err; - json_object_object_add(jdimm, "flag_smart_event", jobj); - } - - sstate = ndctl_dimm_get_security(dimm); - if (sstate == NDCTL_SECURITY_DISABLED) - jobj = json_object_new_string("disabled"); - else if (sstate == NDCTL_SECURITY_UNLOCKED) - jobj = json_object_new_string("unlocked"); - else if (sstate == NDCTL_SECURITY_LOCKED) - jobj = json_object_new_string("locked"); - else if (sstate == NDCTL_SECURITY_FROZEN) - jobj = json_object_new_string("frozen"); - else if (sstate == NDCTL_SECURITY_OVERWRITE) - jobj = json_object_new_string("overwrite"); - else - jobj = NULL; - if (jobj) - json_object_object_add(jdimm, "security", jobj); - - if (ndctl_dimm_security_is_frozen(dimm)) { - jobj = json_object_new_boolean(true); - if (jobj) - json_object_object_add(jdimm, "security_frozen", jobj); - } - - if (flags & UTIL_JSON_FIRMWARE) { - struct json_object *jfirmware; - - jfirmware = util_dimm_firmware_to_json(dimm, flags); - if (jfirmware) - json_object_object_add(jdimm, "firmware", jfirmware); - } - - return jdimm; - err: - json_object_put(jdimm); - return NULL; -} - -struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, - unsigned long flags) -{ - struct daxctl_memory *mem = daxctl_dev_get_memory(dev); - const char *devname = daxctl_dev_get_devname(dev); - struct json_object *jdev, *jobj, *jmappings = NULL; - struct daxctl_mapping *mapping = NULL; - int node, movable, align; - - jdev = json_object_new_object(); - if (!devname || !jdev) - return NULL; - - jobj = json_object_new_string(devname); - if (jobj) - json_object_object_add(jdev, "chardev", jobj); - - jobj = util_json_object_size(daxctl_dev_get_size(dev), flags); - if (jobj) - json_object_object_add(jdev, "size", jobj); - - node = daxctl_dev_get_target_node(dev); - if (node >= 0) { - jobj = json_object_new_int(node); - if (jobj) - json_object_object_add(jdev, "target_node", jobj); - } - - align = daxctl_dev_get_align(dev); - if (align > 0) { - jobj = util_json_object_size(daxctl_dev_get_align(dev), flags); - if (jobj) - json_object_object_add(jdev, "align", jobj); - } - - if (mem) - jobj = json_object_new_string("system-ram"); - else - jobj = json_object_new_string("devdax"); - if (jobj) - json_object_object_add(jdev, "mode", jobj); - - if (mem && daxctl_dev_get_resource(dev) != 0) { - int num_sections = daxctl_memory_num_sections(mem); - int num_online = daxctl_memory_is_online(mem); - - jobj = json_object_new_int(num_online); - if (jobj) - json_object_object_add(jdev, "online_memblocks", jobj); - - jobj = json_object_new_int(num_sections); - if (jobj) - json_object_object_add(jdev, "total_memblocks", jobj); - - movable = daxctl_memory_is_movable(mem); - if (movable == 1) - jobj = json_object_new_boolean(true); - else if (movable == 0) - jobj = json_object_new_boolean(false); - else - jobj = NULL; - if (jobj) - json_object_object_add(jdev, "movable", jobj); - } - - if (!daxctl_dev_is_enabled(dev)) { - jobj = json_object_new_string("disabled"); - if (jobj) - json_object_object_add(jdev, "state", jobj); - } - - if (!(flags & UTIL_JSON_DAX_MAPPINGS)) - return jdev; - - daxctl_mapping_foreach(dev, mapping) { - struct json_object *jmapping; - - if (!jmappings) { - jmappings = json_object_new_array(); - if (!jmappings) - continue; - - json_object_object_add(jdev, "mappings", jmappings); - } - - jmapping = util_daxctl_mapping_to_json(mapping, flags); - if (!jmapping) - continue; - json_object_array_add(jmappings, jmapping); - } - return jdev; -} - -struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, - struct json_object *jdevs, const char *ident, - unsigned long flags) -{ - struct daxctl_dev *dev; - - daxctl_dev_foreach(region, dev) { - struct json_object *jdev; - - if (!util_daxctl_dev_filter(dev, ident)) - continue; - - if (!(flags & (UTIL_JSON_IDLE|UTIL_JSON_CONFIGURED)) - && !daxctl_dev_get_size(dev)) - continue; - - if (!jdevs) { - jdevs = json_object_new_array(); - if (!jdevs) - return NULL; - } - - jdev = util_daxctl_dev_to_json(dev, flags); - if (!jdev) { - json_object_put(jdevs); - return NULL; - } - - json_object_array_add(jdevs, jdev); - } - - return jdevs; -} - -#define _SZ(get_max, get_elem, type) \ -static struct json_object *util_##type##_build_size_array(struct ndctl_##type *arg) \ -{ \ - struct json_object *arr = json_object_new_array(); \ - int i; \ - \ - if (!arr) \ - return NULL; \ - \ - for (i = 0; i < get_max(arg); i++) { \ - struct json_object *jobj; \ - int64_t align; \ - \ - align = get_elem(arg, i); \ - jobj = json_object_new_int64(align); \ - if (!jobj) \ - goto err; \ - json_object_array_add(arr, jobj); \ - } \ - \ - return arr; \ -err: \ - json_object_put(arr); \ - return NULL; \ -} -#define SZ(type, kind) _SZ(ndctl_##type##_get_num_##kind##s, \ - ndctl_##type##_get_supported_##kind, type) -SZ(pfn, alignment) -SZ(dax, alignment) -SZ(btt, sector_size) - -struct json_object *util_region_capabilities_to_json(struct ndctl_region *region) -{ - struct json_object *jcaps, *jcap, *jobj; - struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); - struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); - struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); - - if (!btt || !pfn || !dax) - return NULL; - - jcaps = json_object_new_array(); - if (!jcaps) - return NULL; - - if (btt) { - jcap = json_object_new_object(); - if (!jcap) - goto err; - json_object_array_add(jcaps, jcap); - - jobj = json_object_new_string("sector"); - if (!jobj) - goto err; - json_object_object_add(jcap, "mode", jobj); - jobj = util_btt_build_size_array(btt); - if (!jobj) - goto err; - json_object_object_add(jcap, "sector_sizes", jobj); - } - - if (pfn) { - jcap = json_object_new_object(); - if (!jcap) - goto err; - json_object_array_add(jcaps, jcap); - - jobj = json_object_new_string("fsdax"); - if (!jobj) - goto err; - json_object_object_add(jcap, "mode", jobj); - jobj = util_pfn_build_size_array(pfn); - if (!jobj) - goto err; - json_object_object_add(jcap, "alignments", jobj); - } - - if (dax) { - jcap = json_object_new_object(); - if (!jcap) - goto err; - json_object_array_add(jcaps, jcap); - - jobj = json_object_new_string("devdax"); - if (!jobj) - goto err; - json_object_object_add(jcap, "mode", jobj); - jobj = util_dax_build_size_array(dax); - if (!jobj) - goto err; - json_object_object_add(jcap, "alignments", jobj); - } - - return jcaps; -err: - json_object_put(jcaps); - return NULL; -} - -struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, - const char *ident, unsigned long flags) -{ - unsigned long align; - struct json_object *jregion, *jobj; - unsigned long long available_size, size; - - jregion = json_object_new_object(); - if (!jregion) - return NULL; - - /* - * The flag indicates when we are being called by an agent that - * already knows about the parent device information. - */ - if (!(flags & UTIL_JSON_DAX)) { - /* trim off the redundant /sys/devices prefix */ - const char *path = daxctl_region_get_path(region); - int len = strlen("/sys/devices"); - const char *trim = &path[len]; - - if (strncmp(path, "/sys/devices", len) != 0) - goto err; - jobj = json_object_new_string(trim); - if (!jobj) - goto err; - json_object_object_add(jregion, "path", jobj); - } - - jobj = json_object_new_int(daxctl_region_get_id(region)); - if (!jobj) - goto err; - json_object_object_add(jregion, "id", jobj); - - size = daxctl_region_get_size(region); - if (size < ULLONG_MAX) { - jobj = util_json_object_size(size, flags); - if (!jobj) - goto err; - json_object_object_add(jregion, "size", jobj); - } - - available_size = daxctl_region_get_available_size(region); - if (available_size) { - jobj = util_json_object_size(available_size, flags); - if (!jobj) - goto err; - json_object_object_add(jregion, "available_size", jobj); - } - - align = daxctl_region_get_align(region); - if (align < ULONG_MAX) { - jobj = json_object_new_int64(align); - if (!jobj) - goto err; - json_object_object_add(jregion, "align", jobj); - } - - if (!(flags & UTIL_JSON_DAX_DEVS)) - return jregion; - - jobj = util_daxctl_devs_to_list(region, NULL, ident, flags); - if (jobj) - json_object_object_add(jregion, "devices", jobj); - - return jregion; - err: - json_object_put(jregion); - return NULL; -} - -static int compare_dimm_number(const void *p1, const void *p2) -{ - struct ndctl_dimm *dimm1 = *(struct ndctl_dimm **)p1; - struct ndctl_dimm *dimm2 = *(struct ndctl_dimm **)p2; - const char *dimm1_name = ndctl_dimm_get_devname(dimm1); - const char *dimm2_name = ndctl_dimm_get_devname(dimm2); - int num1, num2; - - if (sscanf(dimm1_name, "nmem%d", &num1) != 1) - num1 = 0; - if (sscanf(dimm2_name, "nmem%d", &num2) != 1) - num2 = 0; - - return num1 - num2; -} - -static struct json_object *badblocks_to_jdimms(struct ndctl_region *region, - unsigned long long addr, unsigned long len) -{ - struct ndctl_bus *bus = ndctl_region_get_bus(region); - int count = ndctl_region_get_interleave_ways(region); - unsigned long long end = addr + len; - struct json_object *jdimms, *jobj; - struct ndctl_dimm **dimms, *dimm; - int found, i; - - jdimms = json_object_new_array(); - if (!jdimms) - return NULL; - - dimms = calloc(count, sizeof(struct ndctl_dimm *)); - if (!dimms) - goto err_dimms; - - for (found = 0; found < count && addr < end; addr += 512) { - dimm = ndctl_bus_get_dimm_by_physical_address(bus, addr); - if (!dimm) - continue; - - for (i = 0; i < count; i++) - if (dimms[i] == dimm) - break; - if (i >= count) - dimms[found++] = dimm; - } - - if (!found) - goto err_found; - - qsort(dimms, found, sizeof(dimm), compare_dimm_number); - - for (i = 0; i < found; i++) { - const char *devname = ndctl_dimm_get_devname(dimms[i]); - - jobj = json_object_new_string(devname); - if (!jobj) - break; - json_object_array_add(jdimms, jobj); - } - - if (!i) - goto err_found; - free(dimms); - return jdimms; - -err_found: - free(dimms); -err_dimms: - json_object_put(jdimms); - return NULL; -} - -struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, - unsigned int *bb_count, unsigned long flags) -{ - struct json_object *jbb = NULL, *jbbs = NULL, *jobj; - struct badblock *bb; - int bbs = 0; - - if (flags & UTIL_JSON_MEDIA_ERRORS) { - jbbs = json_object_new_array(); - if (!jbbs) - return NULL; - } - - ndctl_region_badblock_foreach(region, bb) { - struct json_object *jdimms; - unsigned long long addr; - - bbs += bb->len; - - /* recheck so we can still get the badblocks_count from above */ - if (!(flags & UTIL_JSON_MEDIA_ERRORS)) - continue; - - /* get start address of region */ - addr = ndctl_region_get_resource(region); - if (addr == ULLONG_MAX) - goto err_array; - - /* get address of bad block */ - addr += bb->offset << 9; - - jbb = json_object_new_object(); - if (!jbb) - goto err_array; - - jobj = json_object_new_int64(bb->offset); - if (!jobj) - goto err; - json_object_object_add(jbb, "offset", jobj); - - jobj = json_object_new_int(bb->len); - if (!jobj) - goto err; - json_object_object_add(jbb, "length", jobj); - - jdimms = badblocks_to_jdimms(region, addr, bb->len << 9); - if (jdimms) - json_object_object_add(jbb, "dimms", jdimms); - json_object_array_add(jbbs, jbb); - } - - *bb_count = bbs; - - if (bbs) - return jbbs; - - err: - json_object_put(jbb); - err_array: - json_object_put(jbbs); - return NULL; -} - -static struct json_object *util_namespace_badblocks_to_json( - struct ndctl_namespace *ndns, - unsigned int *bb_count, unsigned long flags) -{ - struct json_object *jbb = NULL, *jbbs = NULL, *jobj; - struct badblock *bb; - int bbs = 0; - - if (flags & UTIL_JSON_MEDIA_ERRORS) { - jbbs = json_object_new_array(); - if (!jbbs) - return NULL; - } else - return NULL; - - ndctl_namespace_badblock_foreach(ndns, bb) { - bbs += bb->len; - - /* recheck so we can still get the badblocks_count from above */ - if (!(flags & UTIL_JSON_MEDIA_ERRORS)) - continue; - - jbb = json_object_new_object(); - if (!jbb) - goto err_array; - - jobj = json_object_new_int64(bb->offset); - if (!jobj) - goto err; - json_object_object_add(jbb, "offset", jobj); - - jobj = json_object_new_int(bb->len); - if (!jobj) - goto err; - json_object_object_add(jbb, "length", jobj); - json_object_array_add(jbbs, jbb); - } - - *bb_count = bbs; - - if (bbs) - return jbbs; - - err: - json_object_put(jbb); - err_array: - json_object_put(jbbs); - return NULL; -} - -static struct json_object *dev_badblocks_to_json(struct ndctl_region *region, - unsigned long long dev_begin, unsigned long long dev_size, - unsigned int *bb_count, unsigned long flags) -{ - struct json_object *jbb = NULL, *jbbs = NULL, *jobj; - unsigned long long region_begin, dev_end, offset; - unsigned int len, bbs = 0; - struct badblock *bb; - - region_begin = ndctl_region_get_resource(region); - if (region_begin == ULLONG_MAX) - return NULL; - - dev_end = dev_begin + dev_size - 1; - - if (flags & UTIL_JSON_MEDIA_ERRORS) { - jbbs = json_object_new_array(); - if (!jbbs) - return NULL; - } - - ndctl_region_badblock_foreach(region, bb) { - unsigned long long bb_begin, bb_end, begin, end; - struct json_object *jdimms; - - bb_begin = region_begin + (bb->offset << 9); - bb_end = bb_begin + (bb->len << 9) - 1; - - if (bb_end <= dev_begin || bb_begin >= dev_end) - continue; - - if (bb_begin < dev_begin) - begin = dev_begin; - else - begin = bb_begin; - - if (bb_end > dev_end) - end = dev_end; - else - end = bb_end; - - offset = (begin - dev_begin) >> 9; - len = (end - begin + 1) >> 9; - - bbs += len; - - /* recheck so we can still get the badblocks_count from above */ - if (!(flags & UTIL_JSON_MEDIA_ERRORS)) - continue; - - jbb = json_object_new_object(); - if (!jbb) - goto err_array; - - jobj = json_object_new_int64(offset); - if (!jobj) - goto err; - json_object_object_add(jbb, "offset", jobj); - - jobj = json_object_new_int(len); - if (!jobj) - goto err; - json_object_object_add(jbb, "length", jobj); - - jdimms = badblocks_to_jdimms(region, begin, len << 9); - if (jdimms) - json_object_object_add(jbb, "dimms", jdimms); - - json_object_array_add(jbbs, jbb); - } - - *bb_count = bbs; - - if (bbs) - return jbbs; - - err: - json_object_put(jbb); - err_array: - json_object_put(jbbs); - return NULL; -} - -static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn, - unsigned int *bb_count, unsigned long flags) -{ - struct ndctl_region *region = ndctl_pfn_get_region(pfn); - unsigned long long pfn_begin, pfn_size; - - pfn_begin = ndctl_pfn_get_resource(pfn); - if (pfn_begin == ULLONG_MAX) { - struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); - - return util_namespace_badblocks_to_json(ndns, bb_count, flags); - } - - pfn_size = ndctl_pfn_get_size(pfn); - if (pfn_size == ULLONG_MAX) - return NULL; - - return dev_badblocks_to_json(region, pfn_begin, pfn_size, - bb_count, flags); -} - -static void util_btt_badblocks_to_json(struct ndctl_btt *btt, - unsigned int *bb_count) -{ - struct ndctl_region *region = ndctl_btt_get_region(btt); - struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); - unsigned long long begin, size; - - if (!ndns) - return; - - begin = ndctl_namespace_get_resource(ndns); - if (begin == ULLONG_MAX) - return; - - size = ndctl_namespace_get_size(ndns); - if (size == ULLONG_MAX) - return; - - /* - * The dev_badblocks_to_json() for BTT is not accurate with - * respect to data vs metadata badblocks, and is only useful for - * a potential bb_count. - * - * FIXME: switch to native BTT badblocks representation - * when / if the kernel provides it. - */ - dev_badblocks_to_json(region, begin, size, bb_count, 0); -} - -static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax, - unsigned int *bb_count, unsigned long flags) -{ - struct ndctl_region *region = ndctl_dax_get_region(dax); - unsigned long long dax_begin, dax_size; - - dax_begin = ndctl_dax_get_resource(dax); - if (dax_begin == ULLONG_MAX) - return NULL; - - dax_size = ndctl_dax_get_size(dax); - if (dax_size == ULLONG_MAX) - return NULL; - - return dev_badblocks_to_json(region, dax_begin, dax_size, - bb_count, flags); -} - -static struct json_object *util_raw_uuid(struct ndctl_namespace *ndns) -{ - char buf[40]; - uuid_t raw_uuid; - - ndctl_namespace_get_uuid(ndns, raw_uuid); - if (uuid_is_null(raw_uuid)) - return NULL; - uuid_unparse(raw_uuid, buf); - return json_object_new_string(buf); -} - -static void util_raw_uuid_to_json(struct ndctl_namespace *ndns, - unsigned long flags, - struct json_object *jndns) -{ - struct json_object *jobj; - - if (!(flags & UTIL_JSON_VERBOSE)) - return; - - jobj = util_raw_uuid(ndns); - if (!jobj) - return; - json_object_object_add(jndns, "raw_uuid", jobj); -} - -struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, - unsigned long flags) -{ - struct json_object *jndns = json_object_new_object(); - enum ndctl_pfn_loc loc = NDCTL_PFN_LOC_NONE; - struct json_object *jobj, *jbbs = NULL; - const char *locations[] = { - [NDCTL_PFN_LOC_NONE] = "none", - [NDCTL_PFN_LOC_RAM] = "mem", - [NDCTL_PFN_LOC_PMEM] = "dev", - }; - unsigned long long size = ULLONG_MAX; - unsigned int sector_size = UINT_MAX; - enum ndctl_namespace_mode mode; - const char *bdev = NULL, *name; - unsigned int bb_count = 0; - struct ndctl_btt *btt; - struct ndctl_pfn *pfn; - struct ndctl_dax *dax; - unsigned long align = 0; - char buf[40]; - uuid_t uuid; - int numa, target; - - if (!jndns) - return NULL; - - jobj = json_object_new_string(ndctl_namespace_get_devname(ndns)); - if (!jobj) - goto err; - json_object_object_add(jndns, "dev", jobj); - - btt = ndctl_namespace_get_btt(ndns); - dax = ndctl_namespace_get_dax(ndns); - pfn = ndctl_namespace_get_pfn(ndns); - mode = ndctl_namespace_get_mode(ndns); - switch (mode) { - case NDCTL_NS_MODE_MEMORY: - if (pfn) { /* dynamic memory mode */ - size = ndctl_pfn_get_size(pfn); - loc = ndctl_pfn_get_location(pfn); - } else { /* native/static memory mode */ - size = ndctl_namespace_get_size(ndns); - loc = NDCTL_PFN_LOC_RAM; - } - jobj = json_object_new_string("fsdax"); - break; - case NDCTL_NS_MODE_DAX: - if (!dax) - goto err; - size = ndctl_dax_get_size(dax); - jobj = json_object_new_string("devdax"); - loc = ndctl_dax_get_location(dax); - break; - case NDCTL_NS_MODE_SECTOR: - if (!btt) - goto err; - jobj = json_object_new_string("sector"); - size = ndctl_btt_get_size(btt); - break; - case NDCTL_NS_MODE_RAW: - size = ndctl_namespace_get_size(ndns); - jobj = json_object_new_string("raw"); - break; - default: - jobj = NULL; - } - if (jobj) - json_object_object_add(jndns, "mode", jobj); - - if ((mode != NDCTL_NS_MODE_SECTOR) && (mode != NDCTL_NS_MODE_RAW)) { - jobj = json_object_new_string(locations[loc]); - if (jobj) - json_object_object_add(jndns, "map", jobj); - } - - if (size < ULLONG_MAX) { - jobj = util_json_object_size(size, flags); - if (jobj) - json_object_object_add(jndns, "size", jobj); - } - - if (btt) { - ndctl_btt_get_uuid(btt, uuid); - uuid_unparse(uuid, buf); - jobj = json_object_new_string(buf); - if (!jobj) - goto err; - json_object_object_add(jndns, "uuid", jobj); - util_raw_uuid_to_json(ndns, flags, jndns); - bdev = ndctl_btt_get_block_device(btt); - } else if (pfn) { - align = ndctl_pfn_get_align(pfn); - ndctl_pfn_get_uuid(pfn, uuid); - uuid_unparse(uuid, buf); - jobj = json_object_new_string(buf); - if (!jobj) - goto err; - json_object_object_add(jndns, "uuid", jobj); - util_raw_uuid_to_json(ndns, flags, jndns); - bdev = ndctl_pfn_get_block_device(pfn); - } else if (dax) { - struct daxctl_region *dax_region; - - dax_region = ndctl_dax_get_daxctl_region(dax); - align = ndctl_dax_get_align(dax); - ndctl_dax_get_uuid(dax, uuid); - uuid_unparse(uuid, buf); - jobj = json_object_new_string(buf); - if (!jobj) - goto err; - json_object_object_add(jndns, "uuid", jobj); - util_raw_uuid_to_json(ndns, flags, jndns); - if ((flags & UTIL_JSON_DAX) && dax_region) { - jobj = util_daxctl_region_to_json(dax_region, NULL, - flags); - if (jobj) - json_object_object_add(jndns, "daxregion", jobj); - } else if (dax_region) { - struct daxctl_dev *dev; - - /* - * We can only find/list these device-dax - * details when the instance is enabled. - */ - dev = daxctl_dev_get_first(dax_region); - if (dev) { - name = daxctl_dev_get_devname(dev); - jobj = json_object_new_string(name); - if (!jobj) - goto err; - json_object_object_add(jndns, "chardev", jobj); - } - } - } else if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { - ndctl_namespace_get_uuid(ndns, uuid); - uuid_unparse(uuid, buf); - jobj = json_object_new_string(buf); - if (!jobj) - goto err; - json_object_object_add(jndns, "uuid", jobj); - bdev = ndctl_namespace_get_block_device(ndns); - } else - bdev = ndctl_namespace_get_block_device(ndns); - - if (btt) - sector_size = ndctl_btt_get_sector_size(btt); - else if (!dax) { - sector_size = ndctl_namespace_get_sector_size(ndns); - if (!sector_size || sector_size == UINT_MAX) - sector_size = 512; - } - - /* - * The kernel will default to a 512 byte sector size on PMEM - * namespaces that don't explicitly have a sector size. This - * happens because they use pre-v1.2 labels or because they - * don't have a label space (devtype=nd_namespace_io). - */ - if (sector_size < UINT_MAX) { - jobj = json_object_new_int(sector_size); - if (!jobj) - goto err; - json_object_object_add(jndns, "sector_size", jobj); - } - - if (align) { - jobj = json_object_new_int64(align); - if (!jobj) - goto err; - json_object_object_add(jndns, "align", jobj); - } - - if (bdev && bdev[0]) { - jobj = json_object_new_string(bdev); - if (!jobj) - goto err; - json_object_object_add(jndns, "blockdev", jobj); - } - - if (!ndctl_namespace_is_active(ndns)) { - jobj = json_object_new_string("disabled"); - if (!jobj) - goto err; - json_object_object_add(jndns, "state", jobj); - } - - name = ndctl_namespace_get_alt_name(ndns); - if (name && name[0]) { - jobj = json_object_new_string(name); - if (!jobj) - goto err; - json_object_object_add(jndns, "name", jobj); - } - - numa = ndctl_namespace_get_numa_node(ndns); - if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { - jobj = json_object_new_int(numa); - if (jobj) - json_object_object_add(jndns, "numa_node", jobj); - } - - target = ndctl_namespace_get_target_node(ndns); - if (target >= 0 && flags & UTIL_JSON_VERBOSE) { - jobj = json_object_new_int(target); - if (jobj) - json_object_object_add(jndns, "target_node", jobj); - } - - if (pfn) - jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags); - else if (dax) - 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), &bb_count, - flags); - if (!jbbs) - jbbs = util_namespace_badblocks_to_json(ndns, &bb_count, - flags); - } - - if (bb_count) { - jobj = json_object_new_int(bb_count); - if (!jobj) { - json_object_put(jbbs); - goto err; - } - json_object_object_add(jndns, "badblock_count", jobj); - } - - if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) - json_object_object_add(jndns, "badblocks", jbbs); - - return jndns; - err: - json_object_put(jndns); - return NULL; -} - -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); - struct json_object *jobj; - int position; - - if (!jmapping) - return NULL; - - jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); - if (!jobj) - goto err; - json_object_object_add(jmapping, "dimm", jobj); - - jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "offset", jobj); - - jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "length", jobj); - - position = ndctl_mapping_get_position(mapping); - if (position >= 0) { - jobj = json_object_new_int(position); - if (!jobj) - goto err; - json_object_object_add(jmapping, "position", jobj); - } - - return jmapping; - err: - json_object_put(jmapping); - return NULL; -} - -struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, - unsigned long flags) -{ - struct json_object *jmapping = json_object_new_object(); - struct json_object *jobj; - - if (!jmapping) - return NULL; - - jobj = util_json_object_hex(daxctl_mapping_get_offset(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "page_offset", jobj); - - jobj = util_json_object_hex(daxctl_mapping_get_start(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "start", jobj); - - jobj = util_json_object_hex(daxctl_mapping_get_end(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "end", jobj); - - jobj = util_json_object_size(daxctl_mapping_get_size(mapping), flags); - if (!jobj) - goto err; - json_object_object_add(jmapping, "size", jobj); - - return jmapping; - err: - json_object_put(jmapping); - return NULL; -} - -struct json_object *util_badblock_rec_to_json(u64 block, u64 count, - unsigned long flags) -{ - struct json_object *jerr = json_object_new_object(); - struct json_object *jobj; - - if (!jerr) - return NULL; - - jobj = util_json_object_hex(block, flags); - if (!jobj) - goto err; - json_object_object_add(jerr, "block", jobj); - - jobj = util_json_object_hex(count, flags); - if (!jobj) - goto err; - json_object_object_add(jerr, "count", jobj); - - return jerr; - err: - json_object_put(jerr); - return NULL; -} - -static struct json_object *util_cxl_memdev_health_to_json( - struct cxl_memdev *memdev, unsigned long flags) -{ - struct json_object *jhealth; - struct json_object *jobj; - struct cxl_cmd *cmd; - u32 field; - int rc; - - jhealth = json_object_new_object(); - if (!jhealth) - return NULL; - if (!memdev) - goto err_jobj; - - cmd = cxl_cmd_new_get_health_info(memdev); - if (!cmd) - goto err_jobj; - - rc = cxl_cmd_submit(cmd); - if (rc < 0) - goto err_cmd; - rc = cxl_cmd_get_mbox_status(cmd); - if (rc != 0) - goto err_cmd; - - /* health_status fields */ - rc = cxl_cmd_health_info_get_maintenance_needed(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "maintenance_needed", jobj); - - rc = cxl_cmd_health_info_get_performance_degraded(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "performance_degraded", jobj); - - rc = cxl_cmd_health_info_get_hw_replacement_needed(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "hw_replacement_needed", jobj); - - /* media_status fields */ - rc = cxl_cmd_health_info_get_media_normal(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_normal", jobj); - - rc = cxl_cmd_health_info_get_media_not_ready(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_not_ready", jobj); - - rc = cxl_cmd_health_info_get_media_persistence_lost(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_persistence_lost", jobj); - - rc = cxl_cmd_health_info_get_media_data_lost(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_data_lost", jobj); - - rc = cxl_cmd_health_info_get_media_powerloss_persistence_loss(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_powerloss_persistence_loss", jobj); - - rc = cxl_cmd_health_info_get_media_shutdown_persistence_loss(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_shutdown_persistence_loss", jobj); - - rc = cxl_cmd_health_info_get_media_persistence_loss_imminent(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_persistence_loss_imminent", jobj); - - rc = cxl_cmd_health_info_get_media_powerloss_data_loss(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_powerloss_data_loss", jobj); - - rc = cxl_cmd_health_info_get_media_shutdown_data_loss(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_shutdown_data_loss", jobj); - - rc = cxl_cmd_health_info_get_media_data_loss_imminent(cmd); - jobj = json_object_new_boolean(rc); - if (jobj) - json_object_object_add(jhealth, "media_data_loss_imminent", jobj); - - /* ext_status fields */ - if (cxl_cmd_health_info_get_ext_life_used_normal(cmd)) - jobj = json_object_new_string("normal"); - else if (cxl_cmd_health_info_get_ext_life_used_warning(cmd)) - jobj = json_object_new_string("warning"); - else if (cxl_cmd_health_info_get_ext_life_used_critical(cmd)) - jobj = json_object_new_string("critical"); - else - jobj = json_object_new_string("unknown"); - if (jobj) - json_object_object_add(jhealth, "ext_life_used", jobj); - - if (cxl_cmd_health_info_get_ext_temperature_normal(cmd)) - jobj = json_object_new_string("normal"); - else if (cxl_cmd_health_info_get_ext_temperature_warning(cmd)) - jobj = json_object_new_string("warning"); - else if (cxl_cmd_health_info_get_ext_temperature_critical(cmd)) - jobj = json_object_new_string("critical"); - else - jobj = json_object_new_string("unknown"); - if (jobj) - json_object_object_add(jhealth, "ext_temperature", jobj); - - if (cxl_cmd_health_info_get_ext_corrected_volatile_normal(cmd)) - jobj = json_object_new_string("normal"); - else if (cxl_cmd_health_info_get_ext_corrected_volatile_warning(cmd)) - jobj = json_object_new_string("warning"); - else - jobj = json_object_new_string("unknown"); - if (jobj) - json_object_object_add(jhealth, "ext_corrected_volatile", jobj); - - if (cxl_cmd_health_info_get_ext_corrected_persistent_normal(cmd)) - jobj = json_object_new_string("normal"); - else if (cxl_cmd_health_info_get_ext_corrected_persistent_warning(cmd)) - jobj = json_object_new_string("warning"); - else - jobj = json_object_new_string("unknown"); - if (jobj) - json_object_object_add(jhealth, "ext_corrected_persistent", jobj); - - /* other fields */ - field = cxl_cmd_health_info_get_life_used(cmd); - if (field != 0xff) { - jobj = json_object_new_int(field); - if (jobj) - json_object_object_add(jhealth, "life_used_percent", jobj); - } - - field = cxl_cmd_health_info_get_temperature(cmd); - if (field != 0xffff) { - jobj = json_object_new_int(field); - if (jobj) - json_object_object_add(jhealth, "temperature", jobj); - } - - field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); - jobj = json_object_new_int64(field); - if (jobj) - json_object_object_add(jhealth, "dirty_shutdowns", jobj); - - field = cxl_cmd_health_info_get_volatile_errors(cmd); - jobj = json_object_new_int64(field); - if (jobj) - json_object_object_add(jhealth, "volatile_errors", jobj); - - field = cxl_cmd_health_info_get_pmem_errors(cmd); - jobj = json_object_new_int64(field); - if (jobj) - json_object_object_add(jhealth, "pmem_errors", jobj); - - cxl_cmd_unref(cmd); - return jhealth; - -err_cmd: - cxl_cmd_unref(cmd); -err_jobj: - json_object_put(jhealth); - return NULL; -} - -struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, - unsigned long flags) -{ - const char *devname = cxl_memdev_get_devname(memdev); - struct json_object *jdev, *jobj; - - jdev = json_object_new_object(); - if (!devname || !jdev) - return NULL; - - jobj = json_object_new_string(devname); - if (jobj) - json_object_object_add(jdev, "memdev", jobj); - - jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags); - if (jobj) - json_object_object_add(jdev, "pmem_size", jobj); - - jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags); - if (jobj) - json_object_object_add(jdev, "ram_size", jobj); - - if (flags & UTIL_JSON_HEALTH) { - jobj = util_cxl_memdev_health_to_json(memdev, flags); - if (jobj) - json_object_object_add(jdev, "health", jobj); - } - return jdev; -} diff -up ndctl-71.1/util/json.h.orig ndctl-71.1/util/json.h --- ndctl-71.1/util/json.h.orig 2022-10-07 15:50:40.097454638 -0400 +++ ndctl-71.1/util/json.h 2022-10-07 15:51:03.927535772 -0400 @@ -1,12 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ -#ifndef __NDCTL_JSON_H__ -#define __NDCTL_JSON_H__ +#ifndef __UTIL_JSON_H__ +#define __UTIL_JSON_H__ #include #include -#include -#include -#include enum util_json_flags { UTIL_JSON_IDLE = (1 << 0), @@ -25,38 +22,8 @@ enum util_json_flags { struct json_object; void util_display_json_array(FILE *f_out, struct json_object *jarray, unsigned long flags); -struct json_object *util_bus_to_json(struct ndctl_bus *bus, - unsigned long flags); -struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, - unsigned long flags); -struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, - unsigned long flags); -struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, - unsigned long flags); -struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, - unsigned long flags); -struct json_object *util_badblock_rec_to_json(u64 block, u64 count, - unsigned long flags); -struct daxctl_region; -struct daxctl_dev; -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, - 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); -struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); -struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, - unsigned long flags); -struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); -struct cxl_memdev; -struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, - unsigned long flags); -#endif /* __NDCTL_JSON_H__ */ +#endif /* __UTIL_JSON_H__ */