mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
Merge branch 'for-6.3/cxl' into cxl/next
Merge the general CXL updates with fixes targeting v6.2-rc for v6.3. Resolve a conflict with the fix and move of cxl_report_and_clear() from pci.c to core/pci.c.
This commit is contained in:
commit
5485eb9559
@ -90,6 +90,21 @@ Description:
|
||||
capability.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/{port,endpoint}X/parent_dport
|
||||
Date: January, 2023
|
||||
KernelVersion: v6.3
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RO) CXL port objects are instantiated for each upstream port in
|
||||
a CXL/PCIe switch, and for each endpoint to map the
|
||||
corresponding memory device into the CXL port hierarchy. When a
|
||||
descendant CXL port (switch or endpoint) is enumerated it is
|
||||
useful to know which 'dport' object in the parent CXL port
|
||||
routes to this descendant. The 'parent_dport' symlink points to
|
||||
the device representing the downstream port of a CXL switch that
|
||||
routes to {port,endpoint}X.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/portX/dportY
|
||||
Date: June, 2021
|
||||
KernelVersion: v5.14
|
||||
|
@ -1047,6 +1047,9 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
|
||||
host_bridge->native_dpc = 0;
|
||||
|
||||
if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
|
||||
host_bridge->native_cxl_error = 0;
|
||||
|
||||
/*
|
||||
* Evaluate the "PCI Boot Configuration" _DSM Function. If it
|
||||
* exists and returns 0, we must preserve any PCI resource
|
||||
|
@ -116,7 +116,7 @@ config CXL_REGION_INVALIDATION_TEST
|
||||
depends on CXL_REGION
|
||||
help
|
||||
CXL Region management and security operations potentially invalidate
|
||||
the content of CPU caches without notifiying those caches to
|
||||
the content of CPU caches without notifying those caches to
|
||||
invalidate the affected cachelines. The CXL Region driver attempts
|
||||
to invalidate caches when those events occur. If that invalidation
|
||||
fails the region will fail to enable. Reasons for cache
|
||||
|
@ -19,7 +19,7 @@ struct cxl_cxims_data {
|
||||
|
||||
/*
|
||||
* Find a targets entry (n) in the host bridge interleave list.
|
||||
* CXL Specfication 3.0 Table 9-22
|
||||
* CXL Specification 3.0 Table 9-22
|
||||
*/
|
||||
static int cxl_xor_calc_n(u64 hpa, struct cxl_cxims_data *cximsd, int iw,
|
||||
int ig)
|
||||
|
@ -3,6 +3,8 @@ obj-$(CONFIG_CXL_BUS) += cxl_core.o
|
||||
obj-$(CONFIG_CXL_SUSPEND) += suspend.o
|
||||
|
||||
ccflags-y += -I$(srctree)/drivers/cxl
|
||||
CFLAGS_trace.o = -DTRACE_INCLUDE_PATH=. -I$(src)
|
||||
|
||||
cxl_core-y := port.o
|
||||
cxl_core-y += pmem.o
|
||||
cxl_core-y += regs.o
|
||||
@ -10,4 +12,5 @@ cxl_core-y += memdev.o
|
||||
cxl_core-y += mbox.o
|
||||
cxl_core-y += pci.o
|
||||
cxl_core-y += hdm.o
|
||||
cxl_core-$(CONFIG_TRACING) += trace.o
|
||||
cxl_core-$(CONFIG_CXL_REGION) += region.o
|
||||
|
@ -170,6 +170,12 @@ int cxl_internal_send_cmd(struct cxl_dev_state *cxlds,
|
||||
out_size = mbox_cmd->size_out;
|
||||
min_out = mbox_cmd->min_out;
|
||||
rc = cxlds->mbox_send(cxlds, mbox_cmd);
|
||||
/*
|
||||
* EIO is reserved for a payload size mismatch and mbox_send()
|
||||
* may not return this error.
|
||||
*/
|
||||
if (WARN_ONCE(rc == -EIO, "Bad return code: -EIO"))
|
||||
return -ENXIO;
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -550,9 +556,9 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 size, u8 *out)
|
||||
static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 *size, u8 *out)
|
||||
{
|
||||
u32 remaining = size;
|
||||
u32 remaining = *size;
|
||||
u32 offset = 0;
|
||||
|
||||
while (remaining) {
|
||||
@ -576,6 +582,17 @@ static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 size, u8
|
||||
};
|
||||
|
||||
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||||
|
||||
/*
|
||||
* The output payload length that indicates the number
|
||||
* of valid bytes can be smaller than the Log buffer
|
||||
* size.
|
||||
*/
|
||||
if (rc == -EIO && mbox_cmd.size_out < xfer_size) {
|
||||
offset += mbox_cmd.size_out;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@ -584,6 +601,8 @@ static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 size, u8
|
||||
offset += xfer_size;
|
||||
}
|
||||
|
||||
*size = offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -610,11 +629,12 @@ static void cxl_walk_cel(struct cxl_dev_state *cxlds, size_t size, u8 *cel)
|
||||
|
||||
if (!cmd) {
|
||||
dev_dbg(cxlds->dev,
|
||||
"Opcode 0x%04x unsupported by driver", opcode);
|
||||
"Opcode 0x%04x unsupported by driver\n", opcode);
|
||||
continue;
|
||||
}
|
||||
|
||||
set_bit(cmd->info.id, cxlds->enabled_cmds);
|
||||
dev_dbg(cxlds->dev, "Opcode 0x%04x enabled\n", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -694,7 +714,7 @@ int cxl_enumerate_cmds(struct cxl_dev_state *cxlds)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cxl_xfer_log(cxlds, &uuid, size, log);
|
||||
rc = cxl_xfer_log(cxlds, &uuid, &size, log);
|
||||
if (rc) {
|
||||
kvfree(log);
|
||||
goto out;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <cxlmem.h>
|
||||
#include <cxl.h>
|
||||
#include "core.h"
|
||||
#include "trace.h"
|
||||
|
||||
/**
|
||||
* DOC: cxl core pci
|
||||
@ -622,3 +623,117 @@ void read_cdat_data(struct cxl_port *port)
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
|
||||
|
||||
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 = &cxlmd->dev;
|
||||
void __iomem *addr;
|
||||
u32 status;
|
||||
|
||||
if (!cxlds->regs.ras)
|
||||
return;
|
||||
|
||||
addr = cxlds->regs.ras + 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(dev, status);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
|
||||
|
||||
/* CXL spec rev3.0 8.2.4.16.1 */
|
||||
static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
|
||||
{
|
||||
void __iomem *addr;
|
||||
u32 *log_addr;
|
||||
int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32);
|
||||
|
||||
addr = cxlds->regs.ras + 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.
|
||||
*/
|
||||
static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
||||
struct device *dev = &cxlmd->dev;
|
||||
u32 hl[CXL_HEADERLOG_SIZE_U32];
|
||||
void __iomem *addr;
|
||||
u32 status;
|
||||
u32 fe;
|
||||
|
||||
if (!cxlds->regs.ras)
|
||||
return false;
|
||||
|
||||
addr = cxlds->regs.ras + 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 =
|
||||
cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
|
||||
|
||||
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
|
||||
readl(rcc_addr)));
|
||||
} else {
|
||||
fe = status;
|
||||
}
|
||||
|
||||
header_log_copy(cxlds, hl);
|
||||
trace_cxl_aer_uncorrectable_error(dev, status, fe, hl);
|
||||
writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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_report_and_clear(cxlds);
|
||||
|
||||
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);
|
||||
|
@ -583,6 +583,29 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
|
||||
return devm_add_action_or_reset(host, cxl_unlink_uport, port);
|
||||
}
|
||||
|
||||
static void cxl_unlink_parent_dport(void *_port)
|
||||
{
|
||||
struct cxl_port *port = _port;
|
||||
|
||||
sysfs_remove_link(&port->dev.kobj, "parent_dport");
|
||||
}
|
||||
|
||||
static int devm_cxl_link_parent_dport(struct device *host,
|
||||
struct cxl_port *port,
|
||||
struct cxl_dport *parent_dport)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!parent_dport)
|
||||
return 0;
|
||||
|
||||
rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport->kobj,
|
||||
"parent_dport");
|
||||
if (rc)
|
||||
return rc;
|
||||
return devm_add_action_or_reset(host, cxl_unlink_parent_dport, port);
|
||||
}
|
||||
|
||||
static struct lock_class_key cxl_port_key;
|
||||
|
||||
static struct cxl_port *cxl_port_alloc(struct device *uport,
|
||||
@ -692,6 +715,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
rc = devm_cxl_link_parent_dport(host, port, parent_dport);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
return port;
|
||||
|
||||
err:
|
||||
@ -1137,7 +1164,7 @@ static struct cxl_port *find_cxl_port_at(struct cxl_port *parent_port,
|
||||
}
|
||||
|
||||
/*
|
||||
* All users of grandparent() are using it to walk PCIe-like swich port
|
||||
* All users of grandparent() are using it to walk PCIe-like switch port
|
||||
* hierarchy. A PCIe switch is comprised of a bridge device representing the
|
||||
* upstream switch port and N bridges representing downstream switch ports. When
|
||||
* bridges stack the grand-parent of a downstream switch port is another
|
||||
@ -1164,6 +1191,7 @@ static void delete_endpoint(void *data)
|
||||
|
||||
device_lock(parent);
|
||||
if (parent->driver && !endpoint->dead) {
|
||||
devm_release_action(parent, cxl_unlink_parent_dport, endpoint);
|
||||
devm_release_action(parent, cxl_unlink_uport, endpoint);
|
||||
devm_release_action(parent, unregister_port, endpoint);
|
||||
}
|
||||
@ -1194,6 +1222,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_endpoint_autoremove, CXL);
|
||||
*/
|
||||
static void delete_switch_port(struct cxl_port *port)
|
||||
{
|
||||
devm_release_action(port->dev.parent, cxl_unlink_parent_dport, port);
|
||||
devm_release_action(port->dev.parent, cxl_unlink_uport, port);
|
||||
devm_release_action(port->dev.parent, unregister_port, port);
|
||||
}
|
||||
|
@ -157,6 +157,22 @@ static int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int commit_decoder(struct cxl_decoder *cxld)
|
||||
{
|
||||
struct cxl_switch_decoder *cxlsd = NULL;
|
||||
|
||||
if (cxld->commit)
|
||||
return cxld->commit(cxld);
|
||||
|
||||
if (is_switch_decoder(&cxld->dev))
|
||||
cxlsd = to_cxl_switch_decoder(&cxld->dev);
|
||||
|
||||
if (dev_WARN_ONCE(&cxld->dev, !cxlsd || cxlsd->nr_targets > 1,
|
||||
"->commit() is required\n"))
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_region_decode_commit(struct cxl_region *cxlr)
|
||||
{
|
||||
struct cxl_region_params *p = &cxlr->params;
|
||||
@ -175,8 +191,7 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
|
||||
iter = to_cxl_port(iter->dev.parent)) {
|
||||
cxl_rr = cxl_rr_load(iter, cxlr);
|
||||
cxld = cxl_rr->decoder;
|
||||
if (cxld->commit)
|
||||
rc = cxld->commit(cxld);
|
||||
rc = commit_decoder(cxld);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
@ -401,7 +416,7 @@ static ssize_t interleave_granularity_store(struct device *dev,
|
||||
* When the host-bridge is interleaved, disallow region granularity !=
|
||||
* root granularity. Regions with a granularity less than the root
|
||||
* interleave result in needing multiple endpoints to support a single
|
||||
* slot in the interleave (possible to suport in the future). Regions
|
||||
* slot in the interleave (possible to support in the future). Regions
|
||||
* with a granularity greater than the root interleave result in invalid
|
||||
* DPA translations (invalid to support).
|
||||
*/
|
||||
@ -1969,7 +1984,7 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
|
||||
|
||||
if (!cpu_cache_has_invalidate_memregion()) {
|
||||
if (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) {
|
||||
dev_warn(
|
||||
dev_warn_once(
|
||||
&cxlr->dev,
|
||||
"Bypassing cpu_cache_invalidate_memregion() for testing!\n");
|
||||
clear_bit(CXL_REGION_F_INCOHERENT, &cxlr->flags);
|
||||
|
5
drivers/cxl/core/trace.c
Normal file
5
drivers/cxl/core/trace.c
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
@ -1,15 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM cxl
|
||||
|
||||
#if !defined(_CXL_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _CXL_EVENTS_H
|
||||
|
||||
#include <cxl.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#define CXL_HEADERLOG_SIZE SZ_512
|
||||
#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32)
|
||||
|
||||
#define CXL_RAS_UC_CACHE_DATA_PARITY BIT(0)
|
||||
#define CXL_RAS_UC_CACHE_ADDR_PARITY BIT(1)
|
||||
#define CXL_RAS_UC_CACHE_BE_PARITY BIT(2)
|
||||
@ -106,7 +105,5 @@ TRACE_EVENT(cxl_aer_correctable_error,
|
||||
|
||||
#endif /* _CXL_EVENTS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE cxl
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
#include <trace/define_trace.h>
|
@ -140,6 +140,8 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
|
||||
#define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0)
|
||||
#define CXL_RAS_HEADER_LOG_OFFSET 0x18
|
||||
#define CXL_RAS_CAPABILITY_LENGTH 0x58
|
||||
#define CXL_HEADERLOG_SIZE SZ_512
|
||||
#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32)
|
||||
|
||||
/* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
|
||||
#define CXLDEV_CAP_ARRAY_OFFSET 0x0
|
||||
|
@ -66,4 +66,7 @@ int devm_cxl_port_enumerate_dports(struct cxl_port *port);
|
||||
struct cxl_dev_state;
|
||||
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm);
|
||||
void read_cdat_data(struct cxl_port *port);
|
||||
void cxl_cor_error_detected(struct pci_dev *pdev);
|
||||
pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
|
||||
pci_channel_state_t state);
|
||||
#endif /* __CXL_PCI_H__ */
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include "cxlmem.h"
|
||||
#include "cxlpci.h"
|
||||
#include "cxl.h"
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/cxl.h>
|
||||
|
||||
/**
|
||||
* DOC: cxl pci
|
||||
@ -162,7 +160,7 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
|
||||
writeq(cmd_reg, cxlds->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
|
||||
|
||||
/* #4 */
|
||||
dev_dbg(dev, "Sending command\n");
|
||||
dev_dbg(dev, "Sending command: 0x%04x\n", mbox_cmd->opcode);
|
||||
writel(CXLDEV_MBOX_CTRL_DOORBELL,
|
||||
cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
|
||||
|
||||
@ -514,99 +512,6 @@ static const struct pci_device_id cxl_mem_pci_tbl[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);
|
||||
|
||||
/* CXL spec rev3.0 8.2.4.16.1 */
|
||||
static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
|
||||
{
|
||||
void __iomem *addr;
|
||||
u32 *log_addr;
|
||||
int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32);
|
||||
|
||||
addr = cxlds->regs.ras + 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.
|
||||
*/
|
||||
static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
||||
struct device *dev = &cxlmd->dev;
|
||||
u32 hl[CXL_HEADERLOG_SIZE_U32];
|
||||
void __iomem *addr;
|
||||
u32 status;
|
||||
u32 fe;
|
||||
|
||||
if (!cxlds->regs.ras)
|
||||
return false;
|
||||
|
||||
addr = cxlds->regs.ras + 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 =
|
||||
cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
|
||||
|
||||
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
|
||||
readl(rcc_addr)));
|
||||
} else {
|
||||
fe = status;
|
||||
}
|
||||
|
||||
header_log_copy(cxlds, hl);
|
||||
trace_cxl_aer_uncorrectable_error(dev, status, fe, hl);
|
||||
writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static 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;
|
||||
|
||||
/*
|
||||
* 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_report_and_clear(cxlds);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static pci_ers_result_t cxl_slot_reset(struct pci_dev *pdev)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
|
||||
@ -631,25 +536,6 @@ static void cxl_error_resume(struct pci_dev *pdev)
|
||||
dev->driver ? "successful" : "failed");
|
||||
}
|
||||
|
||||
static 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 = &cxlmd->dev;
|
||||
void __iomem *addr;
|
||||
u32 status;
|
||||
|
||||
if (!cxlds->regs.ras)
|
||||
return;
|
||||
|
||||
addr = cxlds->regs.ras + 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(dev, status);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pci_error_handlers cxl_error_handlers = {
|
||||
.error_detected = cxl_error_detected,
|
||||
.slot_reset = cxl_slot_reset,
|
||||
|
@ -596,6 +596,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
|
||||
bridge->native_ltr = 1;
|
||||
bridge->native_dpc = 1;
|
||||
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
|
||||
bridge->native_cxl_error = 1;
|
||||
|
||||
device_initialize(&bridge->dev);
|
||||
}
|
||||
|
@ -578,6 +578,7 @@ struct pci_host_bridge {
|
||||
unsigned int native_pme:1; /* OS may use PCIe PME */
|
||||
unsigned int native_ltr:1; /* OS may use PCIe LTR */
|
||||
unsigned int native_dpc:1; /* OS may use PCIe DPC */
|
||||
unsigned int native_cxl_error:1; /* OS may use CXL RAS/Events */
|
||||
unsigned int preserve_config:1; /* Preserve FW resource setup */
|
||||
unsigned int size_windows:1; /* Enable root bus sizing */
|
||||
unsigned int msi_domain:1; /* Bridge wants MSI domain */
|
||||
|
@ -17,28 +17,34 @@ CXL_SRC := $(DRIVERS)/cxl
|
||||
CXL_CORE_SRC := $(DRIVERS)/cxl/core
|
||||
ccflags-y := -I$(srctree)/drivers/cxl/
|
||||
ccflags-y += -D__mock=__weak
|
||||
ccflags-y += -DTRACE_INCLUDE_PATH=$(CXL_CORE_SRC) -I$(srctree)/drivers/cxl/core/
|
||||
|
||||
obj-m += cxl_acpi.o
|
||||
|
||||
cxl_acpi-y := $(CXL_SRC)/acpi.o
|
||||
cxl_acpi-y += mock_acpi.o
|
||||
cxl_acpi-y += config_check.o
|
||||
cxl_acpi-y += cxl_acpi_test.o
|
||||
|
||||
obj-m += cxl_pmem.o
|
||||
|
||||
cxl_pmem-y := $(CXL_SRC)/pmem.o
|
||||
cxl_pmem-y += $(CXL_SRC)/security.o
|
||||
cxl_pmem-y += config_check.o
|
||||
cxl_pmem-y += cxl_pmem_test.o
|
||||
|
||||
obj-m += cxl_port.o
|
||||
|
||||
cxl_port-y := $(CXL_SRC)/port.o
|
||||
cxl_port-y += config_check.o
|
||||
cxl_port-y += cxl_port_test.o
|
||||
|
||||
|
||||
obj-m += cxl_mem.o
|
||||
|
||||
cxl_mem-y := $(CXL_SRC)/mem.o
|
||||
cxl_mem-y += config_check.o
|
||||
cxl_mem-y += cxl_mem_test.o
|
||||
|
||||
obj-m += cxl_core.o
|
||||
|
||||
@ -49,7 +55,9 @@ cxl_core-y += $(CXL_CORE_SRC)/memdev.o
|
||||
cxl_core-y += $(CXL_CORE_SRC)/mbox.o
|
||||
cxl_core-y += $(CXL_CORE_SRC)/pci.o
|
||||
cxl_core-y += $(CXL_CORE_SRC)/hdm.o
|
||||
cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
|
||||
cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
|
||||
cxl_core-y += config_check.o
|
||||
cxl_core-y += cxl_core_test.o
|
||||
|
||||
obj-m += test/
|
||||
|
@ -7,6 +7,7 @@ void check(void)
|
||||
* These kconfig symbols must be set to "m" for cxl_test to load
|
||||
* and operate.
|
||||
*/
|
||||
BUILD_BUG_ON(!IS_ENABLED(CONFIG_64BIT));
|
||||
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_BUS));
|
||||
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_ACPI));
|
||||
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM));
|
||||
|
6
tools/testing/cxl/cxl_acpi_test.c
Normal file
6
tools/testing/cxl/cxl_acpi_test.c
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#include "watermark.h"
|
||||
|
||||
cxl_test_watermark(cxl_acpi);
|
6
tools/testing/cxl/cxl_core_test.c
Normal file
6
tools/testing/cxl/cxl_core_test.c
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#include "watermark.h"
|
||||
|
||||
cxl_test_watermark(cxl_core);
|
6
tools/testing/cxl/cxl_mem_test.c
Normal file
6
tools/testing/cxl/cxl_mem_test.c
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#include "watermark.h"
|
||||
|
||||
cxl_test_watermark(cxl_mem);
|
6
tools/testing/cxl/cxl_pmem_test.c
Normal file
6
tools/testing/cxl/cxl_pmem_test.c
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#include "watermark.h"
|
||||
|
||||
cxl_test_watermark(cxl_pmem);
|
6
tools/testing/cxl/cxl_port_test.c
Normal file
6
tools/testing/cxl/cxl_port_test.c
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
|
||||
#include "watermark.h"
|
||||
|
||||
cxl_test_watermark(cxl_port);
|
@ -9,6 +9,8 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mm.h>
|
||||
#include <cxlmem.h>
|
||||
|
||||
#include "../watermark.h"
|
||||
#include "mock.h"
|
||||
|
||||
static int interleave_arithmetic;
|
||||
@ -1119,6 +1121,12 @@ static __init int cxl_test_init(void)
|
||||
{
|
||||
int rc, i;
|
||||
|
||||
cxl_acpi_test();
|
||||
cxl_core_test();
|
||||
cxl_mem_test();
|
||||
cxl_pmem_test();
|
||||
cxl_port_test();
|
||||
|
||||
register_cxl_mock_ops(&cxl_mock_ops);
|
||||
|
||||
cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
|
||||
@ -1135,11 +1143,9 @@ static __init int cxl_test_init(void)
|
||||
if (interleave_arithmetic == 1) {
|
||||
cfmws_start = CFMWS_XOR_ARRAY_START;
|
||||
cfmws_end = CFMWS_XOR_ARRAY_END;
|
||||
dev_dbg(NULL, "cxl_test loading xor math option\n");
|
||||
} else {
|
||||
cfmws_start = CFMWS_MOD_ARRAY_START;
|
||||
cfmws_end = CFMWS_MOD_ARRAY_END;
|
||||
dev_dbg(NULL, "cxl_test loading modulo math option\n");
|
||||
}
|
||||
|
||||
rc = populate_cedt();
|
||||
@ -1326,7 +1332,7 @@ static __exit void cxl_test_exit(void)
|
||||
unregister_cxl_mock_ops(&cxl_mock_ops);
|
||||
}
|
||||
|
||||
module_param(interleave_arithmetic, int, 0000);
|
||||
module_param(interleave_arithmetic, int, 0444);
|
||||
MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1");
|
||||
module_init(cxl_test_init);
|
||||
module_exit(cxl_test_exit);
|
||||
|
25
tools/testing/cxl/watermark.h
Normal file
25
tools/testing/cxl/watermark.h
Normal file
@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||||
#ifndef _TEST_CXL_WATERMARK_H_
|
||||
#define _TEST_CXL_WATERMARK_H_
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
int cxl_acpi_test(void);
|
||||
int cxl_core_test(void);
|
||||
int cxl_mem_test(void);
|
||||
int cxl_pmem_test(void);
|
||||
int cxl_port_test(void);
|
||||
|
||||
/*
|
||||
* dummy routine for cxl_test to validate it is linking to the properly
|
||||
* mocked module and not the standard one from the base tree.
|
||||
*/
|
||||
#define cxl_test_watermark(x) \
|
||||
int x##_test(void) \
|
||||
{ \
|
||||
pr_debug("%s for cxl_test\n", KBUILD_MODNAME); \
|
||||
return 0; \
|
||||
} \
|
||||
EXPORT_SYMBOL(x##_test)
|
||||
#endif /* _TEST_CXL_WATERMARK_H_ */
|
Loading…
Reference in New Issue
Block a user