mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge branch 'for-6.9/cxl-einj' into for-6.9/cxl
Pick up support for injecting errors via ACPI EINJ into the CXL protocol for v6.9.
This commit is contained in:
commit
75f4d93ee8
@ -33,3 +33,33 @@ Description:
|
||||
device cannot clear poison from the address, -ENXIO is returned.
|
||||
The clear_poison attribute is only visible for devices
|
||||
supporting the capability.
|
||||
|
||||
What: /sys/kernel/debug/cxl/einj_types
|
||||
Date: January, 2024
|
||||
KernelVersion: v6.9
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RO) Prints the CXL protocol error types made available by
|
||||
the platform in the format "0x<error number> <error type>".
|
||||
The possible error types are (as of ACPI v6.5):
|
||||
0x1000 CXL.cache Protocol Correctable
|
||||
0x2000 CXL.cache Protocol Uncorrectable non-fatal
|
||||
0x4000 CXL.cache Protocol Uncorrectable fatal
|
||||
0x8000 CXL.mem Protocol Correctable
|
||||
0x10000 CXL.mem Protocol Uncorrectable non-fatal
|
||||
0x20000 CXL.mem Protocol Uncorrectable fatal
|
||||
|
||||
The <error number> can be written to einj_inject to inject
|
||||
<error type> into a chosen dport.
|
||||
|
||||
What: /sys/kernel/debug/cxl/$dport_dev/einj_inject
|
||||
Date: January, 2024
|
||||
KernelVersion: v6.9
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(WO) Writing an integer to this file injects the corresponding
|
||||
CXL protocol error into $dport_dev ($dport_dev will be a device
|
||||
name from /sys/bus/pci/devices). The integer to type mapping for
|
||||
injection can be found by reading from einj_types. If the dport
|
||||
was enumerated in RCH mode, a CXL 1.1 error is injected, otherwise
|
||||
a CXL 2.0 error is injected.
|
||||
|
@ -32,6 +32,10 @@ configuration::
|
||||
CONFIG_ACPI_APEI
|
||||
CONFIG_ACPI_APEI_EINJ
|
||||
|
||||
...and to (optionally) enable CXL protocol error injection set::
|
||||
|
||||
CONFIG_ACPI_APEI_EINJ_CXL
|
||||
|
||||
The EINJ user interface is in <debugfs mount point>/apei/einj.
|
||||
|
||||
The following files belong to it:
|
||||
@ -118,6 +122,24 @@ The following files belong to it:
|
||||
this actually works depends on what operations the BIOS actually
|
||||
includes in the trigger phase.
|
||||
|
||||
CXL error types are supported from ACPI 6.5 onwards (given a CXL port
|
||||
is present). The EINJ user interface for CXL error types is at
|
||||
<debugfs mount point>/cxl. The following files belong to it:
|
||||
|
||||
- einj_types:
|
||||
|
||||
Provides the same functionality as available_error_types above, but
|
||||
for CXL error types
|
||||
|
||||
- $dport_dev/einj_inject:
|
||||
|
||||
Injects a CXL error type into the CXL port represented by $dport_dev,
|
||||
where $dport_dev is the name of the CXL port (usually a PCIe device name).
|
||||
Error injections targeting a CXL 2.0+ port can use the legacy interface
|
||||
under <debugfs mount point>/apei/einj, while CXL 1.1/1.0 port injections
|
||||
must use this file.
|
||||
|
||||
|
||||
BIOS versions based on the ACPI 4.0 specification have limited options
|
||||
in controlling where the errors are injected. Your BIOS may support an
|
||||
extension (enabled with the param_extension=1 module parameter, or boot
|
||||
@ -181,6 +203,18 @@ You should see something like this in dmesg::
|
||||
[22715.834759] EDAC sbridge MC3: PROCESSOR 0:306e7 TIME 1422553404 SOCKET 0 APIC 0
|
||||
[22716.616173] EDAC MC3: 1 CE memory read error on CPU_SrcID#0_Channel#0_DIMM#0 (channel:0 slot:0 page:0x12345 offset:0x0 grain:32 syndrome:0x0 - area:DRAM err_code:0001:0090 socket:0 channel_mask:1 rank:0)
|
||||
|
||||
A CXL error injection example with $dport_dev=0000:e0:01.1::
|
||||
|
||||
# cd /sys/kernel/debug/cxl/
|
||||
# ls
|
||||
0000:e0:01.1 0000:0c:00.0
|
||||
# cat einj_types # See which errors can be injected
|
||||
0x00008000 CXL.mem Protocol Correctable
|
||||
0x00010000 CXL.mem Protocol Uncorrectable non-fatal
|
||||
0x00020000 CXL.mem Protocol Uncorrectable fatal
|
||||
# cd 0000:e0:01.1 # Navigate to dport to inject into
|
||||
# echo 0x8000 > einj_inject # Inject error
|
||||
|
||||
Special notes for injection into SGX enclaves:
|
||||
|
||||
There may be a separate BIOS setup option to enable SGX injection.
|
||||
|
@ -5289,6 +5289,7 @@ M: Dan Williams <dan.j.williams@intel.com>
|
||||
L: linux-cxl@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/cxl/
|
||||
F: include/linux/cxl-einj.h
|
||||
F: include/linux/cxl-event.h
|
||||
F: include/uapi/linux/cxl_mem.h
|
||||
F: tools/testing/cxl/
|
||||
|
@ -60,6 +60,19 @@ config ACPI_APEI_EINJ
|
||||
mainly used for debugging and testing the other parts of
|
||||
APEI and some other RAS features.
|
||||
|
||||
config ACPI_APEI_EINJ_CXL
|
||||
bool "CXL Error INJection Support"
|
||||
default ACPI_APEI_EINJ
|
||||
depends on ACPI_APEI_EINJ
|
||||
depends on CXL_BUS && CXL_BUS <= ACPI_APEI_EINJ
|
||||
help
|
||||
Support for CXL protocol Error INJection through debugfs/cxl.
|
||||
Availability and which errors are supported is dependent on
|
||||
the host platform. Look to ACPI v6.5 section 18.6.4 and kernel
|
||||
EINJ documentation for more information.
|
||||
|
||||
If unsure say 'n'
|
||||
|
||||
config ACPI_APEI_ERST_DEBUG
|
||||
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
|
||||
depends on ACPI_APEI
|
||||
|
@ -2,6 +2,8 @@
|
||||
obj-$(CONFIG_ACPI_APEI) += apei.o
|
||||
obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
|
||||
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
|
||||
einj-y := einj-core.o
|
||||
einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
|
||||
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
|
||||
|
||||
apei-y := apei-base.o hest.o erst.o bert.o
|
||||
|
@ -130,4 +130,22 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
|
||||
}
|
||||
|
||||
int apei_osc_setup(void);
|
||||
|
||||
int einj_get_available_error_type(u32 *type);
|
||||
int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
|
||||
u64 param4);
|
||||
int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
||||
u64 param3, u64 param4);
|
||||
bool einj_is_cxl_error_type(u64 type);
|
||||
int einj_validate_error_type(u64 type);
|
||||
|
||||
#ifndef ACPI_EINJ_CXL_CACHE_CORRECTABLE
|
||||
#define ACPI_EINJ_CXL_CACHE_CORRECTABLE BIT(12)
|
||||
#define ACPI_EINJ_CXL_CACHE_UNCORRECTABLE BIT(13)
|
||||
#define ACPI_EINJ_CXL_CACHE_FATAL BIT(14)
|
||||
#define ACPI_EINJ_CXL_MEM_CORRECTABLE BIT(15)
|
||||
#define ACPI_EINJ_CXL_MEM_UNCORRECTABLE BIT(16)
|
||||
#define ACPI_EINJ_CXL_MEM_FATAL BIT(17)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "apei-internal.h"
|
||||
@ -36,6 +37,12 @@
|
||||
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
|
||||
ACPI_EINJ_MEMORY_UNCORRECTABLE | \
|
||||
ACPI_EINJ_MEMORY_FATAL)
|
||||
#define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \
|
||||
ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \
|
||||
ACPI_EINJ_CXL_CACHE_FATAL | \
|
||||
ACPI_EINJ_CXL_MEM_CORRECTABLE | \
|
||||
ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \
|
||||
ACPI_EINJ_CXL_MEM_FATAL)
|
||||
|
||||
/*
|
||||
* ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
|
||||
@ -137,6 +144,11 @@ static struct apei_exec_ins_type einj_ins_type[] = {
|
||||
*/
|
||||
static DEFINE_MUTEX(einj_mutex);
|
||||
|
||||
/*
|
||||
* Exported APIs use this flag to exit early if einj_probe() failed.
|
||||
*/
|
||||
bool einj_initialized __ro_after_init;
|
||||
|
||||
static void *einj_param;
|
||||
|
||||
static void einj_exec_ctx_init(struct apei_exec_context *ctx)
|
||||
@ -160,7 +172,7 @@ static int __einj_get_available_error_type(u32 *type)
|
||||
}
|
||||
|
||||
/* Get error injection capabilities of the platform */
|
||||
static int einj_get_available_error_type(u32 *type)
|
||||
int einj_get_available_error_type(u32 *type)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@ -530,8 +542,8 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
||||
}
|
||||
|
||||
/* Inject the specified hardware error */
|
||||
static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
||||
u64 param3, u64 param4)
|
||||
int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
|
||||
u64 param4)
|
||||
{
|
||||
int rc;
|
||||
u64 base_addr, size;
|
||||
@ -554,8 +566,17 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
||||
if (type & ACPI5_VENDOR_BIT) {
|
||||
if (vendor_flags != SETWA_FLAGS_MEM)
|
||||
goto inject;
|
||||
} else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
|
||||
} else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) {
|
||||
goto inject;
|
||||
}
|
||||
|
||||
/*
|
||||
* Injections targeting a CXL 1.0/1.1 port have to be injected
|
||||
* via the einj_cxl_rch_error_inject() path as that does the proper
|
||||
* validation of the given RCRB base (MMIO) address.
|
||||
*/
|
||||
if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Disallow crazy address masks that give BIOS leeway to pick
|
||||
@ -587,6 +608,21 @@ inject:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
||||
u64 param3, u64 param4)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!(einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&einj_mutex);
|
||||
rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
|
||||
mutex_unlock(&einj_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static u32 error_type;
|
||||
static u32 error_flags;
|
||||
static u64 error_param1;
|
||||
@ -607,12 +643,6 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
|
||||
{ BIT(9), "Platform Correctable" },
|
||||
{ BIT(10), "Platform Uncorrectable non-fatal" },
|
||||
{ BIT(11), "Platform Uncorrectable fatal"},
|
||||
{ BIT(12), "CXL.cache Protocol Correctable" },
|
||||
{ BIT(13), "CXL.cache Protocol Uncorrectable non-fatal" },
|
||||
{ BIT(14), "CXL.cache Protocol Uncorrectable fatal" },
|
||||
{ BIT(15), "CXL.mem Protocol Correctable" },
|
||||
{ BIT(16), "CXL.mem Protocol Uncorrectable non-fatal" },
|
||||
{ BIT(17), "CXL.mem Protocol Uncorrectable fatal" },
|
||||
{ BIT(31), "Vendor Defined Error Types" },
|
||||
};
|
||||
|
||||
@ -641,22 +671,26 @@ static int error_type_get(void *data, u64 *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int error_type_set(void *data, u64 val)
|
||||
bool einj_is_cxl_error_type(u64 type)
|
||||
{
|
||||
return (type & CXL_ERROR_MASK) && (!(type & ACPI5_VENDOR_BIT));
|
||||
}
|
||||
|
||||
int einj_validate_error_type(u64 type)
|
||||
{
|
||||
u32 tval, vendor, available_error_type = 0;
|
||||
int rc;
|
||||
u32 available_error_type = 0;
|
||||
u32 tval, vendor;
|
||||
|
||||
/* Only low 32 bits for error type are valid */
|
||||
if (val & GENMASK_ULL(63, 32))
|
||||
if (type & GENMASK_ULL(63, 32))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Vendor defined types have 0x80000000 bit set, and
|
||||
* are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
|
||||
*/
|
||||
vendor = val & ACPI5_VENDOR_BIT;
|
||||
tval = val & 0x7fffffff;
|
||||
vendor = type & ACPI5_VENDOR_BIT;
|
||||
tval = type & GENMASK(30, 0);
|
||||
|
||||
/* Only one error type can be specified */
|
||||
if (tval & (tval - 1))
|
||||
@ -665,9 +699,21 @@ static int error_type_set(void *data, u64 val)
|
||||
rc = einj_get_available_error_type(&available_error_type);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!(val & available_error_type))
|
||||
if (!(type & available_error_type))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int error_type_set(void *data, u64 val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = einj_validate_error_type(val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
error_type = val;
|
||||
|
||||
return 0;
|
||||
@ -703,21 +749,21 @@ static int einj_check_table(struct acpi_table_einj *einj_tab)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init einj_init(void)
|
||||
static int __init einj_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
acpi_status status;
|
||||
struct apei_exec_context ctx;
|
||||
|
||||
if (acpi_disabled) {
|
||||
pr_info("ACPI disabled.\n");
|
||||
pr_debug("ACPI disabled.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status = acpi_get_table(ACPI_SIG_EINJ, 0,
|
||||
(struct acpi_table_header **)&einj_tab);
|
||||
if (status == AE_NOT_FOUND) {
|
||||
pr_warn("EINJ table not found.\n");
|
||||
pr_debug("EINJ table not found.\n");
|
||||
return -ENODEV;
|
||||
} else if (ACPI_FAILURE(status)) {
|
||||
pr_err("Failed to get EINJ table: %s\n",
|
||||
@ -805,7 +851,7 @@ err_put_table:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit einj_exit(void)
|
||||
static void __exit einj_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct apei_exec_context ctx;
|
||||
|
||||
@ -826,6 +872,40 @@ static void __exit einj_exit(void)
|
||||
acpi_put_table((struct acpi_table_header *)einj_tab);
|
||||
}
|
||||
|
||||
static struct platform_device *einj_dev;
|
||||
static struct platform_driver einj_driver = {
|
||||
.remove_new = einj_remove,
|
||||
.driver = {
|
||||
.name = "acpi-einj",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init einj_init(void)
|
||||
{
|
||||
struct platform_device_info einj_dev_info = {
|
||||
.name = "acpi-einj",
|
||||
.id = -1,
|
||||
};
|
||||
int rc;
|
||||
|
||||
einj_dev = platform_device_register_full(&einj_dev_info);
|
||||
if (IS_ERR(einj_dev))
|
||||
return PTR_ERR(einj_dev);
|
||||
|
||||
rc = platform_driver_probe(&einj_driver, einj_probe);
|
||||
einj_initialized = rc == 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit einj_exit(void)
|
||||
{
|
||||
if (einj_initialized)
|
||||
platform_driver_unregister(&einj_driver);
|
||||
|
||||
platform_device_del(einj_dev);
|
||||
}
|
||||
|
||||
module_init(einj_init);
|
||||
module_exit(einj_exit);
|
||||
|
113
drivers/acpi/apei/einj-cxl.c
Normal file
113
drivers/acpi/apei/einj-cxl.c
Normal file
@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* CXL Error INJection support. Used by CXL core to inject
|
||||
* protocol errors into CXL ports.
|
||||
*
|
||||
* Copyright (C) 2023 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Ben Cheatham <benjamin.cheatham@amd.com>
|
||||
*/
|
||||
#include <linux/einj-cxl.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "apei-internal.h"
|
||||
|
||||
/* Defined in einj-core.c */
|
||||
extern bool einj_initialized;
|
||||
|
||||
static struct { u32 mask; const char *str; } const einj_cxl_error_type_string[] = {
|
||||
{ ACPI_EINJ_CXL_CACHE_CORRECTABLE, "CXL.cache Protocol Correctable" },
|
||||
{ ACPI_EINJ_CXL_CACHE_UNCORRECTABLE, "CXL.cache Protocol Uncorrectable non-fatal" },
|
||||
{ ACPI_EINJ_CXL_CACHE_FATAL, "CXL.cache Protocol Uncorrectable fatal" },
|
||||
{ ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Protocol Correctable" },
|
||||
{ ACPI_EINJ_CXL_MEM_UNCORRECTABLE, "CXL.mem Protocol Uncorrectable non-fatal" },
|
||||
{ ACPI_EINJ_CXL_MEM_FATAL, "CXL.mem Protocol Uncorrectable fatal" },
|
||||
};
|
||||
|
||||
int einj_cxl_available_error_type_show(struct seq_file *m, void *v)
|
||||
{
|
||||
int cxl_err, rc;
|
||||
u32 available_error_type = 0;
|
||||
|
||||
rc = einj_get_available_error_type(&available_error_type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (int pos = 0; pos < ARRAY_SIZE(einj_cxl_error_type_string); pos++) {
|
||||
cxl_err = ACPI_EINJ_CXL_CACHE_CORRECTABLE << pos;
|
||||
|
||||
if (available_error_type & cxl_err)
|
||||
seq_printf(m, "0x%08x\t%s\n",
|
||||
einj_cxl_error_type_string[pos].mask,
|
||||
einj_cxl_error_type_string[pos].str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(einj_cxl_available_error_type_show, CXL);
|
||||
|
||||
static int cxl_dport_get_sbdf(struct pci_dev *dport_dev, u64 *sbdf)
|
||||
{
|
||||
struct pci_bus *pbus;
|
||||
struct pci_host_bridge *bridge;
|
||||
u64 seg = 0, bus;
|
||||
|
||||
pbus = dport_dev->bus;
|
||||
bridge = pci_find_host_bridge(pbus);
|
||||
|
||||
if (!bridge)
|
||||
return -ENODEV;
|
||||
|
||||
if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET)
|
||||
seg = bridge->domain_nr;
|
||||
|
||||
bus = pbus->number;
|
||||
*sbdf = (seg << 24) | (bus << 16) | dport_dev->devfn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Only CXL error types can be specified */
|
||||
if (!einj_is_cxl_error_type(type))
|
||||
return -EINVAL;
|
||||
|
||||
rc = einj_validate_error_type(type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return einj_cxl_rch_error_inject(type, 0x2, rcrb, GENMASK_ULL(63, 0),
|
||||
0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_rch_error, CXL);
|
||||
|
||||
int einj_cxl_inject_error(struct pci_dev *dport, u64 type)
|
||||
{
|
||||
u64 param4 = 0;
|
||||
int rc;
|
||||
|
||||
/* Only CXL error types can be specified */
|
||||
if (!einj_is_cxl_error_type(type))
|
||||
return -EINVAL;
|
||||
|
||||
rc = einj_validate_error_type(type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_dport_get_sbdf(dport, ¶m4);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return einj_error_inject(type, 0x4, 0, 0, 0, param4);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_error, CXL);
|
||||
|
||||
bool einj_cxl_is_initialized(void)
|
||||
{
|
||||
return einj_initialized;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(einj_cxl_is_initialized, CXL);
|
@ -3,6 +3,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/memregion.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/einj-cxl.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
@ -793,6 +794,40 @@ static int cxl_dport_setup_regs(struct device *host, struct cxl_dport *dport,
|
||||
return rc;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(einj_cxl_available_error_type);
|
||||
|
||||
static int cxl_einj_inject(void *data, u64 type)
|
||||
{
|
||||
struct cxl_dport *dport = data;
|
||||
|
||||
if (dport->rch)
|
||||
return einj_cxl_inject_rch_error(dport->rcrb.base, type);
|
||||
|
||||
return einj_cxl_inject_error(to_pci_dev(dport->dport_dev), type);
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(cxl_einj_inject_fops, NULL, cxl_einj_inject,
|
||||
"0x%llx\n");
|
||||
|
||||
static void cxl_debugfs_create_dport_dir(struct cxl_dport *dport)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
if (!einj_cxl_is_initialized())
|
||||
return;
|
||||
|
||||
/*
|
||||
* dport_dev needs to be a PCIe port for CXL 2.0+ ports because
|
||||
* EINJ expects a dport SBDF to be specified for 2.0 error injection.
|
||||
*/
|
||||
if (!dport->rch && !dev_is_pci(dport->dport_dev))
|
||||
return;
|
||||
|
||||
dir = cxl_debugfs_create_dir(dev_name(dport->dport_dev));
|
||||
|
||||
debugfs_create_file("einj_inject", 0200, dir, dport,
|
||||
&cxl_einj_inject_fops);
|
||||
}
|
||||
|
||||
static struct cxl_port *__devm_cxl_add_port(struct device *host,
|
||||
struct device *uport_dev,
|
||||
resource_size_t component_reg_phys,
|
||||
@ -1150,6 +1185,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
|
||||
if (dev_is_pci(dport_dev))
|
||||
dport->link_latency = cxl_pci_get_latency(to_pci_dev(dport_dev));
|
||||
|
||||
cxl_debugfs_create_dport_dir(dport);
|
||||
|
||||
return dport;
|
||||
}
|
||||
|
||||
@ -2236,6 +2273,10 @@ static __init int cxl_core_init(void)
|
||||
|
||||
cxl_debugfs = debugfs_create_dir("cxl", NULL);
|
||||
|
||||
if (einj_cxl_is_initialized())
|
||||
debugfs_create_file("einj_types", 0400, cxl_debugfs, NULL,
|
||||
&einj_cxl_available_error_type_fops);
|
||||
|
||||
cxl_mbox_init();
|
||||
|
||||
rc = cxl_memdev_init();
|
||||
|
44
include/linux/einj-cxl.h
Normal file
44
include/linux/einj-cxl.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* CXL protocol Error INJection support.
|
||||
*
|
||||
* Copyright (c) 2023 Advanced Micro Devices, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Ben Cheatham <benjamin.cheatham@amd.com>
|
||||
*/
|
||||
#ifndef EINJ_CXL_H
|
||||
#define EINJ_CXL_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct pci_dev;
|
||||
struct seq_file;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI_APEI_EINJ_CXL)
|
||||
int einj_cxl_available_error_type_show(struct seq_file *m, void *v);
|
||||
int einj_cxl_inject_error(struct pci_dev *dport_dev, u64 type);
|
||||
int einj_cxl_inject_rch_error(u64 rcrb, u64 type);
|
||||
bool einj_cxl_is_initialized(void);
|
||||
#else /* !IS_ENABLED(CONFIG_ACPI_APEI_EINJ_CXL) */
|
||||
static inline int einj_cxl_available_error_type_show(struct seq_file *m,
|
||||
void *v)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int einj_cxl_inject_error(struct pci_dev *dport_dev, u64 type)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline bool einj_cxl_is_initialized(void) { return false; }
|
||||
#endif /* CONFIG_ACPI_APEI_EINJ_CXL */
|
||||
|
||||
#endif /* EINJ_CXL_H */
|
Loading…
Reference in New Issue
Block a user