|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* |
| 3 | + * Copyright IBM Corp. 2024 |
| 4 | + * |
| 5 | + * Author(s): |
| 6 | + * Niklas Schnelle <schnelle@linux.ibm.com> |
| 7 | + * |
| 8 | + */ |
| 9 | + |
| 10 | +#define KMSG_COMPONENT "zpci" |
| 11 | +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
| 12 | + |
| 13 | +#include <linux/kernel.h> |
| 14 | +#include <linux/sprintf.h> |
| 15 | +#include <linux/pci.h> |
| 16 | + |
| 17 | +#include <asm/sclp.h> |
| 18 | + |
| 19 | +#include "pci_report.h" |
| 20 | + |
| 21 | +#define ZPCI_ERR_LOG_ID_KERNEL_REPORT 0x4714 |
| 22 | + |
| 23 | +struct zpci_report_error_data { |
| 24 | + u64 timestamp; |
| 25 | + u64 err_log_id; |
| 26 | + char log_data[]; |
| 27 | +} __packed; |
| 28 | + |
| 29 | +#define ZPCI_REPORT_SIZE (PAGE_SIZE - sizeof(struct err_notify_sccb)) |
| 30 | +#define ZPCI_REPORT_DATA_SIZE (ZPCI_REPORT_SIZE - sizeof(struct zpci_report_error_data)) |
| 31 | + |
| 32 | +struct zpci_report_error { |
| 33 | + struct zpci_report_error_header header; |
| 34 | + struct zpci_report_error_data data; |
| 35 | +} __packed; |
| 36 | + |
| 37 | +static const char *zpci_state_str(pci_channel_state_t state) |
| 38 | +{ |
| 39 | + switch (state) { |
| 40 | + case pci_channel_io_normal: |
| 41 | + return "normal"; |
| 42 | + case pci_channel_io_frozen: |
| 43 | + return "frozen"; |
| 44 | + case pci_channel_io_perm_failure: |
| 45 | + return "permanent-failure"; |
| 46 | + default: |
| 47 | + return "invalid"; |
| 48 | + }; |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * zpci_report_status - Report the status of operations on a PCI device |
| 53 | + * @zdev: The PCI device for which to report status |
| 54 | + * @operation: A string representing the operation reported |
| 55 | + * @status: A string representing the status of the operation |
| 56 | + * |
| 57 | + * This function creates a human readable report about an operation such as |
| 58 | + * PCI device recovery and forwards this to the platform using the SCLP Write |
| 59 | + * Event Data mechanism. Besides the operation and status strings the report |
| 60 | + * also contains additional information about the device deemed useful for |
| 61 | + * debug such as the currently bound device driver, if any, and error state. |
| 62 | + * |
| 63 | + * Return: 0 on success an error code < 0 otherwise. |
| 64 | + */ |
| 65 | +int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status) |
| 66 | +{ |
| 67 | + struct zpci_report_error *report; |
| 68 | + struct pci_driver *driver = NULL; |
| 69 | + struct pci_dev *pdev = NULL; |
| 70 | + char *buf, *end; |
| 71 | + int ret; |
| 72 | + |
| 73 | + if (!zdev || !zdev->zbus) |
| 74 | + return -ENODEV; |
| 75 | + |
| 76 | + /* Protected virtualization hosts get nothing from us */ |
| 77 | + if (prot_virt_guest) |
| 78 | + return -ENODATA; |
| 79 | + |
| 80 | + report = (void *)get_zeroed_page(GFP_KERNEL); |
| 81 | + if (!report) |
| 82 | + return -ENOMEM; |
| 83 | + if (zdev->zbus->bus) |
| 84 | + pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); |
| 85 | + if (pdev) |
| 86 | + driver = to_pci_driver(pdev->dev.driver); |
| 87 | + |
| 88 | + buf = report->data.log_data; |
| 89 | + end = report->data.log_data + ZPCI_REPORT_DATA_SIZE; |
| 90 | + buf += scnprintf(buf, end - buf, "report: %s\n", operation); |
| 91 | + buf += scnprintf(buf, end - buf, "status: %s\n", status); |
| 92 | + buf += scnprintf(buf, end - buf, "state: %s\n", |
| 93 | + (pdev) ? zpci_state_str(pdev->error_state) : "n/a"); |
| 94 | + buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a"); |
| 95 | + |
| 96 | + report->header.version = 1; |
| 97 | + report->header.action = SCLP_ERRNOTIFY_AQ_INFO_LOG; |
| 98 | + report->header.length = buf - (char *)&report->data; |
| 99 | + report->data.timestamp = ktime_get_clocktai_seconds(); |
| 100 | + report->data.err_log_id = ZPCI_ERR_LOG_ID_KERNEL_REPORT; |
| 101 | + |
| 102 | + ret = sclp_pci_report(&report->header, zdev->fh, zdev->fid); |
| 103 | + if (ret) |
| 104 | + pr_err("Reporting PCI status failed with code %d\n", ret); |
| 105 | + else |
| 106 | + pr_info("Reported PCI device status\n"); |
| 107 | + |
| 108 | + free_page((unsigned long)report); |
| 109 | + |
| 110 | + return ret; |
| 111 | +} |
0 commit comments