File Index
Files and directories
Sign in
FILE

pci_report.c

File preview with syntax highlighting.

linux-6.19.14/arch/s390/pci/pci_report.c C
  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 pr_fmt(fmt) "zpci: " fmt
 11
 12#include <linux/kernel.h>
 13#include <linux/sprintf.h>
 14#include <linux/pci.h>
 15
 16#include <asm/sclp.h>
 17#include <asm/debug.h>
 18#include <asm/pci_debug.h>
 19
 20#include "pci_report.h"
 21
 22#define ZPCI_ERR_LOG_ID_KERNEL_REPORT 0x4714
 23
 24struct zpci_report_error_data {
 25	u64 timestamp;
 26	u64 err_log_id;
 27	char log_data[];
 28} __packed;
 29
 30#define ZPCI_REPORT_SIZE	(PAGE_SIZE - sizeof(struct err_notify_sccb))
 31#define ZPCI_REPORT_DATA_SIZE	(ZPCI_REPORT_SIZE - sizeof(struct zpci_report_error_data))
 32
 33struct zpci_report_error {
 34	struct zpci_report_error_header header;
 35	struct zpci_report_error_data data;
 36} __packed;
 37
 38static const char *zpci_state_str(pci_channel_state_t state)
 39{
 40	switch (state) {
 41	case pci_channel_io_normal:
 42		return "normal";
 43	case pci_channel_io_frozen:
 44		return "frozen";
 45	case pci_channel_io_perm_failure:
 46		return "permanent-failure";
 47	default:
 48		return "invalid";
 49	};
 50}
 51
 52static int debug_log_header_fn(debug_info_t *id, struct debug_view *view,
 53			       int area, debug_entry_t *entry, char *out_buf,
 54			       size_t out_buf_size)
 55{
 56	unsigned long sec, usec;
 57	unsigned int level;
 58	char *except_str;
 59	int rc = 0;
 60
 61	level = entry->level;
 62	sec = entry->clock;
 63	usec = do_div(sec, USEC_PER_SEC);
 64
 65	if (entry->exception)
 66		except_str = "*";
 67	else
 68		except_str = "-";
 69	rc += scnprintf(out_buf, out_buf_size, "%011ld:%06lu %1u %1s %04u  ",
 70			sec, usec, level, except_str,
 71			entry->cpu);
 72	return rc;
 73}
 74
 75static int debug_prolog_header(debug_info_t *id, struct debug_view *view,
 76			       char *out_buf, size_t out_buf_size)
 77{
 78	return scnprintf(out_buf, out_buf_size, "sec:usec level except cpu  msg\n");
 79}
 80
 81static struct debug_view debug_log_view = {
 82	"pci_msg_log",
 83	&debug_prolog_header,
 84	&debug_log_header_fn,
 85	&debug_sprintf_format_fn,
 86	NULL,
 87	NULL
 88};
 89
 90/**
 91 * zpci_report_status - Report the status of operations on a PCI device
 92 * @zdev:	The PCI device for which to report status
 93 * @operation:	A string representing the operation reported
 94 * @status:	A string representing the status of the operation
 95 *
 96 * This function creates a human readable report about an operation such as
 97 * PCI device recovery and forwards this to the platform using the SCLP Write
 98 * Event Data mechanism. Besides the operation and status strings the report
 99 * also contains additional information about the device deemed useful for
100 * debug such as the currently bound device driver, if any, and error state.
101 * Additionally a string representation of pci_debug_msg_id, or as much as fits,
102 * is also included.
103 *
104 * Return: 0 on success an error code < 0 otherwise.
105 */
106int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status)
107{
108	struct zpci_report_error *report;
109	struct pci_driver *driver = NULL;
110	struct pci_dev *pdev = NULL;
111	char *buf, *end;
112	int ret;
113
114	if (!zdev || !zdev->zbus)
115		return -ENODEV;
116
117	/* Protected virtualization hosts get nothing from us */
118	if (prot_virt_guest)
119		return -ENODATA;
120
121	report = (void *)get_zeroed_page(GFP_KERNEL);
122	if (!report)
123		return -ENOMEM;
124	if (zdev->zbus->bus)
125		pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
126	if (pdev)
127		driver = to_pci_driver(pdev->dev.driver);
128
129	buf = report->data.log_data;
130	end = report->data.log_data + ZPCI_REPORT_DATA_SIZE;
131	buf += scnprintf(buf, end - buf, "report: %s\n", operation);
132	buf += scnprintf(buf, end - buf, "status: %s\n", status);
133	buf += scnprintf(buf, end - buf, "state: %s\n",
134			 (pdev) ? zpci_state_str(pdev->error_state) : "n/a");
135	buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
136	ret = debug_dump(pci_debug_msg_id, &debug_log_view, buf, end - buf, true);
137	if (ret < 0)
138		pr_err("Reading PCI debug messages failed with code %d\n", ret);
139	else
140		buf += ret;
141
142	report->header.version = 1;
143	report->header.action = SCLP_ERRNOTIFY_AQ_INFO_LOG;
144	report->header.length = buf - (char *)&report->data;
145	report->data.timestamp = ktime_get_clocktai_seconds();
146	report->data.err_log_id = ZPCI_ERR_LOG_ID_KERNEL_REPORT;
147
148	ret = sclp_pci_report(&report->header, zdev->fh, zdev->fid);
149	if (ret)
150		pr_err("Reporting PCI status failed with code %d\n", ret);
151	else
152		pr_info("Reported PCI device status\n");
153
154	free_page((unsigned long)report);
155
156	return ret;
157}
~/ Read-only file preview.