* Re: [PATCH v3] docs: dt: maintainer: Add Devicetree and OF maintainer profile document
From: Jonathan Corbet @ 2026-07-20 20:20 UTC (permalink / raw)
To: Krzysztof Kozlowski, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shuah Khan, devicetree, workflows, linux-doc,
linux-kernel
Cc: Saravana Kannan
In-Reply-To: <3e797ffc-8cf6-437c-81f2-9f82d946d042@oss.qualcomm.com>
Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> writes:
> On 20/07/2026 21:19, Jonathan Corbet wrote:
>>> I expect patch to be picked up by Rob, after review.
>>>
>>> Changes in v3:
>>> 1. Add also F: entry
>>>
>>> Changes in v2:
>>> 1. Correct typos and trailing white spaces.
>>> 2. Fix order of P: after C: in maintainers.
>>> ---
>>> .../process/maintainer-devicetree.rst | 70 +++++++++++++++++++
>>> MAINTAINERS | 3 +
>>> 2 files changed, 73 insertions(+)
>>> create mode 100644 Documentation/process/maintainer-devicetree.rst
>>
>> You'll need to add this new file to index.rst as well.
>
> Hm, where? In docs/process/index.rst? None of the other maintainer
> profiles are mentioned there.
Sigh...I stand corrected. I'd forgotten that we merged the
maintainers-profile-toc Sphinx magic that picks them all up from the
MAINTAINERS file entries.
Sorry for the noise,
jon
^ permalink raw reply
* Re: [PATCH v18 04/13] cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
From: Dave Jiang @ 2026-07-20 20:25 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-5-terry.bowman@amd.com>
On 7/17/26 3:26 PM, Terry Bowman wrote:
> From: Dan Williams <djbw@kernel.org>
>
> find_cxl_port() and find_cxl_port_by_uport() are internal port lookup
> functions that search the CXL bus by dport and uport respectively, but
> their names do not make the lookup method clear.
>
> Rename find_cxl_port() to find_cxl_port_by_dport() to make the lookup
> method explicit and consistent with find_cxl_port_by_uport(). Both
> functions remain static to port.c; the upcoming patch that adds the
> first cross-file caller will widen their scope.
>
> Co-developed-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Dan Williams <djbw@kernel.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17 -> v18:
> - New commit
> ---
> drivers/cxl/core/port.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index f90f899c31d07..cadb51f70f854 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1379,7 +1379,7 @@ static int match_port_by_dport(struct device *dev, const void *data)
> return dport != NULL;
> }
>
> -static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
> +static struct cxl_port *__find_cxl_port_by_dport(struct cxl_find_port_ctx *ctx)
> {
> struct device *dev;
>
> @@ -1392,8 +1392,16 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
> return NULL;
> }
>
> -static struct cxl_port *find_cxl_port(struct device *dport_dev,
> - struct cxl_dport **dport)
> +/**
> + * find_cxl_port_by_dport - find a cxl_port by one of its targets
> + * @dport_dev: device representing the dport target
> + * @dport: optional output of the 'struct cxl_dport' companion of the @dport_dev
> + *
> + * Return a 'struct cxl_port' with an elevated reference if found. Use
> + * __free(put_cxl_port) to release.
> + */
> +static struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
> + struct cxl_dport **dport)
> {
> struct cxl_find_port_ctx ctx = {
> .dport_dev = dport_dev,
> @@ -1401,7 +1409,7 @@ static struct cxl_port *find_cxl_port(struct device *dport_dev,
> };
> struct cxl_port *port;
>
> - port = __find_cxl_port(&ctx);
> + port = __find_cxl_port_by_dport(&ctx);
> return port;
> }
>
> @@ -1895,14 +1903,14 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, "CXL");
> struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
> struct cxl_dport **dport)
> {
> - return find_cxl_port(pdev->dev.parent, dport);
> + return find_cxl_port_by_dport(pdev->dev.parent, dport);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, "CXL");
>
> struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
> struct cxl_dport **dport)
> {
> - return find_cxl_port(grandparent(&cxlmd->dev), dport);
> + return find_cxl_port_by_dport(grandparent(&cxlmd->dev), dport);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, "CXL");
>
^ permalink raw reply
* Re: [PATCH v18 05/13] PCI/AER: Introduce AER-CXL protocol error kfifo
From: Dave Jiang @ 2026-07-20 20:29 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-6-terry.bowman@amd.com>
On 7/17/26 3:26 PM, Terry Bowman wrote:
> CXL VH RAS handling requires a path for the AER driver to hand off
> CXL protocol errors to cxl_core for logging and recovery before PCIe
> AER recovery tears down the device. Add
> drivers/pci/pcie/aer_cxl_vh.c to implement this handoff via a kfifo-backed
> work item.
>
> Introduce is_aer_internal_error() to identify CXL protocol errors
> from AER internal error status bits across both correctable and
> uncorrectable severities.
>
> Introduce is_cxl_error() to gate the VH kfifo path.
>
> Introduce struct cxl_proto_err_work_data to carry the error source
> PCI device and severity through the kfifo. Encapsulate the kfifo,
> per-producer spinlock, registration rwsem, and work pointer in struct
> cxl_proto_err_kfifo. Initialize the embedded kfifo via INIT_KFIFO()
> from a subsys_initcall so its metadata is populated before any
> producer or consumer runs.
>
> Introduce cxl_forward_error() to enqueue a CXL protocol error. A
> reference is taken on the PCI device; the consumer releases it via
> for_each_cxl_proto_err(). On enqueue failure the reference is
> released immediately, the error is dropped, and the consumer is
> scheduled to drain existing entries. A subsequent patch wires
> cxl_forward_error() into handle_error_source() where correctable and
> uncorrectable status clearing is left to pci_aer_handle_error().
>
> Introduce cxl_proto_err_flush() to synchronously wait for the
> consumer worker to drain the kfifo. A subsequent patch wires this
> into handle_error_source() for UCE events so the CXL plane completes
> error handling and panic policy before pci_aer_handle_error() drives
> PCIe recovery.
>
> Introduce cxl_register_proto_err_work() and
> cxl_unregister_proto_err_work() for cxl_core to register and
> deregister its work handler. On unregistration, pending kfifo entries
> are drained and their pdev references released before
> cancel_work_sync() runs. Export these and for_each_cxl_proto_err()
> via EXPORT_SYMBOL_FOR_MODULES restricted to cxl_core.
>
> Protect the work pointer with a rwsem to correctly serialize
> registration, deregistration, enqueue, and dequeue against concurrent
> AER IRQ threads. Serialize concurrent kfifo writers with a spinlock.
>
> Add MAINTAINERS entries for aer_cxl_vh.c and aer_cxl_rch.c under
> the CXL entry so CXL maintainers are CC'd on changes to the AER-CXL
> bridging code.
>
> Co-developed-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17->v18:
> - Remove correctable status clear from cxl_forward_error(); the AER core
> clears all status bits via pci_aer_handle_error() info->status writeback
> - Schedule consumer on kfifo overflow so existing entries can be drained
>
> Changes in v16->v17:
> - Reword "kfifo semaphore" to "kfifo spinlock" to match fifo_lock.
> - Defer the handle_error_source() is_cxl_error() switch to the patch that
> registers the kfifo consumer to keep each commit bisect-safe.
> - Rename rwsema to rwsem
> - Change CPER exports to use EXPORT_SYMBOL_FOR_MODULES.
> - Add work cancel function.
> - Replace kfifo_put() with kfifo_in_spinlocked() for multiple producers
> - Add fifo_lock spinlock for concurrent producer serialisation
> - Initialize the embedded kfifo with INIT_KFIFO() in a subsys_initcall so
> kfifo->mask, ->esize and ->data are set before first use.
> - Clear PCI_ERR_COR_STATUS in cxl_forward_error() after enqueue so the
> device is acked for correctable events even when the consumer drops the
> event. Uncorrectable status is left for cxl_do_recovery() to clear after
> recovery completes, mirroring the AER core convention.
> - WARN on double-registration in cxl_register_proto_err_work() to make an
> unintended second consumer visible at runtime.
> - Add direct rwsem.h, cleanup.h and workqueue.h includes for symbols used
> in aer_cxl_vh.c
> - Add MAINTAINERS entries for drivers/pci/pcie/aer_cxl_*.c
> - Update message
> ---
> MAINTAINERS | 2 +
> drivers/pci/pcie/Makefile | 1 +
> drivers/pci/pcie/aer.c | 10 --
> drivers/pci/pcie/aer_cxl_vh.c | 221 ++++++++++++++++++++++++++++++++++
> drivers/pci/pcie/portdrv.h | 6 +
> include/linux/aer.h | 24 ++++
> 6 files changed, 254 insertions(+), 10 deletions(-)
> create mode 100644 drivers/pci/pcie/aer_cxl_vh.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 806bd2d80d153..39007aa90b20f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6526,6 +6526,8 @@ S: Maintained
> F: Documentation/driver-api/cxl
> F: Documentation/userspace-api/fwctl/fwctl-cxl.rst
> F: drivers/cxl/
> +F: drivers/pci/pcie/aer_cxl_rch.c
> +F: drivers/pci/pcie/aer_cxl_vh.c
> F: include/cxl/
> F: include/uapi/linux/cxl_mem.h
> F: tools/testing/cxl/
> diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
> index b0b43a18c304b..62d3d3c69a5df 100644
> --- a/drivers/pci/pcie/Makefile
> +++ b/drivers/pci/pcie/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o bwctrl.o
> obj-y += aspm.o
> obj-$(CONFIG_PCIEAER) += aer.o err.o tlp.o
> obj-$(CONFIG_CXL_RAS) += aer_cxl_rch.o
> +obj-$(CONFIG_CXL_RAS) += aer_cxl_vh.o
> obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
> obj-$(CONFIG_PCIE_PME) += pme.o
> obj-$(CONFIG_PCIE_DPC) += dpc.o
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index c4fd9c0b2a548..c5bce25df51cb 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -1150,16 +1150,6 @@ void pci_aer_unmask_internal_errors(struct pci_dev *dev)
> */
> EXPORT_SYMBOL_FOR_MODULES(pci_aer_unmask_internal_errors, "cxl_core");
>
> -#ifdef CONFIG_CXL_RAS
> -bool is_aer_internal_error(struct aer_err_info *info)
> -{
> - if (info->severity == AER_CORRECTABLE)
> - return info->status & PCI_ERR_COR_INTERNAL;
> -
> - return info->status & PCI_ERR_UNC_INTN;
> -}
> -#endif
> -
> /**
> * pci_aer_handle_error - handle logging error into an event log
> * @dev: pointer to pci_dev data structure of error source device
> diff --git a/drivers/pci/pcie/aer_cxl_vh.c b/drivers/pci/pcie/aer_cxl_vh.c
> new file mode 100644
> index 0000000000000..93bed07936100
> --- /dev/null
> +++ b/drivers/pci/pcie/aer_cxl_vh.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2026 AMD Corporation. All rights reserved. */
> +
> +#include <linux/aer.h>
> +#include <linux/atomic.h>
> +#include <linux/cleanup.h>
> +#include <linux/init.h>
> +#include <linux/kfifo.h>
> +#include <linux/rwsem.h>
> +#include <linux/wait_bit.h>
> +#include <linux/workqueue.h>
> +#include "../pci.h"
> +#include "portdrv.h"
> +
> +#define CXL_ERROR_SOURCES_MAX 128
> +
> +struct cxl_proto_err_kfifo {
> + struct work_struct *work;
> + void (*flush)(void);
> + struct rw_semaphore rwsem;
> + spinlock_t fifo_lock;
> + atomic_t flush_inflight;
> + DECLARE_KFIFO(fifo, struct cxl_proto_err_work_data,
> + CXL_ERROR_SOURCES_MAX);
> +};
> +
> +static struct cxl_proto_err_kfifo cxl_proto_err_kfifo = {
> + .rwsem = __RWSEM_INITIALIZER(cxl_proto_err_kfifo.rwsem),
> + .fifo_lock = __SPIN_LOCK_UNLOCKED(cxl_proto_err_kfifo.fifo_lock),
> +};
> +
> +static int __init cxl_proto_err_kfifo_init(void)
> +{
> + INIT_KFIFO(cxl_proto_err_kfifo.fifo);
> + return 0;
> +}
> +subsys_initcall(cxl_proto_err_kfifo_init);
> +
> +bool is_aer_internal_error(struct aer_err_info *info)
> +{
> + if (info->severity == AER_CORRECTABLE)
> + return info->status & PCI_ERR_COR_INTERNAL;
> +
> + return info->status & PCI_ERR_UNC_INTN;
> +}
> +
> +bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info)
> +{
> + if (!info || !info->is_cxl)
> + return false;
> +
> + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
> + return false;
> +
> + return is_aer_internal_error(info);
> +}
> +
> +/**
> + * cxl_forward_error - Forward a CXL protocol error to the CXL subsystem via kfifo
> + * @pdev: PCI device that reported the AER error
> + * @info: AER error info containing severity and status
> + *
> + * Producer side of the AER-CXL kfifo. Enqueues a CXL protocol error work
> + * item and schedules the consumer workqueue. Takes a reference on @pdev
> + * that the consumer releases after handling.
> + *
> + * Return: true if the caller must flush the kfifo before AER recovery,
> + * false if no CXL error handling was initiated due to early return on
> + * error.
> + */
> +bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info)
> +{
> + struct cxl_proto_err_work_data wd = {
> + .severity = info->severity,
> + .pdev = pdev,
> + };
> +
> + guard(rwsem_read)(&cxl_proto_err_kfifo.rwsem);
> +
> + if (!cxl_proto_err_kfifo.work) {
> + dev_err_ratelimited(&pdev->dev, "AER-CXL kfifo reader not registered\n");
> + return false;
> + }
> +
> + /*
> + * Reference discipline: the AER caller (handle_error_source())
> + * holds a ref on @pdev for the duration of this call and releases
> + * it on return. Take a fresh ref here so the pdev stays live while
> + * queued in the kfifo; the consumer (for_each_cxl_proto_err())
> + * drops that ref after handling. On enqueue failure below, drop
> + * the ref we just took to avoid a leak.
> + */
> + pci_dev_get(pdev);
> +
> + /* Serialize concurrent kfifo writers: multiple AER threaded IRQs */
> + if (!kfifo_in_spinlocked(&cxl_proto_err_kfifo.fifo, &wd, 1,
> + &cxl_proto_err_kfifo.fifo_lock)) {
> + /* Dropped; no panic - UCE unconfirmed without RAS read */
> + dev_err_ratelimited(&pdev->dev, "AER-CXL kfifo add failed\n");
> + pci_dev_put(pdev);
> + schedule_work(cxl_proto_err_kfifo.work);
> + return true;
> + }
> +
> + schedule_work(cxl_proto_err_kfifo.work);
> + return true;
> +}
> +
> +void cxl_register_proto_err_work(struct work_struct *work,
> + void (*flush)(void))
> +{
> + guard(rwsem_write)(&cxl_proto_err_kfifo.rwsem);
> +
> + /*
> + * Warn on double-registration to surface driver bugs (e.g. missing
> + * cxl_unregister_proto_err_work() on module exit)
> + */
> + if (WARN(cxl_proto_err_kfifo.work,
> + "AER-CXL kfifo consumer already registered\n"))
> + return;
> + cxl_proto_err_kfifo.work = work;
> + cxl_proto_err_kfifo.flush = flush;
> +}
> +EXPORT_SYMBOL_FOR_MODULES(cxl_register_proto_err_work, "cxl_core");
> +
> +static struct work_struct *cancel_cxl_proto_err(void)
> +{
> + struct work_struct *work;
> + struct cxl_proto_err_work_data wd;
> +
> + guard(rwsem_write)(&cxl_proto_err_kfifo.rwsem);
> + work = cxl_proto_err_kfifo.work;
> + cxl_proto_err_kfifo.work = NULL;
> + cxl_proto_err_kfifo.flush = NULL;
> +
> + /* rwsem_write excludes all producers; fifo_lock not needed */
> + while (kfifo_get(&cxl_proto_err_kfifo.fifo, &wd)) {
> + dev_err_ratelimited(&wd.pdev->dev,
> + "AER-CXL error report canceled\n");
> + pci_dev_put(wd.pdev);
> + }
> + return work;
> +}
> +
> +void cxl_unregister_proto_err_work(void)
> +{
> + struct work_struct *work;
> +
> + lockdep_assert_not_held(&cxl_proto_err_kfifo.rwsem);
> +
> + work = cancel_cxl_proto_err();
> +
> + /* Wait for any in-flight cxl_proto_err_flush() calls to complete */
> + wait_var_event(&cxl_proto_err_kfifo.flush_inflight,
> + atomic_read(&cxl_proto_err_kfifo.flush_inflight) == 0);
> +
> + if (work)
> + cancel_work_sync(work);
> +}
> +EXPORT_SYMBOL_FOR_MODULES(cxl_unregister_proto_err_work, "cxl_core");
> +
> +/**
> + * for_each_cxl_proto_err - Call a function for each kfifo work item
> + *
> + * Single-consumer invariant: this function is only called from
> + * cxl_proto_err_work_fn() via a single DECLARE_WORK.
> + *
> + * Holds rwsem_read internally; fn() must not call cxl_register_proto_err_work()
> + * or cxl_unregister_proto_err_work().
> + */
> +void for_each_cxl_proto_err(struct cxl_proto_err_work_data *wd,
> + cxl_proto_err_fn_t fn)
> +{
> + guard(rwsem_read)(&cxl_proto_err_kfifo.rwsem);
> + while (kfifo_get(&cxl_proto_err_kfifo.fifo, wd)) {
> + fn(wd);
> + pci_dev_put(wd->pdev);
> + }
> +}
> +EXPORT_SYMBOL_FOR_MODULES(for_each_cxl_proto_err, "cxl_core");
> +
> +/**
> + * cxl_proto_err_flush - drain pending AER-CXL kfifo work synchronously
> + *
> + * Wait for the consumer worker to finish processing all entries
> + * currently in the kfifo. Used by handle_error_source() for UCE so
> + * the CXL plane can read CXL RAS, apply panic policy, and clear CXL
> + * state before pci_aer_handle_error() drives PCIe recovery.
> + *
> + * Snapshots the flush callback under rwsem_read and releases the rwsem
> + * before calling it. This avoids holding rwsem_read across flush_work(),
> + * which would deadlock via the rwsem HANDOFF mechanism when a concurrent
> + * rwsem_write waiter (cxl_unregister_proto_err_work) blocks new readers
> + * including the worker's for_each_cxl_proto_err() rwsem_read acquisition.
> + *
> + * The flush_inflight counter prevents cxl_core module unload while a
> + * flush is in progress outside the rwsem. The counter is incremented
> + * under rwsem_read (mutually exclusive with the rwsem_write in
> + * cancel_cxl_proto_err() that NULLs the flush pointer) and decremented
> + * after the flush completes. cxl_unregister_proto_err_work() waits for
> + * the counter to reach zero before proceeding with cancel_work_sync().
> + *
> + * For correctable events the consumer can run asynchronously; AER
> + * does not need to call this helper for AER_CORRECTABLE.
> + */
> +void cxl_proto_err_flush(void)
> +{
> + void (*flush)(void);
> +
> + scoped_guard(rwsem_read, &cxl_proto_err_kfifo.rwsem) {
> + flush = cxl_proto_err_kfifo.flush;
> + if (flush)
> + atomic_inc(&cxl_proto_err_kfifo.flush_inflight);
> + }
> +
> + if (flush) {
> + flush();
> + if (atomic_dec_and_test(&cxl_proto_err_kfifo.flush_inflight))
> + wake_up_var(&cxl_proto_err_kfifo.flush_inflight);
> + }
> +}
> diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
> index cc58bf2f2c844..fd203010877bf 100644
> --- a/drivers/pci/pcie/portdrv.h
> +++ b/drivers/pci/pcie/portdrv.h
> @@ -130,9 +130,15 @@ struct aer_err_info;
> bool is_aer_internal_error(struct aer_err_info *info);
> void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info);
> void cxl_rch_enable_rcec(struct pci_dev *rcec);
> +bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info);
> +bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info);
> +void cxl_proto_err_flush(void);
> #else
> static inline bool is_aer_internal_error(struct aer_err_info *info) { return false; }
> static inline void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info) { }
> static inline void cxl_rch_enable_rcec(struct pci_dev *rcec) { }
> +static inline bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info) { return false; }
> +static inline bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info) { return false; }
> +static inline void cxl_proto_err_flush(void) { }
> #endif /* CONFIG_CXL_RAS */
> #endif /* _PORTDRV_H_ */
> diff --git a/include/linux/aer.h b/include/linux/aer.h
> index df0f5c382286f..8eba3192e2d15 100644
> --- a/include/linux/aer.h
> +++ b/include/linux/aer.h
> @@ -25,6 +25,7 @@
> #define PCIE_STD_MAX_TLP_HEADERLOG (PCIE_STD_NUM_TLP_HEADERLOG + 10)
>
> struct pci_dev;
> +struct work_struct;
>
> struct pcie_tlp_log {
> union {
> @@ -66,6 +67,29 @@ static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; }
> static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }
> #endif
>
> +#ifdef CONFIG_CXL_RAS
> +/**
> + * struct cxl_proto_err_work_data - Error information used in CXL error handling
> + * @pdev: PCI device detecting the error
> + * @severity: AER severity
> + */
> +struct cxl_proto_err_work_data {
> + struct pci_dev *pdev;
> + int severity;
> +};
> +
> +/**
> + * Callback for processing a CXL protocol error from the AER-CXL kfifo.
> + */
> +typedef void (*cxl_proto_err_fn_t)(struct cxl_proto_err_work_data *wd);
> +
> +void cxl_register_proto_err_work(struct work_struct *work,
> + void (*flush)(void));
> +void for_each_cxl_proto_err(struct cxl_proto_err_work_data *wd,
> + cxl_proto_err_fn_t fn);
> +void cxl_unregister_proto_err_work(void);
> +#endif
> +
> void pci_print_aer(struct pci_dev *dev, int aer_severity,
> struct aer_capability_regs *aer);
> int cper_severity_to_aer(int cper_severity);
^ permalink raw reply
* Re: [PATCH v3] docs: dt: maintainer: Add Devicetree and OF maintainer profile document
From: Frank Li @ 2026-07-20 20:34 UTC (permalink / raw)
To: Rob Herring
Cc: Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, devicetree, workflows, linux-doc,
linux-kernel, Saravana Kannan
In-Reply-To: <CAL_JsqKkxVTVE=o-uar=JEVvqw9NhBAQcBrJ0ukf+RBXCwFZJA@mail.gmail.com>
On Mon, Jul 20, 2026 at 03:12:17PM -0500, Rob Herring wrote:
> On Mon, Jul 20, 2026 at 1:40 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@oss.qualcomm.com> wrote:
> >
> > Document how Devicetree and Open Firmware maintainers handle their
> > subsystem, especially focusing on two caveats:
> >
> > Devicetree subsystem handles patches with a minor difference comparing
> > to other subsystems: while DT maintainers pick up OF code, they only
> > provide review of DT bindings without applying these.
> >
> > All three DT bindings maintainers rely currently on Patchwork and due to
> > enormous amount of emails per day, regardless how much DT maintainers
> > try, they cannot read all the emails.
> >
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: Saravana Kannan <saravanak@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> >
> > ---
> >
> > I expect patch to be picked up by Rob, after review.
> >
> > Changes in v3:
> > 1. Add also F: entry
> >
> > Changes in v2:
> > 1. Correct typos and trailing white spaces.
> > 2. Fix order of P: after C: in maintainers.
> > ---
> > .../process/maintainer-devicetree.rst | 70 +++++++++++++++++++
> > MAINTAINERS | 3 +
> > 2 files changed, 73 insertions(+)
> > create mode 100644 Documentation/process/maintainer-devicetree.rst
> >
> > diff --git a/Documentation/process/maintainer-devicetree.rst b/Documentation/process/maintainer-devicetree.rst
> > new file mode 100644
> > index 000000000000..d8ffe752bf5d
> > --- /dev/null
> > +++ b/Documentation/process/maintainer-devicetree.rst
> > @@ -0,0 +1,70 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +======================================
> > +Devicetree and Open Firmware Subsystem
> > +======================================
> > +
> > +Other Process Documents
> > +-----------------------
> > +
> > +Please see the documents in Documentation/devicetree/bindings/ for information
> > +on how to write proper Devicetree bindings and how to submit patches.
> > +
> > +Patch Review and Handling
> > +-------------------------
> > +
> > +Patches handled by Devicetree maintainers are processed differently depending
> > +on the patch type:
> > +
> > +1. Core OF driver code, e.g. drivers/of/:
> > + patches are reviewed and applied by DT maintainers.
> > +
> > +2. Devicetree bindings:
> > + patches are reviewed by DT maintainers but, except in certain cases, should
> > + be applied by subsystem maintainers. See also *For kernel maintainers* in
> > + Documentation/devicetree/bindings/submitting-patches.rst.
>
> I would reword:
>
> patches are reviewed by DT maintainers, but should be applied by
> subsystem maintainers except in certain cases.
It would be wonderful if provide a few sample *certain* cases.
Anyways, nice document.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> > +
> > +3. DTS and drivers:
> > + DT maintainers might provide comments, but review is generally not expected.
>
> For DTS, we expect to pass schema checks or at least not add new warnings.
>
> I can address these 2 when applying.
>
> Rob
^ permalink raw reply
* Re: [PATCH v18 01/13] cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
From: Bowman, Terry @ 2026-07-20 20:36 UTC (permalink / raw)
To: Dave Jiang, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma,
linux-cxl@vger.kernel.org, linux-acpi, linux-doc,
linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
linuxppc-dev
In-Reply-To: <9c371c59-c871-4988-83d7-2aa780d8e6c4@intel.com>
On 7/20/2026 3:09 PM, Dave Jiang wrote:
>
>
> On 7/17/26 3:26 PM, Terry Bowman wrote:
>> cxl_rch_get_aer_severity() classifies RCH Downstream Port uncorrectable
>> errors as fatal or non-fatal by ANDing uncorrectable status with
>> PCI_ERR_ROOT_FATAL_RCV. This is wrong because PCI_ERR_ROOT_FATAL_RCV is a
>> Root Error Status register bit (bit 6), not a severity bit. ANDing it
>> against uncorrectable status tests a reserved bit and produces incorrect
>> severity classification.
>>
>> Fix by ANDing the unmasked uncor_status against uncor_severity. Per
>> PCIe Base Spec r6.0 Section 7.8.4.4, each bit in the Uncorrectable
>> Error Severity register indicates whether the corresponding error is
>> fatal (1) or non-fatal (0).
>>
>> Fixes: 6ac07883dbb5 ("cxl/pci: Add RCH downstream port error logging")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> This can be picked up ahead looks like?
>
Yes.
-Terry
>>
>> ---
>>
>> Changes in v17 -> v18:
>> - New patch.
>> ---
>> drivers/cxl/core/ras_rch.c | 3 ++-
>> 1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
>> index 0a8b3b9b63884..44b335d560708 100644
>> --- a/drivers/cxl/core/ras_rch.c
>> +++ b/drivers/cxl/core/ras_rch.c
>> @@ -80,7 +80,8 @@ static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
>> int *severity)
>> {
>> if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
>> - if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
>> + if ((aer_regs->uncor_status & ~aer_regs->uncor_mask) &
>> + aer_regs->uncor_severity)
>> *severity = AER_FATAL;
>> else
>> *severity = AER_NONFATAL;
>
^ permalink raw reply
* Re: [PATCH v18 02/13] acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
From: Bowman, Terry @ 2026-07-20 20:38 UTC (permalink / raw)
To: Dave Jiang, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma,
linux-pci@vger.kernel.org, linux-acpi,
linux-kernel@vger.kernel.org, linux-doc, linuxppc-dev
In-Reply-To: <e66c4a00-53e4-4f5a-bc07-edd9c92beb96@intel.com>
On 7/20/2026 3:12 PM, Dave Jiang wrote:
>
>
> On 7/17/26 3:26 PM, Terry Bowman wrote:
>> The CXL CPER work registration and unregistration helpers in
>> drivers/acpi/apei/ghes.c acquire cxl_cper_work_lock and
>> cxl_cper_prot_err_work_lock with guard(spinlock), which leaves local
>> interrupts enabled. The corresponding post paths
>> (cxl_cper_post_event(), cxl_cper_post_prot_err()) execute in hard IRQ
>> context (they are called from the GHES error notification path) and
>> acquire the same locks via guard(spinlock_irqsave).
>>
>> If a CPU is holding one of these locks via guard(spinlock) when a
>> GHES interrupt arrives on the same CPU, the IRQ handler spins on the
>> held lock waiting for it to release, while the lock holder is
>> preempted by the IRQ. The result is a deadlock.
>>
>> Convert both locks from spinlock_t to raw_spinlock_t and use
>> guard(raw_spinlock_irqsave) at all call sites. On PREEMPT_RT kernels
>> spinlock_t is backed by rt_mutex and sleeping from hard IRQ context is
>> not permitted; raw_spinlock_t is safe in both contexts.
>>
>> Add WARN_ONCE to both register functions to surface double-registration
>> bugs at runtime.
>>
>> Restructure both unregister functions to clear the global work pointer
>> under the lock before calling cancel_work_sync(), closing the window
>> where a CPER interrupt could schedule work on a pointer about to be
>> freed. Add kfifo_reset() after cancel_work_sync() so stale entries
>> are not replayed on next module load.
>>
>> Both kfifos are single-consumer: only one work_struct is registered at
>> a time, enforced by the WARN_ONCE guard in the register functions.
>> kfifo_reset() is safe outside the lock because cancel_work_sync() has
>> already quiesced the consumer, and no new consumer can register until
>> the current module exit completes and a fresh module init runs.
>>
>> Remove the now-redundant cancel_work_sync() call from
>> cxl_pci_driver_exit() - cxl_cper_unregister_work() handles quiescing
>> internally.
>>
>> Reported-by: Sashiko <sashiko@linuxfoundation.org>
>> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>> Fixes: 5e4a264bf8b5 ("acpi/ghes: Process CXL Component Events")
>> Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors")
>> Cc: stable@vger.kernel.org
>
> With the minor sashiko issue addressed,
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> This can probably be picked up ahead of the series.
>
>
Yes.
-Terry
>>
>> ---
>>
>> Changes in v17 -> v18:
>> - New patch.
>> ---
>> drivers/acpi/apei/ghes.c | 50 ++++++++++++++++++++++++++--------------
>> drivers/cxl/pci.c | 1 -
>> 2 files changed, 33 insertions(+), 18 deletions(-)
>>
>> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
>> index 3236a3ce79d6b..ca7a138c1ff2e 100644
>> --- a/drivers/acpi/apei/ghes.c
>> +++ b/drivers/acpi/apei/ghes.c
>> @@ -749,7 +749,7 @@ static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
>> CXL_CPER_PROT_ERR_FIFO_DEPTH);
>>
>> /* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
>> -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
>> +static DEFINE_RAW_SPINLOCK(cxl_cper_prot_err_work_lock);
>> struct work_struct *cxl_cper_prot_err_work;
>>
>> static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
>> @@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
>> if (cxl_cper_sec_prot_err_valid(prot_err))
>> return;
>>
>> - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
>> + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
>>
>> if (!cxl_cper_prot_err_work)
>> return;
>> @@ -780,10 +780,11 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
>>
>> int cxl_cper_register_prot_err_work(struct work_struct *work)
>> {
>> - if (cxl_cper_prot_err_work)
>> - return -EINVAL;
>> + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
>>
>> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
>> + if (WARN_ONCE(cxl_cper_prot_err_work,
>> + "CPER-CXL kfifo consumer already registered\n"))
>> + return -EINVAL;
>> cxl_cper_prot_err_work = work;
>> return 0;
>> }
>> @@ -791,11 +792,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
>>
>> int cxl_cper_unregister_prot_err_work(struct work_struct *work)
>> {
>> - if (cxl_cper_prot_err_work != work)
>> - return -EINVAL;
>> + scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) {
>> + if (WARN_ONCE(cxl_cper_prot_err_work != work,
>> + "CPER-CXL kfifo consumer mismatch on unregister\n"))
>> + return -EINVAL;
>> + cxl_cper_prot_err_work = NULL;
>> + }
>> +
>> + cancel_work_sync(work);
>> +
>> + /* Discard stale entries so they are not replayed on next module load */
>> + kfifo_reset(&cxl_cper_prot_err_fifo);
>>
>> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
>> - cxl_cper_prot_err_work = NULL;
>> return 0;
>> }
>> EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
>> @@ -811,7 +819,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
>> DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
>>
>> /* Synchronize schedule_work() with cxl_cper_work changes */
>> -static DEFINE_SPINLOCK(cxl_cper_work_lock);
>> +static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock);
>> struct work_struct *cxl_cper_work;
>>
>> static void cxl_cper_post_event(enum cxl_event_type event_type,
>> @@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
>> return;
>> }
>>
>> - guard(spinlock_irqsave)(&cxl_cper_work_lock);
>> + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
>>
>> if (!cxl_cper_work)
>> return;
>> @@ -849,10 +857,11 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
>>
>> int cxl_cper_register_work(struct work_struct *work)
>> {
>> - if (cxl_cper_work)
>> + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
>> + if (WARN_ONCE(cxl_cper_work,
>> + "CXL CPER kfifo consumer already registered\n"))
>> return -EINVAL;
>>
>> - guard(spinlock)(&cxl_cper_work_lock);
>> cxl_cper_work = work;
>> return 0;
>> }
>> @@ -860,11 +869,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
>>
>> int cxl_cper_unregister_work(struct work_struct *work)
>> {
>> - if (cxl_cper_work != work)
>> - return -EINVAL;
>> + scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) {
>> + if (WARN_ONCE(cxl_cper_work != work,
>> + "CXL CPER kfifo consumer mismatch on unregister\n"))
>> + return -EINVAL;
>> + cxl_cper_work = NULL;
>> + }
>> +
>> + cancel_work_sync(work);
>> +
>> + /* Discard stale entries so they are not replayed on next module load */
>> + kfifo_reset(&cxl_cper_fifo);
>>
>> - guard(spinlock)(&cxl_cper_work_lock);
>> - cxl_cper_work = NULL;
>> return 0;
>> }
>> EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
>> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
>> index 267c679b0b3c2..7c6faee7f85ed 100644
>> --- a/drivers/cxl/pci.c
>> +++ b/drivers/cxl/pci.c
>> @@ -1083,7 +1083,6 @@ static int __init cxl_pci_driver_init(void)
>> static void __exit cxl_pci_driver_exit(void)
>> {
>> cxl_cper_unregister_work(&cxl_cper_work);
>> - cancel_work_sync(&cxl_cper_work);
>> pci_unregister_driver(&cxl_pci_driver);
>> }
>>
>
^ permalink raw reply
* Re: [PATCH v18 06/13] PCI: Establish common CXL Port protocol error flow
From: Dave Jiang @ 2026-07-20 20:44 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-7-terry.bowman@amd.com>
On 7/17/26 3:26 PM, Terry Bowman wrote:
> Add CXL protocol error dispatch in handle_error_source() using
> is_cxl_error() and cxl_forward_error() to route errors through the
> AER-CXL kfifo. Expand is_cxl_error() from Endpoint-only to include
> Root Port, Upstream Port, and Downstream Port device types. The
> producer and consumer go live in the same commit to avoid silently
> dropping CXL errors during bisect.
>
> For uncorrectable events, call cxl_proto_err_flush() to ensure CXL RAS
> registers are read, panic policy is applied, and CXL state is cleared
> before pci_aer_handle_error() drives PCIe recovery. Without the flush,
> AER recovery can tear down drivers and unmap the CXL RAS iomaps while
> the kfifo consumer is still reading them. Correctable events do not
> need the flush and run asynchronously. RCH kfifo support is added in
> the following patch ("PCI/CXL: Add RCH support to CXL handlers").
>
> Add cxl_handle_proto_error() to dispatch correctable and uncorrectable
> errors through the CXL RAS helpers. Add cxl_do_recovery() to coordinate
> uncorrectable recovery. Panic when a UCE is confirmed by a successful
> CXL RAS status register read. If the RAS registers cannot be read the
> UCE cannot be confirmed and panic is not triggered. Gate error handling
> on the port driver being bound to avoid processing errors on disabled
> devices.
>
> The kfifo consumer holds guard(device)(&port->dev) and checks
> port->dev.driver before accessing RAS registers, serializing against
> driver unbind and devm iomap teardown. For UCE, cxl_proto_err_flush()
> runs the worker synchronously before AER recovery, ensuring the device
> is present during RAS register access.
>
> Add to_ras_base() to centralize RAS base lookup: dport->regs.ras for
> Root/Downstream Ports, port->regs.ras for Upstream Ports and Endpoints.
> Use to_ras_base() to access the CXL devices' RAS registers as it will
> provide an avenue to inject status simulation during testing.
>
> Add CXL RAS logging in cxl_handle_cor_ras() and cxl_handle_ras(). The
> existing cxl_cor_error_detected() and cxl_error_detected() AER
> callbacks remain for all Endpoints and are reworked to use
> find_cxl_port_by_uport() and to_ras_base(), with UCE now triggering
> panic unconditionally. These callbacks are further updated in the
> following patch ("PCI/CXL: Add RCH support to CXL handlers").
>
> Fix a pre-existing race for cxlds between cxl_handle_rdport_errors() and
> cxl_memdev_shutdown() by holding a cxlmd device scoped_guard() around
> the rdport call. Release the lock before taking the Port lock to avoid
> the lock inversion.
>
> Co-developed-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17->v18:
> - Fix pre-existing race: hold memdev device lock around
> cxl_handle_rdport_errors(), release before port lock
> - Fix handle_error_source() to call pci_aer_handle_error() unconditionally
> so AER handling always runs after cxl_forward_error()
> - Add cxl_proto_err_flush() call for CXL UCE to drain kfifo before AER
> recovery tears down the device
> - Fix NULL dereference of dport->dport_dev in cxl_handle_cor_ras() and
> cxl_handle_ras() for UPSTREAM/ENDPOINT port types: use dport->dport_dev
> when dport is non-NULL, else fall back to port->uport_dev
> - Remove duplicate pcie_clear_device_status() call from
> cxl_handle_proto_error() CE path; pci_aer_handle_error() already clears it
> - Clarify panic policy: panic only on confirmed UCE via RAS status read
> - Document kfifo consumer serialization against driver unbind via
> guard(device)(&port->dev) and port->dev.driver check
>
> Changes in v16->v17:
> - get_cxl_port() -> find_cxl_port_by_dev()
> - Simplified find_cxl_port_by_dev()
> - Replace and remove cxl_serial_number() w/ pci_get_dsn()
> - cxl_get_ras_base() -> to_ras_base()
> - Drop dependency on PCI_ERS_RESULT_PANIC; cxl_do_recovery() panics
> directly. (PANIC enum patch dropped from series.)
> - Clarify panic semantics: panic on any uncorrectable CXL RAS error, not
> only AER-FATAL severities.
> - Add is_cxl_error() switch in handle_error_source() here, paired with the
> kfifo consumer registration, to keep each commit bisect-safe.
> - Drop pcie_aer_is_native() guard in cxl_do_recovery() (always native).
> - Swap order with the "Limit" patch for bisectability w/ cxl_ras_exit()
> - Reword for "any uncorrectable" CXL RAS error panics.
> - Restore log messages for port-not-found and port-unbound cases.
> - Whitespace cleanup (Jonathan)
> - Update to get_cxl_port() documentation (Terry)
> - Fix __cxl_proto_err_work_fn() to return 0 for transient errors.
> - Drop !port check in cxl_do_recovery(), caller already validated
> - Fix kerneldoc @pdev -> @dev in find_cxl_port_by_dev()
> - Fix missing space in pr_err_ratelimited()
> - Made pcie_clear_device_status() and pci_aer_clear_fatal_status()
> EXPORT_SYMBOL_FOR_MODULES("cxl_core") (Dan)
> - Move find_cxl_port_by_dport() and find_cxl_port_by_uport()
> de-staticisation and core.h declarations from the rename patch to
> here, where the first cross-file callers in find_cxl_port_by_dev()
> land.
>
> Changes in v15->v16:
> - get_ras_base(), initialize dport to NULL (Jonathan)
> - Remove guard(device)(&cxlmd->dev) (Jonathan)
> - Fix dev_warns() (Jonathan)
> - Remove comment in cxl_port_error_detected() (Dan)
> - Update switch-case brackets to follow clang-format (Dan)
> - Add PCI_EXP_TYPE_RC_END for cxl_get_ras_base() (Terry)
> - Add NULL port check in cxl_serial_number() (Terry)
>
> Changes in v14->v15:
> - Update commit message and title. Added Bjorn's ack.
> - Move CE and UCE handling logic here
>
> Changes in v13->v14:
> - Add Dave Jiang's review-by
> - Update commit message & headline (Bjorn)
> - Refactor cxl_port_error_detected()/cxl_port_cor_error_detected() to
> one line (Jonathan)
> - Remove cxl_walk_port() (Dan)
> - Remove cxl_pci_drv_bound(). Check for 'is_cxl' parent port is
> sufficient (Dan)
> - Remove device_lock_if()
> - Combined CE and UCE here (Terry)
>
> Changes in v12->v13:
> - Move get_pci_cxl_host_dev() and cxl_handle_proto_error() to Dequeue
> patch (Terry)
> - Remove EP case in cxl_get_ras_base(), not used. (Terry)
> - Remove check for dport->dport_dev (Dave)
> - Remove whitespace (Terry)
>
> Changes in v11->v12:
> - Add call to cxl_pci_drv_bound() in cxl_handle_proto_error() and
> pci_to_cxl_dev()
> - Change cxl_error_detected() -> cxl_cor_error_detected()
> - Remove NULL variable assignments
> - Replace bus_find_device() with find_cxl_port_by_uport() for upstream
> port searches.
>
> Changes in v10->v11:
> - None
> ---
> drivers/cxl/core/core.h | 7 ++
> drivers/cxl/core/port.c | 6 +-
> drivers/cxl/core/ras.c | 223 +++++++++++++++++++++++++++-------
> drivers/pci/pci.h | 1 -
> drivers/pci/pcie/aer.c | 13 ++
> drivers/pci/pcie/aer_cxl_vh.c | 16 ++-
> 6 files changed, 220 insertions(+), 46 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 23fe40ddf4c6b..7c70bea06c2db 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -186,6 +186,8 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
> void cxl_ras_init(void);
> void cxl_ras_exit(void);
> bool cxl_handle_ras(struct device *dev, void __iomem *ras_base);
> +void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> + struct cxl_dport *dport);
> void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base);
> void cxl_dport_map_rch_aer(struct cxl_dport *dport);
> void cxl_disable_rch_root_ints(struct cxl_dport *dport);
> @@ -198,6 +200,8 @@ static inline bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
> {
> return false;
> }
> +static inline void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> + struct cxl_dport *dport) { }
> static inline void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base) { }
> static inline void cxl_dport_map_rch_aer(struct cxl_dport *dport) { }
> static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { }
> @@ -206,6 +210,9 @@ static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { }
> #endif /* CONFIG_CXL_RAS */
>
> int cxl_gpf_port_setup(struct cxl_dport *dport);
> +struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
> + struct cxl_dport **dport);
> +struct cxl_port *find_cxl_port_by_uport(struct device *uport_dev);
>
> struct cxl_hdm;
> int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index cadb51f70f854..a76f3ee05cba8 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1400,8 +1400,8 @@ static struct cxl_port *__find_cxl_port_by_dport(struct cxl_find_port_ctx *ctx)
> * Return a 'struct cxl_port' with an elevated reference if found. Use
> * __free(put_cxl_port) to release.
> */
> -static struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
> - struct cxl_dport **dport)
> +struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
> + struct cxl_dport **dport)
> {
> struct cxl_find_port_ctx ctx = {
> .dport_dev = dport_dev,
> @@ -1596,7 +1596,7 @@ static int match_port_by_uport(struct device *dev, const void *data)
> * Function takes a device reference on the port device. Caller should do a
> * put_device() when done.
> */
> -static struct cxl_port *find_cxl_port_by_uport(struct device *uport_dev)
> +struct cxl_port *find_cxl_port_by_uport(struct device *uport_dev)
> {
> struct device *dev;
>
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index 135f1997e6f4f..b190e69c2d415 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -77,6 +77,36 @@ static int match_memdev_by_parent(struct device *dev, const void *uport)
> return 0;
> }
>
> +
> +/**
> + * find_cxl_port_by_dev - Use @dev as hint to do a _by_dport or _by_uport lookup
> + * @dev: generic device that may either be a companion of port or target dport
> + * @dport: output parameter; set to the matched dport for dport-class
> + * lookups (Root Port, Downstream Port), NULL otherwise.
> + *
> + * Return a 'struct cxl_port' with an elevated reference if found. Use
> + * __free(put_cxl_port) to release.
> + */
> +static struct cxl_port *find_cxl_port_by_dev(struct device *dev, struct cxl_dport **dport)
> +{
> + if (dport)
> + *dport = NULL;
> + if (!dev_is_pci(dev))
> + return NULL;
> +
> + switch (pci_pcie_type(to_pci_dev(dev))) {
> + case PCI_EXP_TYPE_ROOT_PORT:
> + case PCI_EXP_TYPE_DOWNSTREAM:
> + return find_cxl_port_by_dport(dev, dport);
> + case PCI_EXP_TYPE_UPSTREAM:
> + case PCI_EXP_TYPE_ENDPOINT:
> + case PCI_EXP_TYPE_RC_END:
> + return find_cxl_port_by_uport(dev);
> + }
> +
> + return NULL;
> +}
> +
> 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,
> @@ -132,16 +162,6 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
> }
> static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>
> -void cxl_ras_init(void)
> -{
> - cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
> -}
> -
> -void cxl_ras_exit(void)
> -{
> - cxl_cper_unregister_prot_err_work();
> -}
> -
> static void cxl_dport_map_ras(struct cxl_dport *dport)
> {
> struct cxl_register_map *map = &dport->reg_map;
> @@ -198,10 +218,39 @@ void devm_cxl_port_ras_setup(struct cxl_port *port)
> }
> EXPORT_SYMBOL_NS_GPL(devm_cxl_port_ras_setup, "CXL");
>
> +static void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> +{
> + if (!port)
> + return NULL;
> +
> + if (dport)
> + return dport->regs.ras;
> +
> + return port->regs.ras;
> +}
> +
> +void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port, struct cxl_dport *dport)
> +{
> + struct device *dev = dport ? dport->dport_dev : port->uport_dev;
> + void __iomem *ras_base = to_ras_base(port, dport);
> +
> + if (!ras_base) {
> + dev_err(&pdev->dev,
> + "CXL UCE signaled but RAS registers not mapped\n");
> + return;
> + }
> +
> + if (cxl_handle_ras(dev, ras_base))
> + panic("CXL cachemem error");
> +
> + dev_dbg(&pdev->dev,
> + "CXL UCE signaled but no CXL RAS status bits set\n");
> +}
> +
> void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base)
> {
> - void __iomem *addr;
> u32 status;
> + void __iomem *addr;
>
> if (!ras_base)
> return;
> @@ -210,7 +259,10 @@ void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base)
> 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);
> + if (is_cxl_memdev(dev))
> + trace_cxl_aer_correctable_error(to_cxl_memdev(dev), status);
> + else
> + trace_cxl_port_aer_correctable_error(dev, status);
> }
> }
>
> @@ -262,7 +314,11 @@ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
> }
>
> header_log_copy(ras_base, hl);
> - trace_cxl_aer_uncorrectable_error(to_cxl_memdev(dev), status, fe, hl);
> + if (is_cxl_memdev(dev))
> + trace_cxl_aer_uncorrectable_error(to_cxl_memdev(dev), status, fe, hl);
> + else
> + trace_cxl_port_aer_uncorrectable_error(dev, status, fe, hl);
> +
> writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
>
> return true;
> @@ -270,22 +326,32 @@ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
>
> 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;
> + guard(device)(&pdev->dev);
> + if (!pdev->dev.driver)
> + return;
> +
> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_uport(&pdev->dev);
> + if (!port)
> + return;
> +
> + if (is_cxl_restricted(pdev)) {
> + struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
> + struct cxl_memdev *cxlmd = cxlds->cxlmd;
>
> - scoped_guard(device, dev) {
> - if (!dev->driver) {
> + scoped_guard(device, &cxlmd->dev) {
> + cxl_handle_rdport_errors(cxlds);
> + }
> + }
> +
> + scoped_guard(device, &port->dev) {
> + if (!port->dev.driver) {
> dev_warn(&pdev->dev,
> - "%s: memdev disabled, abort error handling\n",
> - dev_name(dev));
> + "%s: port disabled, abort error handling\n",
> + dev_name(&port->dev));
> return;
> }
>
> - if (cxlds->rcd)
> - cxl_handle_rdport_errors(cxlds);
> -
> - cxl_handle_cor_ras(&cxlds->cxlmd->dev, cxlmd->endpoint->regs.ras);
> + cxl_handle_cor_ras(port->uport_dev, to_ras_base(port, NULL));
> }
> }
> EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL");
> @@ -293,42 +359,53 @@ 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;
> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_uport(&pdev->dev);
> + bool ue = false;
>
> - scoped_guard(device, dev) {
> - if (!dev->driver) {
> + if (!port)
> + return PCI_ERS_RESULT_DISCONNECT;
> +
> + if (is_cxl_restricted(pdev)) {
> + struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
> + struct cxl_memdev *cxlmd = cxlds->cxlmd;
> +
> + scoped_guard(device, &cxlmd->dev) {
> + cxl_handle_rdport_errors(cxlds);
> + }
> + }
> +
> + scoped_guard(device, &port->dev) {
> + if (!port->dev.driver) {
> dev_warn(&pdev->dev,
> - "%s: memdev disabled, abort error handling\n",
> - dev_name(dev));
> + "%s: port disabled, abort error handling\n",
> + dev_name(&port->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);
> + ue = cxl_handle_ras(port->uport_dev, to_ras_base(port, NULL));
> }
>
> + /*
> + * CXL.mem UCE means cache coherency is lost. Continuing risks
> + * silent data corruption across interleaved HDM regions.
> + */
> + if (ue)
> + panic("CXL cachemem error");
> +
> 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);
> + dev_name(port->uport_dev));
> + device_release_driver(port->uport_dev);
> return PCI_ERS_RESULT_NEED_RESET;
> case pci_channel_io_perm_failure:
> dev_warn(&pdev->dev,
> @@ -338,3 +415,67 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> return PCI_ERS_RESULT_NEED_RESET;
> }
> EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL");
> +
> +static void cxl_handle_proto_error(struct pci_dev *pdev, struct cxl_port *port,
> + struct cxl_dport *dport, int severity)
> +{
> + struct device *dev = dport ? dport->dport_dev : port->uport_dev;
> +
> + if (severity == AER_CORRECTABLE)
> + cxl_handle_cor_ras(dev, to_ras_base(port, dport));
> + else
> + cxl_do_recovery(pdev, port, dport);
> +}
> +
> +static void __cxl_proto_err_work_fn(struct cxl_proto_err_work_data *wd)
> +{
> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&wd->pdev->dev, NULL);
> + if (!port) {
> + dev_err_ratelimited(&wd->pdev->dev,
> + "Failed to find parent port device in CXL topology\n");
> + return;
> + }
> + guard(device)(&port->dev);
> + if (!port->dev.driver) {
> + dev_err_ratelimited(&port->dev,
> + "Port device is unbound, abort error handling\n");
> + return;
> + }
> +
> + struct cxl_dport *dport = cxl_find_dport_by_dev(port, &wd->pdev->dev);
> + if (!dport && (pci_pcie_type(wd->pdev) == PCI_EXP_TYPE_ROOT_PORT ||
> + pci_pcie_type(wd->pdev) == PCI_EXP_TYPE_DOWNSTREAM)) {
> + dev_err_ratelimited(&wd->pdev->dev,
> + "Failed to find dport device in CXL topology\n");
> + return;
> + }
> +
> + cxl_handle_proto_error(wd->pdev, port, dport, wd->severity);
> +}
> +
> +static void cxl_proto_err_work_fn(struct work_struct *work)
> +{
> + struct cxl_proto_err_work_data wd;
> +
> + for_each_cxl_proto_err(&wd, __cxl_proto_err_work_fn);
> +}
> +
> +static DECLARE_WORK(cxl_proto_err_work, cxl_proto_err_work_fn);
> +
> +static void cxl_proto_err_do_flush(void)
> +{
> + flush_work(&cxl_proto_err_work);
> +}
> +
> +void cxl_ras_init(void)
> +{
> + cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
> + cxl_register_proto_err_work(&cxl_proto_err_work,
> + cxl_proto_err_do_flush);
> +}
> +
> +void cxl_ras_exit(void)
> +{
> + cxl_unregister_proto_err_work();
> + cxl_cper_unregister_prot_err_work();
> +}
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index 4469e1a77f3c1..a83e2aef75912 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -1296,7 +1296,6 @@ void pci_restore_aer_state(struct pci_dev *dev);
> static inline void pci_no_aer(void) { }
> static inline void pci_aer_init(struct pci_dev *d) { }
> static inline void pci_aer_exit(struct pci_dev *d) { }
> -static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { }
> static inline int pci_aer_clear_status(struct pci_dev *dev) { return -EINVAL; }
> static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL; }
> static inline void pci_save_aer_state(struct pci_dev *dev) { }
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index c5bce25df51cb..2d9d40528e709 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -1185,7 +1185,20 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
>
> static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> {
> + bool cxl_pending = false;
> +
> cxl_rch_handle_error(dev, info);
> +
> + if (is_cxl_error(dev, info))
> + cxl_pending |= cxl_forward_error(dev, info);
> +
> + /*
> + * Wait for UCE CXL work to complete before AER recovery
> + * tears down the device. CE can run asynchronously.
> + */
> + if (cxl_pending && info->severity != AER_CORRECTABLE)
> + cxl_proto_err_flush();
> +
> pci_aer_handle_error(dev, info);
> pci_dev_put(dev);
> }
> diff --git a/drivers/pci/pcie/aer_cxl_vh.c b/drivers/pci/pcie/aer_cxl_vh.c
> index 93bed07936100..ecf47bba0c9d4 100644
> --- a/drivers/pci/pcie/aer_cxl_vh.c
> +++ b/drivers/pci/pcie/aer_cxl_vh.c
> @@ -49,8 +49,22 @@ bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info)
> if (!info || !info->is_cxl)
> return false;
>
> - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
> + /*
> + * RC_END (Restricted CXL Device) is not included here because RC_END
> + * reports errors on behalf of upstream RCH Downstream Port and thus
> + * requires a unique discovery detailed in CXL4.0 spec (12.2.1.1).
> + * The RCH device error discovery and RC_END forwarding flow begins
> + * in cxl_rch_handle_error().
> + */
> + switch (pci_pcie_type(pdev)) {
> + case PCI_EXP_TYPE_ENDPOINT:
> + case PCI_EXP_TYPE_ROOT_PORT:
> + case PCI_EXP_TYPE_UPSTREAM:
> + case PCI_EXP_TYPE_DOWNSTREAM:
> + break;
> + default:
> return false;
> + }
>
> return is_aer_internal_error(info);
> }
^ permalink raw reply
* Re: [RFC] cxl: Device protocol AER injection
From: Jonathan Cameron @ 2026-07-20 21:04 UTC (permalink / raw)
To: Terry Bowman, Ashok Raj
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma
In-Reply-To: <20260717225700.3543801-1-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:57:00 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
> This patch is intended to provide a method of testing the recently submitted
> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
> here:
>
> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
Hi Terry,
https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com
Works fine. Generally you can crop that end bit off the links.
>
> The changes in this patch will allow CXL RAS protocol testing by injecting
> AER errors using AER EINJ. The RAS register block status is updated
> using a central function to augment RAS register block returned by
> to_ras_base(). This supports all CXL devices including Root Ports,
> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
> Downstream Ports.
Why is this an RFC rather than a final proposal? There should always
be something to give the reviewer that info in the patch description.
I'd actually be tempted to throw a cover letter in to have somewhere
out of the way to put that information.
Is it simply because it only makes sense once the other seris lands.
>
> Add debugfs-based CXL protocol error injection for testing CXL RAS
> error handling paths. Injects CXL RAS protocol errors using AER internal
> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
>
> RAS CXL status is set using to_ras_base() function override when kernel config
> CONFIG_CXL_PROTO_AER_EINJ is enabled.
>
> Usage:
> echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
> /sys/kernel/debug/cxl/aer_einj_inject
>
> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
> can invoke AER injection directly. Export aer_inject() with
> EXPORT_SYMBOL_GPL.
>
> Make cxl_debugfs non-static in port.c and declare it extern in
> core.h so the debugfs file can be created under the existing CXL
> debugfs root.
>
> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
+CC Ashok,
Various thing inline. Mostly this review is a bit superficial as I'd like
ideally to see a cleaner separation of this at level of files etc.
Thanks,
Jonathan
> ---
> drivers/cxl/Kconfig | 13 +++
> drivers/cxl/core/core.h | 21 ++++
> drivers/cxl/core/port.c | 2 +-
> drivers/cxl/core/ras.c | 208 ++++++++++++++++++++++++++++++++++
> drivers/cxl/core/ras_rch.c | 12 ++
> drivers/pci/pcie/aer_inject.c | 29 ++---
> include/linux/aer.h | 15 +++
> 7 files changed, 281 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index 80aeb0d556bd7..ef449228b2549 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -238,6 +238,19 @@ config CXL_RAS
> def_bool y
> depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>
> +config CXL_PROTO_AER_EINJ
> + bool "CXL: RAS Protocol Error Injection using AER EINJ"
> + depends on CXL_RAS
> + depends on PCIEAER_INJECT
Do we think anyone who has CXL and PCIEAER_INJECT support will want
to carefully not build this? I'm just wondering if we can avoid asking
the question and base the built or not on the combination of those.
> + help
> + Enable debugfs-based CXL protocol error injection. Writes to
> + /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
> + errors using the AER internal error inject interface.
> +
> + This is a debug/test facility. Say N for production kernels.
> +
> + If unsure say N.
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index a55a4e409feda..91910d2bb5d39 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
...
> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>
> resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
> struct cxl_dport *dport);
> +
> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
Do we need the ifdefs? If the option isn't built none of this should get
used. So small benefit.
> +
> +#define AER_REGISTER_SIZE 5
> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
> +
> +struct cxl_aer_einj {
> + int correctable;
> + bool is_rch;
> + struct mutex *lock;
> + struct device *dev;
> + u32 aer_registers[AER_REGISTER_SIZE];
> + u32 ras_registers[RAS_REGISTER_SIZE];
> +};
> +
> +extern struct cxl_aer_einj cxl_aer_einj;
> +#endif
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index d77208af41e03..d41deea899d30 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -3,6 +3,7 @@
>
> #include <linux/pci.h>
> #include <linux/aer.h>
> +#include <linux/debugfs.h>
> #include <cxl/event.h>
> #include <cxlmem.h>
> #include <cxlpci.h>
> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
> }
> static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +
Unless very strong reasons for it, generally don't do #if stuff in c files.
Just have a separate c file for this. Otherwise it hurts readability and
we tend to loose the clean separation over time. A file makes that less
likely.
> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
Needs a comment for what data it is protecting.
> +
> +struct cxl_aer_einj cxl_aer_einj = {
> + .lock = &cxl_aer_einj_mutex,
> +};
> +
> +static const char cxl_aer_einj_usage[] =
> + "ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
> +
> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
> + u32 aer_status, u32 ras_status)
> +{
> + /* RCD errors are signaled as internal errors on the associated RCEC */
> + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
> + if (!pdev->rcec)
> + return -ENODEV;
> + pdev = pdev->rcec;
> + }
> +
> + struct aer_error_inj einj = {
> + .bus = pdev->bus->number,
> + .dev = PCI_SLOT(pdev->devfn),
> + .fn = PCI_FUNC(pdev->devfn),
> + .domain = pci_domain_nr(pdev->bus),
> + };
> + int ret;
> + int aer_offset;
> + int ras_offset;
> +
> + if (correctable) {
> + einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
> + aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
> + ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
Given these are offsets into cxl_aer_einj.aer_registers / ras_registers
can we use sizeof(*cxl_aer_einj.aer_registers) etc
> + } else {
> + einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
> + aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
> + ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
> + }
> +
> + cxl_aer_einj.correctable = correctable;
> + cxl_aer_einj.aer_registers[aer_offset] = aer_status;
> + cxl_aer_einj.ras_registers[ras_offset] = ras_status;
> +
> + ret = aer_inject(&einj);
> + if (ret) {
> + pr_err("cxl-einj: aer_inject failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t cxl_aer_einj_write(struct file *file,
> + const char __user *ubuf,
> + size_t count, loff_t *ppos)
Might as well wrap to 80 chars and save a line.
> +{
...
> +
> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
> + if (!port) {
> + dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
> + return -ENODEV;
> + }
> +
> + if (!to_ras_base(port, dport)) {
> + dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
> + return -ENODEV;
> + }
> +
> + cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_get(pdev);
> + cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : pci_dev_get(&pdev->dev);
Or use an if else for both is_rch based choices.
Lazy me wonders.... Can we just grab a reference to the dev.parent for is_rch and
simplify the code? We don't really need it I think but it is harmless.
> + ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
> + aer_status, ras_status);
> + if (ret) {
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_put(pdev);
> + cxl_aer_einj.dev = NULL;
> + pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
> + return ret;
> + }
> +
> + return count;
> +}
> +
> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
> + size_t count, loff_t *ppos)
> +{
> + return simple_read_from_buffer(ubuf, count, ppos,
> + cxl_aer_einj_usage,
Probably wrap this to push that up a line.
> + sizeof(cxl_aer_einj_usage) - 1);
> +}
> +
> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> +{
> + if (dport) {
> + if (cxl_aer_einj.is_rch) {
> + if (cxl_aer_einj.dev == dport->dport_dev) {
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
Given the output of this is always force cast, maybe move that force up to the caller?
> + }
> + } else {
> + if (cxl_aer_einj.dev == dport->dport_dev) {
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
Not locally obvious why a thing called to_einj_ras_base should put anything it didn't
get. I think this needs a restructure to more obviously be tidying up references
that were held over the queue. At very leads needs a comment.
/* Reference held from X no longer needed so drop */
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
> + }
> + }
> + } else if (!cxl_aer_einj.is_rch) {
> + struct device *dev = is_cxl_endpoint(port) ?
> + port->uport_dev->parent : port->uport_dev;
> +
> + if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
> + }
> + }
> +
> + return NULL;
> +}
> +#endif
> +
> static void cxl_unmask_proto_interrupts(struct device *dev)
> {
> struct pci_dev *pdev;
> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> if (!port)
> return NULL;
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
Wrap with
if (IS_ENABLED()) to keep it visible to the compiler.
> + if (cxl_aer_einj.dev) {
> + void __iomem *einj = to_einj_ras_base(port, dport);
> + if (einj)
> + return einj;
> + }
> +#endif
> +
> if (dport)
> return dport->regs.ras;
>
> @@ -458,10 +656,20 @@ void cxl_ras_init(void)
> cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
> cxl_register_proto_err_work(&cxl_proto_err_work,
> cxl_proto_err_do_flush);
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> + cxl_ras_create_debugfs(cxl_debugfs);
stub that in a header.
> +#endif
> }
>
> void cxl_ras_exit(void)
> {
> cxl_unregister_proto_err_work();
> cxl_cper_unregister_prot_err_work();
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
As below. Use
if (IS_ENABLED())
and keep everything visible.
> + if (cxl_aer_einj.dev) {
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> + cxl_aer_einj.dev = NULL;
> + }
> +#endif
> }
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index 14bb3bdb2d092..5071cf86e4a68 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -110,6 +110,14 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
> if (!dport)
> return;
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> + if (cxl_aer_einj.is_rch && cxl_aer_einj.dev) {
> + severity = cxl_aer_einj.correctable ?
> + AER_CORRECTABLE : AER_FATAL;
> + goto handle_ras;
> + }
Use instead
if (IS_ENABLED(CONFIG_CXL_PROTO_AR_EINJ))
then compiler can see the code (but remove it) which means
you don't need the dance around the label below.
In general follow this pattern anyway rather than #if
when you can.
> +#endif
> +
> if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
> return;
>
> @@ -117,6 +125,10 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
> return;
>
> pci_print_aer(pdev, severity, &aer_regs);
> +
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +handle_ras:
> +#endif
> if (severity == AER_CORRECTABLE)
> cxl_handle_cor_ras(dport->port, dport,
> to_ras_base(port, dport), pdev->dsn);
> diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
> index 09bfc7194ef31..b313adef680ae 100644
> --- a/drivers/pci/pcie/aer_inject.c
> +++ b/drivers/pci/pcie/aer_inject.c
> @@ -14,6 +14,7 @@
>
> #define dev_fmt(fmt) "aer_inject: " fmt
>
> +#include <linux/aer.h>
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/interrupt.h>
> @@ -31,19 +32,6 @@
> static bool aer_mask_override;
> module_param(aer_mask_override, bool, 0);
>
> -struct aer_error_inj {
> - u8 bus;
> - u8 dev;
> - u8 fn;
> - u32 uncor_status;
> - u32 cor_status;
> - u32 header_log0;
> - u32 header_log1;
> - u32 header_log2;
> - u32 header_log3;
> - u32 domain;
> -};
> -
> struct aer_error {
> struct list_head list;
> u32 domain;
> @@ -316,7 +304,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
> return 0;
> }
>
> -static int aer_inject(struct aer_error_inj *einj)
> +int aer_inject(struct aer_error_inj *einj)
> {
> struct aer_error *err, *rperr;
> struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
> @@ -332,10 +320,14 @@ static int aer_inject(struct aer_error_inj *einj)
> dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
> if (!dev)
> return -ENODEV;
> - rpdev = pcie_find_root_port(dev);
> - /* If Root Port not found, try to find an RCEC */
> - if (!rpdev)
> - rpdev = dev->rcec;
> + if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)
{ }
as the else is multiline (see coding standard)
Maybe need a comment for why it might be an RCEC for injection.
Is this an RCH specific path where there is nothing else to target?
> + rpdev = dev;
> + else {
> + rpdev = pcie_find_root_port(dev);
> + /* If Root Port not found, try to find an RCEC */
> + if (!rpdev)
> + rpdev = dev->rcec;
> + }
> if (!rpdev) {
> pci_err(dev, "Neither Root Port nor RCEC found\n");
> ret = -ENODEV;
> @@ -482,6 +474,7 @@ static int aer_inject(struct aer_error_inj *einj)
> pci_dev_put(dev);
> return ret;
> }
> +EXPORT_SYMBOL_GPL(aer_inject);
I wonder if we want to restrict this to specific modules?
One for Bjorn probably.
>
> static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
> size_t usize, loff_t *off)
> diff --git a/include/linux/aer.h b/include/linux/aer.h
> index b3657b80564b9..65c22ba597657 100644
> --- a/include/linux/aer.h
> +++ b/include/linux/aer.h
> @@ -27,6 +27,21 @@
> struct pci_dev;
> struct work_struct;
>
> +struct aer_error_inj {
> + u8 bus;
> + u8 dev;
> + u8 fn;
> + u32 uncor_status;
> + u32 cor_status;
> + u32 header_log0;
> + u32 header_log1;
> + u32 header_log2;
> + u32 header_log3;
> + u32 domain;
> +};
> +
> +int aer_inject(struct aer_error_inj *einj);
> +
> struct pcie_tlp_log {
> union {
> u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];
^ permalink raw reply
* Re: [PATCH] Documentation: wmi: lenovo-wmi-other: Document intentional BIOS misspelling
From: Yahya Toubali @ 2026-07-20 21:06 UTC (permalink / raw)
To: Jonathan Corbet
Cc: Yahya Toubali, Weijie Yuan, mpearson-lenovo, linux-doc,
platform-driver-x86, linux-kernel
In-Reply-To: <87jyqp4yf4.fsf@trenco.lwn.net>
Hi Jon,
Thank you for the link. I have read the policy carefully.
To be completely honest: I used an LLM to identify potential typos and
whitespace issues across the documentation files, as well as to draft
the initial commit messages and email syntax.
I see now why sending automated, low-value patches creates
unnecessary churn for maintainers. I take full responsibility for
those submissions.
Going forward, I will not rely on LLM output for patch generation. Any
future contributions from me will consist strictly of manual technical
work and substantive fixes that I thoroughly understand and verify
myself.
Appreciate your patience and guidance,
Yahya
^ permalink raw reply
* Re: [PATCH v18 01/13] cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
From: Jonathan Cameron @ 2026-07-20 21:26 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma
In-Reply-To: <20260717222706.3540281-2-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:26:54 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
> cxl_rch_get_aer_severity() classifies RCH Downstream Port uncorrectable
> errors as fatal or non-fatal by ANDing uncorrectable status with
> PCI_ERR_ROOT_FATAL_RCV. This is wrong because PCI_ERR_ROOT_FATAL_RCV is a
> Root Error Status register bit (bit 6), not a severity bit. ANDing it
> against uncorrectable status tests a reserved bit and produces incorrect
> severity classification.
>
> Fix by ANDing the unmasked uncor_status against uncor_severity. Per
> PCIe Base Spec r6.0 Section 7.8.4.4, each bit in the Uncorrectable
> Error Severity register indicates whether the corresponding error is
> fatal (1) or non-fatal (0).
>
> Fixes: 6ac07883dbb5 ("cxl/pci: Add RCH downstream port error logging")
> Cc: stable@vger.kernel.org
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>
One trivial thing below, otherwise LGTM
Either way
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> ---
>
> Changes in v17 -> v18:
> - New patch.
> ---
> drivers/cxl/core/ras_rch.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index 0a8b3b9b63884..44b335d560708 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -80,7 +80,8 @@ static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
> int *severity)
> {
> if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
> - if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
> + if ((aer_regs->uncor_status & ~aer_regs->uncor_mask) &
This bit looks familiar (see line above!) Worth a local variable maybe?
> + aer_regs->uncor_severity)
> *severity = AER_FATAL;
> else
> *severity = AER_NONFATAL;
^ permalink raw reply
* Re: [PATCH v18 02/13] acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
From: Jonathan Cameron @ 2026-07-20 21:41 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma
In-Reply-To: <20260717222706.3540281-3-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:26:55 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
> The CXL CPER work registration and unregistration helpers in
> drivers/acpi/apei/ghes.c acquire cxl_cper_work_lock and
> cxl_cper_prot_err_work_lock with guard(spinlock), which leaves local
> interrupts enabled. The corresponding post paths
> (cxl_cper_post_event(), cxl_cper_post_prot_err()) execute in hard IRQ
> context (they are called from the GHES error notification path) and
> acquire the same locks via guard(spinlock_irqsave).
>
> If a CPU is holding one of these locks via guard(spinlock) when a
> GHES interrupt arrives on the same CPU, the IRQ handler spins on the
> held lock waiting for it to release, while the lock holder is
> preempted by the IRQ. The result is a deadlock.
>
> Convert both locks from spinlock_t to raw_spinlock_t and use
> guard(raw_spinlock_irqsave) at all call sites. On PREEMPT_RT kernels
> spinlock_t is backed by rt_mutex and sleeping from hard IRQ context is
> not permitted; raw_spinlock_t is safe in both contexts.
>
> Add WARN_ONCE to both register functions to surface double-registration
> bugs at runtime.
>
> Restructure both unregister functions to clear the global work pointer
> under the lock before calling cancel_work_sync(), closing the window
> where a CPER interrupt could schedule work on a pointer about to be
> freed. Add kfifo_reset() after cancel_work_sync() so stale entries
> are not replayed on next module load.
>
> Both kfifos are single-consumer: only one work_struct is registered at
> a time, enforced by the WARN_ONCE guard in the register functions.
> kfifo_reset() is safe outside the lock because cancel_work_sync() has
> already quiesced the consumer, and no new consumer can register until
> the current module exit completes and a fresh module init runs.
>
> Remove the now-redundant cancel_work_sync() call from
> cxl_pci_driver_exit() - cxl_cper_unregister_work() handles quiescing
> internally.
>
> Reported-by: Sashiko <sashiko@linuxfoundation.org>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> Fixes: 5e4a264bf8b5 ("acpi/ghes: Process CXL Component Events")
> Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors")
> Cc: stable@vger.kernel.org
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
>
> ---
>
> Changes in v17 -> v18:
> - New patch.
> ---
> drivers/acpi/apei/ghes.c | 50 ++++++++++++++++++++++++++--------------
> drivers/cxl/pci.c | 1 -
> 2 files changed, 33 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 3236a3ce79d6b..ca7a138c1ff2e 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -749,7 +749,7 @@ static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
> CXL_CPER_PROT_ERR_FIFO_DEPTH);
>
> /* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
> -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
> +static DEFINE_RAW_SPINLOCK(cxl_cper_prot_err_work_lock);
> struct work_struct *cxl_cper_prot_err_work;
>
> static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
> @@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
> if (cxl_cper_sec_prot_err_valid(prot_err))
> return;
>
> - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
> + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
>
> if (!cxl_cper_prot_err_work)
> return;
> @@ -780,10 +780,11 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
>
> int cxl_cper_register_prot_err_work(struct work_struct *work)
> {
> - if (cxl_cper_prot_err_work)
> - return -EINVAL;
> + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
>
> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
> + if (WARN_ONCE(cxl_cper_prot_err_work,
> + "CPER-CXL kfifo consumer already registered\n"))
> + return -EINVAL;
> cxl_cper_prot_err_work = work;
> return 0;
> }
> @@ -791,11 +792,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
>
> int cxl_cper_unregister_prot_err_work(struct work_struct *work)
> {
> - if (cxl_cper_prot_err_work != work)
> - return -EINVAL;
> + scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) {
> + if (WARN_ONCE(cxl_cper_prot_err_work != work,
> + "CPER-CXL kfifo consumer mismatch on unregister\n"))
> + return -EINVAL;
> + cxl_cper_prot_err_work = NULL;
> + }
> +
> + cancel_work_sync(work);
> +
> + /* Discard stale entries so they are not replayed on next module load */
> + kfifo_reset(&cxl_cper_prot_err_fifo);
>
> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
> - cxl_cper_prot_err_work = NULL;
> return 0;
> }
> EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
> @@ -811,7 +819,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
> DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
>
> /* Synchronize schedule_work() with cxl_cper_work changes */
> -static DEFINE_SPINLOCK(cxl_cper_work_lock);
> +static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock);
> struct work_struct *cxl_cper_work;
>
> static void cxl_cper_post_event(enum cxl_event_type event_type,
> @@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
> return;
> }
>
> - guard(spinlock_irqsave)(&cxl_cper_work_lock);
> + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
>
> if (!cxl_cper_work)
> return;
> @@ -849,10 +857,11 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
>
> int cxl_cper_register_work(struct work_struct *work)
> {
> - if (cxl_cper_work)
> + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
> + if (WARN_ONCE(cxl_cper_work,
> + "CXL CPER kfifo consumer already registered\n"))
> return -EINVAL;
>
> - guard(spinlock)(&cxl_cper_work_lock);
> cxl_cper_work = work;
> return 0;
> }
> @@ -860,11 +869,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
>
> int cxl_cper_unregister_work(struct work_struct *work)
> {
> - if (cxl_cper_work != work)
> - return -EINVAL;
> + scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) {
> + if (WARN_ONCE(cxl_cper_work != work,
> + "CXL CPER kfifo consumer mismatch on unregister\n"))
> + return -EINVAL;
> + cxl_cper_work = NULL;
> + }
> +
> + cancel_work_sync(work);
> +
> + /* Discard stale entries so they are not replayed on next module load */
> + kfifo_reset(&cxl_cper_fifo);
>
> - guard(spinlock)(&cxl_cper_work_lock);
> - cxl_cper_work = NULL;
> return 0;
> }
> EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 267c679b0b3c2..7c6faee7f85ed 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -1083,7 +1083,6 @@ static int __init cxl_pci_driver_init(void)
> static void __exit cxl_pci_driver_exit(void)
> {
> cxl_cper_unregister_work(&cxl_cper_work);
> - cancel_work_sync(&cxl_cper_work);
> pci_unregister_driver(&cxl_pci_driver);
> }
>
^ permalink raw reply
* Re: [PATCH v18 07/13] PCI/CXL: Add RCH support to CXL handlers
From: Dave Jiang @ 2026-07-20 21:47 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-8-terry.bowman@amd.com>
On 7/17/26 3:27 PM, Terry Bowman wrote:
> Restricted CXL Host (RCH) error handling is a separate path from the
> new CXL Port error handling flow. Fold RCH error handling into the
> Port flow so both share a common entry point.
>
> Update cxl_rch_handle_error_iter() to forward RCH protocol errors
> through the AER-CXL kfifo. Change cxl_rch_handle_error() return type
> from void to bool so handle_error_source() can determine whether work
> was enqueued and call cxl_proto_err_flush() before AER recovery
> proceeds.
>
> For RC_END devices, __cxl_proto_err_work_fn() calls
> cxl_handle_rdport_errors() to process RCH Downstream Port errors,
> then falls through to the VH path for RC_END Endpoint handling.
>
> An RCD uncorrectable CXL RAS error now panics via cxl_do_recovery().
> Before this patch the RCH Downstream Port UCE path called
> cxl_handle_ras() but ignored its return value - no panic. After this
> patch the same condition calls cxl_do_recovery() which panics on
> confirmed UCE. The Endpoint UCE path already panicked at the parent
> commit. This matches the panic policy added in the common CXL Port
> protocol error flow.
>
> Remove cxl_cor_error_detected() and its .cor_error_detected
> registration in cxl_error_handlers. Correctable Endpoint errors are
> now routed through the AER-CXL kfifo like all other CXL protocol
> errors.
>
> Drop the cxlds->rcd / cxl_handle_rdport_errors(cxlds) branches from
> cxl_error_detected(). RCH downstream port error handling is now
> performed by __cxl_proto_err_work_fn() via the kfifo path, which
> calls cxl_handle_rdport_errors(pdev) before the common dispatch.
>
> Change cxl_handle_rdport_errors() to take a struct pci_dev * instead
> of a struct cxl_dev_state *, matching the new caller context. Re-fetch
> dport under guard() to close the TOCTOU window between
> cxl_pci_find_port()'s lockless xa_load() and the first dereference of
> the returned pointer.
>
> Change find_cxl_port_by_dev() RC_END lookup from
> find_cxl_port_by_dport(dev->parent) to find_cxl_port_by_uport(dev),
> matching the Endpoint lookup path. RC_END Endpoint port resolution
> uses the uport (the RC_END device itself), while the separate RCH
> Downstream Port lookup is handled by cxl_handle_rdport_errors().
>
> The RCH Downstream Port and the RCD Endpoint (RC_END) are separate
> devices with independent RAS register blocks. cxl_handle_rdport_errors()
> handles the RCH Downstream Port RAS. RCD Endpoint (RC_END) is handled in
> cxl_handle_proto_error().
>
> Use to_ras_base() in cxl_handle_rdport_errors() instead of referencing
> dport->regs.ras directly. Make to_ras_base() non-static in ras.c and
> declare it in core.h so ras_rch.c can access it. Route all RAS base address lookups
> through a single helper to prepare for CXL RAS error injection testing
> that follows this series.
>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17->v18:
> - Pass &pdev->dev instead of dport->port->uport_dev in
> cxl_handle_rdport_errors() to avoid dropping RCH trace events.
> - Document trace event attribution change.
> - Document removal of cxl_cor_error_detected() and cxlds->rcd branches.
> - Capitalize Endpoint per PCI spec convention.
> - Use to_ras_base() in cxl_handle_rdport_errors() to centralize RAS base
> address lookup in preparation for error injection testing.
>
> Changes in v16->v17:
> - Drop now-dead cxlds->rcd branches from cxl_{cor_,}error_detected().
> - Drop duplicate subject line from commit body.
> - Document panic-on-uncorrectable behavior change for RCD path.
> - Document trace event device-name change (memN -> PCI BDF) for RCH path.
> - Rewrite cxl_handle_proto_error() RC_END comment to clarify RCD/RCH shared
> interrupt relationship
> - Rewrite commit message
>
> Changes in v16:
> - New commit
> ---
> drivers/cxl/core/core.h | 10 +++++--
> drivers/cxl/core/ras.c | 50 ++++++++--------------------------
> drivers/cxl/core/ras_rch.c | 16 ++++++-----
> drivers/cxl/cxlpci.h | 3 --
> drivers/cxl/pci.c | 1 -
> drivers/pci/pcie/aer.c | 4 +--
> drivers/pci/pcie/aer_cxl_rch.c | 39 ++++++++++++--------------
> drivers/pci/pcie/portdrv.h | 4 +--
> 8 files changed, 48 insertions(+), 79 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 7c70bea06c2db..272634ff2615b 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -191,7 +191,8 @@ void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base);
> void cxl_dport_map_rch_aer(struct cxl_dport *dport);
> void cxl_disable_rch_root_ints(struct cxl_dport *dport);
> -void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds);
> +void cxl_handle_rdport_errors(struct pci_dev *pdev);
> +void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport);
> void devm_cxl_dport_ras_setup(struct cxl_dport *dport);
> #else
> static inline void cxl_ras_init(void) { }
> @@ -205,7 +206,12 @@ static inline void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> static inline void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base) { }
> static inline void cxl_dport_map_rch_aer(struct cxl_dport *dport) { }
> static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { }
> -static inline void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { }
> +static inline void cxl_handle_rdport_errors(struct pci_dev *pdev) { }
> +static inline void __iomem *to_ras_base(struct cxl_port *port,
> + struct cxl_dport *dport)
> +{
> + return NULL;
> +}
> static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { }
> #endif /* CONFIG_CXL_RAS */
>
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index b190e69c2d415..9a142abcf4f8b 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -218,7 +218,8 @@ void devm_cxl_port_ras_setup(struct cxl_port *port)
> }
> EXPORT_SYMBOL_NS_GPL(devm_cxl_port_ras_setup, "CXL");
>
> -static void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> +
> +void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> {
> if (!port)
> return NULL;
> @@ -324,37 +325,7 @@ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
> return true;
> }
>
> -void cxl_cor_error_detected(struct pci_dev *pdev)
> -{
> - guard(device)(&pdev->dev);
> - if (!pdev->dev.driver)
> - return;
> -
> - struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_uport(&pdev->dev);
> - if (!port)
> - return;
> -
> - if (is_cxl_restricted(pdev)) {
> - struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
> - struct cxl_memdev *cxlmd = cxlds->cxlmd;
>
> - scoped_guard(device, &cxlmd->dev) {
> - cxl_handle_rdport_errors(cxlds);
> - }
> - }
> -
> - scoped_guard(device, &port->dev) {
> - if (!port->dev.driver) {
> - dev_warn(&pdev->dev,
> - "%s: port disabled, abort error handling\n",
> - dev_name(&port->dev));
> - return;
> - }
> -
> - cxl_handle_cor_ras(port->uport_dev, to_ras_base(port, NULL));
> - }
> -}
> -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)
> @@ -365,14 +336,6 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> if (!port)
> return PCI_ERS_RESULT_DISCONNECT;
>
> - if (is_cxl_restricted(pdev)) {
> - struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
> - struct cxl_memdev *cxlmd = cxlds->cxlmd;
> -
> - scoped_guard(device, &cxlmd->dev) {
> - cxl_handle_rdport_errors(cxlds);
> - }
> - }
>
> scoped_guard(device, &port->dev) {
> if (!port->dev.driver) {
> @@ -429,6 +392,15 @@ static void cxl_handle_proto_error(struct pci_dev *pdev, struct cxl_port *port,
>
> static void __cxl_proto_err_work_fn(struct cxl_proto_err_work_data *wd)
> {
> + /*
> + * For RC_END (RCD) devices, handle RCH Downstream Port errors
> + * first. cxl_handle_rdport_errors() does its own port lookup
> + * and locking, keeping the Downstream Port lock separate from the
> + * Endpoint Port lock taken below.
> + */
> + if (is_cxl_restricted(wd->pdev))
> + cxl_handle_rdport_errors(wd->pdev);
> +
> struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&wd->pdev->dev, NULL);
> if (!port) {
> dev_err_ratelimited(&wd->pdev->dev,
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index 44b335d560708..f2d2fb83758b9 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -1,7 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /* Copyright(c) 2025 AMD Corporation. All rights reserved. */
>
> -#include <linux/types.h>
> #include <linux/aer.h>
> #include "cxl.h"
> #include "core.h"
> @@ -96,18 +95,21 @@ static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
> return false;
> }
>
> -void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
> +void cxl_handle_rdport_errors(struct pci_dev *pdev)
> {
> - struct pci_dev *pdev = to_pci_dev(cxlds->dev);
> struct aer_capability_regs aer_regs;
> struct cxl_dport *dport;
> int severity;
>
> - struct cxl_port *port __free(put_cxl_port) =
> - cxl_pci_find_port(pdev, &dport);
> + struct cxl_port *port __free(put_cxl_port) = cxl_pci_find_port(pdev, NULL);
> if (!port)
> return;
>
> + guard(device)(&port->dev);
> + dport = cxl_find_dport_by_dev(port, pdev->dev.parent);
> + if (!dport)
> + return;
> +
> if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
> return;
>
> @@ -116,7 +118,7 @@ void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
>
> pci_print_aer(pdev, severity, &aer_regs);
> if (severity == AER_CORRECTABLE)
> - cxl_handle_cor_ras(&cxlds->cxlmd->dev, dport->regs.ras);
> + cxl_handle_cor_ras(&pdev->dev, to_ras_base(port, dport));
> else
> - cxl_handle_ras(&cxlds->cxlmd->dev, dport->regs.ras);
> + cxl_do_recovery(pdev, dport->port, dport);
> }
> diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
> index b826eb53cf7ba..06c46adcf0f6c 100644
> --- a/drivers/cxl/cxlpci.h
> +++ b/drivers/cxl/cxlpci.h
> @@ -89,14 +89,11 @@ struct cxl_dev_state;
> void read_cdat_data(struct cxl_port *port);
>
> #ifdef CONFIG_CXL_RAS
> -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);
> void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport);
> void devm_cxl_port_ras_setup(struct cxl_port *port);
> #else
> -static inline void cxl_cor_error_detected(struct pci_dev *pdev) { }
> -
> static inline pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> pci_channel_state_t state)
> {
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 7c6faee7f85ed..5c21db36073fe 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -1004,7 +1004,6 @@ static const struct pci_error_handlers cxl_error_handlers = {
> .error_detected = cxl_error_detected,
> .slot_reset = cxl_slot_reset,
> .resume = cxl_error_resume,
> - .cor_error_detected = cxl_cor_error_detected,
> .reset_done = cxl_reset_done,
> };
>
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index 2d9d40528e709..0bd23a65e7ebc 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -1185,9 +1185,7 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
>
> static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> {
> - bool cxl_pending = false;
> -
> - cxl_rch_handle_error(dev, info);
> + bool cxl_pending = cxl_rch_handle_error(dev, info);
>
> if (is_cxl_error(dev, info))
> cxl_pending |= cxl_forward_error(dev, info);
> diff --git a/drivers/pci/pcie/aer_cxl_rch.c b/drivers/pci/pcie/aer_cxl_rch.c
> index e471eefec9c40..683712fc965ff 100644
> --- a/drivers/pci/pcie/aer_cxl_rch.c
> +++ b/drivers/pci/pcie/aer_cxl_rch.c
> @@ -34,42 +34,37 @@ static bool cxl_error_is_native(struct pci_dev *dev)
> return (pcie_ports_native || host->native_aer);
> }
>
> +struct cxl_rch_error_ctx {
> + struct aer_err_info *info;
> + bool enqueued;
> +};
> +
> static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
> {
> - struct aer_err_info *info = (struct aer_err_info *)data;
> - const struct pci_error_handlers *err_handler;
> + struct cxl_rch_error_ctx *ctx = data;
>
> if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
> return 0;
>
> - guard(device)(&dev->dev);
> -
> - err_handler = dev->driver ? dev->driver->err_handler : NULL;
> - if (!err_handler)
> - return 0;
> -
> - if (info->severity == AER_CORRECTABLE) {
> - if (err_handler->cor_error_detected)
> - err_handler->cor_error_detected(dev);
> - } else if (err_handler->error_detected) {
> - if (info->severity == AER_NONFATAL)
> - err_handler->error_detected(dev, pci_channel_io_normal);
> - else if (info->severity == AER_FATAL)
> - err_handler->error_detected(dev, pci_channel_io_frozen);
> - }
> + if (cxl_forward_error(dev, ctx->info))
> + ctx->enqueued = true;
> return 0;
> }
>
> -void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> +bool cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> {
> + struct cxl_rch_error_ctx ctx = { .info = info };
> +
> /*
> - * Internal errors of an RCEC indicate an AER error in an
> - * RCH's downstream port. Check and handle them in the CXL.mem
> - * device driver.
> + * An RCEC AER internal error indicates an error in an
> + * associated RCH Downstream Port or RC_END device or both.
> + * Forward to the cxl_core module for handling.
> */
> if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
> is_aer_internal_error(info))
> - pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
> + pcie_walk_rcec(dev, cxl_rch_handle_error_iter, &ctx);
> +
> + return ctx.enqueued;
> }
>
> static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
> diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
> index fd203010877bf..807bca90dee0e 100644
> --- a/drivers/pci/pcie/portdrv.h
> +++ b/drivers/pci/pcie/portdrv.h
> @@ -128,14 +128,14 @@ struct aer_err_info;
>
> #ifdef CONFIG_CXL_RAS
> bool is_aer_internal_error(struct aer_err_info *info);
> -void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info);
> +bool cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info);
> void cxl_rch_enable_rcec(struct pci_dev *rcec);
> bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info);
> bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info);
> void cxl_proto_err_flush(void);
> #else
> static inline bool is_aer_internal_error(struct aer_err_info *info) { return false; }
> -static inline void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info) { }
> +static inline bool cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info) { return false; }
> static inline void cxl_rch_enable_rcec(struct pci_dev *rcec) { }
> static inline bool is_cxl_error(struct pci_dev *pdev, struct aer_err_info *info) { return false; }
> static inline bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info) { return false; }
^ permalink raw reply
* Re: [PATCH v7 03/12] PCI: liveupdate: Track incoming preserved PCI devices
From: David Matlack @ 2026-07-20 21:54 UTC (permalink / raw)
To: Pasha Tatashin
Cc: kexec, linux-doc, linux-kernel, linux-mm, linux-pci,
Adithya Jayachandran, Alexander Graf, Alex Williamson,
Bjorn Helgaas, Chris Li, David Rientjes, Jacob Pan,
Jason Gunthorpe, Jonathan Corbet, Josh Hilke, Leon Romanovsky,
Lukas Wunner, Mike Rapoport, Parav Pandit, Pranjal Shrivastava,
Pratyush Yadav, Saeed Mahameed, Samiullah Khawaja, Shuah Khan,
Vipin Sharma, William Tu, Yi Liu
In-Reply-To: <178432481253.189683.3297727348836286619.b4-review@b4>
On Fri, Jul 17, 2026 at 2:47 PM Pasha Tatashin
<pasha.tatashin@soleen.com> wrote:
>
> On Fri, 10 Jul 2026 21:26:06 +0000, David Matlack <dmatlack@google.com> wrote:
> > diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c
> > index 03075ce06ac9..df6a02240aa4 100644
> > --- a/drivers/pci/liveupdate.c
> > +++ b/drivers/pci/liveupdate.c
> > @@ -298,6 +377,87 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
> > [ ... skip 76 lines ... ]
> > + /*
> > + * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
> > + * that dev->liveupdate.incoming cannot get freed while the PCI core
> > + * has a pointer to it. It's better to leak the incoming FLB than do a
> > + * use-after-free if driver does not call pci_liveupdate_finish().
> > + */
>
> I am confused by this. Each preserved PCI device must have an associated
> FD preserved with it via LUO. I.e., vfiofd would need to be preserved. If
> the vfiofd was not reclaimed, and finish is not possible, that vfiofd
> would still be owned by LUO, and therefore PCI FLB refcount would stay
> positive.
>
> However, if finish is possible, and this is the last vfiofd that is
> finished, FLB will be freed as soon as the reference count reaches zero,
> which I would think is the expected behavior.
>
> What is the point of holding a reference here, instead of only for the
> duration of FLB access, i.e. to make sure we are accessing a valid data?
The duration of the access is from here until pci_liveupdate_finish()
because that it when the pointer (dev->liveupdate.incoming) is
cleared. So that is why the PCI core holds the reference from here
until pci_liveupdate_finish().
We could avoid this by deleteing dev->liveupdate.incoming and,
instead, fetching the incoming FLB and doing the xarray lookup every
atime the PCI core needs to access the device's incoming ser struct,
but that would be inefficient.
^ permalink raw reply
* Re: [PATCH v18 03/13] cxl: Tighten CPER kfifo registration API and symbol visibility
From: Jonathan Cameron @ 2026-07-20 21:59 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma
In-Reply-To: <20260717222706.3540281-4-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:26:56 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
> From: Dan Williams <djbw@kernel.org>
>
Hi Terry, Dan,
There are a few different things in here. Having them all together
may make it a little harder to keep track of the different changes.
> Tighten the CPER protocol error kfifo registration API and symbol
> visibility.
>
> Use EXPORT_SYMBOL_FOR_MODULES() instead of EXPORT_SYMBOL_NS_GPL() for
> the CPER kfifo registration symbols. This names the consuming module
> explicitly and gives compile-time enforcement.
To me this is one patch.
>
> Drop the work_struct argument from the unregister path. Change the
> WARN_ONCE condition to a NULL check since there is no caller pointer
> to compare against anymore.
This is one.
>
> Change register/unregister return types to void. Flag double registration
> with WARN_ONCE() inside the lock instead of returning an error.
Another change.
That covers why for the register, for unregister maybe mention it was
unchecked anyway.
>
> Change cxl_ras_init() to void because there is one consumer and one producer
> so the error return was unnecessary. Remove the now-dead error check in
> cxl_core_init().
This is change
>
> Add a diagnostic log when the driver is not bound in
> cxl_cper_handle_prot_err().
Unrelated really to all the rest.
All nice changes and maybe a few of them can be combined but to me this
is too many in one. Anyhow I might be letting perfect be enemy of
good and all that so if others think it should go in as one then
I'm fine with that.
Trivial thing inline todo with formatting consistency.
>
> Co-developed-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Dan Williams <djbw@kernel.org>
>
> ---
>
> Changes in v17 -> v18:
> - New patch.
> ---
> drivers/acpi/apei/ghes.c | 32 +++++++++++++++-----------------
> drivers/cxl/core/core.h | 7 ++-----
> drivers/cxl/core/port.c | 6 +-----
> drivers/cxl/core/ras.c | 12 +++++++-----
> include/cxl/event.h | 17 ++++++-----------
> 5 files changed, 31 insertions(+), 43 deletions(-)
>
> diff --git a/include/cxl/event.h b/include/cxl/event.h
> index ff97fea718d2c..3471d4f75c025 100644
> --- a/include/cxl/event.h
> +++ b/include/cxl/event.h
> @@ -287,10 +287,10 @@ struct cxl_cper_prot_err_work_data {
> -static inline int cxl_cper_register_prot_err_work(struct work_struct *work)
> +static inline void cxl_cper_register_prot_err_work(struct work_struct *work)
> {
> - return 0;
> }
I'd go a bit long and move { } up. Compact stubs are always nice!
> -static inline int cxl_cper_unregister_prot_err_work(struct work_struct *work)
> +static inline void cxl_cper_unregister_prot_err_work(void)
This one is shorter than the one you did it for above!
> {
> - return 0;
> }
> static inline int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
> {
^ permalink raw reply
* Re: [PATCH v7 03/12] PCI: liveupdate: Track incoming preserved PCI devices
From: David Matlack @ 2026-07-20 22:00 UTC (permalink / raw)
To: Alex Williamson
Cc: kexec, linux-doc, linux-kernel, linux-mm, linux-pci,
Adithya Jayachandran, Alexander Graf, Bjorn Helgaas, Chris Li,
David Rientjes, Jacob Pan, Jason Gunthorpe, Jonathan Corbet,
Josh Hilke, Leon Romanovsky, Lukas Wunner, Mike Rapoport,
Parav Pandit, Pasha Tatashin, Pranjal Shrivastava, Pratyush Yadav,
Saeed Mahameed, Samiullah Khawaja, Shuah Khan, Vipin Sharma,
William Tu, Yi Liu
In-Reply-To: <20260717163820.6bc8c13b@shazbot.org>
On Fri, Jul 17, 2026 at 3:38 PM Alex Williamson <alex@shazbot.org> wrote:
>
> On Fri, 10 Jul 2026 21:26:06 +0000
> David Matlack <dmatlack@google.com> wrote:
>
> > @@ -298,6 +377,87 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
> > }
> > EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
> >
> > +static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
> > +{
> > + struct pci_flb_incoming *incoming = NULL;
> > + int ret;
> > +
> > + ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
> > +
> > + /* Live Update is not enabled. */
> > + if (ret == -EOPNOTSUPP)
> > + return NULL;
> > +
> > + /* Live Update is enabled, but there is no incoming FLB data. */
> > + if (ret == -ENODATA)
> > + return NULL;
> > +
> > + /*
> > + * Live Update is enabled and there is incoming FLB data, but none of it
> > + * matches pci_liveupdate_flb.compatible.
> > + *
> > + * This could mean that no PCI FLB data was passed by the previous
> > + * kernel, but it could also mean the previous kernel used a different
> > + * compatibility string (i.e. a different ABI).
> > + */
> > + if (ret == -ENOENT) {
> > + pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);
> > + return NULL;
> > + }
> > +
> > + /*
> > + * There is incoming FLB data that matches pci_liveupdate_flb.compatible
> > + * but it cannot be retrieved.
> > + */
> > + if (ret)
> > + panic("Failed to retrieve incoming FLB data (%d)\n", ret);
> > +
> > + return incoming;
> > +}
>
> I'm having trouble following the error escalation here. What's
> fundamentally the difference between FLB data being provided and not
> compatible (subtle log message) versus FLB data being provided and
> compatible but we cannot access it (panic!)?
There is no fundamental difference IMO and I think we should handle
them the same way (panic). The problem is the PCI core cannot actually
distinguish between "PCI FLB provided and not compatible" (should
panic) and "no PCI FLB provided" (should log). I would like to propose
changes to LUO so that the FLB handlers can detect the former
situation but that requires some non-trivial work that I didn't want
to block on.
For now, I guess you could say it is up to the user to not perform
Live Update between incompatible versions. I can update the
documentation.
>
> Don't both suggest devices are running but we can't get their FLB data
> to continue letting them run?
>
> The errno interpretation is also slightly different than the comment
> above liveupdate_flb_get_incoming():
>
> * Return: 0 on success, or a negative errno on failure. -ENODATA means no
> * incoming FLB data, -ENOENT means specific flb not found in the incoming
> * data, -ENODEV if the FLB's module is unloading, and -EOPNOTSUPP when
> * live update is disabled or not configured.
I think the only disagreement is the lack of specific -ENODEV
handling. Is that what you are referring to?
>
> Thanks,
> Alex
^ permalink raw reply
* Re: [patch 00/18] entry: Consolidate and rework syscall entry handling
From: Magnus Lindholm @ 2026-07-20 22:01 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Peter Zijlstra, Michael Ellerman, Shrikanth Hegde,
linuxppc-dev, Kees Cook, Huacai Chen, loongarch, Paul Walmsley,
Palmer Dabbelt, linux-riscv, Sven Schnelle, linux-s390, x86,
Mark Rutland, Jinjie Ruan, Andy Lutomirski, Oleg Nesterov,
Richard Henderson, Russell King, Catalin Marinas, Guo Ren,
Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
Yoshinori Sato, Richard Weinberger, Chris Zankel,
linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
linux-parisc, linux-sh, linux-um, Arnd Bergmann, Vineet Gupta,
Will Deacon, Brian Cain, Michal Simek, Dinh Nguyen,
David S. Miller, Andreas Larsson, linux-snps-arc, linux-hexagon,
linux-openrisc, sparclinux, linux-arch, Michal Suchánek,
Jonathan Corbet, linux-doc
In-Reply-To: <87wluplb6k.ffs@fw13>
Thomas,
On Mon, Jul 20, 2026 at 9:21 PM Thomas Gleixner <tglx@kernel.org> wrote:
>
>
> Similar to what Peter and me suggested in the related discussion
> vs. s390 which has a similar issue, you can just have a dedicated
> syscall_return member in your pt_regs struct, which is preset to -ENOSYS
> and operate on that. You have an unused padding entry there which means
> it won't even change the size.
>
Thanks, that makes sense in principle.
One complication is that my Alpha GENERIC_ENTRY branch already reuses that
former pt_regs padding slot, while keeping the pt_regs size and stack
alignment unchanged. The extra syscall-entry bookkeeping I needed so far lives
in thread_info rather than adding more fields to pt_regs.
So the padding slot is not free in my current branch. I can still look at
whether a dedicated syscall_return scratch value would be cleaner, but the
tested version currently handles this by setting the default -ENOSYS result
only on the no-dispatch path, after generic entry processing, and only if
ptrace/seccomp/BPF did not already provide a return value.
>
> You were on CC for the V2, no?
>
Sorry, I must have missed that.
> The whole pile including Jinjie's seccomp bypass fix is in
>
> git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/entry
>
> and is targeted for the 7.3 merge window.
>
Great!
> If you want to base your stuff on that for 7.3, I can add a tag which
> makes it immutable so it can be pulled into the alpha tree.
>
Yes, please. There is still some ongoing testing/review of my Alpha
GENERIC_ENTRY series, but assuming it is ready in time for the 7.3 merge
window, an immutable tag would be very helpful.
Magnus
^ permalink raw reply
* Re: [PATCH v18 04/13] cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
From: Jonathan Cameron @ 2026-07-20 22:02 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma
In-Reply-To: <20260717222706.3540281-5-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:26:57 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
> From: Dan Williams <djbw@kernel.org>
>
> find_cxl_port() and find_cxl_port_by_uport() are internal port lookup
> functions that search the CXL bus by dport and uport respectively, but
> their names do not make the lookup method clear.
>
> Rename find_cxl_port() to find_cxl_port_by_dport() to make the lookup
> method explicit and consistent with find_cxl_port_by_uport(). Both
> functions remain static to port.c; the upcoming patch that adds the
> first cross-file caller will widen their scope.
Could have mentioned the __ one, but it is fairly obvious why you did
that even without saying it.
Nice improvement
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
>
> Co-developed-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Dan Williams <djbw@kernel.org>
>
> ---
>
> Changes in v17 -> v18:
> - New commit
> ---
> drivers/cxl/core/port.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index f90f899c31d07..cadb51f70f854 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1379,7 +1379,7 @@ static int match_port_by_dport(struct device *dev, const void *data)
> return dport != NULL;
> }
>
> -static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
> +static struct cxl_port *__find_cxl_port_by_dport(struct cxl_find_port_ctx *ctx)
> {
> struct device *dev;
>
> @@ -1392,8 +1392,16 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
> return NULL;
> }
>
> -static struct cxl_port *find_cxl_port(struct device *dport_dev,
> - struct cxl_dport **dport)
> +/**
> + * find_cxl_port_by_dport - find a cxl_port by one of its targets
> + * @dport_dev: device representing the dport target
> + * @dport: optional output of the 'struct cxl_dport' companion of the @dport_dev
> + *
> + * Return a 'struct cxl_port' with an elevated reference if found. Use
> + * __free(put_cxl_port) to release.
> + */
> +static struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
> + struct cxl_dport **dport)
> {
> struct cxl_find_port_ctx ctx = {
> .dport_dev = dport_dev,
> @@ -1401,7 +1409,7 @@ static struct cxl_port *find_cxl_port(struct device *dport_dev,
> };
> struct cxl_port *port;
>
> - port = __find_cxl_port(&ctx);
> + port = __find_cxl_port_by_dport(&ctx);
> return port;
> }
>
> @@ -1895,14 +1903,14 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, "CXL");
> struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
> struct cxl_dport **dport)
> {
> - return find_cxl_port(pdev->dev.parent, dport);
> + return find_cxl_port_by_dport(pdev->dev.parent, dport);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, "CXL");
>
> struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
> struct cxl_dport **dport)
> {
> - return find_cxl_port(grandparent(&cxlmd->dev), dport);
> + return find_cxl_port_by_dport(grandparent(&cxlmd->dev), dport);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, "CXL");
>
^ permalink raw reply
* Re: [PATCH] power: supply: bd71828: add a terminating table border
From: Sebastian Reichel @ 2026-07-20 22:03 UTC (permalink / raw)
To: linux-doc, Randy Dunlap; +Cc: Andreas Kemnade, Matti Vaittinen, linux-pm
In-Reply-To: <20260620011821.3568674-1-rdunlap@infradead.org>
On Fri, 19 Jun 2026 18:18:21 -0700, Randy Dunlap wrote:
> Fix a documentation build error by adding a bottom table border:
>
> Documentation/ABI/testing/sysfs-class-power-bd71828:1: ERROR: Malformed table.
> No bottom table border found.
> ============ ===========================================
> 1 automatic adjustment of input current limit
> 0 no adjustment of input current limit. This
> helps for more unusual power sources like
> solar modules. [docutils]
>
> [...]
Applied, thanks!
[1/1] power: supply: bd71828: add a terminating table border
commit: b056f21a38276ead20353d71d50a52206609d242
Best regards,
--
Sebastian Reichel <sebastian.reichel@collabora.com>
^ permalink raw reply
* Re: [PATCH bpf-next v5 8/8] selftests: net: add test for XDP_PASS skb checksum invalidation
From: Lorenzo Bianconi @ 2026-07-20 22:08 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Andrew Lunn, Tony Nguyen, Przemek Kitszel, Alexander Lobakin,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, KP Singh, Hao Luo, Jiri Olsa, Shuah Khan,
Maciej Fijalkowski, Jonathan Corbet, Shuah Khan,
Kumar Kartikeya Dwivedi, Emil Tsalapatis, Vladimir Vdovin,
Jakub Sitnicki, netdev, bpf, intel-wired-lan, linux-kselftest,
linux-doc
In-Reply-To: <al53WS9FMTnu7tBI@devvm7509.cco0.facebook.com>
[-- Attachment #1: Type: text/plain, Size: 4470 bytes --]
> On 07/16, Lorenzo Bianconi wrote:
> > On Jul 16, Stanislav Fomichev wrote:
> > > On 07/15, Lorenzo Bianconi wrote:
> > > > Add a test that verifies skb->ip_summed is set to CHECKSUM_NONE
> > > > when a device running in XDP mode creates an skb from a xdp_buff
> > > > if the attached ebpf program returns an XDP_PASS.
> > > > The test attaches an XDP program returning XDP_PASS, and a TC
> > > > ingress program that runs the bpf_skb_rx_checksum() kfunc to
> > > > inspect the resulting skb. After XDP_PASS the driver must invalidate
> > > > any previously computed hardware RX checksum since XDP may have
> > > > modified the packet data.
> > > > The BPF program counts packets per checksum type in a map, and the
> > > > test runner verifies that after sending traffic the CHECKSUM_NONE
> > > > counter is non-zero while CHECKSUM_UNNECESSARY and CHECKSUM_COMPLETE
> > > > counters are zero.
> > > >
> > > > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > > ---
> > > > Documentation/networking/xdp-rx-metadata.rst | 5 ++
> > > > .../selftests/drivers/net/hw/xdp_metadata.py | 55 +++++++++++++++-
> > > > .../selftests/net/lib/skb_metadata_csum.bpf.c | 73 ++++++++++++++++++++++
> > > > 3 files changed, 132 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/Documentation/networking/xdp-rx-metadata.rst b/Documentation/networking/xdp-rx-metadata.rst
> > > > index 93918b3769a3..7434ac98242a 100644
> > > > --- a/Documentation/networking/xdp-rx-metadata.rst
> > > > +++ b/Documentation/networking/xdp-rx-metadata.rst
> > > > @@ -90,6 +90,11 @@ conversion, and the XDP metadata is not used by the kernel when building
> > > > ``skbs``. However, TC-BPF programs can access the XDP metadata area using
> > > > the ``data_meta`` pointer.
> > >
> > > [..]
> > >
> > > > +If a driver is running in XDP mode, any existing hardware RX checksum
> > > > +(``CHECKSUM_UNNECESSARY`` or ``CHECKSUM_COMPLETE``) must be invalidated
> > > > +by setting ``skb->ip_summed`` to ``CHECKSUM_NONE`` before passing the
> > > > +skb to the kernel, since XDP may have modified the packet data.
> > > > +
> > > > In the future, we'd like to support a case where an XDP program
> > > > can override some of the metadata used for building ``skbs``.
> > >
> > > Sorry for keeping nitpicking on this, but I'm still not convinced that
> > > it is what we currently do. From my previous reply:
> >
> > no worries :)
> > My current take-away from the previous discussion is we just need to document
> > what would be the driver expected behaviour adding a kselftest for it (without
> > modifying any driver).
> >
> > >
> > > > > Looking at a few drivers:
> > > > > - bnxt (bnxt_rx_pkt) does UNNECESSARY - ok
> > > > > - mlx5 (mlx5e_handle_csum) does UNNECESSARY and skips COMPLETE if there is
> > > > > bpf prog attached
> > > > > - fbnic (fbnic_rx_csum) - can do COMPLETE even with xdp attached?
> > > > > - gve (gve_rx) - can do COMPLETE even with xdp attached?
> > >
> > > (although for gve I might be wrong, there is also gve_rx_skb_csum that only
> > > does UNNECESSARY).
> > >
> > > I'd wait for Jakub to chime in, but it feels like we should just document
> > > what we currently do as a recommended approach: for the drivers
> > > that support COMPLETE, do not report it when the bpf program is attached.
> > > Both NONE and UNNECESSARY are ok.
> >
> > I am not completely sure the UNNECESSARY case is different from the COMPLETE
> > one. What are we supposed to do if the driver reports UNNECESSARY and the ebpf
> > program modifies some fields covered by the rx-checksum?
>
> For unnecessary, I think the safe expectation is that the bpf program
> will update the value of the checksum in the packet if it touches the data?
I do not have a strong opinion on it.
@Jakub: any input on it?
>
> > > Also, did you run this test on real HW? NIPA now has HW tests, maybe it
> > > makes sense to route this series via net-next to get the real coverage?
> >
> > What about splitting this series and have two different series:
> > - bpf-next: add xdp rx kfunc and related selftest
> > - net-next: add kselftest for the driver expected behaviour.
> >
> > What do you think?
>
> I'd post everything to net-next to get the HW coverage. Once you get all
> the acks we can ask the maintainers' guidance.
ack, I am fine with that.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v18 08/13] cxl/pci: Thread port and dport through RAS handling helpers
From: Dave Jiang @ 2026-07-20 22:15 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-9-terry.bowman@amd.com>
On 7/17/26 3:27 PM, Terry Bowman wrote:
> From: Dan Williams <djbw@kernel.org>
>
> The callers of cxl_handle_ras() and cxl_handle_cor_ras() already hold
> a struct cxl_port * and struct cxl_dport * for the device being
> handled. Passing a generic struct device * requires is_cxl_memdev()
> to distinguish Endpoints from ports at trace emission time. Threading
> port and dport directly enables is_cxl_endpoint(port) and explicit
> dport/port branching for cleaner trace dispatch.
>
> Refactor cxl_handle_ras() and cxl_handle_cor_ras() to accept struct
> cxl_port * and struct cxl_dport * directly. The CXL RAS trace event
> emission logic is split into three branches: Endpoint events are
> identified via is_cxl_endpoint(port) and emit with the memdev, dport
> events emit with dport->dport_dev, and Upstream Port events fall back
> to port->uport_dev.
>
> Update cxl_handle_rdport_errors() in ras_rch.c and
> cxl_handle_proto_error() in ras.c to pass port and dport to the
> refactored functions.
>
> RCH Downstream Port correctable trace events now report the dport
> device (dport->dport_dev) as a consequence of threading port and dport
> through the RAS helpers. The following trace event rework ("cxl: Add
> port and dport identifiers to CXL AER trace events") adds explicit
> memdev, port, dport, and host fields that provide full context for
> all device types.
>
> Co-developed-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> Signed-off-by: Dan Williams <djbw@kernel.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17 -> v18:
> - New patch.
> ---
> drivers/cxl/core/core.h | 12 ++++++++----
> drivers/cxl/core/ras.c | 29 +++++++++++++++--------------
> drivers/cxl/core/ras_rch.c | 2 +-
> 3 files changed, 24 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 272634ff2615b..5ca1275fd8f35 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -185,10 +185,12 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
> #ifdef CONFIG_CXL_RAS
> void cxl_ras_init(void);
> void cxl_ras_exit(void);
> -bool cxl_handle_ras(struct device *dev, void __iomem *ras_base);
> +bool cxl_handle_ras(struct cxl_port *port, struct cxl_dport *dport,
> + void __iomem *ras_base);
> void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> struct cxl_dport *dport);
> -void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base);
> +void cxl_handle_cor_ras(struct cxl_port *port, struct cxl_dport *dport,
> + void __iomem *ras_base);
> void cxl_dport_map_rch_aer(struct cxl_dport *dport);
> void cxl_disable_rch_root_ints(struct cxl_dport *dport);
> void cxl_handle_rdport_errors(struct pci_dev *pdev);
> @@ -197,13 +199,15 @@ void devm_cxl_dport_ras_setup(struct cxl_dport *dport);
> #else
> static inline void cxl_ras_init(void) { }
> static inline void cxl_ras_exit(void) { }
> -static inline bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
> +static inline bool cxl_handle_ras(struct cxl_port *port, struct cxl_dport *dport,
> + void __iomem *ras_base)
> {
> return false;
> }
> static inline void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port,
> struct cxl_dport *dport) { }
> -static inline void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base) { }
> +static inline void cxl_handle_cor_ras(struct cxl_port *port, struct cxl_dport *dport,
> + void __iomem *ras_base) { }
> static inline void cxl_dport_map_rch_aer(struct cxl_dport *dport) { }
> static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { }
> static inline void cxl_handle_rdport_errors(struct pci_dev *pdev) { }
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index 9a142abcf4f8b..6f4a3c1b0bb85 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -232,7 +232,6 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>
> void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port, struct cxl_dport *dport)
> {
> - struct device *dev = dport ? dport->dport_dev : port->uport_dev;
> void __iomem *ras_base = to_ras_base(port, dport);
>
> if (!ras_base) {
> @@ -241,14 +240,14 @@ void cxl_do_recovery(struct pci_dev *pdev, struct cxl_port *port, struct cxl_dpo
> return;
> }
>
> - if (cxl_handle_ras(dev, ras_base))
> + if (cxl_handle_ras(port, dport, ras_base))
> panic("CXL cachemem error");
>
> dev_dbg(&pdev->dev,
> "CXL UCE signaled but no CXL RAS status bits set\n");
> }
>
> -void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base)
> +void cxl_handle_cor_ras(struct cxl_port *port, struct cxl_dport *dport, void __iomem *ras_base)
> {
> u32 status;
> void __iomem *addr;
> @@ -260,10 +259,12 @@ void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base)
> status = readl(addr);
> if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) {
> writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
> - if (is_cxl_memdev(dev))
> - trace_cxl_aer_correctable_error(to_cxl_memdev(dev), status);
> + if (is_cxl_endpoint(port))
> + trace_cxl_aer_correctable_error(to_cxl_memdev(port->uport_dev), status);
> + else if (dport)
> + trace_cxl_port_aer_correctable_error(dport->dport_dev, status);
> else
> - trace_cxl_port_aer_correctable_error(dev, status);
> + trace_cxl_port_aer_correctable_error(port->uport_dev, status);
> }
> }
>
> @@ -288,7 +289,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log)
> * 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)
> +bool cxl_handle_ras(struct cxl_port *port, struct cxl_dport *dport, void __iomem *ras_base)
> {
> u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {};
> void __iomem *addr;
> @@ -315,10 +316,12 @@ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
> }
>
> header_log_copy(ras_base, hl);
> - if (is_cxl_memdev(dev))
> - trace_cxl_aer_uncorrectable_error(to_cxl_memdev(dev), status, fe, hl);
> + if (is_cxl_endpoint(port))
> + trace_cxl_aer_uncorrectable_error(to_cxl_memdev(port->uport_dev), status, fe, hl);
> + else if (dport)
> + trace_cxl_port_aer_uncorrectable_error(dport->dport_dev, status, fe, hl);
> else
> - trace_cxl_port_aer_uncorrectable_error(dev, status, fe, hl);
> + trace_cxl_port_aer_uncorrectable_error(port->uport_dev, status, fe, hl);
>
> writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
>
> @@ -351,7 +354,7 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> * 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(port->uport_dev, to_ras_base(port, NULL));
> + ue = cxl_handle_ras(port, NULL, to_ras_base(port, NULL));
> }
>
> /*
> @@ -382,10 +385,8 @@ EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL");
> static void cxl_handle_proto_error(struct pci_dev *pdev, struct cxl_port *port,
> struct cxl_dport *dport, int severity)
> {
> - struct device *dev = dport ? dport->dport_dev : port->uport_dev;
> -
> if (severity == AER_CORRECTABLE)
> - cxl_handle_cor_ras(dev, to_ras_base(port, dport));
> + cxl_handle_cor_ras(port, dport, to_ras_base(port, dport));
> else
> cxl_do_recovery(pdev, port, dport);
> }
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index f2d2fb83758b9..f4b98f2c11a1c 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -118,7 +118,7 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
>
> pci_print_aer(pdev, severity, &aer_regs);
> if (severity == AER_CORRECTABLE)
> - cxl_handle_cor_ras(&pdev->dev, to_ras_base(port, dport));
> + cxl_handle_cor_ras(dport->port, dport, to_ras_base(port, dport));
> else
> cxl_do_recovery(pdev, dport->port, dport);
> }
^ permalink raw reply
* Re: [PATCH v18 09/13] cxl: Update CXL Endpoint AER handler
From: Dave Jiang @ 2026-07-20 22:25 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-10-terry.bowman@amd.com>
On 7/17/26 3:27 PM, Terry Bowman wrote:
> Rename cxl_error_detected() to cxl_pci_error_detected() and rename
> the struct pci_error_handlers instance to cxl_pci_error_handlers to
> avoid shadowing the struct type tag.
>
> Document the unconditional CXL RAS read policy: on a dead link,
> readl() returns 0xFFFFFFFF which is interpreted as UCE bits set and
> triggers a panic. If RAS registers are not mapped the read is
> skipped and the frozen/perm_failure switch cases defer to AER
> recovery for devices without active CXL.mem traffic.
>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
>
> Changes in v17->v18:
> - Fix cxl_pci_error_detected() to use find_cxl_port_by_uport() and port->uport_dev
> - Read CXL RAS unconditionally; panic on UCE regardless of channel state
> - Document unconditional read policy and 0xFFFFFFFF behavior in comment
> - Drop guard removal paragraph from commit message (not in this diff)
> - Drop Reviewed-by tags pending re-review after message change
>
> Changes in v16->v17:
> - Rename pci_error_handlers struct instance to cxl_pci_error_handlers to
> avoid shadowing the struct type tag.
> - Restore scoped_guard(device) and dev->driver check around AER read.
> - NULL-check find_cxl_port_by_dev() before deref of port->uport_dev.
> - Updated commit message. (Terry)
> - Add scope cleanup for port variable in cxl_pci_error_detected() (Terry)
> - Drop cxl_uncor_aer_present(), rely on AER state
>
> Changes in v15->v16:
> - Update commit message (DaveJ)
> - s/cxl_handle_aer()/cxl_uncor_aer_present()/g (Jonathan)
> - cxl_uncor_aer_present(): Leave original result calculation based on
> if a UCE is present and the provided state (Terry)
> - Add call to pci_print_aer(). AER fails to log because is upstream
> link (Terry)
>
> Changes in v14->v15:
> - Update commit message and title. Added Bjorn's ack.
> - Move CE and UCE handling logic here
>
> Changes in v13->v14:
> - Add Dave Jiang's review-by
> - Update commit message & headline (Bjorn)
> - Refactor cxl_port_error_detected()/cxl_port_cor_error_detected() to
> one line (Jonathan)
> - Remove cxl_walk_port() (Dan)
> - Remove cxl_pci_drv_bound(). Check for 'is_cxl' parent port is
> sufficient (Dan)
> - Remove device_lock_if()
> - Combined CE and UCE here (Terry)
>
> Changes in v12->v13:
> - Move get_pci_cxl_host_dev() and cxl_handle_proto_error() to Dequeue
> patch (Terry)
> - Remove EP case in cxl_get_ras_base(), not used. (Terry)
> - Remove check for dport->dport_dev (Dave)
> - Remove whitespace (Terry)
>
> Changes in v11->v12:
> - Add call to cxl_pci_drv_bound() in cxl_handle_proto_error() and
> pci_to_cxl_dev()
> - Change cxl_error_detected() -> cxl_cor_error_detected()
> - Remove NULL variable assignments
> - Replace bus_find_device() with find_cxl_port_by_uport() for upstream
> port searches.
>
> Changes in v10->v11:
> - None
> ---
> drivers/cxl/core/ras.c | 24 +++++++++++++++---------
> drivers/cxl/cxlpci.h | 8 ++++----
> drivers/cxl/pci.c | 12 ++++++------
> 3 files changed, 25 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index 6f4a3c1b0bb85..d5dc2c22565da 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -328,10 +328,8 @@ bool cxl_handle_ras(struct cxl_port *port, struct cxl_dport *dport, void __iomem
> return true;
> }
>
> -
> -
> -pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> - pci_channel_state_t state)
> +pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
> + pci_channel_state_t state)
> {
> struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_uport(&pdev->dev);
> bool ue = false;
> @@ -349,10 +347,18 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> }
>
> /*
> - * 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.
> + * The CXL RAS read is unconditional regardless of channel
> + * state. Any uncorrectable error bit set in the CXL RAS
> + * status register triggers a panic because CXL.mem cache
> + * coherency is already lost; continuing risks silent data
> + * corruption across interleaved HDM regions.
> + *
> + * On a dead link readl() returns 0xFFFFFFFF which sets all
> + * UCE bits and also triggers the panic - this is intentional.
> + * If RAS registers are not mapped the read is skipped, the
> + * panic is not reached, and the frozen/perm_failure switch
> + * cases below handle AER recovery for devices without active
> + * CXL.mem traffic.
> */
> ue = cxl_handle_ras(port, NULL, to_ras_base(port, NULL));
> }
> @@ -380,7 +386,7 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> }
> return PCI_ERS_RESULT_NEED_RESET;
> }
> -EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL");
> +EXPORT_SYMBOL_NS_GPL(cxl_pci_error_detected, "CXL");
>
> static void cxl_handle_proto_error(struct pci_dev *pdev, struct cxl_port *port,
> struct cxl_dport *dport, int severity)
> diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
> index 06c46adcf0f6c..8aeb80a4e5732 100644
> --- a/drivers/cxl/cxlpci.h
> +++ b/drivers/cxl/cxlpci.h
> @@ -89,13 +89,13 @@ struct cxl_dev_state;
> void read_cdat_data(struct cxl_port *port);
>
> #ifdef CONFIG_CXL_RAS
> -pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> - pci_channel_state_t state);
> +pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
> + pci_channel_state_t state);
> void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport);
> void devm_cxl_port_ras_setup(struct cxl_port *port);
> #else
> -static inline pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> - pci_channel_state_t state)
> +static inline pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
> + pci_channel_state_t state)
> {
> return PCI_ERS_RESULT_NONE;
> }
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 5c21db36073fe..6cf1db7b85020 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -1000,18 +1000,18 @@ static void cxl_reset_done(struct pci_dev *pdev)
> }
> }
>
> -static const struct pci_error_handlers cxl_error_handlers = {
> - .error_detected = cxl_error_detected,
> - .slot_reset = cxl_slot_reset,
> - .resume = cxl_error_resume,
> - .reset_done = cxl_reset_done,
> +static const struct pci_error_handlers cxl_pci_error_handlers = {
> + .error_detected = cxl_pci_error_detected,
> + .slot_reset = cxl_slot_reset,
> + .resume = cxl_error_resume,
> + .reset_done = cxl_reset_done,
> };
>
> static struct pci_driver cxl_pci_driver = {
> .name = KBUILD_MODNAME,
> .id_table = cxl_mem_pci_tbl,
> .probe = cxl_pci_probe,
> - .err_handler = &cxl_error_handlers,
> + .err_handler = &cxl_pci_error_handlers,
> .dev_groups = cxl_rcd_groups,
> .driver = {
> .probe_type = PROBE_PREFER_ASYNCHRONOUS,
^ permalink raw reply
* Re: [RFC PATCH 1/2] KVM: x86/pmu: Add CAP to disable SW accounting of emulated instructions
From: Sean Christopherson @ 2026-07-20 22:27 UTC (permalink / raw)
To: Luka Absandze
Cc: Paolo Bonzini, kvm, linux-kernel, linux-doc, Alexander Graf,
David Woodhouse
In-Reply-To: <20260720192221.72912-2-absandze@amazon.de>
On Mon, Jul 20, 2026, Luka Absandze wrote:
> The only functional change for an opted-in VM is reduced accuracy: a
> guest counting instructions-retired or branches-retired undercounts by
> the instructions KVM emulates in host context, i.e. the behavior that
> predates the accounting cited above. Hardware-executed guest
> instructions continue to be counted by the backing perf_event, and its
> overflow/PMI path is unchanged.
Do you *need* per-VM control, or would a module param (or a magic value for
enable_pmu) suffice? While I mostly buy the "it used to work this way" argument
(just "mostly", because that commit landed 4.5 years ago), I'm not exactly keen
on adding uAPI that is effectively "re-introduce a bug to workaround fundamental
design issues in KVM's emulated PMU implementation".
If we do add proper uAPI, we should extend KVM_CAP_PMU_CAPABILITY, not add a new
CAP entirely.
^ permalink raw reply
* Re: [RFC] cxl: Device protocol AER injection
From: Bowman, Terry @ 2026-07-20 22:27 UTC (permalink / raw)
To: Jonathan Cameron, Ashok Raj
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma, linux-cxl@vger.kernel.org, linux-acpi, linux-doc,
linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
linuxppc-dev
In-Reply-To: <20260720220430.30b7e3ef@jic23-huawei>
On 7/20/2026 4:04 PM, Jonathan Cameron wrote:
> On Fri, 17 Jul 2026 17:57:00 -0500
> Terry Bowman <terry.bowman@amd.com> wrote:
>
>> This patch is intended to provide a method of testing the recently submitted
>> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
>> here:
>>
>> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
>
> Hi Terry,
>
> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com
>
> Works fine. Generally you can crop that end bit off the links.
>
Good to know. I'll use that going forward.
>>
>> The changes in this patch will allow CXL RAS protocol testing by injecting
>> AER errors using AER EINJ. The RAS register block status is updated
>> using a central function to augment RAS register block returned by
>> to_ras_base(). This supports all CXL devices including Root Ports,
>> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
>> Downstream Ports.
>
> Why is this an RFC rather than a final proposal? There should always
> be something to give the reviewer that info in the patch description.
> I'd actually be tempted to throw a cover letter in to have somewhere
> out of the way to put that information.
>
> Is it simply because it only makes sense once the other seris lands.
>
The immediate priority was providing a testing procedure for the v18 series.
I wasn't sure how the design would be received. For instance, the to_einj_ras_base()
could be moved into cxl-test as a to_ras_base() mock implemented function or
it could remain in ras.c (or maybe even ras_einj.c) outside of cxl-mock. I'm
looking forward to Alison's review and comments.
My preference is to introduce core/ras_einj,c and add these changes. This
would be to isolate all the changes except for: cxl_ras_einj_init(), cxl_ras_einj_exit(),
and to_einj_ras_base(). I've started moving forward with making these changes
knowing the direction could change once this receives more reviews.
Also worth discussing is the commandline takes multiple parameters for
a single sysfs file which I know isn't acceptable by everyone. I personally
like the interface as-is because its simpler to use in comparison to multiple
files that must be set individually.
>>
>> Add debugfs-based CXL protocol error injection for testing CXL RAS
>> error handling paths. Injects CXL RAS protocol errors using AER internal
>> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
>>
>> RAS CXL status is set using to_ras_base() function override when kernel config
>> CONFIG_CXL_PROTO_AER_EINJ is enabled.
>>
>> Usage:
>> echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
>> /sys/kernel/debug/cxl/aer_einj_inject
>>
>> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
>> can invoke AER injection directly. Export aer_inject() with
>> EXPORT_SYMBOL_GPL.
>>
>> Make cxl_debugfs non-static in port.c and declare it extern in
>> core.h so the debugfs file can be created under the existing CXL
>> debugfs root.
>>
>> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> +CC Ashok,
>
> Various thing inline. Mostly this review is a bit superficial as I'd like
> ideally to see a cleaner separation of this at level of files etc.
>
> Thanks,
>
> Jonathan
>> ---
>> drivers/cxl/Kconfig | 13 +++
>> drivers/cxl/core/core.h | 21 ++++
>> drivers/cxl/core/port.c | 2 +-
>> drivers/cxl/core/ras.c | 208 ++++++++++++++++++++++++++++++++++
>> drivers/cxl/core/ras_rch.c | 12 ++
>> drivers/pci/pcie/aer_inject.c | 29 ++---
>> include/linux/aer.h | 15 +++
>> 7 files changed, 281 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
>> index 80aeb0d556bd7..ef449228b2549 100644
>> --- a/drivers/cxl/Kconfig
>> +++ b/drivers/cxl/Kconfig
>> @@ -238,6 +238,19 @@ config CXL_RAS
>> def_bool y
>> depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>>
>> +config CXL_PROTO_AER_EINJ
>> + bool "CXL: RAS Protocol Error Injection using AER EINJ"
>> + depends on CXL_RAS
>> + depends on PCIEAER_INJECT
>
> Do we think anyone who has CXL and PCIEAER_INJECT support will want
> to carefully not build this? I'm just wondering if we can avoid asking
> the question and base the built or not on the combination of those.
>
Good question: How do we incorporate this with the existing CXL EINJ
functionality making them complementary and consistant? The existing
EINJ is true ACPI injection but only supports RPs. The ACPI EINJ callouts
are currrently in core/port.c. We should consider moving it into a common file
such as core/ras_einj.c. With that move it will help force us merge the interfaces
where/if possible and at least give central location for error RAS injection.
I think folding the AER injection into PCIEAER_INJECT kernel config is
reasonable but looking for others feedback.
>> + help
>> + Enable debugfs-based CXL protocol error injection. Writes to
>> + /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
>> + errors using the AER internal error inject interface.
>> +
>> + This is a debug/test facility. Say N for production kernels.
>> +
>> + If unsure say N.
>
>> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
>> index a55a4e409feda..91910d2bb5d39 100644
>> --- a/drivers/cxl/core/core.h
>> +++ b/drivers/cxl/core/core.h
> ...
>
>> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>>
>> resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
>> struct cxl_dport *dport);
>> +
>> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
>
> Do we need the ifdefs? If the option isn't built none of this should get
> used. So small benefit.
>
Youre right. You pointed that trick/pattern out before and results in less
unneeded code. I'll make that change.
>> +
>> +#define AER_REGISTER_SIZE 5
>> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
>> +
>> +struct cxl_aer_einj {
>> + int correctable;
>> + bool is_rch;
>> + struct mutex *lock;
>> + struct device *dev;
>> + u32 aer_registers[AER_REGISTER_SIZE];
>> + u32 ras_registers[RAS_REGISTER_SIZE];
>> +};
>> +
>> +extern struct cxl_aer_einj cxl_aer_einj;
>> +#endif
>
>> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
>> index d77208af41e03..d41deea899d30 100644
>> --- a/drivers/cxl/core/ras.c
>> +++ b/drivers/cxl/core/ras.c
>> @@ -3,6 +3,7 @@
>>
>> #include <linux/pci.h>
>> #include <linux/aer.h>
>> +#include <linux/debugfs.h>
>> #include <cxl/event.h>
>> #include <cxlmem.h>
>> #include <cxlpci.h>
>> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
>> }
>> static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>>
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>> +
>
> Unless very strong reasons for it, generally don't do #if stuff in c files.
> Just have a separate c file for this. Otherwise it hurts readability and
> we tend to loose the clean separation over time. A file makes that less
> likely.
>
Ok.
>
>> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
>
> Needs a comment for what data it is protecting.
>
>> +
>> +struct cxl_aer_einj cxl_aer_einj = {
>> + .lock = &cxl_aer_einj_mutex,
>> +};
>> +
>> +static const char cxl_aer_einj_usage[] =
>> + "ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
>> +
>> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
>> + u32 aer_status, u32 ras_status)
>> +{
>> + /* RCD errors are signaled as internal errors on the associated RCEC */
>> + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
>> + if (!pdev->rcec)
>> + return -ENODEV;
>> + pdev = pdev->rcec;
>> + }
>> +
>> + struct aer_error_inj einj = {
>> + .bus = pdev->bus->number,
>> + .dev = PCI_SLOT(pdev->devfn),
>> + .fn = PCI_FUNC(pdev->devfn),
>> + .domain = pci_domain_nr(pdev->bus),
>> + };
>> + int ret;
>> + int aer_offset;
>> + int ras_offset;
>> +
>> + if (correctable) {
>> + einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
>> + aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
>> + ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
>
> Given these are offsets into cxl_aer_einj.aer_registers / ras_registers
> can we use sizeof(*cxl_aer_einj.aer_registers) etc
>
We could but that assumes the CEs are book ending the register blocks. And this would
be inconsistent with UCE case below, right? Tell me if I misunderstaood the question.
>> + } else {
>> + einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
>> + aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
>> + ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
>> + }
>> +
>> + cxl_aer_einj.correctable = correctable;
>> + cxl_aer_einj.aer_registers[aer_offset] = aer_status;
>> + cxl_aer_einj.ras_registers[ras_offset] = ras_status;
>> +
>> + ret = aer_inject(&einj);
>> + if (ret) {
>> + pr_err("cxl-einj: aer_inject failed: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_write(struct file *file,
>> + const char __user *ubuf,
>> + size_t count, loff_t *ppos)
>
> Might as well wrap to 80 chars and save a line.
>
Ok
>> +{
>
> ...
>
>> +
>> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
>> + if (!port) {
>> + dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
>> + return -ENODEV;
>> + }
>> +
>> + if (!to_ras_base(port, dport)) {
>> + dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
>> + return -ENODEV;
>> + }
>> +
>> + cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
>> + if (!cxl_aer_einj.is_rch)
>> + pci_dev_get(pdev);
>> + cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
>
> cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : pci_dev_get(&pdev->dev);
>
> Or use an if else for both is_rch based choices.
> Lazy me wonders.... Can we just grab a reference to the dev.parent for is_rch and
> simplify the code? We don't really need it I think but it is harmless.
>
>
Your change is cleaner and the parent ref increment doesnt hurt as you mentioned.
>> + ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
>> + aer_status, ras_status);
>> + if (ret) {
>> + if (!cxl_aer_einj.is_rch)
>> + pci_dev_put(pdev);
>> + cxl_aer_einj.dev = NULL;
>> + pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
>> + return ret;
>> + }
>> +
>> + return count;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
>> + size_t count, loff_t *ppos)
>> +{
>> + return simple_read_from_buffer(ubuf, count, ppos,
>> + cxl_aer_einj_usage,
>
> Probably wrap this to push that up a line.
>
Ok
>> + sizeof(cxl_aer_einj_usage) - 1);
>> +}
>
>> +
>> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>> +{
>> + if (dport) {
>> + if (cxl_aer_einj.is_rch) {
>> + if (cxl_aer_einj.dev == dport->dport_dev) {
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>
> Given the output of this is always force cast, maybe move that force up to the caller?
>
I think that works if it remains an internal helper and isn't made a mock function to to_ras_base().
If to_einj_ras_base() is used as a mock than it would require changing the to_ras_base()
as well.
>> + }
>> + } else {
>> + if (cxl_aer_einj.dev == dport->dport_dev) {
>> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>
> Not locally obvious why a thing called to_einj_ras_base should put anything it didn't
> get. I think this needs a restructure to more obviously be tidying up references
> that were held over the queue. At very leads needs a comment.
> /* Reference held from X no longer needed so drop */
>
The ref was incremented in cxl_aer_einj_write() on invoking injection. The ref is
decremented here after its usage. RCHs are excluded because they dont have a SBDF.
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> + }
>> + }
>> + } else if (!cxl_aer_einj.is_rch) {
>> + struct device *dev = is_cxl_endpoint(port) ?
>> + port->uport_dev->parent : port->uport_dev;
>> +
>> + if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
>> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> + }
>> + }
>> +
>> + return NULL;
>> +}
>> +#endif
>> +
>> static void cxl_unmask_proto_interrupts(struct device *dev)
>> {
>> struct pci_dev *pdev;
>> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>> if (!port)
>> return NULL;
>>
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>
> Wrap with
> if (IS_ENABLED()) to keep it visible to the compiler.
>
Ok
>> + if (cxl_aer_einj.dev) {
>> + void __iomem *einj = to_einj_ras_base(port, dport);
>> + if (einj)
>> + return einj;
>> + }
>> +#endif
>> +
>> if (dport)
>> return dport->regs.ras;
>>
>> @@ -458,10 +656,20 @@ void cxl_ras_init(void)
>> cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
>> cxl_register_proto_err_work(&cxl_proto_err_work,
>> cxl_proto_err_do_flush);
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>> + cxl_ras_create_debugfs(cxl_debugfs);
> stub that in a header.
>
Actually, during rework today I moved it to cxl_ras_einj_init(),
a new function. Thoughts?
>> +#endif
>> }
>>
>> void cxl_ras_exit(void)
>> {
>> cxl_unregister_proto_err_work();
>> cxl_cper_unregister_prot_err_work();
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>
> As below. Use
> if (IS_ENABLED())
>
> and keep everything visible.
>
Ok
>> + if (cxl_aer_einj.dev) {
>> + if (!cxl_aer_einj.is_rch)
>> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> + cxl_aer_einj.dev = NULL;
>> + }
>> +#endif
>> }
>> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
>> index 14bb3bdb2d092..5071cf86e4a68 100644
>> --- a/drivers/cxl/core/ras_rch.c
>> +++ b/drivers/cxl/core/ras_rch.c
>> @@ -110,6 +110,14 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
>> if (!dport)
>> return;
>>
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>> + if (cxl_aer_einj.is_rch && cxl_aer_einj.dev) {
>> + severity = cxl_aer_einj.correctable ?
>> + AER_CORRECTABLE : AER_FATAL;
>> + goto handle_ras;
>> + }
>
> Use instead
> if (IS_ENABLED(CONFIG_CXL_PROTO_AR_EINJ))
>
> then compiler can see the code (but remove it) which means
> you don't need the dance around the label below.
>
> In general follow this pattern anyway rather than #if
> when you can.
>
Got it.
>> +#endif
>> +
>> if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
>> return;
>>
>> @@ -117,6 +125,10 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
>> return;
>>
>> pci_print_aer(pdev, severity, &aer_regs);
>> +
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>> +handle_ras:
>> +#endif
>> if (severity == AER_CORRECTABLE)
>> cxl_handle_cor_ras(dport->port, dport,
>> to_ras_base(port, dport), pdev->dsn);
>> diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
>> index 09bfc7194ef31..b313adef680ae 100644
>> --- a/drivers/pci/pcie/aer_inject.c
>> +++ b/drivers/pci/pcie/aer_inject.c
>> @@ -14,6 +14,7 @@
>>
>> #define dev_fmt(fmt) "aer_inject: " fmt
>>
>> +#include <linux/aer.h>
>> #include <linux/module.h>
>> #include <linux/init.h>
>> #include <linux/interrupt.h>
>> @@ -31,19 +32,6 @@
>> static bool aer_mask_override;
>> module_param(aer_mask_override, bool, 0);
>>
>> -struct aer_error_inj {
>> - u8 bus;
>> - u8 dev;
>> - u8 fn;
>> - u32 uncor_status;
>> - u32 cor_status;
>> - u32 header_log0;
>> - u32 header_log1;
>> - u32 header_log2;
>> - u32 header_log3;
>> - u32 domain;
>> -};
>> -
>> struct aer_error {
>> struct list_head list;
>> u32 domain;
>> @@ -316,7 +304,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
>> return 0;
>> }
>>
>> -static int aer_inject(struct aer_error_inj *einj)
>> +int aer_inject(struct aer_error_inj *einj)
>> {
>> struct aer_error *err, *rperr;
>> struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
>> @@ -332,10 +320,14 @@ static int aer_inject(struct aer_error_inj *einj)
>> dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
>> if (!dev)
>> return -ENODEV;
>> - rpdev = pcie_find_root_port(dev);
>> - /* If Root Port not found, try to find an RCEC */
>> - if (!rpdev)
>> - rpdev = dev->rcec;
>> + if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)
>
> { }
> as the else is multiline (see coding standard)
>
Ok
> Maybe need a comment for why it might be an RCEC for injection.
> Is this an RCH specific path where there is nothing else to target?
>
>> + rpdev = dev;
>> + else {
>> + rpdev = pcie_find_root_port(dev);
>> + /* If Root Port not found, try to find an RCEC */
>> + if (!rpdev)
>> + rpdev = dev->rcec;
>> + }
>> if (!rpdev) {
>> pci_err(dev, "Neither Root Port nor RCEC found\n");
>> ret = -ENODEV;
>> @@ -482,6 +474,7 @@ static int aer_inject(struct aer_error_inj *einj)
>> pci_dev_put(dev);
>> return ret;
>> }
>> +EXPORT_SYMBOL_GPL(aer_inject);
> I wonder if we want to restrict this to specific modules?
>
> One for Bjorn probably.
>
Because right now it injects AER to any PCI device.
Also, worth discussing is the commandline currently uses multiple parameters for
a single sysfs file. I mentioned this at the top.
Thanks for reviewing Jonathan.
-Terry
>>
>> static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
>> size_t usize, loff_t *off)
>> diff --git a/include/linux/aer.h b/include/linux/aer.h
>> index b3657b80564b9..65c22ba597657 100644
>> --- a/include/linux/aer.h
>> +++ b/include/linux/aer.h
>> @@ -27,6 +27,21 @@
>> struct pci_dev;
>> struct work_struct;
>>
>> +struct aer_error_inj {
>> + u8 bus;
>> + u8 dev;
>> + u8 fn;
>> + u32 uncor_status;
>> + u32 cor_status;
>> + u32 header_log0;
>> + u32 header_log1;
>> + u32 header_log2;
>> + u32 header_log3;
>> + u32 domain;
>> +};
>> +
>> +int aer_inject(struct aer_error_inj *einj);
>> +
>> struct pcie_tlp_log {
>> union {
>> u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];
>
^ permalink raw reply
* [PATCH v6] docs/ja_JP: translate submitting-patches.rst (sign-off)
From: Akiyoshi Kurita @ 2026-07-20 22:34 UTC (permalink / raw)
To: linux-doc; +Cc: linux-kernel, corbet, akiyks, weibu
Translate the "Include PATCH in the subject" and "Sign your work -
the Developer's Certificate of Origin" sections into Japanese.
Keep the DCO text in English as the original certificate text, and add
a Japanese note that the sign-off refers to the English DCO text.
Use a reStructuredText note directive to make it clear that the note is
specific to the Japanese translation.
Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
---
Changes in v6:
Remove the extra blank line at EOF introduced during the rebase.
.../ja_JP/process/submitting-patches.rst | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/Documentation/translations/ja_JP/process/submitting-patches.rst b/Documentation/translations/ja_JP/process/submitting-patches.rst
index d8ee82ba790b..22d54b663051 100644
--- a/Documentation/translations/ja_JP/process/submitting-patches.rst
+++ b/Documentation/translations/ja_JP/process/submitting-patches.rst
@@ -402,3 +402,80 @@ ping したりする前に、少なくとも 1 週間は待ってください。
を追加しないでください。
"RESEND" は、前回の投稿から一切変更のないパッチまたはパッチシリーズの
再送だけに当てはまります。
+
+
+件名に PATCH を含める
+---------------------
+
+Linus と linux-kernel メーリングリストには大量のメールが届くため、
+件名の先頭に ``[PATCH]`` を付けることが一般的な慣例となっています。
+これにより、Linus や他のカーネル開発者は、パッチとその他の議論を
+容易に区別できます。
+
+``git send-email`` は、この指定を自動的に行います。
+
+
+作業への署名 - Developer's Certificate of Origin
+--------------------------------------------------
+
+誰が何を行ったのかを追跡しやすくするため、特にパッチが複数階層の
+メンテナーを経由して最終的にカーネルへ取り込まれる場合に備えて、
+メールでやり取りされるパッチには sign-off の手続きが導入されています。
+
+sign-off は、パッチの説明の末尾に追加する単純な一行です。これは、
+そのパッチを自分で作成したか、オープンソースのパッチとして提出する
+権利を持っていることを証明します。
+
+.. note:: 【訳註】
+
+ ``Signed-off-by`` によって同意する対象は、翻訳文ではなく、
+ 以下に示す英語原文の Developer's Certificate of Origin 1.1 です。
+ DCO は法的な性質を持つ文書であるため、本文は翻訳せず、原文のまま
+ 掲載します。内容を確認する場合は、必ず英語原文を参照してください。
+
+規則は単純で、以下を証明できる場合です::
+
+ Developer's Certificate of Origin 1.1
+
+ By making a contribution to this project, I certify that:
+
+ (a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+ (b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+ (c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+ (d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
+
+上記を証明できる場合は、次のような行を追加します::
+
+ Signed-off-by: Random J Developer <random@developer.example.org>
+
+既知の身元を使用してください。匿名での貢献は認められません。
+``git commit -s`` を使用すると、この行を自動的に追加できます。
+
+revert にも ``Signed-off-by:`` を含める必要があります。
+``git revert -s`` を使用すると、自動的に追加できます。
+
+末尾に追加のタグを付ける人もいます。現時点では無視されますが、
+社内手続きを示したり、sign-off に関する特記事項を記録したりするために
+使用できます。
+
+作者の SoB に続く追加の SoB(``Signed-off-by:``)は、パッチの開発には
+関与せず、その取り扱いや転送を行った人によるものです。SoB の連鎖は、
+パッチがメンテナーを経て最終的に Linus へ届いた実際の経路を反映する
+必要があります。最初の SoB は、単独の主要作者であることを示します。
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v18 05/13] PCI/AER: Introduce AER-CXL protocol error kfifo
From: Jonathan Cameron @ 2026-07-20 22:41 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny, Len Brown,
Rafael J . Wysocki, Robert Richter, linux-acpi, linux-cxl,
linux-doc, linux-kernel, linux-pci, linuxppc-dev,
Alejandro Lucero, Alison Schofield, Ankit Agrawal, Ard Biesheuvel,
Ben Cheatham, Borislav Petkov, Breno Leitao, Davidlohr Bueso,
Fabio M . De Francesco, Gregory Price, Hanjun Guo,
Jonathan Corbet, Kees Cook, Kuppuswamy Sathyanarayanan, Li Ming,
Mahesh J Salgaonkar, Mauro Carvalho Chehab, Oliver O'Halloran,
Shiju Jose, Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck,
Vishal Verma, Ashok Raj
In-Reply-To: <20260717222706.3540281-6-terry.bowman@amd.com>
On Fri, 17 Jul 2026 17:26:58 -0500
Terry Bowman <terry.bowman@amd.com> wrote:
Hi Terry.
This is a rather length patch description. Might be worth
a parse to see if key details can be covered in something that folk
are more likely to read!
> CXL VH RAS handling requires a path for the AER driver to hand off
> CXL protocol errors to cxl_core for logging and recovery before PCIe
> AER recovery tears down the device. Add
> drivers/pci/pcie/aer_cxl_vh.c to implement this handoff via a kfifo-backed
> work item.
>
> Introduce is_aer_internal_error() to identify CXL protocol errors
> from AER internal error status bits across both correctable and
> uncorrectable severities.
Already existed. Just in a different location.
>
> Introduce is_cxl_error() to gate the VH kfifo path.
Feels like to much detail to me given not really anything to say
about it other than the obvious.
>
> Introduce struct cxl_proto_err_work_data to carry the error source
> PCI device and severity through the kfifo. Encapsulate the kfifo,
> per-producer spinlock, registration rwsem, and work pointer in struct
> cxl_proto_err_kfifo. Initialize the embedded kfifo via INIT_KFIFO()
Can we cut this down a little where we don't use useful info. e.g.
"Initialize the kfifo from a subsys_initcall() to it is ready before
any producer or consumer runs."
> from a subsys_initcall so its metadata is populated before any
> producer or consumer runs.
>
> Introduce cxl_forward_error() to enqueue a CXL protocol error. A
> reference is taken on the PCI device; the consumer releases it via
> for_each_cxl_proto_err(). On enqueue failure the reference is
> released immediately, the error is dropped, and the consumer is
> scheduled to drain existing entries.
We don't need to cover error paths in the patch description unless
they are really complex and need explanation. Even then probably belongs
more in comments.
Anyhow you get the idea..
> A subsequent patch wires
> cxl_forward_error() into handle_error_source() where correctable and
> uncorrectable status clearing is left to pci_aer_handle_error().
>
> Introduce cxl_proto_err_flush() to synchronously wait for the
> consumer worker to drain the kfifo. A subsequent patch wires this
> into handle_error_source() for UCE events so the CXL plane completes
> error handling and panic policy before pci_aer_handle_error() drives
> PCIe recovery.
>
> Introduce cxl_register_proto_err_work() and
> cxl_unregister_proto_err_work() for cxl_core to register and
> deregister its work handler. On unregistration, pending kfifo entries
> are drained and their pdev references released before
> cancel_work_sync() runs. Export these and for_each_cxl_proto_err()
> via EXPORT_SYMBOL_FOR_MODULES restricted to cxl_core.
>
> Protect the work pointer with a rwsem to correctly serialize
> registration, deregistration, enqueue, and dequeue against concurrent
> AER IRQ threads. Serialize concurrent kfifo writers with a spinlock.
>
> Add MAINTAINERS entries for aer_cxl_vh.c and aer_cxl_rch.c under
> the CXL entry so CXL maintainers are CC'd on changes to the AER-CXL
> bridging code.
Various things inline. The one potential thing I'd like to highlight is
the loss of tracking assume panic isn't appropriate. My paranoid hat
says always go the other way. If we don't know we didn't lose an
uncorrectable error panic. Maybe it's worth us considering when this
might happen in a real system?
>
> Co-developed-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Dan Williams <djbw@kernel.org>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>
> ---
>
> Changes in v17->v18:
> - Remove correctable status clear from cxl_forward_error(); the AER core
> clears all status bits via pci_aer_handle_error() info->status writeback
> - Schedule consumer on kfifo overflow so existing entries can be drained
>
> Changes in v16->v17:
> - Reword "kfifo semaphore" to "kfifo spinlock" to match fifo_lock.
> - Defer the handle_error_source() is_cxl_error() switch to the patch that
> registers the kfifo consumer to keep each commit bisect-safe.
> - Rename rwsema to rwsem
> - Change CPER exports to use EXPORT_SYMBOL_FOR_MODULES.
> - Add work cancel function.
> - Replace kfifo_put() with kfifo_in_spinlocked() for multiple producers
> - Add fifo_lock spinlock for concurrent producer serialisation
> - Initialize the embedded kfifo with INIT_KFIFO() in a subsys_initcall so
> kfifo->mask, ->esize and ->data are set before first use.
> - Clear PCI_ERR_COR_STATUS in cxl_forward_error() after enqueue so the
> device is acked for correctable events even when the consumer drops the
> event. Uncorrectable status is left for cxl_do_recovery() to clear after
> recovery completes, mirroring the AER core convention.
> - WARN on double-registration in cxl_register_proto_err_work() to make an
> unintended second consumer visible at runtime.
> - Add direct rwsem.h, cleanup.h and workqueue.h includes for symbols used
> in aer_cxl_vh.c
> - Add MAINTAINERS entries for drivers/pci/pcie/aer_cxl_*.c
> - Update message
> ---
> MAINTAINERS | 2 +
> drivers/pci/pcie/Makefile | 1 +
> drivers/pci/pcie/aer.c | 10 --
> drivers/pci/pcie/aer_cxl_vh.c | 221 ++++++++++++++++++++++++++++++++++
> drivers/pci/pcie/portdrv.h | 6 +
> include/linux/aer.h | 24 ++++
> 6 files changed, 254 insertions(+), 10 deletions(-)
> create mode 100644 drivers/pci/pcie/aer_cxl_vh.c
>
> diff --git a/drivers/pci/pcie/aer_cxl_vh.c b/drivers/pci/pcie/aer_cxl_vh.c
> new file mode 100644
> index 0000000000000..93bed07936100
> --- /dev/null
> +++ b/drivers/pci/pcie/aer_cxl_vh.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2026 AMD Corporation. All rights reserved. */
> +
> +#include <linux/aer.h>
> +#include <linux/atomic.h>
> +#include <linux/cleanup.h>
> +#include <linux/init.h>
> +#include <linux/kfifo.h>
> +#include <linux/rwsem.h>
Check these. I'd expect at least spinlock.h to meet the rough include what
you use aim.
> +#include <linux/wait_bit.h>
> +#include <linux/workqueue.h>
> +#include "../pci.h"
> +#include "portdrv.h"
> +
> +#define CXL_ERROR_SOURCES_MAX 128
> +
> +struct cxl_proto_err_kfifo {
> + struct work_struct *work;
> + void (*flush)(void);
> + struct rw_semaphore rwsem;
> + spinlock_t fifo_lock;
Ideally add a quick comment to every lock to say what data it covers.
Kind of obvious for this one but in general it is good practice and
reduces chance of later scope confusion.
> + atomic_t flush_inflight;
> + DECLARE_KFIFO(fifo, struct cxl_proto_err_work_data,
> + CXL_ERROR_SOURCES_MAX);
> +};
> +/**
> + * cxl_forward_error - Forward a CXL protocol error to the CXL subsystem via kfifo
> + * @pdev: PCI device that reported the AER error
> + * @info: AER error info containing severity and status
> + *
> + * Producer side of the AER-CXL kfifo. Enqueues a CXL protocol error work
> + * item and schedules the consumer workqueue. Takes a reference on @pdev
> + * that the consumer releases after handling.
> + *
> + * Return: true if the caller must flush the kfifo before AER recovery,
> + * false if no CXL error handling was initiated due to early return on
> + * error.
> + */
> +bool cxl_forward_error(struct pci_dev *pdev, struct aer_err_info *info)
> +{
> + struct cxl_proto_err_work_data wd = {
> + .severity = info->severity,
> + .pdev = pdev,
> + };
> +
> + guard(rwsem_read)(&cxl_proto_err_kfifo.rwsem);
> +
> + if (!cxl_proto_err_kfifo.work) {
> + dev_err_ratelimited(&pdev->dev, "AER-CXL kfifo reader not registered\n");
> + return false;
> + }
> +
> + /*
> + * Reference discipline: the AER caller (handle_error_source())
> + * holds a ref on @pdev for the duration of this call and releases
> + * it on return. Take a fresh ref here so the pdev stays live while
> + * queued in the kfifo; the consumer (for_each_cxl_proto_err())
> + * drops that ref after handling. On enqueue failure below, drop
> + * the ref we just took to avoid a leak.
Most of this is useful. The what we do in error handling (given so local)
probably not.
> + */
> + pci_dev_get(pdev);
> +
> + /* Serialize concurrent kfifo writers: multiple AER threaded IRQs */
> + if (!kfifo_in_spinlocked(&cxl_proto_err_kfifo.fifo, &wd, 1,
> + &cxl_proto_err_kfifo.fifo_lock)) {
> + /* Dropped; no panic - UCE unconfirmed without RAS read */
Hmm. That's interesting. To me it fails the normal ras thing of assume
the worst if we lost track. I'd panic. But I'm open to other views on this!
> + dev_err_ratelimited(&pdev->dev, "AER-CXL kfifo add failed\n");
> + pci_dev_put(pdev);
> + schedule_work(cxl_proto_err_kfifo.work);
Shared with below. Maybe just drop out of the if unless this gets more
complex in later patches.
> + return true;
> + }
> +
> + schedule_work(cxl_proto_err_kfifo.work);
> + return true;
> +}
> +/**
> + * for_each_cxl_proto_err - Call a function for each kfifo work item
> + *
> + * Single-consumer invariant: this function is only called from
> + * cxl_proto_err_work_fn() via a single DECLARE_WORK.
> + *
> + * Holds rwsem_read internally; fn() must not call cxl_register_proto_err_work()
> + * or cxl_unregister_proto_err_work().
> + */
> +void for_each_cxl_proto_err(struct cxl_proto_err_work_data *wd,
> + cxl_proto_err_fn_t fn)
> +{
> + guard(rwsem_read)(&cxl_proto_err_kfifo.rwsem);
> + while (kfifo_get(&cxl_proto_err_kfifo.fifo, wd)) {
> + fn(wd);
Like the earlier case I razed, I'd like a comment here to say where
the reference we re releasing was taken.
> + pci_dev_put(wd->pdev);
> + }
> +}
> +EXPORT_SYMBOL_FOR_MODULES(for_each_cxl_proto_err, "cxl_core");
> +
> +/**
> + * cxl_proto_err_flush - drain pending AER-CXL kfifo work synchronously
> + *
> + * Wait for the consumer worker to finish processing all entries
> + * currently in the kfifo. Used by handle_error_source() for UCE so
> + * the CXL plane can read CXL RAS, apply panic policy, and clear CXL
> + * state before pci_aer_handle_error() drives PCIe recovery.
> + *
> + * Snapshots the flush callback under rwsem_read and releases the rwsem
> + * before calling it. This avoids holding rwsem_read across flush_work(),
> + * which would deadlock via the rwsem HANDOFF mechanism when a concurrent
> + * rwsem_write waiter (cxl_unregister_proto_err_work) blocks new readers
> + * including the worker's for_each_cxl_proto_err() rwsem_read acquisition.
> + *
> + * The flush_inflight counter prevents cxl_core module unload while a
> + * flush is in progress outside the rwsem. The counter is incremented
> + * under rwsem_read (mutually exclusive with the rwsem_write in
> + * cancel_cxl_proto_err() that NULLs the flush pointer) and decremented
> + * after the flush completes. cxl_unregister_proto_err_work() waits for
> + * the counter to reach zero before proceeding with cancel_work_sync().
I can't see it from just this patch so it might be useful to say if the
counter is ever not equal to 1 or 0. If it isn't can we make that explicit
in the code?
> + *
> + * For correctable events the consumer can run asynchronously; AER
> + * does not need to call this helper for AER_CORRECTABLE.
For me this is both too much and likely to rot over time. Some stuff
feels like it belongs with the flags it is talking about rather than here.
> + */
> +void cxl_proto_err_flush(void)
> +{
> + void (*flush)(void);
> +
> + scoped_guard(rwsem_read, &cxl_proto_err_kfifo.rwsem) {
> + flush = cxl_proto_err_kfifo.flush;
> + if (flush)
> + atomic_inc(&cxl_proto_err_kfifo.flush_inflight);
> + }
> +
> + if (flush) {
> + flush();
> + if (atomic_dec_and_test(&cxl_proto_err_kfifo.flush_inflight))
> + wake_up_var(&cxl_proto_err_kfifo.flush_inflight);
> + }
> +}
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox