From 2eceedeb111453949ac78685c21c0f6a05f02bcd Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Mon, 22 Jun 2026 12:43:07 +0200 Subject: [PATCH] zpcimon: Add NVMe SMART data monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new monitor which uses libnvme to collect SMART data from NVMes. This monitor only implements the .collect_adapter_data operation since it carries no state across data collections. Nevertheless for future symmetry and possible future expansion also add an empty struct nvmemon_ctx. Reviewed-by: Jan Höppner Signed-off-by: Niklas Schnelle Signed-off-by: Jan Höppner --- README.md | 7 +-- include/lib/pci_sclp.h | 2 + zpcimon/Makefile | 22 +++++++++- zpcimon/nvmemon.c | 98 ++++++++++++++++++++++++++++++++++++++++++ zpcimon/nvmemon.h | 13 ++++++ zpcimon/zpcimon.8 | 26 +++++++---- zpcimon/zpcimon.c | 5 +++ zpcimon/zpcimon.h | 4 ++ zpcimon/zpcimon_cli.h | 8 ++++ 9 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 zpcimon/nvmemon.c create mode 100644 zpcimon/nvmemon.h diff --git a/README.md b/README.md index 4045b0f8..ab3dcd4e 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,7 @@ build options: | systemd | `HAVE_SYSTEMD` | hsavmcore | | libudev | `HAVE_LIBUDEV` | cpacfstatsd | | libnl3 | `HAVE_LIBNL3` | zpcimon | +| libnvme | `HAVE_LIBNVME` | zpcimon | This table lists additional build or install options: @@ -375,10 +376,10 @@ the different tools are provided: The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl. * zpcimon: - For building zpcimon OpenSSL and the Netlink Library Suite (libnl3) are - required. + For building zpcimon OpenSSL, the Netlink Library Suite (libnl3), + and libnvme are required. Tip: you may skip the zpcimon build by adding - `HAVE_OPENSSL=0` or `HAVE_LIBNL3=0` + `HAVE_OPENSSL=0` or `HAVE_LIBNL3=0` or `HAVE_LIBNVME=0`. * osasnmpd: You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm) diff --git a/include/lib/pci_sclp.h b/include/lib/pci_sclp.h index 688a7b76..c075b264 100644 --- a/include/lib/pci_sclp.h +++ b/include/lib/pci_sclp.h @@ -21,9 +21,11 @@ #define SCLP_ERRNOTIFY_AQ_DECONF 1 #define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 #define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3 +#define SCLP_ERRNOTIFY_AQ_NVME_SMART_DATA 4 #define SCLP_ERRNOTIFY_ID_ZPCICTL 0x4713 #define SCLP_ERRNOTIFY_ID_OPTICSMON 0x4714 +#define SCLP_ERRNOTIFY_ID_NVMEMON 0x4715 #define SCLP_ERRNOTIFY_DATA_SIZE 4054 diff --git a/zpcimon/Makefile b/zpcimon/Makefile index cc0a8af0..ce815a31 100644 --- a/zpcimon/Makefile +++ b/zpcimon/Makefile @@ -29,6 +29,16 @@ check_dep_libnl3: BUILDTARGET += check_dep_libnl3 endif # HAVE_LIBNL3 +ifneq (${HAVE_LIBNVME},0) +check_dep_libnvme: + $(call check_dep, \ + "zpcimon", \ + "libnvme.h", \ + "libnvme-devel", \ + "HAVE_LIBNVME=0") +BUILDTARGET += check_dep_libnvme +endif # HAVE_LIBNVME + ifeq (${HAVE_OPENSSL},0) all: @@ -44,6 +54,13 @@ all: install: $(SKIP) HAVE_LIBNL3=0 +else ifeq (${HAVE_LIBNVME},0) +all: + $(SKIP) HAVE_LIBNVME=0 + +install: + $(SKIP) HAVE_LIBNVME=0 + else ifneq ($(shell sh -c 'command -v pkg-config'),) @@ -58,12 +75,15 @@ LIB_LFLAGS += $(shell $(PKG_CONFIG) --silence-errors --libs libnl-genl-3.0) LIB_LFLAGS += $(shell $(PKG_CONFIG) --silence-errors --libs libnl-route-3.0) LIB_LFLAGS += $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto) +LIB_LFLAGS += $(shell $(PKG_CONFIG) --silence-errors --libs libnvme) else LIB_CFLAGS += -I /usr/include/libnl3/ LIB_LFLAGS += -lnl-route-3 -lnl-genl-3 -lnl-3 LIB_CFLAGS += -I /usr/include/openssl/ LIB_LFLAGS += -lcrypto + +LIB_LFLAGS += -lnvme endif ALL_CPPFLAGS += $(LIB_CFLAGS) @@ -73,7 +93,7 @@ BUILDTARGET += zpcimon all: ${BUILDTARGET} -zpcimon: zpcimon.o opticsmon.o optics_info.o optics_sclp.o ethtool.o link_mon.o $(libs) +zpcimon: zpcimon.o nvmemon.o opticsmon.o optics_info.o optics_sclp.o ethtool.o link_mon.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 diff --git a/zpcimon/nvmemon.c b/zpcimon/nvmemon.c new file mode 100644 index 00000000..85a5d0ad --- /dev/null +++ b/zpcimon/nvmemon.c @@ -0,0 +1,98 @@ +/* + * zpcimon - Report monitoring data to firmware + * + * Copyright IBM Corp. 2025 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/pci_list.h" +#include "lib/pci_sclp.h" +#include "lib/util_fmt.h" + +#include "nvmemon.h" +#include "zpcimon.h" + +static void nvme_json_print(struct zpcimon_ctx *ctx, struct zpci_dev *zdev, const char *name, + struct nvme_smart_log *log) +{ + zpci_adapter_json_print_start(zdev); + util_fmt_obj_start(FMT_DEFAULT, "nvmedev"); + util_fmt_pair(FMT_QUOTE, "dev", name); + if (ctx->opts.smart_blob) + zpcimon_json_base64_pair("smart-log-raw", (uint8_t *)log, sizeof(*log)); + util_fmt_obj_end(); + zpci_adapter_json_print_end(); + fflush(stdout); +} + +static int sclp_issue_nvme_smart_report(struct zpci_dev *zdev, const uint8_t *smart, int smart_len) +{ + char *pci_addr; + int rc; + + if (zdev->pft != ZPCI_PFT_NVME) + return -ENOTSUP; + pci_addr = zpci_pci_addr(zdev); + rc = zpci_sclp_issue_action(pci_addr, SCLP_ERRNOTIFY_AQ_NVME_SMART_DATA, + (char *)smart, smart_len, SCLP_ERRNOTIFY_ID_NVMEMON); + free(pci_addr); + return rc; +} + +static int nvmemon_collect_adapter_data(struct zpcimon_ctx *ctx, struct zpci_dev *zdev) +{ + struct nvme_smart_log log = {}; + int nvme_fd, rc = -ENODEV; + char *dev, *pci_addr; + + if (zdev->pft != ZPCI_PFT_NVME) + return -ENODEV; + + pci_addr = zpci_pci_addr(zdev); + dev = zpci_get_nvme_device_node(pci_addr); + if (!dev) + goto exit_free_addr; + + nvme_fd = openat(AT_FDCWD, dev, O_RDONLY); + if (nvme_fd < 0) { + warn("Failed to open %s", dev); + rc = -errno; + goto exit_free_dev; + } + + rc = nvme_get_log_smart(nvme_fd, NVME_NSID_ALL, false, &log); + if (rc) { + warnx("Getting NVMe SMART log failed %d", rc); + goto exit_close; + } + if (!ctx->opts.quiet) + nvme_json_print(ctx, zdev, dev, &log); + if (ctx->opts.report) { + rc = sclp_issue_nvme_smart_report(zdev, (uint8_t *)&log, sizeof(log)); + if (rc < 0 && rc != -ENOTSUP) + warnx("Error issuing SCLP for NVMe SMART log failed"); + } + +exit_close: + close(nvme_fd); +exit_free_dev: + free(dev); +exit_free_addr: + free(pci_addr); + return rc; +} + +const struct zpcimon_ops nvmemon_ops = { + .collect_adapter_data = nvmemon_collect_adapter_data, +}; diff --git a/zpcimon/nvmemon.h b/zpcimon/nvmemon.h new file mode 100644 index 00000000..0658bcaa --- /dev/null +++ b/zpcimon/nvmemon.h @@ -0,0 +1,13 @@ +/* + * Copyright IBM Corp. 2025 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#ifndef ZPCIMON_NVMEMON_H +#define ZPCIMON_NVMEMON_H + +struct nvmemon_ctx {}; + +extern const struct zpcimon_ops nvmemon_ops; +#endif /* ZPCIMON_NVMEMON_H */ diff --git a/zpcimon/zpcimon.8 b/zpcimon/zpcimon.8 index 9b16fa91..6cc6ab29 100644 --- a/zpcimon/zpcimon.8 +++ b/zpcimon/zpcimon.8 @@ -33,7 +33,7 @@ .TH zpcimon 8 "Oct 2024" s390-tools zpcictl . .SH NAME -zpcimon - Monitor optical modules for directly attached PCI based NICs +zpcimon - Monitor health of directly attached PCI devices including NVMes and NICs. . . .SH SYNOPSIS @@ -45,26 +45,29 @@ zpcimon - Monitor optical modules for directly attached PCI based NICs Use .B zpcimon to monitor the health of the optical modules of directly attached PCI based -NICs. When executed without the \fB--monitor\fR option it will collect optical -module data from all available PCI network interface physical functions and -print a summary in JSON format. Add the \fB--send-report\fR option to report -this data to the support element. - +NICs as well as the health of NVMe devices using their SMART data. When +executed without the \fB--monitor\fR option it will collect health data from +all available and relevant PCI devices and print a summary in JSON format. Add +the \fB--send-report\fR option to report this data to the support element. . . .SH OPTIONS .SS Operation Options .OD monitor "m" -Run continuously and report on link state changes and periodically +Run continuously and monitor all supported devices. On link state changes +optical module data is collected and NVMe SMART data is collected on hotplug. +Moreover every +.B --interval +seconds data from all adapters is collected as a whole system snapshot. .PP . .OD send-report "r" -Report the optics health data to the Support Element (SE) +Report the optics and NVMe health data to the Support Element (SE) .PP . .OD quiet "q" -Be quiet and don't print optics health summary +Be quiet and don't print health summaries. .PP . .OD interval "i" "seconds" @@ -84,6 +87,11 @@ on". .PP .PP . +.OD smart-log-raw "" +Include a base64 encoded binary dump of each NVMe's SMART data. This matches +"nvme smart-log --output-format=binary /dev/nvmeX". +.PP +. .SS Output Format Options .OD format "" Set the output format out of json, json-seq, jsonl, pairs, and csv. Note that diff --git a/zpcimon/zpcimon.c b/zpcimon/zpcimon.c index e30f251e..625a1363 100644 --- a/zpcimon/zpcimon.c +++ b/zpcimon/zpcimon.c @@ -28,6 +28,7 @@ #include "lib/util_time.h" #include "lib/zt_common.h" +#include "nvmemon.h" #include "opticsmon.h" #include "zpcimon.h" #include "zpcimon_cli.h" @@ -42,6 +43,7 @@ struct zpcimon_monitor { static struct zpcimon_monitor monitors[] = { {.ops = &opticsmon_ops, .initialized = 0, .opened = 0}, + {.ops = &nvmemon_ops, .initialized = 0, .opened = 0}, }; static const struct util_prg prg = { @@ -79,6 +81,9 @@ static void parse_cmdline(int argc, char *argv[], struct options *opts) case OPT_DUMP: opts->module_info = true; break; + case OPT_SMART_DUMP: + opts->smart_blob = true; + break; case OPT_FORMAT: if (!util_fmt_name_to_type(optarg, &fmt)) errx(EXIT_FAILURE, "Unknown format %s", optarg); diff --git a/zpcimon/zpcimon.h b/zpcimon/zpcimon.h index cb124056..da22eabe 100644 --- a/zpcimon/zpcimon.h +++ b/zpcimon/zpcimon.h @@ -11,6 +11,7 @@ #include "lib/util_fmt.h" +#include "nvmemon.h" #include "opticsmon.h" #define API_LEVEL 1 @@ -26,12 +27,15 @@ struct options { /* Optics Monitoring Specific */ bool module_info; + /* NVMe Monitoring Specific */ + bool smart_blob; }; struct zpcimon_ctx { struct options opts; struct util_list *zpci_list; struct opticsmon_ctx opticsmon_ctx; + struct nvmemon_ctx nvmemon_ctx; }; struct zpcimon_ops { diff --git a/zpcimon/zpcimon_cli.h b/zpcimon/zpcimon_cli.h index f5a0dc3e..3a0f21a1 100644 --- a/zpcimon/zpcimon_cli.h +++ b/zpcimon/zpcimon_cli.h @@ -12,6 +12,7 @@ #define OPT_DUMP 128 #define OPT_FORMAT 129 +#define OPT_SMART_DUMP 130 static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPERATION OPTIONS"), @@ -35,6 +36,13 @@ static struct util_opt opt_vec[] = { "This matches 'ethtool --module-info raw on'", .flags = UTIL_OPT_FLAG_NOSHORT, }, + { + .option = { "smart-log-raw", no_argument, NULL, OPT_SMART_DUMP }, + .desc = "Include a base64 encoded binary dump of the " + "SMART log data for each NVMe. " + "This matches 'nvme smart-log --raw-binary '", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"), { .option = { "interval", required_argument, NULL, 'i' },