Files
s390-tools/zpcimon/opticsmon.c
Niklas Schnelle a9dbb8a1a2 zpcimon: Factor out JSON printing of common PCI adapter attributes
Some of the attributes printed as JSON by opticsmon are common to all
PCI adapters factor these out into reusable helper functions
zpci_adapter_json_print_start() and zpci_adapter_json_print_end().

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00

168 lines
4.6 KiB
C

/*
* 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 <errno.h>
#include <linux/if.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
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) {
zpci_adapter_json_print_start(zdev);
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 */
zpci_adapter_json_print_end();
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,
};