Re: [RFC] cxl: Device protocol AER injection

Junjie Cao <[email protected]>
Newsgroups org.ozlabs.lists.linuxppc-dev,org.kernel.vger.linux-acpi,org.kernel.vger.linux-cxl,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci
Message-ID <[email protected]>
Hi Terry,

On Fri, 17 Jul 2026 17:57:00 -0500, Terry Bowman 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"

Ran this on 7.2-rc3 with v18, under QEMU with a switch topology so all
four port classes are present.  v19 wasn't out yet when I ran it; the
findings below are in the RFC's own code and don't move with the delta.
It works -- one CE per class with a different RAS status bit each time,
attribution correct in all four:

  RP  ras=0x1  port=port1 dport=0000:0c:00.0  'Cache Data ECC Error'
  USP ras=0x2  port=port2 dport=              'Memory Data ECC Error'
  DSP ras=0x4  port=port2 dport=0000:0e:00.0  'CRC Threshold Hit'
  EP  ras=0x8  memdev=mem1 port=endpoint4     'Retry Threshold'

A UCE panics via cxl_pci_error_detected() as the series intends, and six
malformed inputs are rejected.  Only VH here, so I can't say anything
about whether the RCH branch is right.

Tested-by: Junjie Cao <[email protected]>

> +	depends on PCIEAER_INJECT

Beyond Jonathan's question about asking at all -- a bool depending on a
tristate means kconfig will give you PCIEAER_INJECT=m with CXL_BUS=y
and this =y.  olddefconfig produces that from a defconfig and it
doesn't link:

  ld: ras.c: undefined reference to `aer_inject'

Deriving it from the combination rather than asking would take the
broken config with the question.

> +	cxl_aer_einj.aer_registers[aer_offset] = aer_status;

Only reference to aer_registers[] in the tree.  aer_inject() builds
its own AER config space and the RCH path skips
cxl_rch_get_aer_info(), so nothing reads it.  Is it still needed, or
can it go with the core/ras_einj.c move?

> +	cxl_aer_einj.dev = NULL;

Clears the pointer but not the reference.  Every non-RCH arm takes a
pci_dev_get() below; to_einj_ras_base() on a match and
cxl_ras_exit() drop it.  Both need .dev to still point there.  So an
arm not consumed before the next write is orphaned.

Thirty back-to-back injections gave 26 trace events, with aer_inject
run 30 times and the kfifo-add-failed, port-not-found, port-unbound,
dport-not-found and RAS-not-mapped counts all zero (kfifo is 128 deep,
so no overflow).  Four arms were never disarmed.  I can't tell whether
the AER core folded them into one pass or the next write replaced .dev
first, and either way the reference goes.  I couldn't make it fire on
demand -- unbinding cxl_port fails the arm-time lookup instead -- so
it's a race, but it's the one a test loop hits.

Behind it the handshake has no single owner: the mutex is writer-only,
while to_einj_ras_base() mutates the same fields and drops a reference
without it.  Both consumers are live: the CE runs consume arms from
the kfifo work, and the UCE backtrace has cxl_pci_error_detected()
doing it from the AER IRQ thread.  A disarm helper
owning the reference would cover this and the asymmetry Jonathan
raised about putting a reference it didn't get.

> +	cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);

RCH on a VH device is accepted:

  # echo "0000:0c:00.0 CE 0 0x80 RCH" > .../aer_einj_inject   -> 0

aer_inject() fires and AER logs it, but no CXL trace event appears --
the RCH branch can't match a VH dport.  Reproduces every time.  So a
typo gets you a real AER error with the CXL path silently skipped,
which reads as "the handler didn't fire".  is_cxl_restricted(pdev),
already used further down the file, would make it -EINVAL.

> Also worth discussing is the commandline takes multiple parameters for
> a single sysfs file

One file also suits the state handling above better.  Several files
spread one request over several writes with no commit point.

> How do we incorporate this with the existing CXL EINJ functionality

core/ras_einj.c sounds right, and the Kconfig above is another reason
for both in one place.  Worth a line in the ABI doc that ACPI EINJ is
a real injection but Root Port only while this covers every class with
software registers -- that's what decides which one a test wants.

Last thing, off this patch: with the registers a kernel-side array this
can't exercise the MMIO readback, the offsets against a real capability
block, or the header log.  QEMU has RAS on root and switch ports but
injects only on endpoints, so it can't cover the classes today either.
I'm looking at adding port-level injection there as a second path
through the real reads.

Many thanks,
Junjie
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.