mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
- A set of commits that introduces cxl_memdev_attach and pave way for soft reserved handling, type2 accelerator enabling, and LSA 2.0 enabling. All these series require the endpoint driver to settle before continuing the memdev driver probe. dax/hmem, e820, resource: Defer Soft Reserved insertion until hmem is ready cxl/mem: Introduce cxl_memdev_attach for CXL-dependent operation cxl/mem: Drop @host argument to devm_cxl_add_memdev() cxl/mem: Convert devm_cxl_add_memdev() to scope-based-cleanup cxl/port: Arrange for always synchronous endpoint attach cxl/mem: Arrange for always-synchronous memdev attach cxl/mem: Fix devm_cxl_memdev_edac_release() confusion - A set to address CXL port error protocol handling and reporting. The large patch series was split into 3 parts. Part 1 and 2 are included here with part 3 coming later. Part 1 consists of a series of code refactoring to PCI AER sub-system that addresses CXL and also CXL RAS code to prepare for port error handling. Part 2 refactors the CXL code to move management of component registers to cxl_port objects to allow all CXL AER errors to be handled through the cxl_port hierarchy. Part 2: cxl/port: Move endpoint component register management to cxl_port cxl/port: Map Port RAS registers cxl/port: Move dport RAS setup to dport add time cxl/port: Move dport probe operations to a driver event cxl/port: Move decoder setup before dport creation cxl/port: Cleanup dport removal with a devres group cxl/port: Reduce number of @dport variables in cxl_port_add_dport() cxl/port: Cleanup handling of the nr_dports 0 -> 1 transition Part 1: cxl: Update RAS handler interfaces to also support CXL Ports cxl/mem: Clarify @host for devm_cxl_add_nvdimm() PCI/AER: Update struct aer_err_info with kernel-doc formatting PCI/AER: Report CXL or PCIe bus type in AER trace logging PCI/AER: Use guard() in cxl_rch_handle_error_iter() PCI/AER: Move CXL RCH error handling to aer_cxl_rch.c PCI/AER: Update is_internal_error() to be non-static is_aer_internal_error() PCI/AER: Export pci_aer_unmask_internal_errors() cxl/pci: Move CXL driver's RCH error handling into core/ras_rch.c PCI/AER: Replace PCIEAER_CXL symbol with CXL_RAS cxl/pci: Remove CXL VH handling in CONFIG_PCIEAER_CXL conditional blocks from core/pci.c PCI: Replace cxl_error_is_native() with pcie_aer_is_native() cxl/pci: Remove unnecessary CXL RCH handling helper functions cxl/pci: Remove unnecessary CXL Endpoint handling helper functions PCI: Introduce pcie_is_cxl() PCI: Update CXL DVSEC definitions PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h - A set of patches to provide AMD Zen5 platform address translation for CXL using ACPI PRMT. Set includes a conventions document to explain why this is needed and how it's implemented. cxl: Disable HPA/SPA translation handlers for Normalized Addressing cxl/region: Factor out code into cxl_region_setup_poison() cxl/atl: Lock decoders that need address translation cxl: Enable AMD Zen5 address translation using ACPI PRMT cxl/acpi: Prepare use of EFI runtime services cxl: Introduce callback for HPA address ranges translation cxl/region: Use region data to get the root decoder cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos() cxl/region: Separate region parameter setup and region construction cxl: Simplify cxl_root_ops allocation and handling cxl/region: Store HPA range in struct cxl_region cxl/region: Store root decoder in struct cxl_region cxl/region: Rename misleading variable name @hpa to @hpa_range Documentation/driver-api/cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement cxl, doc: Moving conventions in separate files cxl, doc: Remove isonum.txt inclusion - A set of misc CXL patches of fixes, cleanups, and updates. Including CXL address translation for unaligned MOD3 regions. cxl: Fix premature commit_end increment on decoder commit failure cxl/region: Use do_div() for 64-bit modulo operation cxl/region: Translate HPA to DPA and memdev in unaligned regions cxl/region: Translate DPA->HPA in unaligned MOD3 regions cxl/core: Fix cxl_dport debugfs EINJ entries cxl/acpi: Remove cxl_acpi_set_cache_size() cxl/hdm: Fix newline character in dev_err() messages cxl/pci: Remove outdated FIXME comment and BUILD_BUG_ON Documentation/driver-api/cxl: device hotplug section Documentation/driver-api/cxl: BIOS/EFI expectation update -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE5DAy15EJMCV1R6v9YGjFFmlTOEoFAmmOFXcACgkQYGjFFmlT OEojaxAApQJFLyX1MkPbhtm6j6GRzzEAEWTBX2XsmliZf1JhfahsNMWI69kO33rm LddF+nyZNEl/foyHgUaxVzlQwqWuihyp7Qk2djXnMzLsuCAsWhPbB9j0RgJUN8h5 N4U76AmOdmhLlXH4CCqoW2jNy0OjxNdgp1FtTHv7VO7RxgRE9MFJRkLulKxB03wy t6lRZXPofEFcHen40DlYRtW26vy1BYUO0dng2f16DxWrb1ztdACH/zVqCJJtdoFc FAT5EaQCeRYZ9Yz4dONw3DcUjYlG6NcRN9FWNiptBn1Pb7pUX55Le8lfD3qZg0an m3lWRs1T/lGz7pWmz4GPUKDwGFCEqLqd4oSz5v+dFR3JJxjJpRzKa19y5TfqK/LF diqNZsDD9gCXE1HXzNr1YcbllpU2cPRPf58gWG9bLmG5xUUmScib8LoTMfgcCJW5 SlC6kf7BFLkJfDTcFaILc/UANeZaLGhrV0vyJntfGyT5EqKOcfjQEvrZvofA8mef bdxt0IRDW4D+7kkcuR33OipTVUFG3ban8yYq4zXD64dmeHF76gwdJm3nyXsqdtpc IYIIhz0W6pbTKjJ2fy1rZcTac1ZaALstyaF4bYWIjyF3NylPM8tDi48DFr+DGgeX xkFs2B9p5vY5Cq73gCmSWsi3PBPTjWzeRp7YZrV6VoBd9uqewUs= =blFQ -----END PGP SIGNATURE----- Merge tag 'cxl-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl Pull CXL updates from Dave Jiang: - Introduce cxl_memdev_attach and pave way for soft reserved handling, type2 accelerator enabling, and LSA 2.0 enabling. All these series require the endpoint driver to settle before continuing the memdev driver probe. - Address CXL port error protocol handling and reporting. The large patch series was split into three parts. The first two parts are included here with the final part coming later. The first part consists of a series of code refactoring to PCI AER sub-system that addresses CXL and also CXL RAS code to prepare for port error handling. The second part refactors the CXL code to move management of component registers to cxl_port objects to allow all CXL AER errors to be handled through the cxl_port hierarchy. - Provide AMD Zen5 platform address translation for CXL using ACPI PRMT. This includes a conventions document to explain why this is needed and how it's implemented. - Misc CXL patches of fixes, cleanups, and updates. Including CXL address translation for unaligned MOD3 regions. [ TLA service: CXL is "Compute Express Link" ] * tag 'cxl-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (59 commits) cxl: Disable HPA/SPA translation handlers for Normalized Addressing cxl/region: Factor out code into cxl_region_setup_poison() cxl/atl: Lock decoders that need address translation cxl: Enable AMD Zen5 address translation using ACPI PRMT cxl/acpi: Prepare use of EFI runtime services cxl: Introduce callback for HPA address ranges translation cxl/region: Use region data to get the root decoder cxl/region: Add @hpa_range argument to function cxl_calc_interleave_pos() cxl/region: Separate region parameter setup and region construction cxl: Simplify cxl_root_ops allocation and handling cxl/region: Store HPA range in struct cxl_region cxl/region: Store root decoder in struct cxl_region cxl/region: Rename misleading variable name @hpa to @hpa_range Documentation/driver-api/cxl: ACPI PRM Address Translation Support and AMD Zen5 enablement cxl, doc: Moving conventions in separate files cxl, doc: Remove isonum.txt inclusion cxl/port: Unify endpoint and switch port lookup cxl/port: Move endpoint component register management to cxl_port cxl/port: Map Port RAS registers cxl/port: Move dport RAS setup to dport add time ...
326 lines
8.3 KiB
C
326 lines
8.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright(c) 2025 AMD Corporation. All rights reserved. */
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/aer.h>
|
|
#include <cxl/event.h>
|
|
#include <cxlmem.h>
|
|
#include <cxlpci.h>
|
|
#include "trace.h"
|
|
|
|
static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev,
|
|
struct cxl_ras_capability_regs ras_cap)
|
|
{
|
|
u32 status = ras_cap.cor_status & ~ras_cap.cor_mask;
|
|
|
|
trace_cxl_port_aer_correctable_error(&pdev->dev, status);
|
|
}
|
|
|
|
static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev,
|
|
struct cxl_ras_capability_regs ras_cap)
|
|
{
|
|
u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask;
|
|
u32 fe;
|
|
|
|
if (hweight32(status) > 1)
|
|
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
|
|
ras_cap.cap_control));
|
|
else
|
|
fe = status;
|
|
|
|
trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe,
|
|
ras_cap.header_log);
|
|
}
|
|
|
|
static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd,
|
|
struct cxl_ras_capability_regs ras_cap)
|
|
{
|
|
u32 status = ras_cap.cor_status & ~ras_cap.cor_mask;
|
|
|
|
trace_cxl_aer_correctable_error(cxlmd, status);
|
|
}
|
|
|
|
static void
|
|
cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd,
|
|
struct cxl_ras_capability_regs ras_cap)
|
|
{
|
|
u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask;
|
|
u32 fe;
|
|
|
|
if (hweight32(status) > 1)
|
|
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
|
|
ras_cap.cap_control));
|
|
else
|
|
fe = status;
|
|
|
|
trace_cxl_aer_uncorrectable_error(cxlmd, status, fe,
|
|
ras_cap.header_log);
|
|
}
|
|
|
|
static int match_memdev_by_parent(struct device *dev, const void *uport)
|
|
{
|
|
if (is_cxl_memdev(dev) && dev->parent == uport)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data)
|
|
{
|
|
unsigned int devfn = PCI_DEVFN(data->prot_err.agent_addr.device,
|
|
data->prot_err.agent_addr.function);
|
|
struct pci_dev *pdev __free(pci_dev_put) =
|
|
pci_get_domain_bus_and_slot(data->prot_err.agent_addr.segment,
|
|
data->prot_err.agent_addr.bus,
|
|
devfn);
|
|
struct cxl_memdev *cxlmd;
|
|
int port_type;
|
|
|
|
if (!pdev)
|
|
return;
|
|
|
|
port_type = pci_pcie_type(pdev);
|
|
if (port_type == PCI_EXP_TYPE_ROOT_PORT ||
|
|
port_type == PCI_EXP_TYPE_DOWNSTREAM ||
|
|
port_type == PCI_EXP_TYPE_UPSTREAM) {
|
|
if (data->severity == AER_CORRECTABLE)
|
|
cxl_cper_trace_corr_port_prot_err(pdev, data->ras_cap);
|
|
else
|
|
cxl_cper_trace_uncorr_port_prot_err(pdev, data->ras_cap);
|
|
|
|
return;
|
|
}
|
|
|
|
guard(device)(&pdev->dev);
|
|
if (!pdev->dev.driver)
|
|
return;
|
|
|
|
struct device *mem_dev __free(put_device) = bus_find_device(
|
|
&cxl_bus_type, NULL, pdev, match_memdev_by_parent);
|
|
if (!mem_dev)
|
|
return;
|
|
|
|
cxlmd = to_cxl_memdev(mem_dev);
|
|
if (data->severity == AER_CORRECTABLE)
|
|
cxl_cper_trace_corr_prot_err(cxlmd, data->ras_cap);
|
|
else
|
|
cxl_cper_trace_uncorr_prot_err(cxlmd, data->ras_cap);
|
|
}
|
|
EXPORT_SYMBOL_GPL(cxl_cper_handle_prot_err);
|
|
|
|
static void cxl_cper_prot_err_work_fn(struct work_struct *work)
|
|
{
|
|
struct cxl_cper_prot_err_work_data wd;
|
|
|
|
while (cxl_cper_prot_err_kfifo_get(&wd))
|
|
cxl_cper_handle_prot_err(&wd);
|
|
}
|
|
static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
|
|
|
|
int cxl_ras_init(void)
|
|
{
|
|
return cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
|
|
}
|
|
|
|
void cxl_ras_exit(void)
|
|
{
|
|
cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work);
|
|
cancel_work_sync(&cxl_cper_prot_err_work);
|
|
}
|
|
|
|
static void cxl_dport_map_ras(struct cxl_dport *dport)
|
|
{
|
|
struct cxl_register_map *map = &dport->reg_map;
|
|
struct device *dev = dport->dport_dev;
|
|
|
|
if (!map->component_map.ras.valid)
|
|
dev_dbg(dev, "RAS registers not found\n");
|
|
else if (cxl_map_component_regs(map, &dport->regs.component,
|
|
BIT(CXL_CM_CAP_CAP_ID_RAS)))
|
|
dev_dbg(dev, "Failed to map RAS capability.\n");
|
|
}
|
|
|
|
/**
|
|
* devm_cxl_dport_ras_setup - Setup CXL RAS report on this dport
|
|
* @dport: the cxl_dport that needs to be initialized
|
|
*/
|
|
void devm_cxl_dport_ras_setup(struct cxl_dport *dport)
|
|
{
|
|
dport->reg_map.host = dport_to_host(dport);
|
|
cxl_dport_map_ras(dport);
|
|
}
|
|
|
|
void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport)
|
|
{
|
|
struct pci_host_bridge *host_bridge;
|
|
|
|
if (!dev_is_pci(dport->dport_dev))
|
|
return;
|
|
|
|
devm_cxl_dport_ras_setup(dport);
|
|
|
|
host_bridge = to_pci_host_bridge(dport->dport_dev);
|
|
if (!host_bridge->native_aer)
|
|
return;
|
|
|
|
cxl_dport_map_rch_aer(dport);
|
|
cxl_disable_rch_root_ints(dport);
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_dport_rch_ras_setup, "CXL");
|
|
|
|
void devm_cxl_port_ras_setup(struct cxl_port *port)
|
|
{
|
|
struct cxl_register_map *map = &port->reg_map;
|
|
|
|
if (!map->component_map.ras.valid) {
|
|
dev_dbg(&port->dev, "RAS registers not found\n");
|
|
return;
|
|
}
|
|
|
|
map->host = &port->dev;
|
|
if (cxl_map_component_regs(map, &port->regs,
|
|
BIT(CXL_CM_CAP_CAP_ID_RAS)))
|
|
dev_dbg(&port->dev, "Failed to map RAS capability\n");
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_port_ras_setup, "CXL");
|
|
|
|
void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base)
|
|
{
|
|
void __iomem *addr;
|
|
u32 status;
|
|
|
|
if (!ras_base)
|
|
return;
|
|
|
|
addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
|
|
status = readl(addr);
|
|
if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) {
|
|
writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
|
|
trace_cxl_aer_correctable_error(to_cxl_memdev(dev), status);
|
|
}
|
|
}
|
|
|
|
/* CXL spec rev3.0 8.2.4.16.1 */
|
|
static void header_log_copy(void __iomem *ras_base, u32 *log)
|
|
{
|
|
void __iomem *addr;
|
|
u32 *log_addr;
|
|
int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32);
|
|
|
|
addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET;
|
|
log_addr = log;
|
|
|
|
for (i = 0; i < log_u32_size; i++) {
|
|
*log_addr = readl(addr);
|
|
log_addr++;
|
|
addr += sizeof(u32);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Log the state of the RAS status registers and prepare them to log the
|
|
* next error status. Return 1 if reset needed.
|
|
*/
|
|
bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
|
|
{
|
|
u32 hl[CXL_HEADERLOG_SIZE_U32];
|
|
void __iomem *addr;
|
|
u32 status;
|
|
u32 fe;
|
|
|
|
if (!ras_base)
|
|
return false;
|
|
|
|
addr = ras_base + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET;
|
|
status = readl(addr);
|
|
if (!(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK))
|
|
return false;
|
|
|
|
/* If multiple errors, log header points to first error from ctrl reg */
|
|
if (hweight32(status) > 1) {
|
|
void __iomem *rcc_addr =
|
|
ras_base + CXL_RAS_CAP_CONTROL_OFFSET;
|
|
|
|
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
|
|
readl(rcc_addr)));
|
|
} else {
|
|
fe = status;
|
|
}
|
|
|
|
header_log_copy(ras_base, hl);
|
|
trace_cxl_aer_uncorrectable_error(to_cxl_memdev(dev), status, fe, hl);
|
|
writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cxl_cor_error_detected(struct pci_dev *pdev)
|
|
{
|
|
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
|
|
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
|
struct device *dev = &cxlds->cxlmd->dev;
|
|
|
|
scoped_guard(device, dev) {
|
|
if (!dev->driver) {
|
|
dev_warn(&pdev->dev,
|
|
"%s: memdev disabled, abort error handling\n",
|
|
dev_name(dev));
|
|
return;
|
|
}
|
|
|
|
if (cxlds->rcd)
|
|
cxl_handle_rdport_errors(cxlds);
|
|
|
|
cxl_handle_cor_ras(&cxlds->cxlmd->dev, cxlmd->endpoint->regs.ras);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL");
|
|
|
|
pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
|
|
pci_channel_state_t state)
|
|
{
|
|
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
|
|
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
|
struct device *dev = &cxlmd->dev;
|
|
bool ue;
|
|
|
|
scoped_guard(device, dev) {
|
|
if (!dev->driver) {
|
|
dev_warn(&pdev->dev,
|
|
"%s: memdev disabled, abort error handling\n",
|
|
dev_name(dev));
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
if (cxlds->rcd)
|
|
cxl_handle_rdport_errors(cxlds);
|
|
/*
|
|
* A frozen channel indicates an impending reset which is fatal to
|
|
* CXL.mem operation, and will likely crash the system. On the off
|
|
* chance the situation is recoverable dump the status of the RAS
|
|
* capability registers and bounce the active state of the memdev.
|
|
*/
|
|
ue = cxl_handle_ras(&cxlds->cxlmd->dev, cxlmd->endpoint->regs.ras);
|
|
}
|
|
|
|
switch (state) {
|
|
case pci_channel_io_normal:
|
|
if (ue) {
|
|
device_release_driver(dev);
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
}
|
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
|
case pci_channel_io_frozen:
|
|
dev_warn(&pdev->dev,
|
|
"%s: frozen state error detected, disable CXL.mem\n",
|
|
dev_name(dev));
|
|
device_release_driver(dev);
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
case pci_channel_io_perm_failure:
|
|
dev_warn(&pdev->dev,
|
|
"failure state error detected, request disconnect\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
}
|
|
EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL");
|