Files
s390-tools/opticsmon/optics_info.c
Niklas Schnelle c34adb9cab opticsmon: Introduce opticsmon tool
The optics monitoring tool opticsmon implements the user-space portion
of reporting optics data to the SE. Its basic functionality is to
collect optical module information equivalent to "ethtool --module-info"
for PCI Physical Functions and forwards this data to the SE using the
new SCLP Write Event Data Action Qualifier 3.

For the part of finding all PFs we need to look at all PCI
functions and determine which ones are PFs and what netdevs they
correspond to. This is a generally useful functionality so this part as
well as the SCLP issuing code go into a new libzpci library which also
includes a standalone example for listing PCI functions and their s390x
specific attributes. Medium term we plan to add this functionality to
lszdev.

For the opticsmon tool itself there are 2 basic operating modes:

* One-shot Mode: Without parameters opticsmon collects optical module
  data and prints a summary of the netdevice in JSON format. With
  --module-data it also includes a base64 encoded raw dump equivalent to
  ethtool --module-info <netdev> raw on.
* Monitor Mode: With the --monitor flag opticsmon runs continuously
  usually started via a systemd unit and collects new optical module
  data on a time interval (default 24h) or when the operational state
  ("/sys/class/net/<netdev/operstate") changes. The tool listens for
  changes via netlink so no polling on sysfs is necessary

Note: Both modes will *NOT* issues SCLPs without adding the
--send-report flag but will output a JSON summary for each data
collection so can be tested without firmware impact.

Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-06 10:57:34 +01:00

172 lines
4.0 KiB
C

#include <stdlib.h>
#include "optics_info.h"
#define OPTICS_TYPE_OFFSET 0x0
#define OPTICS_SFP_LOS_IMPLEMENTED_OFFSET 0x41
#define OPTICS_SFP_LOS_IMPLEMENTED_MASK 0x2
#define OPTICS_SFP_A2H_OFFSET 0x100
#define OPTICS_SFP_LOS_OFFSET (OPTICS_SFP_A2H_OFFSET + 0x6e)
#define OPTICS_SFP_DATA_NOT_READY_MASK 0x1
#define OPTICS_SFP_TX_FAULT_MASK 0x4
#define OPTICS_SFP_RX_LOS_MASK 0x2
#define OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET 0xC3
#define OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK 0x2
#define OPTICS_QSFP28_TX_FAULT_IMPLEMENTED_MASK 0x8
#define OPTICS_QSFP28_LOS_OFFSET 0x3
#define OPTICS_QSFP28_LOS_MASK 0xf
#define OPTICS_QSFP28_TX_LOS_MASK 0xf0
#define OPTICS_QSFP28_TX_LOS_SHIFT 0x4
#define OPTICS_QSFP28_TX_FAULT_OFFSET 0x4
#define OPTICS_QSFP28_TX_FAULT_MASK 0xf
const char *optics_type_str(enum optics_type type)
{
switch (type) {
case OPTICS_TYPE_UNKNOWN:
return "unknown";
case OPTICS_TYPE_SFP:
return "SFP/SFP+/SFP28";
case OPTICS_TYPE_QSFP28:
return "QSFP28";
};
return "n.a.";
}
enum optics_type optics_type(struct optics *oi)
{
if (!oi || !oi->raw || oi->size < OPTICS_TYPE_OFFSET + 1)
return OPTICS_TYPE_UNKNOWN;
switch (oi->raw[OPTICS_TYPE_OFFSET]) {
case (uint8_t)OPTICS_TYPE_SFP:
return OPTICS_TYPE_SFP;
case (uint8_t)OPTICS_TYPE_QSFP28:
return OPTICS_TYPE_QSFP28;
default:
return OPTICS_TYPE_UNKNOWN;
};
}
bool optics_los_implemented(struct optics *oi)
{
enum optics_type type = optics_type(oi);
uint8_t implemented;
if (type == OPTICS_TYPE_SFP) {
if (oi->size < OPTICS_SFP_LOS_IMPLEMENTED_OFFSET + 1)
return false;
implemented = oi->raw[OPTICS_SFP_LOS_IMPLEMENTED_OFFSET];
return !!(implemented & OPTICS_SFP_LOS_IMPLEMENTED_MASK);
} else if (type == OPTICS_TYPE_QSFP28) {
if (oi->size < OPTICS_QSFP28_LOS_OFFSET + 1)
return false;
if (oi->size < OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET)
return false;
implemented = oi->raw[OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET];
/*
* No RX LoS implemented flag take TX LOS implemented like
* ethtool
*/
return !!(implemented & OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK);
}
return false;
}
enum optics_los optics_rx_los(struct optics *oi)
{
enum optics_los los = OPTICS_UNKNOWN_LOS;
enum optics_type type = optics_type(oi);
if (!optics_los_implemented(oi))
return los;
if (type == OPTICS_TYPE_SFP) {
los = oi->raw[OPTICS_SFP_LOS_OFFSET];
if (los & OPTICS_SFP_DATA_NOT_READY_MASK)
return OPTICS_UNKNOWN_LOS;
if (los & OPTICS_SFP_RX_LOS_MASK)
return OPTICS_LOS;
else
return OPTICS_NO_LOS;
} else if (type == OPTICS_TYPE_QSFP28) {
los = oi->raw[OPTICS_QSFP28_LOS_OFFSET];
if (los & OPTICS_QSFP28_LOS_MASK)
los = OPTICS_LOS;
else
los = OPTICS_NO_LOS;
}
return los;
}
const char *optics_los_str(enum optics_los los)
{
switch (los) {
case OPTICS_LOS:
return "yes";
case OPTICS_NO_LOS:
return "no";
case OPTICS_UNAVAILABLE_LOS:
return "unavailable";
default:
return "unknown";
}
}
enum optics_los optics_tx_fault(struct optics *oi)
{
enum optics_los los = OPTICS_UNKNOWN_LOS;
enum optics_type type = optics_type(oi);
if (!optics_los_implemented(oi))
return los;
if (type == OPTICS_TYPE_SFP) {
los = oi->raw[OPTICS_SFP_LOS_OFFSET];
if (los & OPTICS_SFP_DATA_NOT_READY_MASK)
return OPTICS_UNKNOWN_LOS;
if (los & OPTICS_SFP_TX_FAULT_MASK)
return OPTICS_LOS;
else
return OPTICS_NO_LOS;
} else if (type == OPTICS_TYPE_QSFP28) {
los = oi->raw[OPTICS_QSFP28_TX_FAULT_OFFSET];
if (los & OPTICS_QSFP28_TX_FAULT_MASK)
los = OPTICS_LOS;
else
los = OPTICS_NO_LOS;
}
return los;
}
enum optics_los optics_tx_los(struct optics *oi)
{
enum optics_los los = OPTICS_UNKNOWN_LOS;
enum optics_type type = optics_type(oi);
if (!optics_los_implemented(oi))
return los;
if (type == OPTICS_TYPE_SFP) {
return OPTICS_UNAVAILABLE_LOS;
} else if (type == OPTICS_TYPE_QSFP28) {
los = oi->raw[OPTICS_QSFP28_LOS_OFFSET];
if (los & OPTICS_QSFP28_TX_LOS_MASK)
los = OPTICS_LOS;
else
los = OPTICS_NO_LOS;
}
return los;
}
void optics_free(struct optics *oi)
{
free(oi->raw);
free(oi);
}