From 113e9ebfef67253f4faee798d2fe785f182f9eec Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Thu, 9 Apr 2026 15:32:47 +0200 Subject: [PATCH] zpcimon: Split optics monitoring out into separate monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the concept of a generic zPCI device monitor by creating the zpcimon_ops operations struct turning all optics specific calls into abstract monitor calls. Handle monitors as a static array of zpcimon_ops based monitors of which the now split out optics monitor is currently the only one. All operations are in principle optional though a monitor which provides an init operation must also provide the corresponding destroy operation. Keep the base64 based JSON pair as non optics specific for later re-use and make it just skip the output in the very unlikely case that encoding fails. In follow on commits a monitor for NVMe devices collecting SMART data will be added. Reviewed-by: Jan Höppner Signed-off-by: Niklas Schnelle Signed-off-by: Jan Höppner --- zpcimon/Makefile | 2 +- zpcimon/opticsmon.c | 177 ++++++++++++++++++ zpcimon/opticsmon.h | 20 +++ zpcimon/zpcimon.c | 424 ++++++++++++++++++++++---------------------- zpcimon/zpcimon.h | 52 ++++++ 5 files changed, 463 insertions(+), 212 deletions(-) create mode 100644 zpcimon/opticsmon.c create mode 100644 zpcimon/opticsmon.h create mode 100644 zpcimon/zpcimon.h diff --git a/zpcimon/Makefile b/zpcimon/Makefile index 43fc2a04..cc0a8af0 100644 --- a/zpcimon/Makefile +++ b/zpcimon/Makefile @@ -73,7 +73,7 @@ BUILDTARGET += zpcimon all: ${BUILDTARGET} -zpcimon: zpcimon.o optics_info.o optics_sclp.o ethtool.o link_mon.o $(libs) +zpcimon: zpcimon.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/opticsmon.c b/zpcimon/opticsmon.c new file mode 100644 index 00000000..4da456f1 --- /dev/null +++ b/zpcimon/opticsmon.c @@ -0,0 +1,177 @@ +/* + * zpcimon - Report monitoring data to firmware + * + * Copyright IBM Corp. 2024 + * + * 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 "lib/pci_list.h" +#include "lib/util_fmt.h" +#include "lib/util_libc.h" + +#include "ethtool.h" +#include "link_mon.h" +#include "optics_info.h" +#include "optics_sclp.h" +#include "opticsmon.h" +#include "zpcimon.h" + +static void optics_json_print(struct options *opts, struct zpci_netdev *nd, struct optics *oi) +{ + util_fmt_obj_start(FMT_DEFAULT, "netdev"); + util_fmt_pair(FMT_QUOTE, "name", nd->name); + util_fmt_pair(FMT_QUOTE, "operstate", zpci_operstate_str(nd->operstate)); + util_fmt_obj_start(FMT_DEFAULT, "optics"); + util_fmt_pair(FMT_QUOTE, "type", optics_type_str(optics_type(oi))); + util_fmt_pair(FMT_QUOTE, "rx_los", optics_los_str(optics_rx_los(oi))); + util_fmt_pair(FMT_QUOTE, "tx_los", optics_los_str(optics_tx_los(oi))); + util_fmt_pair(FMT_QUOTE, "tx_fault", optics_los_str(optics_tx_fault(oi))); + if (opts->module_info) + zpcimon_json_base64_pair("module_info", oi->raw, (int)oi->size); + util_fmt_obj_end(); + util_fmt_obj_end(); +} + +static int opticsmon_collect_adapter_data(struct zpcimon_ctx *ctx, struct zpci_dev *zdev) +{ + struct options *opts = &ctx->opts; + struct optics **ois; + int num_ois = 0; + char *pci_addr; + int i, rc; + + /* Filter non-NIC devices and VFs */ + if (zpci_is_vf(zdev) || !zdev->num_netdevs) + return -ENODEV; + ois = (struct optics **)util_zalloc(sizeof(ois[0]) * zdev->num_netdevs); + for (i = 0; i < zdev->num_netdevs; i++) { + rc = ethtool_nl_get_optics(&ctx->opticsmon_ctx.ethtool_ctx, zdev->netdevs[i].name, + &ois[i]); + if (rc) + goto free_ois; + num_ois++; + } + if (!opts->quiet) { + util_fmt_obj_start(FMT_DEFAULT, "adapter"); + util_fmt_pair(FMT_QUOTE, "pft", zpci_pft_str(zdev)); + util_fmt_obj_start(FMT_DEFAULT, "ids"); + util_fmt_pair(FMT_QUOTE, "fid", "0x%0x", zdev->fid); + if (zdev->uid_is_unique) + util_fmt_pair(FMT_QUOTE, "uid", "0x%0x", zdev->uid); + pci_addr = zpci_pci_addr(zdev); + util_fmt_pair(FMT_QUOTE, "pci_address", pci_addr); + free(pci_addr); + util_fmt_obj_end(); + util_fmt_obj_start(FMT_LIST, "netdevs"); + for (i = 0; i < zdev->num_netdevs; i++) + optics_json_print(opts, &zdev->netdevs[i], ois[i]); + util_fmt_obj_end(); /* netdevs list */ + util_fmt_obj_end(); /* adapter */ + fflush(stdout); + } + if (opts->report) { + for (i = 0; i < zdev->num_netdevs; i++) { + rc = sclp_issue_optics_report(zdev, ois[i]); + if (rc == -ENOTSUP) { + fprintf(stderr, "Skipping %s which does not support reporting\n", + zdev->netdevs[i].name); + } else if (rc < 0) { + fprintf(stderr, "Error issuing SCLP for optics data failed: %s\n", + strerror(-rc)); + } + } + } +free_ois: + for (i = 0; i < num_ois; i++) + optics_free(ois[i]); + free((void *)ois); + return rc; +} + +static void on_link_change(struct zpci_netdev *change_netdev, void *arg) +{ + struct zpcimon_ctx *ctx = arg; + struct zpci_dev *zdev = NULL; + struct zpci_netdev *netdev; + int reloads = 1; + + do { + if (ctx->zpci_list) { + zdev = zpci_find_by_netdev(ctx->zpci_list, change_netdev->name, + &netdev); + if (zdev) { + /* Skip data collection if operational state is + * unchanged + */ + if (netdev->operstate == change_netdev->operstate) + return; + /* Update operation state for VFs even though + * they are skipped just for a consistent view + */ + netdev->operstate = change_netdev->operstate; + /* Only collect optics data for PFs */ + if (!zpci_is_vf(zdev)) + opticsmon_collect_adapter_data(ctx, zdev); + return; + } + } + /* Could be uninitalized list or a new device, retry after reload */ + zpci_list_reload(&ctx->zpci_list); + reloads--; + } while (reloads > 0); +} + +static int opticsmon_open_monitor(struct zpcimon_ctx *ctx) +{ + int ret; + + ret = link_mon_nl_waitfd_create(&ctx->opticsmon_ctx.lctx, on_link_change, ctx); + if (ret) { + fprintf(stderr, "Failed to create link monitoring socket\n"); + ret = -EIO; + } + return ret; +} + +static int opticsmon_get_monitor_fd(struct zpcimon_ctx *ctx) +{ + return link_mon_nl_waitfd_getfd(&ctx->opticsmon_ctx.lctx); +} + +static void opticsmon_monitor_fd_handle(struct zpcimon_ctx *ctx) +{ + link_mon_nl_waitfd_read(&ctx->opticsmon_ctx.lctx); +} + +static void opticsmon_close_monitor(struct zpcimon_ctx *ctx) +{ + link_mon_nl_waitfd_destroy(&ctx->opticsmon_ctx.lctx); +} + +static int opticsmon_init(struct zpcimon_ctx *ctx) +{ + return ethtool_nl_connect(&ctx->opticsmon_ctx.ethtool_ctx); +} + +static void opticsmon_destroy(struct zpcimon_ctx *ctx) +{ + ethtool_nl_close(&ctx->opticsmon_ctx.ethtool_ctx); +} + +const struct zpcimon_ops opticsmon_ops = { + .collect_adapter_data = opticsmon_collect_adapter_data, + .open_monitor = opticsmon_open_monitor, + .get_monitor_fd = opticsmon_get_monitor_fd, + .monitor_fd_handle = opticsmon_monitor_fd_handle, + .close_monitor = opticsmon_close_monitor, + .init = opticsmon_init, + .destroy = opticsmon_destroy, +}; diff --git a/zpcimon/opticsmon.h b/zpcimon/opticsmon.h new file mode 100644 index 00000000..04798201 --- /dev/null +++ b/zpcimon/opticsmon.h @@ -0,0 +1,20 @@ +/* + * 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_OPTICSMON_H +#define ZPCIMON_OPTICSMON_H +#include "ethtool.h" +#include "link_mon.h" + +struct options; + +struct opticsmon_ctx { + struct ethtool_nl_ctx ethtool_ctx; + struct link_mon_nl_ctx lctx; +}; + +extern const struct zpcimon_ops opticsmon_ops; +#endif /* ZPCIMON_OPTICSMON_H */ diff --git a/zpcimon/zpcimon.c b/zpcimon/zpcimon.c index 40b6edd7..32550ff0 100644 --- a/zpcimon/zpcimon.c +++ b/zpcimon/zpcimon.c @@ -1,57 +1,47 @@ /* - * opticsmon - Report optics monitoring data to firmware + * zpcimon - Report monitoring data to firmware * * Copyright IBM Corp. 2024 * * 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 #include -#include #include -#include -#include - -#include "lib/util_list.h" -#include "lib/pci_list.h" -#include "lib/util_prg.h" -#include "lib/util_opt.h" -#include "lib/util_fmt.h" -#include "lib/util_libc.h" -#include "lib/util_time.h" #include -#include "ethtool.h" -#include "link_mon.h" -#include "optics_info.h" -#include "optics_sclp.h" +#include "lib/pci_list.h" +#include "lib/util_fmt.h" +#include "lib/util_libc.h" +#include "lib/util_opt.h" +#include "lib/util_prg.h" +#include "lib/util_time.h" +#include "lib/zt_common.h" + +#include "opticsmon.h" +#include "zpcimon.h" #include "zpcimon_cli.h" #define API_LEVEL 1 -struct options { - bool monitor; - bool report; - bool module_info; - bool quiet; - enum util_fmt_t format; - bool explicit_format; - - uint32_t interval_seconds; +struct zpcimon_monitor { + const struct zpcimon_ops *ops; + uint32_t initialized : 1; + uint32_t opened : 1; }; -struct opticsmon_ctx { - struct options opts; - struct ethtool_nl_ctx ethtool_ctx; - struct link_mon_nl_ctx lctx; - struct util_list *zpci_list; +static struct zpcimon_monitor monitors[] = { + {.ops = &opticsmon_ops, .initialized = 0, .opened = 0}, }; static const struct util_prg prg = { @@ -122,171 +112,131 @@ static void parse_cmdline(int argc, char *argv[], struct options *opts) } while (cmd != -1); } -static int module_info_pair(struct optics *oi) +void zpcimon_json_base64_pair(char *name, uint8_t *buf, int len) { - size_t b64_calclen, b64len; - int rc = EXIT_SUCCESS; + int b64_calclen, b64len; char *b64; - b64_calclen = (oi->size / 3) * 4; - if (oi->size % 3 > 0) + b64_calclen = (len / 3) * 4; + if (len % 3 > 0) b64_calclen += 4; b64 = util_zalloc(b64_calclen + 1); /* adds NUL byte */ - b64len = EVP_EncodeBlock((unsigned char *)b64, oi->raw, oi->size); + b64len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)buf, len); if (b64len != b64_calclen) { - fprintf(stderr, "encoding base64 via openssl failed\n"); - rc = EXIT_FAILURE; + warnx("encoding base64 via openssl failed\n"); goto out; } - util_fmt_pair(FMT_QUOTE, "module_info", b64); + util_fmt_pair(FMT_QUOTE, name, b64); out: free(b64); - return rc; } -static void optics_json_print(struct opticsmon_ctx *ctx, struct zpci_netdev *nd, struct optics *oi) -{ - util_fmt_obj_start(FMT_DEFAULT, "netdev"); - util_fmt_pair(FMT_QUOTE, "name", nd->name); - util_fmt_pair(FMT_QUOTE, "operstate", zpci_operstate_str(nd->operstate)); - util_fmt_obj_start(FMT_DEFAULT, "optics"); - util_fmt_pair(FMT_QUOTE, "type", optics_type_str(optics_type(oi))); - util_fmt_pair(FMT_QUOTE, "rx_los", optics_los_str(optics_rx_los(oi))); - util_fmt_pair(FMT_QUOTE, "tx_los", optics_los_str(optics_tx_los(oi))); - util_fmt_pair(FMT_QUOTE, "tx_fault", optics_los_str(optics_tx_fault(oi))); - if (ctx->opts.module_info) - module_info_pair(oi); - util_fmt_obj_end(); - util_fmt_obj_end(); -} - -static int dump_adapter_data(struct opticsmon_ctx *ctx, struct zpci_dev *zdev) -{ - struct optics **ois; - int num_ois = 0; - char *pci_addr; - int i, rc; - - ois = util_zalloc(sizeof(ois[0]) * zdev->num_netdevs); - for (i = 0; i < zdev->num_netdevs; i++) { - rc = ethtool_nl_get_optics(&ctx->ethtool_ctx, zdev->netdevs[i].name, &ois[i]); - if (rc) - goto free_ois; - num_ois++; - } - if (!ctx->opts.quiet) { - util_fmt_obj_start(FMT_DEFAULT, "adapter"); - util_fmt_pair(FMT_QUOTE, "pft", zpci_pft_str(zdev)); - util_fmt_obj_start(FMT_DEFAULT, "ids"); - util_fmt_pair(FMT_QUOTE, "fid", "0x%0x", zdev->fid); - if (zdev->uid_is_unique) - util_fmt_pair(FMT_QUOTE, "uid", "0x%0x", zdev->uid); - pci_addr = zpci_pci_addr(zdev); - util_fmt_pair(FMT_QUOTE, "pci_address", pci_addr); - free(pci_addr); - util_fmt_obj_end(); - util_fmt_obj_start(FMT_LIST, "netdevs"); - for (i = 0; i < zdev->num_netdevs; i++) - optics_json_print(ctx, &zdev->netdevs[i], ois[i]); - util_fmt_obj_end(); /* netdevs list */ - util_fmt_obj_end(); /* adapter */ - fflush(stdout); - } - if (ctx->opts.report) { - for (i = 0; i < zdev->num_netdevs; i++) { - rc = sclp_issue_optics_report(zdev, ois[i]); - if (rc == -ENOTSUP) { - fprintf(stderr, "Skipping %s which does not support reporting\n", - zdev->netdevs[i].name); - } else if (rc < 0) { - fprintf(stderr, "Error issuing SCLP for optics data failed: %s\n", - strerror(-rc)); - } - } - } -free_ois: - for (i = 0; i < num_ois; i++) - optics_free(ois[i]); - free(ois); - return rc; -} - -static void zpci_list_reload(struct util_list **zpci_list) +void zpci_list_reload(struct util_list **zpci_list) { if (*zpci_list) zpci_free_dev_list(*zpci_list); *zpci_list = zpci_dev_list(); } -static void dump_all_adapter_data(struct opticsmon_ctx *ctx) +static void collect_all_adapter_data(struct zpcimon_ctx *ctx) { struct zpci_dev *zdev; + int i; zpci_list_reload(&ctx->zpci_list); util_list_iterate(ctx->zpci_list, zdev) { - /* Filter non-NIC devices and VFs */ - if (zpci_is_vf(zdev) || !zdev->num_netdevs) - continue; - dump_adapter_data(ctx, zdev); + for (i = 0; i < (int)ARRAY_SIZE(monitors); i++) + if (monitors[i].ops->collect_adapter_data) + monitors[i].ops->collect_adapter_data(ctx, zdev); } } -static int oneshot_mode(struct opticsmon_ctx *ctx) -{ - util_fmt_init(stdout, ctx->opts.format, FMT_DEFAULT, API_LEVEL); - if (!ctx->opts.quiet) - util_fmt_obj_start(FMT_LIST, "adapters"); - dump_all_adapter_data(ctx); - if (!ctx->opts.quiet) - util_fmt_obj_end(); - util_fmt_exit(); - - return EXIT_SUCCESS; -} - -void on_link_change(struct zpci_netdev *netdev, void *arg) -{ - struct opticsmon_ctx *ctx = arg; - struct zpci_netdev *found_netdev; - struct zpci_dev *zdev = NULL; - int reloads = 1; - - do { - if (ctx->zpci_list) { - zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev); - if (zdev) { - /* Skip data collection if operational state is - * unchanged - */ - if (found_netdev->operstate == netdev->operstate) - return; - /* Update operation state for VFs even though - * they are skipped just for a consistent view - */ - found_netdev->operstate = netdev->operstate; - /* Only collect optics data for PFs */ - if (!zpci_is_vf(zdev)) - dump_adapter_data(ctx, zdev); - return; - } - } - /* Could be uninitalized list or a new device, retry after reload */ - zpci_list_reload(&ctx->zpci_list); - reloads--; - } while (reloads > 0); -} - #define MAX_EVENTS 8 -static int monitor_wait_loop(struct opticsmon_ctx *ctx, int sigfd, int timerfd) +static int monitor_epoll_mon_fds_prepare(struct zpcimon_ctx *ctx, int epfd, int mon_fds[]) +{ + struct epoll_event ev; + int mon_idx; + + for (mon_idx = 0; mon_idx < (int)ARRAY_SIZE(monitors); mon_idx++) { + if (!monitors[mon_idx].ops->get_monitor_fd) { + mon_fds[mon_idx] = -1; + continue; + } + mon_fds[mon_idx] = monitors[mon_idx].ops->get_monitor_fd(ctx); + if (mon_fds[mon_idx] < 0) + return -EIO; + ev.events = EPOLLIN; + ev.data.fd = mon_fds[mon_idx]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, mon_fds[mon_idx], &ev) == -1) + return -EIO; + } + return 0; +} + +static void monitor_epoll_mon_fds(struct zpcimon_ctx *ctx, struct epoll_event event, + const int mon_fds[]) +{ + int mon_idx; + + for (mon_idx = 0; mon_idx < (int)ARRAY_SIZE(monitors); mon_idx++) { + if (event.data.fd != mon_fds[mon_idx]) + continue; + if (!monitors[mon_idx].ops->monitor_fd_handle) + continue; + monitors[mon_idx].ops->monitor_fd_handle(ctx); + } +} + +static int monitor_epoll(struct zpcimon_ctx *ctx, const int mon_fds[], int epfd, int sigfd, + int timerfd) { struct epoll_event events[MAX_EVENTS]; - int i, nlfd, epfd, nfds, ret = -EIO; struct signalfd_siginfo fdsi; - struct epoll_event ev; uint64_t expirations; ssize_t sread; + int i, nfds; + + nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); + if (nfds < 0) + return nfds; + for (i = 0; i < nfds; i++) { + /* signal fd */ + if (events[i].data.fd == sigfd) { + sread = read(sigfd, &fdsi, sizeof(fdsi)); + if (sread != sizeof(fdsi)) + return -EIO; + switch (fdsi.ssi_signo) { + case SIGINT: + case SIGTERM: + case SIGQUIT: + return -EINTR; + /* Unexpected signal */ + default: + return -EIO; + } + /* timer fd */ + } else if (events[i].data.fd == timerfd) { + sread = read(timerfd, &expirations, sizeof(uint64_t)); + if (sread != sizeof(uint64_t)) + return -EIO; + if (!expirations) + continue; + collect_all_adapter_data(ctx); + /* netlink fd */ + } else { + monitor_epoll_mon_fds(ctx, events[i], mon_fds); + } + } + return 0; +} + +static int monitor_wait_loop(struct zpcimon_ctx *ctx, int sigfd, int timerfd) +{ + int mon_fds[ARRAY_SIZE(monitors)]; + struct epoll_event ev; + int epfd, ret = -EIO; epfd = epoll_create1(EPOLL_CLOEXEC); if (epfd < 0) @@ -302,52 +252,57 @@ static int monitor_wait_loop(struct opticsmon_ctx *ctx, int sigfd, int timerfd) if (epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev) == -1) goto out_close; - nlfd = link_mon_nl_waitfd_getfd(&ctx->lctx); - ev.events = EPOLLIN; - ev.data.fd = nlfd; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, nlfd, &ev) == -1) + ret = monitor_epoll_mon_fds_prepare(ctx, epfd, mon_fds); + if (ret) goto out_close; while (1) { - nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); - if (nfds < 0) - goto out_close; - for (i = 0; i < nfds; i++) { - /* signal fd */ - if (events[i].data.fd == sigfd) { - sread = read(sigfd, &fdsi, sizeof(fdsi)); - if (sread != sizeof(fdsi)) - goto out_close; - switch (fdsi.ssi_signo) { - case SIGINT: - case SIGTERM: - case SIGQUIT: - ret = 0; - goto out_close; - /* Unexpected signal */ - default: - goto out_close; - } - /* timer fd */ - } else if (events[i].data.fd == timerfd) { - sread = read(timerfd, &expirations, sizeof(uint64_t)); - if (sread != sizeof(uint64_t)) - goto out_close; - if (!expirations) - continue; - dump_all_adapter_data(ctx); - /* netlink fd */ - } else if (events[i].data.fd == nlfd) { - link_mon_nl_waitfd_read(&ctx->lctx); - } - } + ret = monitor_epoll(ctx, mon_fds, epfd, sigfd, timerfd); + if (ret) + break; } out_close: + /* Getting interrupted by a signal is not an error */ + if (ret == -EINTR) + ret = 0; close(epfd); return ret; } -static int monitor_mode(struct opticsmon_ctx *ctx) +static void zpcimon_close_monitor(struct zpcimon_ctx *ctx) +{ + int i; + + for (i = 0; i < (int)ARRAY_SIZE(monitors); i++) { + if (!monitors[i].ops->close_monitor || !monitors[i].opened) + continue; + monitors[i].ops->close_monitor(ctx); + monitors[i].opened = 0; + } +} + +static int zpcimon_open_monitor(struct zpcimon_ctx *ctx) +{ + int i, ret = -ENXIO; + + for (i = 0; i < (int)ARRAY_SIZE(monitors); i++) { + if (!monitors[i].ops->open_monitor || monitors[i].opened) + continue; + if (monitors[i].opened) + goto error; + ret = monitors[i].ops->open_monitor(ctx); + if (ret) + goto error; + monitors[i].opened = 1; + } + return 0; + +error: + zpcimon_close_monitor(ctx); + return ret; +} + +static int monitor_mode(struct zpcimon_ctx *ctx) { struct itimerspec timerspec; int sigfd, timerfd, ret; @@ -386,20 +341,67 @@ static int monitor_mode(struct opticsmon_ctx *ctx) } util_fmt_init(stdout, ctx->opts.format, FMT_DEFAULT, API_LEVEL); - ret = link_mon_nl_waitfd_create(&ctx->lctx, on_link_change, ctx); - if (ret) { - fprintf(stderr, "Failed to create link monitoring socket\n"); - goto close_timerfd; - } + ret = zpcimon_open_monitor(ctx); + if (ret < 0) + goto cleanup_fmt; ret = monitor_wait_loop(ctx, sigfd, timerfd); - link_mon_nl_waitfd_destroy(&ctx->lctx); + zpcimon_close_monitor(ctx); +cleanup_fmt: util_fmt_exit(); -close_signalfd: - close(sigfd); close_timerfd: close(timerfd); +close_signalfd: + close(sigfd); + return ret; +} + +static int oneshot_mode(struct zpcimon_ctx *ctx) +{ + util_fmt_init(stdout, ctx->opts.format, FMT_DEFAULT, API_LEVEL); + if (!ctx->opts.quiet) + util_fmt_obj_start(FMT_LIST, "adapters"); + + collect_all_adapter_data(ctx); + + if (!ctx->opts.quiet) + util_fmt_obj_end(); + util_fmt_exit(); + + return EXIT_SUCCESS; +} + +static void zpcimon_destroy(struct zpcimon_ctx *ctx) +{ + int i; + + for (i = 0; i < (int)ARRAY_SIZE(monitors); i++) { + if (!monitors[i].ops->destroy || !monitors[i].initialized) + continue; + monitors[i].ops->destroy(ctx); + monitors[i].initialized = false; + } +} + +static int zpcimon_init(struct zpcimon_ctx *ctx) +{ + int i, ret = -ENXIO; + + for (i = 0; i < (int)ARRAY_SIZE(monitors); i++) { + if (!monitors[i].ops->init) + continue; + if (monitors[i].initialized) + goto error; + ret = monitors[i].ops->init(ctx); + if (ret) + goto error; + monitors[i].initialized = 1; + } + return 0; + +error: + zpcimon_destroy(ctx); return ret; } @@ -432,21 +434,21 @@ static int set_format(struct options *opts) int main(int argc, char **argv) { - struct opticsmon_ctx ctx = { .opts = { .interval_seconds = 86400 } }; + struct zpcimon_ctx ctx = { .opts = { .interval_seconds = SEC_PER_DAY } }; int ret; parse_cmdline(argc, argv, &ctx.opts); ret = set_format(&ctx.opts); if (ret) return ret; - ret = ethtool_nl_connect(&ctx.ethtool_ctx); + ret = zpcimon_init(&ctx); if (ret) return ret; if (ctx.opts.monitor) ret = monitor_mode(&ctx); else ret = oneshot_mode(&ctx); - ethtool_nl_close(&ctx.ethtool_ctx); + zpcimon_destroy(&ctx); if (ctx.zpci_list) zpci_free_dev_list(ctx.zpci_list); diff --git a/zpcimon/zpcimon.h b/zpcimon/zpcimon.h new file mode 100644 index 00000000..768d5921 --- /dev/null +++ b/zpcimon/zpcimon.h @@ -0,0 +1,52 @@ +/* + * Copyright IBM Corp. 2024 + * + * 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_ZPCIMON_H +#define ZPCIMON_ZPCIMON_H +#include +#include + +#include "lib/util_fmt.h" + +#include "opticsmon.h" + +#define API_LEVEL 1 + +struct options { + uint32_t interval_seconds; + + bool monitor; + bool report; + bool quiet; + enum util_fmt_t format; + bool explicit_format; + + /* Optics Monitoring Specific */ + bool module_info; +}; + +struct zpcimon_ctx { + struct options opts; + struct util_list *zpci_list; + struct opticsmon_ctx opticsmon_ctx; +}; + +struct zpcimon_ops { + int (*collect_adapter_data)(struct zpcimon_ctx *ctx, struct zpci_dev *zdev); + + int (*open_monitor)(struct zpcimon_ctx *ctx); + int (*get_monitor_fd)(struct zpcimon_ctx *ctx); + void (*monitor_fd_handle)(struct zpcimon_ctx *ctx); + void (*close_monitor)(struct zpcimon_ctx *ctx); + + int (*init)(struct zpcimon_ctx *ctx); + void (*destroy)(struct zpcimon_ctx *ctx); +}; + +void zpci_list_reload(struct util_list **zpci_list); + +void zpcimon_json_base64_pair(char *name, uint8_t *buf, int len); +#endif /* ZPCIMON_ZPCIMON_H */