diff --git a/include/lib/pci_sclp.h b/include/lib/pci_sclp.h new file mode 100644 index 00000000..8713e897 --- /dev/null +++ b/include/lib/pci_sclp.h @@ -0,0 +1,53 @@ +/** + * @defgroup pci_sclp_h libzpci: zPCI device handling + * @{ + * @brief Issue SCLPs for zPCI devices + * + * 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 LIB_ZPCI_PCI_SCLP_H +#define LIB_ZPCI_PCI_SCLP_H + +#include +#include + +#include "lib/zt_common.h" + +#define SCLP_ERRNOTIFY_AQ_RESET 0 +#define SCLP_ERRNOTIFY_AQ_DECONF 1 +#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 + +#define SCLP_ERRNOTIFY_ID_ZPCICTL 0x4713 + +#define SCLP_ERRNOTIFY_DATA_SIZE 4054 + +struct zpci_report_error_header { + __u8 version; /* Interface version byte */ + __u8 action; /* Action qualifier byte + * 0: Adapter Reset Request + * 1: Deconfigure and repair action requested + * 2: Informational Report + */ + __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ +} __packed; + +struct zpci_report_error_data { + __u64 timestamp; + __u64 err_log_id; + /* We cannot exceed a total of 4074 bytes (header + data) */ + char log_data[SCLP_ERRNOTIFY_DATA_SIZE]; +} __packed; + +struct zpci_report_error { + struct zpci_report_error_header header; + struct zpci_report_error_data data; +} __packed; + +int zpci_sclp_issue_action(char *pci_addr, int action, + char *data, size_t length, u64 err_log_id); + +#endif /* LIB_ZPCI_PCI_SCLP_H */ diff --git a/libzpci/Makefile b/libzpci/Makefile index 344f32b4..f5a83cf8 100644 --- a/libzpci/Makefile +++ b/libzpci/Makefile @@ -4,7 +4,7 @@ lib = libzpci.a all: $(lib) -objects = pci_list.o +objects = pci_list.o pci_sclp.o examples := $(patsubst %.c,%,$(wildcard *_example.c)) diff --git a/libzpci/pci_sclp.c b/libzpci/pci_sclp.c new file mode 100644 index 00000000..90ac0f25 --- /dev/null +++ b/libzpci/pci_sclp.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lib/pci_sclp.h" +#include "lib/util_path.h" + +static int zpci_sclp_report(char *pci_addr, struct zpci_report_error *report) +{ + size_t r_size = sizeof(*report); + char *path; + FILE *fp; + + path = util_path_sysfs("bus/pci/devices/%s/report_error", pci_addr); + fp = fopen(path, "w"); + free(path); + if (!fp) + return -ENODEV; + if (fwrite(report, 1, r_size, fp) != r_size) + return -EIO; + if (fclose(fp)) + return -EIO; + return 0; +} + +/** + * Issue an SCLP Adapter Error Notification event with a specific action + * qualifier and optional log data. + * + * The logged data is truncated if needed. + * + * @return the number of bytes of the data which were actually logged + * or a negative value on error. + */ +int zpci_sclp_issue_action(char *pci_addr, int action, + char *data, size_t length, u64 err_log_id) +{ + struct zpci_report_error report = {0}; + size_t copy_length = 0; + int ret; + + /* Data is truncated to fit in the report */ + if (data) + copy_length = MIN(length, sizeof(report.data.log_data)); + report.header.version = 1; + report.header.action = action; + report.header.length = offsetof(struct zpci_report_error_data, log_data) + copy_length; + report.data.timestamp = (__u64)time(NULL); + report.data.err_log_id = err_log_id; + + if (data) + memcpy(report.data.log_data, data, copy_length); + ret = zpci_sclp_report(pci_addr, &report); + if (ret) + return ret; + return copy_length; +} diff --git a/zpcictl/Makefile b/zpcictl/Makefile index 2b315c5c..9f9ae5cf 100644 --- a/zpcictl/Makefile +++ b/zpcictl/Makefile @@ -2,7 +2,7 @@ include ../common.mak all: zpcictl -libs = $(rootdir)/libutil/libutil.a +libs = $(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a zpcictl: zpcictl.o $(libs) diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c index 8f27f577..d693f737 100644 --- a/zpcictl/zpcictl.c +++ b/zpcictl/zpcictl.c @@ -9,20 +9,17 @@ #include #include -#include #include #include #include -#include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" -#include "lib/util_proc.h" -#include "lib/util_rec.h" #include "lib/util_scandir.h" #include "lib/util_sys.h" +#include "lib/pci_sclp.h" #include "zpcictl.h" @@ -224,25 +221,6 @@ static void sysfs_write_value(struct zpci_device *pdev, const char *attr, free(path); } -static void sysfs_report_error(struct zpci_report_error *report, char *slot) -{ - size_t r_size; - char *path; - FILE *fp; - - r_size = sizeof(*report); - - path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); - fp = fopen(path, "w"); - if (!fp) - fopen_err(path); - if (fwrite(report, 1, r_size, fp) != r_size) - fwrite_err(fp, path); - if (fclose(fp)) - fclose_err(path); - free(path); -} - static void get_device_node(struct zpci_device *pdev) { struct dirent **de_vec; @@ -326,26 +304,22 @@ static void get_device_info(struct zpci_device *pdev, char *dev) */ static void sclp_issue_action(struct zpci_device *pdev, int action) { - struct zpci_report_error report = { - .header = { 0 }, - .data = { 0 } - }; char *sdata = NULL; - - report.header.version = 1; - report.header.action = action; - report.header.length = offsetof(struct zpci_report_error_data, log_data); - report.data.timestamp = (__u64)time(NULL); - report.data.err_log_id = 0x4713; + int length = 0; + int ret; if (pdev->class == PCI_CLASS_NVME) sdata = collect_smart_data(pdev); if (sdata) { - report.header.length += util_strlcpy(report.data.log_data, sdata, - sizeof(report.data.log_data)); - free(sdata); + /* Account for and ensure '\0' byte */ + length = MIN((int)strlen(sdata) + 1, SCLP_ERRNOTIFY_DATA_SIZE); + sdata[length - 1] = '\0'; } - sysfs_report_error(&report, pdev->slot); + ret = zpci_sclp_issue_action(pdev->slot, action, + sdata, length, SCLP_ERRNOTIFY_ID_ZPCICTL); + free(sdata); + if (ret < 0) + warn("Could not issue SCLP action"); } /* diff --git a/zpcictl/zpcictl.h b/zpcictl/zpcictl.h index 7b34ebb1..449505c0 100644 --- a/zpcictl/zpcictl.h +++ b/zpcictl/zpcictl.h @@ -11,11 +11,8 @@ #define ZPCICTL_H #include -#include "lib/zt_common.h" -#define SCLP_ERRNOTIFY_AQ_RESET 0 -#define SCLP_ERRNOTIFY_AQ_DECONF 1 -#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 +#include "lib/zt_common.h" #define PCI_CLASS_UNCLASSIFIED 0x000000U #define PCI_CLASS_NVME 0x010802U @@ -36,26 +33,4 @@ struct zpci_device { char *device; }; -struct zpci_report_error_header { - __u8 version; /* Interface version byte */ - __u8 action; /* Action qualifier byte - * 0: Adapter Reset Request - * 1: Deconfigure and repair action requested - * 2: Informational Report - */ - __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ - __u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */ -}; - -struct zpci_report_error_data { - __u64 timestamp; - __u64 err_log_id; - char log_data[4054]; /* We cannot exceed a total of 4074 bytes (header + data) */ -}; - -struct zpci_report_error { - struct zpci_report_error_header header; - struct zpci_report_error_data data; -} __packed; - #endif /* ZPCICTL_H */