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