Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2025 AMD Corporation. All rights reserved. */
3
4#include <linux/pci.h>
5#include <linux/aer.h>
6#include <cxl/event.h>
7#include <cxlmem.h>
8#include "trace.h"
9
10static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev,
11 struct cxl_ras_capability_regs ras_cap)
12{
13 u32 status = ras_cap.cor_status & ~ras_cap.cor_mask;
14
15 trace_cxl_port_aer_correctable_error(&pdev->dev, status);
16}
17
18static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev,
19 struct cxl_ras_capability_regs ras_cap)
20{
21 u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask;
22 u32 fe;
23
24 if (hweight32(status) > 1)
25 fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
26 ras_cap.cap_control));
27 else
28 fe = status;
29
30 trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe,
31 ras_cap.header_log);
32}
33
34static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd,
35 struct cxl_ras_capability_regs ras_cap)
36{
37 u32 status = ras_cap.cor_status & ~ras_cap.cor_mask;
38
39 trace_cxl_aer_correctable_error(cxlmd, status);
40}
41
42static void
43cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd,
44 struct cxl_ras_capability_regs ras_cap)
45{
46 u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask;
47 u32 fe;
48
49 if (hweight32(status) > 1)
50 fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
51 ras_cap.cap_control));
52 else
53 fe = status;
54
55 trace_cxl_aer_uncorrectable_error(cxlmd, status, fe,
56 ras_cap.header_log);
57}
58
59static int match_memdev_by_parent(struct device *dev, const void *uport)
60{
61 if (is_cxl_memdev(dev) && dev->parent == uport)
62 return 1;
63 return 0;
64}
65
66static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data)
67{
68 unsigned int devfn = PCI_DEVFN(data->prot_err.agent_addr.device,
69 data->prot_err.agent_addr.function);
70 struct pci_dev *pdev __free(pci_dev_put) =
71 pci_get_domain_bus_and_slot(data->prot_err.agent_addr.segment,
72 data->prot_err.agent_addr.bus,
73 devfn);
74 struct cxl_memdev *cxlmd;
75 int port_type;
76
77 if (!pdev)
78 return;
79
80 port_type = pci_pcie_type(pdev);
81 if (port_type == PCI_EXP_TYPE_ROOT_PORT ||
82 port_type == PCI_EXP_TYPE_DOWNSTREAM ||
83 port_type == PCI_EXP_TYPE_UPSTREAM) {
84 if (data->severity == AER_CORRECTABLE)
85 cxl_cper_trace_corr_port_prot_err(pdev, data->ras_cap);
86 else
87 cxl_cper_trace_uncorr_port_prot_err(pdev, data->ras_cap);
88
89 return;
90 }
91
92 guard(device)(&pdev->dev);
93 if (!pdev->dev.driver)
94 return;
95
96 struct device *mem_dev __free(put_device) = bus_find_device(
97 &cxl_bus_type, NULL, pdev, match_memdev_by_parent);
98 if (!mem_dev)
99 return;
100
101 cxlmd = to_cxl_memdev(mem_dev);
102 if (data->severity == AER_CORRECTABLE)
103 cxl_cper_trace_corr_prot_err(cxlmd, data->ras_cap);
104 else
105 cxl_cper_trace_uncorr_prot_err(cxlmd, data->ras_cap);
106}
107
108static void cxl_cper_prot_err_work_fn(struct work_struct *work)
109{
110 struct cxl_cper_prot_err_work_data wd;
111
112 while (cxl_cper_prot_err_kfifo_get(&wd))
113 cxl_cper_handle_prot_err(&wd);
114}
115static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
116
117int cxl_ras_init(void)
118{
119 return cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
120}
121
122void cxl_ras_exit(void)
123{
124 cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work);
125 cancel_work_sync(&cxl_cper_prot_err_work);
126}