Skip to content

Commit 7e077e6

Browse files
ij-intelbjorn-helgaas
authored andcommitted
PCI/ERR: Handle TLP Log in Flit mode
Flit mode introduced in PCIe r6.0 alters how the TLP Header Log is presented through AER and DPC Capability registers. The TLP Prefix Log Register is not present with Flit mode, and the register becomes an extension of the TLP Header Log (PCIe r6.1 secs 7.8.4.12 & 7.9.14.13). Adapt pcie_read_tlp_log() and struct pcie_tlp_log to read and store the extended TLP Header Log when the Link is in Flit mode. As the Prefix Log and Extended TLP Header are not present at the same time, a C union can be used. Determining whether the error occurred while the Link was in Flit mode is a bit complicated. In case of AER, the Advanced Error Capabilities and Control Register directly tells whether the error was logged in Flit mode or not (PCIe r6.1 sec 7.8.4.7). The DPC Capability (PCIe r6.1 sec 7.9.14), unfortunately, does not contain the same information. Unlike AER, the DPC Capability does not provide a way to discern whether the error was logged in Flit mode (this is confirmed by PCI WG to be an oversight in the spec). DPC will bring the Link down immediately following an error, which makes it impossible to acquire the Flit Mode Status directly from the Link Status 2 register because Flit Mode Status is only set in certain Link states (PCIe r6.1 sec 7.5.3.20). As a workaround, use the flit_mode value stored into the struct pci_bus. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ilpo Järvinen <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 79c731e commit 7e077e6

File tree

7 files changed

+77
-31
lines changed

7 files changed

+77
-31
lines changed

drivers/pci/pci.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,8 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
554554
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
555555

556556
int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
557-
unsigned int tlp_len, struct pcie_tlp_log *log);
557+
unsigned int tlp_len, bool flit,
558+
struct pcie_tlp_log *log);
558559
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc);
559560
void pcie_print_tlp_log(const struct pci_dev *dev,
560561
const struct pcie_tlp_log *log, const char *pfx);

drivers/pci/pcie/aer.c

+1
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,7 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
12491249
pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
12501250
aer + PCI_ERR_PREFIX_LOG,
12511251
aer_tlp_log_len(dev, aercc),
1252+
aercc & PCI_ERR_CAP_TLP_LOG_FLIT,
12521253
&info->tlp);
12531254
}
12541255
}

drivers/pci/pcie/dpc.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,9 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
219219
goto clear_status;
220220
pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
221221
cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG,
222-
dpc_tlp_log_len(pdev), &tlp_log);
222+
dpc_tlp_log_len(pdev),
223+
pdev->subordinate->flit_mode,
224+
&tlp_log);
223225
pcie_print_tlp_log(pdev, &tlp_log, dev_fmt(""));
224226

225227
if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG + 1)
@@ -398,11 +400,21 @@ void pci_dpc_init(struct pci_dev *pdev)
398400

399401
/* Quirks may set dpc_rp_log_size if device or firmware is buggy */
400402
if (!pdev->dpc_rp_log_size) {
403+
u16 flags;
404+
int ret;
405+
406+
ret = pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &flags);
407+
if (ret)
408+
return;
409+
401410
pdev->dpc_rp_log_size =
402411
FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, cap);
412+
if (FIELD_GET(PCI_EXP_FLAGS_FLIT, flags))
413+
pdev->dpc_rp_log_size += FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE4,
414+
cap) << 4;
415+
403416
if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG ||
404-
pdev->dpc_rp_log_size > PCIE_STD_NUM_TLP_HEADERLOG + 1 +
405-
PCIE_STD_MAX_TLP_PREFIXLOG) {
417+
pdev->dpc_rp_log_size > PCIE_STD_MAX_TLP_HEADERLOG + 1) {
406418
pci_err(pdev, "RP PIO log size %u is invalid\n",
407419
pdev->dpc_rp_log_size);
408420
pdev->dpc_rp_log_size = 0;

drivers/pci/pcie/tlp.c

+38-18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <linux/aer.h>
99
#include <linux/array_size.h>
10+
#include <linux/bitfield.h>
1011
#include <linux/pci.h>
1112
#include <linux/string.h>
1213

@@ -21,6 +22,9 @@
2122
*/
2223
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc)
2324
{
25+
if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT)
26+
return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc);
27+
2428
return PCIE_STD_NUM_TLP_HEADERLOG +
2529
((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ?
2630
dev->eetlp_prefix_max : 0);
@@ -49,35 +53,42 @@ unsigned int dpc_tlp_log_len(struct pci_dev *dev)
4953
* @where: PCI Config offset of TLP Header Log
5054
* @where2: PCI Config offset of TLP Prefix Log
5155
* @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs)
56+
* @flit: TLP Logged in Flit mode
5257
* @log: TLP Log structure to fill
5358
*
5459
* Fill @log from TLP Header Log registers, e.g., AER or DPC.
5560
*
5661
* Return: 0 on success and filled TLP Log structure, <0 on error.
5762
*/
5863
int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
59-
unsigned int tlp_len, struct pcie_tlp_log *log)
64+
unsigned int tlp_len, bool flit, struct pcie_tlp_log *log)
6065
{
6166
unsigned int i;
6267
int off, ret;
63-
u32 *to;
68+
69+
if (tlp_len > ARRAY_SIZE(log->dw))
70+
tlp_len = ARRAY_SIZE(log->dw);
6471

6572
memset(log, 0, sizeof(*log));
6673

6774
for (i = 0; i < tlp_len; i++) {
68-
if (i < PCIE_STD_NUM_TLP_HEADERLOG) {
75+
if (i < PCIE_STD_NUM_TLP_HEADERLOG)
6976
off = where + i * 4;
70-
to = &log->dw[i];
71-
} else {
77+
else
7278
off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4;
73-
to = &log->prefix[i - PCIE_STD_NUM_TLP_HEADERLOG];
74-
}
7579

76-
ret = pci_read_config_dword(dev, off, to);
80+
ret = pci_read_config_dword(dev, off, &log->dw[i]);
7781
if (ret)
7882
return pcibios_err_to_errno(ret);
7983
}
8084

85+
/*
86+
* Hard-code non-Flit mode to 4 DWORDs, for now. The exact length
87+
* can only be known if the TLP is parsed.
88+
*/
89+
log->header_len = flit ? tlp_len : 4;
90+
log->flit = flit;
91+
8192
return 0;
8293
}
8394

@@ -94,22 +105,31 @@ int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
94105
void pcie_print_tlp_log(const struct pci_dev *dev,
95106
const struct pcie_tlp_log *log, const char *pfx)
96107
{
97-
char buf[11 * (PCIE_STD_NUM_TLP_HEADERLOG + ARRAY_SIZE(log->prefix)) +
98-
sizeof(EE_PREFIX_STR)];
108+
/* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
109+
char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
99110
unsigned int i;
100111
int len;
101112

102113
len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x",
103114
log->dw[0], log->dw[1], log->dw[2], log->dw[3]);
104115

105-
if (log->prefix[0])
106-
len += scnprintf(buf + len, sizeof(buf) - len, EE_PREFIX_STR);
107-
for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
108-
if (!log->prefix[i])
109-
break;
110-
len += scnprintf(buf + len, sizeof(buf) - len,
111-
" %#010x", log->prefix[i]);
116+
if (log->flit) {
117+
for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) {
118+
len += scnprintf(buf + len, sizeof(buf) - len,
119+
" %#010x", log->dw[i]);
120+
}
121+
} else {
122+
if (log->prefix[0])
123+
len += scnprintf(buf + len, sizeof(buf) - len,
124+
EE_PREFIX_STR);
125+
for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
126+
if (!log->prefix[i])
127+
break;
128+
len += scnprintf(buf + len, sizeof(buf) - len,
129+
" %#010x", log->prefix[i]);
130+
}
112131
}
113132

114-
pci_err(dev, "%sTLP Header: %s\n", pfx, buf);
133+
pci_err(dev, "%sTLP Header%s: %s\n", pfx,
134+
log->flit ? " (Flit)" : "", buf);
115135
}

include/linux/aer.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@
2222
*/
2323
#define PCIE_STD_NUM_TLP_HEADERLOG 4
2424
#define PCIE_STD_MAX_TLP_PREFIXLOG 4
25+
#define PCIE_STD_MAX_TLP_HEADERLOG (PCIE_STD_NUM_TLP_HEADERLOG + 10)
2526

2627
struct pci_dev;
2728

2829
struct pcie_tlp_log {
29-
u32 dw[PCIE_STD_NUM_TLP_HEADERLOG];
30-
u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG];
30+
union {
31+
u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];
32+
struct {
33+
u32 _do_not_use[PCIE_STD_NUM_TLP_HEADERLOG];
34+
u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG];
35+
};
36+
};
37+
u8 header_len; /* Length of the Logged TLP Header in DWORDs */
38+
bool flit; /* TLP was logged when in Flit mode */
3139
};
3240

3341
struct aer_capability_regs {

include/ras/ras_event.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ TRACE_EVENT(aer_event,
309309
__field( u32, status )
310310
__field( u8, severity )
311311
__field( u8, tlp_header_valid)
312-
__array( u32, tlp_header, 4 )
312+
__array( u32, tlp_header, PCIE_STD_MAX_TLP_HEADERLOG)
313313
),
314314

315315
TP_fast_assign(
@@ -318,10 +318,10 @@ TRACE_EVENT(aer_event,
318318
__entry->severity = severity;
319319
__entry->tlp_header_valid = tlp_header_valid;
320320
if (tlp_header_valid) {
321-
__entry->tlp_header[0] = tlp->dw[0];
322-
__entry->tlp_header[1] = tlp->dw[1];
323-
__entry->tlp_header[2] = tlp->dw[2];
324-
__entry->tlp_header[3] = tlp->dw[3];
321+
int i;
322+
323+
for (i = 0; i < PCIE_STD_MAX_TLP_HEADERLOG; i++)
324+
__entry->tlp_header[i] = tlp->dw[i];
325325
}
326326
),
327327

@@ -334,7 +334,7 @@ TRACE_EVENT(aer_event,
334334
__print_flags(__entry->status, "|", aer_correctable_errors) :
335335
__print_flags(__entry->status, "|", aer_uncorrectable_errors),
336336
__entry->tlp_header_valid ?
337-
__print_array(__entry->tlp_header, 4, 4) :
337+
__print_array(__entry->tlp_header, PCIE_STD_MAX_TLP_HEADERLOG, 4) :
338338
"Not available")
339339
);
340340

include/uapi/linux/pci_regs.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@
486486
#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
487487
#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
488488
#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
489+
#define PCI_EXP_FLAGS_FLIT 0x8000 /* Flit Mode Supported */
489490
#define PCI_EXP_DEVCAP 0x04 /* Device capabilities */
490491
#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */
491492
#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */
@@ -795,6 +796,8 @@
795796
#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
796797
#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
797798
#define PCI_ERR_CAP_PREFIX_LOG_PRESENT 0x00000800 /* TLP Prefix Log Present */
799+
#define PCI_ERR_CAP_TLP_LOG_FLIT 0x00040000 /* TLP was logged in Flit Mode */
800+
#define PCI_ERR_CAP_TLP_LOG_SIZE 0x00f80000 /* Logged TLP Size (only in Flit mode) */
798801
#define PCI_ERR_HEADER_LOG 0x1c /* Header Log Register (16 bytes) */
799802
#define PCI_ERR_ROOT_COMMAND 0x2c /* Root Error Command */
800803
#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Err Reporting Enable */
@@ -1061,8 +1064,9 @@
10611064
#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */
10621065
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */
10631066
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */
1064-
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */
1067+
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size [3:0] */
10651068
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
1069+
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE4 0x2000 /* RP PIO Log Size [4] */
10661070

10671071
#define PCI_EXP_DPC_CTL 0x06 /* DPC control */
10681072
#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */

0 commit comments

Comments
 (0)