* [PATCH v10 00/10] cxl: Support Back-Invalidate
@ 2026-11-01 21:50 Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 01/10] cxl: Add BI register probing and port initialization Davidlohr Bueso
` (9 more replies)
0 siblings, 10 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:50 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso
Hello,
Changes from v9 (https://lore.kernel.org/all/cover.1790103847.git.dave@stgolabs.net/):
- Patch 1: comment that the BI capability lengths are not the IDs (Richard)
- Patch 2: adopt a device whose BI Enable firmware already set rather
than refuse it with -EBUSY, moved here from patch 8 (Alison)
- Patch 2: on teardown, account for locked decoders (Richard)
- Patch 2: a level found already up on first use is held, and only a
level this driver brought up from zero is ever cleared
- Patch 2: setup's unwind and teardown share the new cxl_bi_release(),
replacing the open-coded rollback in cxl_bi_enable_path()
- Patch 2: .reset_done holds the endpoint port's device lock and skips
the check once that port is unbound, also closing a race with an
endpoint port rebind (sashiko)
- Patch 2: reference the CXL 4.0 9.14.2 allocate/deallocate sequences
in the enable and release walks (Ming)
- Patch 3: dropped the refusal of auto-committed BI decoders (Alison)
- Patch 4: dropped the dead root decoder target_type hunk (Alison)
- Patch 4: changelog: only sysfs regions inherit from the root (Alison)
- Patch 4: attach checks now in cxl_region_check_coherency() (Alison)
- Patch 4: attach refusals use dev_dbg(), not dev_err/warn (Alison)
- Patch 4: committed decoders keep upstream's type mismatch refusal at
attach; moved from patch 8 (Alison)
- Patch 4: the window range type check for committed decoders moved
here from patch 8 (Alison)
- Patch 4: detach keeps a still-committed decoder's target_type.
- Patch 4: ISP comment citing CXL 4.0 Table 8-123 (Alison)
- Patch 8: renamed "Allow" -> "Handle": patch 3 no longer refuses
committed BI decoders and endpoint adoption moved to patch 2 (Alison)
- Patch 8: committed BI state is now the CXL_DECODER_F_BI flag, set at
enumeration and commit and cleared on reset, instead of an HDM
register read at assembly (Alison)
- Patch 8: the BI attach check joins patch 4's check for committed
decoders, so it covers any F_ENABLE one and uses dev_dbg() (Alison)
- Patch 8: setup failure unwinds through patch 2's release, like
teardown, replacing the prefix-based partial unwind.
- Patch 9: set CXL_DECODER_F_BI instead of emulating HDM decoder regs (Alison)
- Patch 9: fixed a mock BI Decoder page use-after-free on rebind (sashiko)
- Collected Reviewed-by tags.
This series is the plumbing to enable HDM-DB in Linux. The model allows
Type 2 and Type 3 devices to expose their local memory to the host CPU
in a coherent manner. In alignment with what was discussed at 2024 LPC
type2 support session, this series takes the Type 3 memory expander
approach, which is more direct. Further, afaik there is no Type 2 + BI
hardware out there.
A flagship use case of Type 3 + BI is coherent shared memory, and
there is currently a big gap in this regard (ie: GFAM). Another
is P2P via PCIe UIO, which is also lacking today. Media Operation
(4402h) for ranged sanitize/zero also trigger snoops and there are
some patches out there[0]. As such this series focuses on BI enablement
in terms of discovery and configuration.
A device firmware already enabled for BI is taken up as found
(patch 2), and the levels firmware staged above it are adopted level
by level (patch 8). A BI decoder firmware committed is recorded at
enumeration; one that cannot route BISnp, or whose BI bit
contradicts its window, is refused (patches 7-8). See sections 8
and 12.
o Patch 1 probes BI capabilities during register discovery, and maps BI
Decoder registers.
o Patch 2 implements the BI-ID allocation (and deallocation) from
the endpoint port's lifecycle, at any switch depth. It takes up a
device firmware already enabled, keeps BI up for a device that still
decodes with BI at teardown, and detects a reset that wiped the
device's BI Enable.
o Patch 3 adds coherency support for endpoint decoders based on the
HDM's Supported Coherency Models.
o Patch 4 adds support for HDM-DB region creation.
o Patch 5 trivially renames some coherency flags.
o Patch 6 logs the coherency model (HDM-H, HDM-D, HDM-DB) when a
region is created.
o Patch 7 splits the BI capability walk from BI-ID allocation so the
answer is available when committed decoders are first parsed.
o Patch 8 handles BI decoders that firmware committed, and is required
rather than optional: through patch 7 such a decoder is assembled
without its BI bit ever being looked at. It records the bit
(CXL_DECODER_F_BI), refuses BI with a host-only range type or on a
path that cannot route BISnp, refuses a committed decoder whose BI
bit does not match its window, and adopts a firmware-staged path
level by level.
o Patch 9 adds a mock BI topology to cxl_test.
o Patch 10 updates the maturity map.
Semantics
---------
Who owns a BI level, and when one may be cleared or must be committed,
comes down to three rules:
1. A level is cleared only if this driver brought it up from zero and
has not since held it. A dport found with Enable or Forward
already set while nothing this driver counts is routed through it
is held for the dport's lifetime, whoever set it.
2. The device keeps BI Enable while one of its HDM decoders still
decodes with BI. At release the driver reads the decoders from the
hardware; if one is committed with BI set, the device keeps its
Enable and every level on its path is held. A failed walk never
holds.
3. A commit is owed when a level's BI-ID assignments change, never
per register write: once for each device newly enabled below it,
and once when its last device is unassigned. An adopted level, a
held level reaching zero, and a root port are not committed.
Locked BI HDM decoders need no special case; rules 1 and 2 cover them.
A decoder committed with Lock On Commit set cannot be reset, and one in
a locked region is never reset by this driver, so either keeps decoding
with BI after the driver lets go of it. Release reads the Committed and
BI bits from the hardware, not the lock, so this holds whether or not
the kernel knows the decoder is locked: the device keeps BI Enable and
every level on its path is held. That covers an unbind, an orderly
hot-remove, and switch ports above the device being deleted and later
re-created, where the re-created levels read up and are held again. A
rebind takes the device up as found and adopts the levels left up for
it, with no write (see testing below t24 fw-locked, fw-switch-locked
and fw-orphan, t17 locked-remove, t25 nested-hold).
Testing
-------
The qemu-based test suite covers 35 topologies, including cascaded
switches, interleaves across switches, firmware-adopted and partially
staged paths, locked decoders, device hotplug, resets and AER recovery.
All 40 suites pass (889 checks).
https://github.com/davidlohr/cxl-bi-tests
The QEMU knobs the suites use to stage firmware state and inject
commit faults are described in that repo's README. QEMU v11.x declares
Host-only together with BI on its BI windows, which this series
rejects as undefined; QEMU master no longer does (cb52f088f221).
This passes regression testing (nothing breaks) ndctl suite via
cxl_test, all 18 tests with KASAN, lockdep and UBSAN enabled, with the
mock HDM-DB window of patch 9 and the BI phase covering HDM-DB
assembly, cxl_acpi rebind replay and the Type 2 accelerator. The BI
mock device test script along with tooling to consume the new sysfs ABI
from this series is in the ndctl tree here:
https://github.com/davidlohr/ndctl/tree/cxl-back-invalidate-test
1. HDM Decoder with BI through ad-hoc region creation.
------------------------------------------------------
# cxl list -D
[
{
"decoder":"decoder0.0",
"resource":6710886400,
"size":4294967296,
"interleave_ways":1,
"accelmem_capable":true,
"nr_targets":1
}
]
# echo region0 > $ROOT/create_ram_region
[ 15.856712] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
# echo ram > $EP/mode
# echo 0x40000000 > $EP/dpa_size
# echo 1 > $REG/interleave_ways
# echo 4096 > $REG/interleave_granularity
# echo 0x40000000 > $REG/size
# cat $EP/back_invalidate
0
# echo decoder2.0 > $REG/target0
[ 18.577151] cxl_core:cxl_port_attach_region:1351: cxl region0: mem0:endpoint2 decoder2.0 add: mem0:decoder2.0 @ 0 next: none nr_eps: 1 nr_targets: 1
[ 18.578039] cxl_core:cxl_port_attach_region:1351: cxl region0: pci0000:0c:port1 decoder1.0 add: mem0:decoder2.0 @ 0 next: mem0 nr_eps: 1 nr_targets: 1
[ 18.578063] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port1 iw: 1 ig: 4096
[ 18.578070] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[0] = 0000:0c:00.0 for mem0:decoder2.0 @ 0
[ 18.578158] cxl_core:cxl_calc_interleave_pos:2096: cxl_mem mem0: decoder:decoder2.0 parent:0000:0d:00.0 port:endpoint2 range:0x190000000-0x1cfffffff pos:0
# cat $EP/back_invalidate
1
# echo 1 > $REG/commit
# cat $EP/back_invalidate
1
# cxl list -D
[
{
"root decoders":[
{
"decoder":"decoder0.0",
"resource":6710886400,
"size":4294967296,
"interleave_ways":1,
"accelmem_capable":true,
"nr_targets":1
}
]
},
{
"port decoders":[
{
"decoder":"decoder1.0",
"resource":6710886400,
"size":1073741824,
"interleave_ways":1,
"region":"region0",
"nr_targets":1
}
]
},
{
"endpoint decoders":[
{
"decoder":"decoder2.0",
"resource":6710886400,
"size":1073741824,
"interleave_ways":1,
"region":"region0",
"dpa_resource":0,
"dpa_size":1073741824,
"mode":"ram"
}
]
}
]
2. Type3 device does not support BI, but CFMW has BI restriction
----------------------------------------------------------------
# (region0 created on decoder0.0; interleave ways/granularity, size and DPA set as in section 1)
# echo decoder3.0 > $REG/target0
[ 15.483525] cxl_core:cxl_region_check_coherency:2144: cxl region0: mem0:decoder3.0 BI not enabled on device
[ 15.483533] cxl_port endpoint3: failed to attach decoder3.0 to region0: -6
3. Type3 device supports BI but CFMW does not, create HDM-H region
------------------------------------------------------------------
# cxl list -D
[
{
"decoder":"decoder0.0",
"resource":6710886400,
"size":4294967296,
"interleave_ways":1,
"max_available_extent":4294967296,
"pmem_capable":true,
"volatile_capable":true,
"accelmem_capable":true,
"nr_targets":1
}
]
# echo region0 > $ROOT/create_ram_region
[ 15.801502] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-H region0
# (interleave ways/granularity, size and DPA set as in section 1)
# cat $EP/back_invalidate
0
# echo decoder3.0 > $REG/target0
[ 16.611397] cxl_core:cxl_port_attach_region:1351: cxl region0: mem0:endpoint3 decoder3.0 add: mem0:decoder3.0 @ 0 next: none nr_eps: 1 nr_targets: 1
[ 16.611805] cxl_core:cxl_port_attach_region:1351: cxl region0: 0000:0d:00.0:port2 decoder2.0 add: mem0:decoder3.0 @ 0 next: mem0 nr_eps: 1 nr_targets: 1
[ 16.612149] cxl_core:cxl_port_attach_region:1351: cxl region0: pci0000:0c:port1 decoder1.0 add: mem0:decoder3.0 @ 0 next: 0000:0d:00.0 nr_eps: 1 nr_targets: 1
[ 16.612155] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port1 iw: 1 ig: 4096
[ 16.612158] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[0] = 0000:0c:00.0 for mem0:decoder3.0 @ 0
[ 16.612164] cxl_core:cxl_port_setup_targets:1671: cxl region0: 0000:0d:00.0:port2 iw: 1 ig: 4096
[ 16.612166] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:0d:00.0:port2 target[0] = 0000:0e:00.0 for mem0:decoder3.0 @ 0
[ 16.612211] cxl_core:cxl_calc_interleave_pos:2096: cxl_mem mem0: decoder:decoder3.0 parent:0000:0f:00.0 port:endpoint3 range:0x190000000-0x1cfffffff pos:0
# echo 1 > $REG/commit
# cat $EP/back_invalidate
0
4. Unbind + Bind
----------------
On section 1's system, with region0 still committed:
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/unbind
[ 20.559469] cxl_core:cxl_port_commit_reap:991: cxl_port endpoint2: reap: decoder2.0 commit_end: -1
[ 20.563821] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests disabled
[ 20.564756] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/bind
[ 20.952772] cxl_core:devm_cxl_enumerate_ports:1945: cxl_mem mem0: scan: iter: mem0 dport_dev: 0000:0c:00.0 parent: pci0000:0c
[ 20.952787] cxl_core:devm_cxl_enumerate_ports:1951: cxl_mem mem0: found already registered port port1:pci0000:0c
[ 20.959742] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests enabled
[ 20.980606] cxl_core:init_hdm_decoder:1214: cxl_port endpoint2: decoder2.0: range: 0x0-0xffffffffffffffff iw: 1 ig: 4096
[ 20.982581] cxl_core:add_hdm_decoder:40: cxl_mem mem0: decoder2.0 added to endpoint2
[ 20.992731] cxl_core:cxl_bus_probe:2390: cxl_port endpoint2: probe: 0
[ 20.993261] cxl_core:cxl_bus_probe:2390: cxl_mem mem0: probe: 0
The same applies to the endpoint port, with the memdev rebind
recovering the endpoint:
# echo endpoint2 > /sys/bus/cxl/drivers/cxl_port/unbind
[ 23.441567] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests disabled
[ 23.444331] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/bind
[ 23.828837] cxl_core:devm_cxl_enumerate_ports:1945: cxl_mem mem0: scan: iter: mem0 dport_dev: 0000:0c:00.0 parent: pci0000:0c
[ 23.828854] cxl_core:devm_cxl_enumerate_ports:1951: cxl_mem mem0: found already registered port port1:pci0000:0c
[ 23.837017] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests enabled
[ 23.858724] cxl_core:init_hdm_decoder:1214: cxl_port endpoint2: decoder2.0: range: 0x0-0xffffffffffffffff iw: 1 ig: 4096
[ 23.859613] cxl_core:add_hdm_decoder:40: cxl_mem mem0: decoder2.0 added to endpoint2
[ 23.862914] cxl_core:cxl_bus_probe:2390: cxl_port endpoint2: probe: 0
[ 23.863297] cxl_core:cxl_bus_probe:2390: cxl_mem mem0: probe: 0
From a cold boot, without a region ever created, the endpoint port
unbind only disables BI - there is no committed decoder to reap:
# echo endpoint2 > /sys/bus/cxl/drivers/cxl_port/unbind
[ 15.532967] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests disabled
[ 15.539194] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/bind
[ 15.950005] cxl_core:devm_cxl_enumerate_ports:1945: cxl_mem mem0: scan: iter: mem0 dport_dev: 0000:0c:00.0 parent: pci0000:0c
[ 15.950021] cxl_core:devm_cxl_enumerate_ports:1951: cxl_mem mem0: found already registered port port1:pci0000:0c
[ 15.956131] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests enabled
[ 15.976762] cxl_core:init_hdm_decoder:1214: cxl_port endpoint2: decoder2.0: range: 0x0-0xffffffffffffffff iw: 1 ig: 256
[ 15.978163] cxl_core:add_hdm_decoder:40: cxl_mem mem0: decoder2.0 added to endpoint2
[ 15.984492] cxl_core:cxl_bus_probe:2390: cxl_port endpoint2: probe: 0
[ 15.984878] cxl_core:cxl_bus_probe:2390: cxl_mem mem0: probe: 0
5. Discovery behind a Switch
----------------------------
One BI-capable device on a switch downstream port, the switch under a
root port, all in 256B flit. The capability walk finds a BI Decoder
on the device, the root port and the downstream port, and the route
table on the switch upstream port:
[ 1.576146] cxl_core:cxl_probe_component_regs:103: cxl_pci 0000:0f:00.0: found BI Decoder capability (0xab4)
[ 1.585191] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0c:00.0: found BI Decoder capability (0xab4)
[ 1.588697] cxl_core:cxl_probe_component_regs:97: cxl_port port2: found BI RT capability (0xaa8)
[ 1.618201] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0e:00.0: found BI Decoder capability (0xab4)
[ 1.765119] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 140737us
[ 1.965340] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200139us
[ 1.965412] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0f:00.0: BI requests enabled
6. Corrupted hw (commit timeout handling)
-----------------------------------------
A switch downstream port that never completes its BI Decoder commit,
advertising a two second window (t23). It is the device-adjacent level
and the first one the walk programs, so there is nothing below it to
unwind. The walk waits the window out, restores the control register and
commits the undo, which times out as well, and BI setup fails. The
device's BI Enable is never set; the endpoint still enumerates its
decoders and probes, with back_invalidate reading 0:
[ 3.907286] pcieport 0000:0e:00.0: BI-ID commit timed out (2000000us)
[ 5.954857] pcieport 0000:0e:00.0: BI-ID commit timed out (2000000us)
[ 5.954962] cxl_core:devm_cxl_endpoint_decoders_setup:1457: cxl_port endpoint3: BI setup failed rc=-110
[ 5.988287] cxl_core:cxl_bus_probe:2390: cxl_port endpoint3: probe: 0
[ 5.989110] cxl_core:cxl_bus_probe:2390: cxl_mem mem0: probe: 0
7. Mixed Configurations (BI-capable Type 3 behind a 68B hop)
------------------------------------------------------------
[ 1.613617] cxl_core:cxl_probe_component_regs:103: cxl_pci 0000:0f:00.0: found BI Decoder capability (0xab4)
[ 1.623517] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0c:00.0: found BI Decoder capability (0xab4)
[ 1.654123] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0e:00.0: found BI Decoder capability (0xab4)
[ 1.658849] cxl_core:cxl_bi_probe_capable:1497: cxl_pci 0000:0f:00.0: BI not supported by USP
# cat /sys/bus/cxl/devices/endpoint3/decoder3.0/back_invalidate
0
# echo region0 > /sys/bus/cxl/devices/decoder0.0/create_ram_region
[ 15.848894] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
# (interleave ways 1, granularity 256, size and DPA 256M)
# echo decoder3.0 > /sys/bus/cxl/devices/region0/target0
[ 15.913258] cxl_core:cxl_region_check_coherency:2144: cxl region0: mem0:decoder3.0 BI not enabled on device
[ 15.913270] cxl_port endpoint3: failed to attach decoder3.0 to region0: -6
The same endpoint decoder still commits into an HDM-H region on the
plain window:
# echo region1 > /sys/bus/cxl/devices/decoder0.1/create_ram_region
[ 15.996159] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.1: created HDM-H region1
# (interleave ways 1, granularity 256, size and DPA 256M)
# echo decoder3.0 > /sys/bus/cxl/devices/region1/target0
# echo 1 > /sys/bus/cxl/devices/region1/commit
# cat /sys/bus/cxl/devices/endpoint3/decoder3.0/back_invalidate
0
8. Auto-discovered (committed) BI decoders
------------------------------------------
For testing, qemu emulates a firmware-committed decoder (x-committed)
with this patch:
https://lore.kernel.org/all/20260909184728.mznbg6ox6nqnvv5p@offworld/
[ 1.531772] cxl_core:devm_cxl_switch_port_decoders_setup:1407: cxl_port port1: Fallback to passthrough decoder
[ 1.546029] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:0d:00.0: adopting firmware-enabled BI
[ 1.567513] cxl_core:init_hdm_decoder:1214: cxl_port endpoint2: decoder2.0: range: 0x190000000-0x19fffffff iw: 1 ig: 256
[ 1.579179] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
[ 1.580453] cxl_core:__construct_region:3930: cxl_pci 0000:0d:00.0: mem0:decoder2.0: __construct_region region0 res: [mem 0x190000000-0x19fffffff flags 0x200] iw: 1 ig: 256
[ 1.580520] cxl_core:cxl_region_sort_targets:2126: cxl region0: region sort successful
[ 1.585749] cxl_core:devm_cxl_add_dax_region:101: cxl_region region0: region0: register dax_region0
[ 1.585825] cxl_core:cxl_bus_probe:2390: cxl_region region0: probe: 0
# cat /sys/bus/cxl/devices/region0/commit
1
# cat /sys/bus/cxl/devices/decoder2.0/back_invalidate
1
A committed BI decoder under a window without the BI restriction is
refused at region assembly, the refusal naming the window (t16 neg,
t24 fw-orphan):
[ 1.533388] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:0d:00.0: adopting firmware-enabled BI
[ 1.563754] cxl decoder0.0: mem0:decoder2.0 coherency model not permitted by the window
[ 1.563760] cxl_port:discover_region:58: cxl decoder2.0: failed to add to region: 0x190000000-0x1cfffffff
A committed 2-way set mixing a BI decoder with a host-only one under
a BI window has its host-only member refused whichever is discovered
first: constructing a host-only region under the window is refused by
the window check above, and joining the HDM-DB region the BI member
constructed is refused at attach ("BI not enabled on device"). The
set never reaches a committed region (t16 mixed).
And a committed decoder cannot inherit a region's coherency model the
way a decoder the driver programs does. Of a committed 2-way set
pairing an HDM-D (device coherent, BI clear) accelerator decoder with
an HDM-H Type 3 one under a window that permits both, whichever member
constructs the region, the other is refused at attach (t16 mixed-trt).
Here the Type 3 constructed it:
[ 1.802379] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-H region0
[ 1.803579] cxl_core:__construct_region:3930: cxl_pci 0000:41:00.0: mem0:decoder3.0: __construct_region region0 res: [mem 0x190000000-0x1afffffff flags 0x200] iw: 2 ig: 4096
[ 1.812579] cxl_core:cxl_region_check_coherency:2178: cxl region0: mem1:decoder4.0 coherency model mismatch: HDM-D vs HDM-H
[ 1.812584] cxl_port endpoint4: failed to attach decoder4.0 to region0: -6
That holds for as long as the decoder is committed in hardware, not
only while it sits in the region discovered for it. A member of a
committed 2-way HDM-DB set whose peer never commits leaves its auto
region waiting, uncommitted. Deleting that region resets nothing, so
the decoder keeps the type it decodes, and an HDM-H region on a plain
window refuses it rather than take it in: cxl_decoder_commit() skips
an already committed decoder, so the region would report committed
with the decoder never reprogrammed (t16 orphan):
[ 1.738705] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:0d:00.0: adopting firmware-enabled BI
[ 1.765548] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
[ 1.766270] cxl_core:__construct_region:3930: cxl_pci 0000:0d:00.0: mem0:decoder4.0: __construct_region region0 res: [mem 0x190000000-0x1afffffff flags 0x200] iw: 2 ig: 4096
# cat /sys/bus/cxl/devices/region0/commit
0
# echo region0 > /sys/bus/cxl/devices/decoder0.0/delete_region
# cat /sys/bus/cxl/devices/decoder4.0/target_type
accelerator
# echo region1 > /sys/bus/cxl/devices/decoder0.1/create_ram_region
[ 18.281105] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.1: created HDM-H region1
# echo 4096 > /sys/bus/cxl/devices/region1/interleave_granularity
# echo 1 > /sys/bus/cxl/devices/region1/interleave_ways
# cat /sys/bus/cxl/devices/decoder4.0/dpa_size > /sys/bus/cxl/devices/region1/size
# echo decoder4.0 > /sys/bus/cxl/devices/region1/target0
[ 18.290076] cxl_core:cxl_region_check_coherency:2178: cxl region1: mem0:decoder4.0 coherency model mismatch: HDM-DB vs HDM-H
[ 18.290085] cxl_port endpoint4: failed to attach decoder4.0 to region1: -6
The attach refusals are dev_dbg, like the upstream type mismatch
refusal they extend, and show above with dynamic debug enabled; the
failed attach itself is always reported. The window refusal at region
construction is logged as an error.
9. Device reset
---------------
A Secondary Bus Reset through the kernel's reset attribute clears
the device's BI Enable, whose reset default is 0. .reset_done
compares cxlds->bi against the hardware and fails closed. With a
region attached but not yet committed (t34), the commit is refused
rather than programming BI into a device that no longer has it, and
a rebind recovers:
# echo region0 > $ROOT/create_ram_region
[ 15.186715] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
# echo ram > $EP/mode
# echo 0x40000000 > $EP/dpa_size
# echo 1 > $REG/interleave_ways
# echo 4096 > $REG/interleave_granularity
# echo 0x40000000 > $REG/size
# echo decoder2.0 > $REG/target0
[ 17.491121] cxl_core:cxl_port_attach_region:1351: cxl region0: mem0:endpoint2 decoder2.0 add: mem0:decoder2.0 @ 0 next: none nr_eps: 1 nr_targets: 1
[ 17.491546] cxl_core:cxl_port_attach_region:1351: cxl region0: pci0000:0c:port1 decoder1.0 add: mem0:decoder2.0 @ 0 next: mem0 nr_eps: 1 nr_targets: 1
[ 17.491553] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port1 iw: 1 ig: 4096
[ 17.491556] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[0] = 0000:0c:00.0 for mem0:decoder2.0 @ 0
[ 17.491600] cxl_core:cxl_calc_interleave_pos:2096: cxl_mem mem0: decoder:decoder2.0 parent:0000:0d:00.0 port:endpoint2 range:0x190000000-0x1cfffffff pos:0
# cat $EP/target_type
accelerator
# echo cxl_bus > /sys/bus/pci/devices/0000:0d:00.0/reset_method
# echo 1 > /sys/bus/pci/devices/0000:0d:00.0/reset
[ 18.695604] cxl_pci 0000:0d:00.0: resetting
[ 18.696461] cxl_pci 0000:0d:00.0: reset via cxl_bus
[ 18.704372] cxl_pci 0000:0d:00.0: BI disabled by reset
# echo 1 > $REG/commit
[ 19.091791] cxl region0: mem0:decoder2.0 BI not enabled on device
# cat $EP/back_invalidate
0
# echo region0 > $ROOT/delete_region
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/unbind
[ 20.257107] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
# echo mem0 > /sys/bus/cxl/drivers/cxl_mem/bind
[ 20.647241] cxl_core:devm_cxl_enumerate_ports:1945: cxl_mem mem0: scan: iter: mem0 dport_dev: 0000:0c:00.0 parent: pci0000:0c
[ 20.647260] cxl_core:devm_cxl_enumerate_ports:1951: cxl_mem mem0: found already registered port port1:pci0000:0c
[ 20.659460] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests enabled
[ 20.680513] cxl_core:init_hdm_decoder:1214: cxl_port endpoint2: decoder2.0: range: 0x0-0xffffffffffffffff iw: 1 ig: 256
[ 20.683594] cxl_core:add_hdm_decoder:40: cxl_mem mem0: decoder2.0 added to endpoint2
[ 20.690671] cxl_core:cxl_bus_probe:2390: cxl_port endpoint2: probe: 0
[ 20.693012] cxl_core:cxl_bus_probe:2390: cxl_mem mem0: probe: 0
# echo region0 > $ROOT/create_ram_region
[ 23.186452] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
# echo ram > $EP/mode
# echo 0x40000000 > $EP/dpa_size
# echo 1 > $REG/interleave_ways
# echo 4096 > $REG/interleave_granularity
# echo 0x40000000 > $REG/size
# echo decoder2.0 > $REG/target0
[ 25.500903] cxl_core:cxl_port_attach_region:1351: cxl region0: mem0:endpoint2 decoder2.0 add: mem0:decoder2.0 @ 0 next: none nr_eps: 1 nr_targets: 1
[ 25.501254] cxl_core:cxl_port_attach_region:1351: cxl region0: pci0000:0c:port1 decoder1.0 add: mem0:decoder2.0 @ 0 next: mem0 nr_eps: 1 nr_targets: 1
[ 25.501260] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port1 iw: 1 ig: 4096
[ 25.501263] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[0] = 0000:0c:00.0 for mem0:decoder2.0 @ 0
[ 25.501295] cxl_core:cxl_calc_interleave_pos:2096: cxl_mem mem0: decoder:decoder2.0 parent:0000:0d:00.0 port:endpoint2 range:0x190000000-0x1cfffffff pos:0
# echo 1 > $REG/commit
# cat $EP/back_invalidate
1
The same reset with the HDM-DB region committed (t27) trips the
existing decoder-loss detection alongside the BI invalidation:
# echo 1 > /sys/bus/pci/devices/0000:0d:00.0/reset
[ 26.651521] cxl_pci 0000:0d:00.0: resetting
[ 26.652479] cxl_pci 0000:0d:00.0: reset via cxl_bus
[ 26.663574] cxl_pci 0000:0d:00.0: BI disabled by reset
[ 26.663587] cxl_pci 0000:0d:00.0: SBR happened without memory regions removal.
[ 26.663589] cxl_pci 0000:0d:00.0: System may be unstable if regions hosted system memory.
# cat $EP/back_invalidate
0
A reset landing after the commit's own check but before the decoder
is programmed is caught by re-reading BI Decoder Control once the
target is committed, and the target is undone (t34). The run below
clears BI Enable through /dev/mem before the commit, leaving
cxlds->bi set, to stand in for a reset whose .reset_done has not yet
run:
# echo 1 > $REG/commit
[ 15.463266] cxl region0: mem0:decoder2.0 BI disabled by reset during commit
[ 15.463283] cxl_core:cxl_port_commit_reap:991: cxl_port endpoint2: reap: decoder2.0 commit_end: -1
# cat $REG/commit
0
An FLR leaves the CXL.cache/mem component registers alone, so BI
Enable, the region and its decoders all survive it and nothing is
invalidated (t27 flr-region). Resets around a switch, including one
below a held level, are in section 15.
10. AER recovery
----------------
An AER Uncorrectable Fatal error injected into the endpoint (t28)
runs the recovery flow, which never reaches .reset_done: the memdev
bounce brings BI down through devres before the link reset, and the
re-attach after slot reset runs the enable path from scratch. An
HDM-DB region commits again afterwards.
[ 17.122893] pcieport 0000:0c:00.0: AER: Uncorrectable (Fatal) Error message received from 0000:0d:00.0
[ 17.122901] cxl_pci 0000:0d:00.0: AER: CXL Bus Error: severity=Uncorrectable (Fatal) (Inaccessible)
[ 17.122920] pcieport 0000:0c:00.0: AER: broadcast error_detected message
[ 17.175801] cxl_pci 0000:0d:00.0: mem0: frozen state error detected, disable CXL.mem
[ 17.176523] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests disabled
[ 17.181262] pcieport 0000:0c:00.0: AER: Root Port link has been reset (0)
[ 17.181288] pcieport 0000:0c:00.0: AER: broadcast slot_reset message
[ 17.181291] cxl_pci 0000:0d:00.0: mem0: restart CXL.mem after slot reset
[ 17.190943] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests enabled
[ 17.218302] pcieport 0000:0c:00.0: AER: broadcast resume message
[ 17.218307] cxl_pci 0000:0d:00.0: mem0: error resume successful
[ 17.218481] pcieport 0000:0c:00.0: AER: device recovery successful
[ 19.522263] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
With a committed HDM-DB region live (t28 region), the bounce resets
the endpoint decoder before BI comes down, so no decoder is left
decoding with BI once BI Enable is cleared. The region is detached
and decommitted but left for userspace to delete, and a new one
commits after recovery:
[ 17.976231] cxl_pci 0000:0d:00.0: mem0: frozen state error detected, disable CXL.mem
[ 17.977026] cxl_core:cxl_port_commit_reap:991: cxl_port endpoint2: reap: decoder2.0 commit_end: -1
[ 17.977436] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0d:00.0: BI requests disabled
[ 18.018267] pcieport 0000:0c:00.0: AER: device recovery successful
[ 20.270882] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region1
11. Cascaded switches and interleaves across switches
-----------------------------------------------------
Two cascaded switches (t9): both route tables are found and committed,
the transit DSP takes Forward and the device-adjacent DSP Enable, and
an HDM-DB region commits two switch hops down next to an HDM-H one:
[ 1.638170] cxl_core:cxl_probe_component_regs:103: cxl_pci 0000:11:00.0: found BI Decoder capability (0xab4)
[ 1.648278] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0c:00.0: found BI Decoder capability (0xab4)
[ 1.653275] cxl_core:cxl_probe_component_regs:97: cxl_port port2: found BI RT capability (0xaa8)
[ 1.683172] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0e:00.0: found BI Decoder capability (0xab4)
[ 1.691573] cxl_core:cxl_probe_component_regs:97: cxl_port port3: found BI RT capability (0xaa8)
[ 1.717012] cxl_core:cxl_probe_component_regs:103: pcieport 0000:10:00.0: found BI Decoder capability (0xab4)
[ 1.923997] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 195797us
[ 2.178803] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 254725us
[ 2.379136] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200277us
[ 2.647352] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 268086us
[ 2.647521] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:11:00.0: BI requests enabled
[ 15.870770] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
[ 16.525134] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.1: created HDM-H region1
Unbinding the outer switch port takes the cascade down (t9 phase 2):
the device's BI Enable is released first, then each DSP is cleared and
committed on the way up, along with its switch's route table, the
deallocate sequence of 9.14.2:
[ 18.314428] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:11:00.0: BI requests disabled
[ 18.424815] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 110308us
[ 18.626048] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 201026us
[ 18.826717] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200370us
[ 19.027462] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200510us
A rebind commits all four again.
Three switch levels (t25), one commit per DSP and per route table:
[ 2.145524] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:12:00.0: BI-ID commit wait took 134272us
[ 2.400261] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port4: BI-ID commit wait took 254563us
[ 2.600108] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 199741us
[ 2.800669] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 200458us
[ 3.001641] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200649us
[ 3.108539] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 106634us
[ 3.108813] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:13:00.0: BI requests enabled
A 4-way HDM-DB region across two host bridges, each a switch with two
BI endpoints (t32): the root interleaves across the host bridges and
each USP decoder lands iw=2 below it, all four endpoints at bi=1:
[ 17.078571] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
[ 17.491240] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port2 iw: 1 ig: 8192
[ 17.491246] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port2 target[0] = 0000:0c:00.0 for mem2:decoder8.0 @ 0
[ 17.491258] cxl_core:cxl_port_setup_targets:1671: cxl region0: 0000:0d:00.0:port3 iw: 2 ig: 8192
[ 17.491263] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:0d:00.0:port3 target[0] = 0000:0e:00.0 for mem2:decoder8.0 @ 0
[ 17.491271] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:40:port1 iw: 1 ig: 8192
[ 17.491275] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:40:port1 target[0] = 0000:40:00.0 for mem3:decoder7.0 @ 1
[ 17.491285] cxl_core:cxl_port_setup_targets:1671: cxl region0: 0000:41:00.0:port4 iw: 2 ig: 8192
[ 17.491289] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:41:00.0:port4 target[0] = 0000:42:00.0 for mem3:decoder7.0 @ 1
[ 17.491299] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port2 target[0] = 0000:0c:00.0 for mem0:decoder5.0 @ 2
[ 17.491309] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:0d:00.0:port3 target[1] = 0000:0e:01.0 for mem0:decoder5.0 @ 2
[ 17.491319] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:40:port1 target[0] = 0000:40:00.0 for mem1:decoder6.0 @ 3
[ 17.491329] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:41:00.0:port4 target[1] = 0000:42:01.0 for mem1:decoder6.0 @ 3
The same under one host bridge with two root ports (t33), the host
bridge decoder interleaving across the root ports:
[ 17.116490] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
[ 17.544042] cxl_core:cxl_port_setup_targets:1671: cxl region0: pci0000:0c:port1 iw: 2 ig: 1024
[ 17.544044] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[0] = 0000:0c:00.0 for mem2:decoder6.0 @ 0
[ 17.544053] cxl_core:cxl_port_setup_targets:1671: cxl region0: 0000:0d:00.0:port3 iw: 2 ig: 2048
[ 17.544055] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:0d:00.0:port3 target[0] = 0000:0e:00.0 for mem2:decoder6.0 @ 0
[ 17.544060] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[1] = 0000:0c:01.0 for mem0:decoder5.0 @ 1
[ 17.544065] cxl_core:cxl_port_setup_targets:1671: cxl region0: 0000:11:00.0:port2 iw: 2 ig: 2048
[ 17.544067] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:11:00.0:port2 target[0] = 0000:12:00.0 for mem0:decoder5.0 @ 1
[ 17.544073] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[1] = 0000:0c:00.0 for mem1:decoder4.0 @ 2
[ 17.544079] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:0d:00.0:port3 target[1] = 0000:0e:01.0 for mem1:decoder4.0 @ 2
[ 17.544084] cxl_core:cxl_port_setup_targets:1697: cxl region0: pci0000:0c:port1 target[1] = 0000:0c:01.0 for mem3:decoder7.0 @ 3
[ 17.544089] cxl_core:cxl_port_setup_targets:1697: cxl region0: 0000:11:00.0:port2 target[1] = 0000:12:01.0 for mem3:decoder7.0 @ 3
12. Firmware-staged paths behind switches
-----------------------------------------
A device whose BI Enable firmware already set is taken as found (patch
2), and patch 8 takes its path the same way, from the device up: a
level is adopted, with no write and no commit, only while nothing this
driver routed sits below it, it already holds the role the walk would
write, and its BI Decoder and its switch's route table both read
committed. The first level that falls short ends adoption, and from
there up the driver programs and commits as for a new device. A level
found up, adopted or not, is held: this driver never clears it
(bi-semantics.md in the test repo, section 2.2 and rules 1 and 3).
With the device and both switch levels of t9's shape staged (t24
fw-path), nothing is committed where t9 committed four times (a root
port never commits):
[ 1.724285] cxl_core:cxl_probe_component_regs:97: cxl_port port2: found BI RT capability (0xaa8)
[ 1.757353] cxl_core:cxl_probe_component_regs:103: pcieport 0000:0e:00.0: found BI Decoder capability (0xab4)
[ 1.767828] cxl_core:cxl_probe_component_regs:97: cxl_port port3: found BI RT capability (0xaa8)
[ 1.797714] cxl_core:cxl_probe_component_regs:103: pcieport 0000:10:00.0: found BI Decoder capability (0xab4)
[ 1.809306] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:11:00.0: adopting firmware-enabled BI
Leave only the inner route table uncommitted (fw-partial) and no level
is adopted: the adjacent DSP fails on its switch's route table, and
from there up everything is committed, the correctly staged transit
included, four commits rather than two. The device's own Enable is
still taken as found:
[ 1.936171] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 192789us
[ 2.135862] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 199616us
[ 2.335752] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 199858us
[ 2.435437] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 99643us
[ 2.435577] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:11:00.0: adopting firmware-enabled BI
Two firmware-enabled devices sharing a staged transit DSP
(fw-shared-dsp): the first adopts both of its switch levels with no
commit; the second adopts its own adjacent DSP, finds the transit
already counted, and from there up commits exactly the transit decoder
and the outer route table:
[ 1.813094] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:12:00.0: adopting firmware-enabled BI
[ 2.006189] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 154721us
[ 2.123792] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 117578us
[ 2.123809] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:11:00.0: adopting firmware-enabled BI
Staged levels above a device firmware did not enable (fresh-dev) are
not adopted: the device is new below them, so they are committed for
it (Tables 8-152, 8-156) and the driver enables the device. They were
found up, so the unbind releases the device but leaves the DSP at
Enable, with no deallocate commit:
[ 1.830229] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 145515us
[ 1.935515] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 105208us
[ 1.935587] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0f:00.0: BI requests enabled
[ 17.693540] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0f:00.0: BI requests disabled
[ 17.696025] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port2
13. A BI decoder that outlives the driver
-----------------------------------------
An HDM decoder can keep decoding after the driver lets go: a locked
one cannot be reset, a locked region is never reset, and a committed
decoder that never joined a region is left alone. BI Enable is the
device-wide permission behind such a range, so release reads the
device's HDM decoders from the hardware (patch 2). If one is still
committed with its BI bit set, the device keeps its Enable and every
level on its path is held. The check reads the Committed and BI bits,
not the lock (bi-semantics.md, rules 1 and 2).
The decoder section 8 shows refused by the window stays committed
with no region (t24 fw-orphan). The unbind keeps the device's
Enable, with no "BI requests disabled", and holds the root port the
walk itself brought up; the rebind adopts it all again without a
write:
# echo 0000:0d:00.0 > /sys/bus/pci/drivers/cxl_pci/unbind
[ 17.584871] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
# echo 0000:0d:00.0 > /sys/bus/pci/drivers/cxl_pci/bind
[ 20.471048] cxl_core:__cxl_bi_ctrl_endpoint:1286: cxl_pci 0000:0d:00.0: adopting firmware-enabled BI
[ 20.508076] cxl decoder0.0: mem0:decoder2.0 coherency model not permitted by the window
The same for a hot-removed device whose decoder was committed with BI
and Lock On Commit (t17 locked-remove): no disable on the way out, and
its adjacent DSP reads back Enable afterwards:
[ 36.742754] pcieport 0000:0e:02.0: pciehp: Slot(6): Button press: will power off in 5 sec
[ 41.904582] pcieport 0000:0e:02.0: pciehp: pciehp_unconfigure_device: domain:bus:dev = 0000:11:00
[ 41.913321] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port2
[ 41.913365] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port1
[ 41.925222] pci 0000:11:00.0: device released
A hold outlives its switch ports. With a locked BI decoder on memA and
both endpoints unbound, the switch ports are deleted; memB (mem1 below)
then binds alone through re-created dports that remember nothing (t25
nested-hold). The re-created transit reads Forward with nothing
counted, so it is held again, and is still committed for memB, a new
device below it. memB's unbind clears only its own adjacent DSP,
committing it with the inner route table; the transit keeps Forward
for memA, and the switch ports are deleted again at the end:
[ 38.495473] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 115255us
[ 38.599460] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 103903us
[ 38.704372] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 104809us
[ 38.904649] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200198us
[ 38.904712] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:11:00.0: BI requests enabled
[ 40.902283] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:11:00.0: BI requests disabled
[ 41.060586] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 160559us
[ 41.260825] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 203050us
[ 41.261556] cxl_core:cxl_detach_ep:1609: cxl_mem mem1: disconnect mem1 from port3
[ 41.262034] cxl_core:cxl_detach_ep:1626: cxl_mem mem1: delete port3
[ 41.271155] cxl_core:cxl_detach_ep:1626: cxl_mem mem1: delete port2
QEMU cannot stage a locked decoder, so t17 and t25 commit it from the
guest through /dev/mem. Upstream QEMU does not enforce Lock On Commit;
these runs used a QEMU carrying "hw/cxl: Enforce Lock On Commit on HDM
decoders", not yet upstream.
14. Commit faults: an unwind never holds
----------------------------------------
A failed commit restores that level's previous value, committing the
restore if the walk wrote one, and the walk unwinds through the same
release as teardown (patch 2): a level the walk brought up is cleared
and committed, a level it adopted is held and left as firmware had it,
and the device, whose Enable is the walk's last step, is never touched.
Unlike teardown, an unwind holds nothing for a decoder that already
decodes with BI: the device's BI was never taken up, so there is no path
to keep. Setup fails, the probe continues with BI off, and an HDM-DB
attach is refused (bi-semantics.md, section 4).
A transit DSP that rejects every commit, above a device whose decoder
was committed with BI behind the driver (t25 live-rollback, the
rebind's walk): the adjacent DSP and inner route table are committed,
the transit rejects its commit and then the commit of its restore, the
adjacent level is cleared and committed rather than held, and the
region built from the committed decoder refuses it:
[ 19.301946] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 200415us
[ 19.502386] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 200233us
[ 19.702742] pcieport 0000:0e:00.0: BI-ID commit rejected by hardware
[ 19.903084] pcieport 0000:0e:00.0: BI-ID commit rejected by hardware
[ 20.103526] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:10:00.0: BI-ID commit wait took 200294us
[ 20.303914] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port3: BI-ID commit wait took 200124us
[ 20.303925] cxl_core:devm_cxl_endpoint_decoders_setup:1457: cxl_port endpoint4: BI setup failed rc=-5
[ 20.325216] cxl_core:init_hdm_decoder:1214: cxl_port endpoint4: decoder4.0: range: 0x190000000-0x1cfffffff iw: 1 ig: 256
[ 20.340658] cxl_core:cxl_region_check_coherency:2144: cxl region0: mem0:decoder4.0 BI not enabled on device
[ 20.340663] cxl_port endpoint4: failed to attach decoder4.0 to region0: -6
The two rejections are the commit of the new value and the commit of
the restored one, since restoring the register is itself a BI-ID
change; the emulated fault rejects both, where real hardware would
normally accept the restore.
15. Resets below and above a switch
-----------------------------------
The reset handling of section 9 still applies; what is new is how it
meets a switch and a held level (bi-semantics.md, section 5 and rule 1).
A reset of a device behind a switch (t27 switch) invalidates cxlds->bi,
and the unbind that follows leaves the device alone, its Enable already
clear, while still clearing the DSP the driver brought up and committing
it with its route table:
[ 17.220400] cxl_pci 0000:0f:00.0: resetting
[ 17.221471] cxl_pci 0000:0f:00.0: reset via cxl_bus
[ 17.227903] cxl_pci 0000:0f:00.0: BI disabled by reset
[ 19.714878] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200819us
[ 19.915605] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200509us
[ 19.921869] cxl_core:cxl_detach_ep:1609: cxl_mem mem0: disconnect mem0 from port2
When that DSP was staged by firmware (t27 held-reset), it was found up
and is held: after the same reset the unbind leaves the device alone,
the DSP reads back Enable, and only the root port the driver brought
up is cleared. The rebind adopts nothing, since the reset cleared the
device's Enable, and commits the held DSP and its route table again,
the device being new below them:
[ 23.155746] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200933us
[ 23.356282] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200309us
[ 23.356364] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0f:00.0: BI requests enabled
A bus reset issued from the root port, above the switch (t27
switch-sbr), wipes the switch's BI Decoder and route table with the
device, while the root port keeps Forward. Only the device's .reset_done
sees it, so the dports keep their counts and the unbind still reaches
the root port and clears it. The rebind finds nothing up and programs
the switch from scratch, and an HDM-DB region commits:
[ 27.466061] cxl_core:__cxl_bi_wait_commit:1058: pcieport 0000:0e:00.0: BI-ID commit wait took 200541us
[ 27.666632] cxl_core:__cxl_bi_wait_commit:1058: cxl_port port2: BI-ID commit wait took 200227us
[ 27.666742] cxl_core:__cxl_bi_ctrl_endpoint:1303: cxl_pci 0000:0f:00.0: BI requests enabled
[ 32.146539] cxl_core:devm_cxl_add_region:2921: cxl_acpi ACPI0017:00: decoder0.0: created HDM-DB region0
Applies against Linus' latest. Please consider for v7.5.
[0] https://lore.kernel.org/linux-cxl/20260428200410.705675-1-dave@stgolabs.net/
Thanks!
Davidlohr Bueso (10):
cxl: Add BI register probing and port initialization
cxl/pci: Add BI topology enable/disable
cxl/hdm: Add BI coherency support for endpoint decoders
cxl: Add HDM-DB region creation
cxl/hdm: Rename decoder coherency flags
cxl/region: Log the coherency model at region creation
cxl/pci: Split BI capability probe from setup
cxl: Handle auto-committed BI hdm decoders
cxl/test: Add mock BI topology support
cxl/doc: Update maturity map with BI support
Documentation/ABI/testing/sysfs-bus-cxl | 22 +
Documentation/driver-api/cxl/maturity-map.rst | 16 +-
drivers/cxl/acpi.c | 24 +-
drivers/cxl/core/core.h | 10 +
drivers/cxl/core/hdm.c | 122 +++-
drivers/cxl/core/pci.c | 583 ++++++++++++++++++
drivers/cxl/core/port.c | 48 +-
drivers/cxl/core/region.c | 165 ++++-
drivers/cxl/core/regs.c | 14 +
drivers/cxl/cxl.h | 66 +-
drivers/cxl/cxlmem.h | 2 +
drivers/cxl/pci.c | 8 +-
drivers/cxl/port.c | 46 ++
include/cxl/cxl.h | 13 +-
tools/testing/cxl/Kbuild | 1 +
tools/testing/cxl/test/accel.c | 2 +-
tools/testing/cxl/test/cxl.c | 149 ++++-
tools/testing/cxl/test/mock.c | 13 +
tools/testing/cxl/test/mock.h | 1 +
19 files changed, 1254 insertions(+), 51 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v10 01/10] cxl: Add BI register probing and port initialization
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
@ 2026-11-01 21:50 ` Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 02/10] cxl/pci: Add BI topology enable/disable Davidlohr Bueso
` (8 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:50 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Add register probing for BI Route Table and BI Decoder capability
structures in cxl_probe_component_regs(), and helpers to map them.
cxl_dport_map_bi() maps the BI Decoder of a downstream port (root
port or switch DSP) at dport-creation time via cxl_port_add_dport();
devm_cxl_port_bi_setup() maps a port's own BI capability when the
upstream link is in 256B Flit operation - the BI Decoder during
endpoint port probe, the BI RT of a switch USP on first-dport setup.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Richard Cheng <icheng@nvidia.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/pci.c | 38 ++++++++++++++++++++++++++++++++++
drivers/cxl/core/port.c | 4 +++-
drivers/cxl/core/regs.c | 14 +++++++++++++
drivers/cxl/cxl.h | 10 +++++++++
drivers/cxl/port.c | 45 +++++++++++++++++++++++++++++++++++++++++
include/cxl/cxl.h | 6 ++++++
7 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 79918fde1575..73478ebaf47e 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -204,6 +204,7 @@ 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);
+void devm_cxl_dport_bi_setup(struct cxl_dport *dport);
struct cxl_hdm;
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 125abb36df7f..80fda8acdf17 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -949,3 +949,41 @@ int cxl_port_get_possible_dports(struct cxl_port *port)
return ctx.count;
}
+
+static void cxl_dport_map_bi(struct cxl_dport *dport)
+{
+ struct cxl_register_map *map = &dport->reg_map;
+ struct device *dev = dport->dport_dev;
+
+ if (!map->component_map.bi_decoder.valid) {
+ dev_dbg(dev, "BI Decoder registers not found\n");
+ return;
+ }
+
+ if (cxl_map_component_regs(map, &dport->regs.component,
+ BIT(CXL_CM_CAP_CAP_ID_BI_DECODER)))
+ dev_dbg(dev, "Failed to map BI Decoder capability\n");
+}
+
+/**
+ * devm_cxl_dport_bi_setup - Map BI Decoder registers on a CXL dport
+ * @dport: the cxl_dport that needs to be initialized
+ *
+ * Must be called while the dport's devres group is open so iomap
+ * allocations are released on dport removal.
+ */
+void devm_cxl_dport_bi_setup(struct cxl_dport *dport)
+{
+ if (!dev_is_pci(dport->dport_dev))
+ return;
+
+ switch (pci_pcie_type(to_pci_dev(dport->dport_dev))) {
+ case PCI_EXP_TYPE_ROOT_PORT:
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ dport->reg_map.host = dport_to_host(dport);
+ cxl_dport_map_bi(dport);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index f3705ea44db7..3371b2d9ba9a 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1288,8 +1288,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
cxl_debugfs_create_dport_dir(dport);
- if (!dport->rch)
+ if (!dport->rch) {
devm_cxl_dport_ras_setup(dport);
+ devm_cxl_dport_bi_setup(dport);
+ }
/* keep the group, and mark the end of devm actions */
cxl_dport_close_dr_group(dport, no_free_ptr(dport_dr_group));
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 41416fa2ce4a..76bcc316a85c 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -93,6 +93,18 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
length = CXL_RAS_CAPABILITY_LENGTH;
rmap = &map->ras;
break;
+ case CXL_CM_CAP_CAP_ID_BI_RT:
+ dev_dbg(dev, "found BI RT capability (0x%x)\n",
+ offset);
+ length = CXL_BI_RT_CAPABILITY_LENGTH;
+ rmap = &map->bi_rt;
+ break;
+ case CXL_CM_CAP_CAP_ID_BI_DECODER:
+ dev_dbg(dev, "found BI Decoder capability (0x%x)\n",
+ offset);
+ length = CXL_BI_DECODER_CAPABILITY_LENGTH;
+ rmap = &map->bi_decoder;
+ break;
default:
dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
offset);
@@ -212,6 +224,8 @@ int cxl_map_component_regs(const struct cxl_register_map *map,
} mapinfo[] = {
{ &map->component_map.hdm_decoder, ®s->hdm_decoder },
{ &map->component_map.ras, ®s->ras },
+ { &map->component_map.bi_rt, ®s->bi_rt },
+ { &map->component_map.bi_decoder, ®s->bi_decoder },
};
int i;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index baa6df2d362a..33fadf4dda52 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -39,9 +39,19 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_CM_CAP_HDR_ARRAY_SIZE_MASK GENMASK(31, 24)
#define CXL_CM_CAP_PTR_MASK GENMASK(31, 20)
+/* CXL 4.0 8.2.4 Table 8-74 */
#define CXL_CM_CAP_CAP_ID_RAS 0x2
#define CXL_CM_CAP_CAP_ID_HDM 0x5
#define CXL_CM_CAP_CAP_HDM_VERSION 1
+#define CXL_CM_CAP_CAP_ID_BI_RT 0xB
+#define CXL_CM_CAP_CAP_ID_BI_DECODER 0xC
+
+/*
+ * CXL 4.0 8.2.4.26 / 8.2.4.27 BI Capability Structure lengths (Tables
+ * 8-151 and 8-155), unrelated to the capability IDs above.
+ */
+#define CXL_BI_RT_CAPABILITY_LENGTH 0xC
+#define CXL_BI_DECODER_CAPABILITY_LENGTH 0xC
/* HDM decoders CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure */
#define CXL_HDM_DECODER_CAP_OFFSET 0x0
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index c12fd0b89883..1a69be0ec7c1 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -61,6 +61,47 @@ static int discover_region(struct device *dev, void *unused)
return 0;
}
+static void devm_cxl_port_bi_setup(struct cxl_port *port)
+{
+ struct cxl_register_map *map = &port->reg_map;
+ struct cxl_dport *parent_dport = port->parent_dport;
+ struct device *udev;
+ int cap_id;
+
+ /* no upstream BI registers above host bridges or the cxl_root */
+ if (!parent_dport || is_cxl_root(parent_dport->port))
+ return;
+
+ udev = is_cxl_endpoint(port) ?
+ port->uport_dev->parent : port->uport_dev;
+ if (!dev_is_pci(udev))
+ return;
+
+ /* BI requires 256B Flit on the upstream link */
+ if (!cxl_pci_flit_256(to_pci_dev(udev)))
+ return;
+
+ /* map this port's own BI capability */
+ if (is_cxl_endpoint(port)) {
+ if (!map->component_map.bi_decoder.valid) {
+ dev_dbg(&port->dev, "BI Decoder registers not found\n");
+ return;
+ }
+ cap_id = CXL_CM_CAP_CAP_ID_BI_DECODER;
+ } else {
+ if (!map->component_map.bi_rt.valid) {
+ dev_dbg(&port->dev, "BI RT registers not found\n");
+ return;
+ }
+ cap_id = CXL_CM_CAP_CAP_ID_BI_RT;
+ }
+
+ map->host = &port->dev;
+ if (cxl_map_component_regs(map, &port->regs, BIT(cap_id)))
+ dev_dbg(&port->dev, "Failed to map BI capability 0x%x\n",
+ cap_id);
+}
+
static int cxl_switch_port_probe(struct cxl_port *port)
{
/* Reset nr_dports for rebind of driver */
@@ -131,6 +172,8 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
read_cdat_data(port);
cxl_endpoint_parse_cdat(port);
+ devm_cxl_port_bi_setup(port);
+
get_device(&cxlmd->dev);
rc = devm_add_action_or_reset(&port->dev, schedule_detach, cxlmd);
if (rc)
@@ -255,6 +298,8 @@ static struct cxl_dport *cxl_port_add_dport(struct cxl_port *port,
* on failure, or the device does not implement RAS registers.
*/
devm_cxl_port_ras_setup(port);
+
+ devm_cxl_port_bi_setup(port);
}
dport = devm_cxl_add_dport_by_dev(port, dport_dev);
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 1c04a7823f64..1df75fdefbfa 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -34,10 +34,14 @@ struct cxl_regs {
* Common set of CXL Component register block base pointers
* @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure
* @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure
+ * @bi_rt: CXL 4.0 8.2.4.26 CXL BI Route Table Capability Structure
+ * @bi_decoder: CXL 4.0 8.2.4.27 CXL BI Decoder Capability Structure
*/
struct_group_tagged(cxl_component_regs, component,
void __iomem *hdm_decoder;
void __iomem *ras;
+ void __iomem *bi_rt;
+ void __iomem *bi_decoder;
);
/*
* Common set of CXL Device register block base pointers
@@ -80,6 +84,8 @@ struct cxl_reg_map {
struct cxl_component_reg_map {
struct cxl_reg_map hdm_decoder;
struct cxl_reg_map ras;
+ struct cxl_reg_map bi_rt;
+ struct cxl_reg_map bi_decoder;
};
struct cxl_device_reg_map {
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 02/10] cxl/pci: Add BI topology enable/disable
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 01/10] cxl: Add BI register probing and port initialization Davidlohr Bueso
@ 2026-11-01 21:50 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 03/10] cxl/hdm: Add BI coherency support for endpoint decoders Davidlohr Bueso
` (7 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:50 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Implement cxl_bi_setup() to enable BI flows on the device and every
component in the path, and its teardown counterpart cxl_bi_dealloc().
Setup runs from devm_cxl_endpoint_decoders_setup(), between the
port's HDM state and its decoders. Registered there, its devres
teardown brings BI down after the decoders quiesce and before the
HDM state is freed, and BI is settled before the decoders, and later
the regions, are looked at. The BI-ID and path enablement belong to
the endpoint port's lifetime.
Setup is safe in endpoint port probe context. The port probes
synchronously from cxl_mem_probe(), pinning the memdev state the
walk consumes, and the whole ancestor path already exists with BI
registers mapped (dports at dport-add time, the switch USP RT at
first-dport setup) because devm_cxl_enumerate_ports() completes
before the endpoint is created.
Dealloc is safe in endpoint devres context. Both setup and dealloc
walk the endpoint's parent_dport topology rather than getting the
port by bus lookup - an ancestor teardown delists the parent port
before the endpoint's devres runs.
The topology walk is stable as parent_dport pointers are fixed at
port creation; ancestors cannot be reaped while holding this
memdev's cxl_ep; and their own teardown frees dports only after
the endpoint is gone.
Likewise, the device state outlives the walk - cxlmd->cxlds is
nulled only after cxl_memdev_unregister() has torn the endpoint
down, and delete_endpoint() clears cxlmd->endpoint only after the
endpoint devres has run.
Each dport is programmed by its position - the one immediately above
the device takes BI Enable, every dport above it takes BI Forward
(Table 8-157, Table 9-13), at any switch depth (Table 7-97). Any
level can be shared, so nr_bi refcounts endpoints at every dport.
Registers are written on the first endpoint and cleared on the last,
but only downstream ports commit (Table 8-156), once per endpoint
(Table 8-152), and a failed commit undoes its write and commits the
undo. A USP advertising a BI Route Table that failed to map is
refused rather than treated as absent. nr_bi counts only the
endpoints this driver enabled; a level firmware brought up is kept,
see below.
A device firmware already enabled is taken up as found, with no
write.
A reset may wipe the device's BI Enable, whose reset default is 0
(Table 8-157). .reset_done reads the hardware rather than assume
which reset ran, and invalidates cxlds->bi, failing closed with
recovery by rebind as for decoder loss; dealloc unwinds the dport
refcounts regardless. It also clears cxlds->bi unconditionally - the
endpoint disable fails when the hardware already shows BI Enable
clear, from a reset .reset_done never saw, and the flag must not
outlive the BI Decoder mapping it describes, which the endpoint port
releases moments later.
With dealloc in the endpoint's devres, delete_endpoint() already
holds the parent port's device lock, so to avoid deadlocking, add a
per-port bi_lock, serializing the dports that share state (nr_bi and
the control register at any shared level, the switch USP's BI RT).
A locked HDM decoder is the main reason release cannot simply turn BI
off: once committed with Lock On Commit set (CXL 4.0 8.2.4.20.13) it
cannot be reset, so it keeps decoding after this driver lets go, as
does a committed decoder that never joined a region. While its BI bit
is set, the device still needs BI Enable to snoop that range.
So setup's unwind and teardown share one release, which reads the
decoders from the hardware. If one is still committed with BI set,
the device keeps BI Enable and every level on its path is held: each
level's nr_bi reference is dropped, but a held level is never
cleared, so a rebind finds the path enabled. An unwind holds nothing,
since the device's BI was never taken up.
A level found already up, with nothing this driver enabled below it,
is held too: firmware may have brought it up, or its dport was
re-created and cannot tell whether a device below still decodes. Only
a level this driver brought up from zero is ever cleared.
The BI Decoder mapping belongs to the endpoint port, which an unbind
of that port alone releases, so .reset_done reads it only while
holding the port with its driver still bound.
Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/core.h | 8 +
drivers/cxl/core/hdm.c | 42 ++++
drivers/cxl/core/pci.c | 473 ++++++++++++++++++++++++++++++++++++++++
drivers/cxl/core/port.c | 1 +
drivers/cxl/cxl.h | 34 +++
drivers/cxl/pci.c | 8 +-
include/cxl/cxl.h | 2 +
7 files changed, 566 insertions(+), 2 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 73478ebaf47e..72be96df57f5 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -206,9 +206,17 @@ static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { }
int cxl_gpf_port_setup(struct cxl_dport *dport);
void devm_cxl_dport_bi_setup(struct cxl_dport *dport);
+static inline bool cxl_bi_decoder_enabled(struct cxl_port *port)
+{
+ return FIELD_GET(CXL_BI_DECODER_CTRL_BI_ENABLE,
+ readl(port->regs.bi_decoder +
+ CXL_BI_DECODER_CTRL_OFFSET));
+}
+
struct cxl_hdm;
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info);
+bool cxl_hdm_bi_committed(struct cxl_port *endpoint);
int cxl_port_get_possible_dports(struct cxl_port *port);
#ifdef CONFIG_CXL_FEATURES
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index fdcef20e01ba..f8fe1b12de3d 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -191,6 +191,38 @@ static struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
return cxlhdm;
}
+/*
+ * Whether any of the endpoint's HDM decoders still decodes with its BI
+ * bit set, read from the hardware: a decoder can outlive the driver's
+ * teardown committed, whether locked, in a region that is never reset,
+ * or never in a region at all.
+ */
+bool cxl_hdm_bi_committed(struct cxl_port *endpoint)
+{
+ struct cxl_hdm *cxlhdm = dev_get_drvdata(&endpoint->dev);
+ void __iomem *hdm;
+ u32 ctrl;
+ int i;
+
+ if (!cxlhdm || !cxlhdm->regs.hdm_decoder)
+ return false;
+ hdm = cxlhdm->regs.hdm_decoder;
+
+ /* nothing decodes with decoding off, emulated, or the device gone */
+ ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+ if (ctrl == U32_MAX || !(ctrl & CXL_HDM_DECODER_ENABLE))
+ return false;
+
+ for (i = 0; i < cxlhdm->decoder_count; i++) {
+ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
+ if ((ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED) &&
+ (ctrl & CXL_HDM_DECODER0_CTRL_BI))
+ return true;
+ }
+
+ return false;
+}
+
static void __cxl_dpa_debug(struct seq_file *file, struct resource *r, int depth)
{
unsigned long long start = r->start, end = r->end;
@@ -1358,6 +1390,16 @@ int devm_cxl_endpoint_decoders_setup(struct cxl_port *port)
if (rc)
return rc;
+ /*
+ * Between the port's HDM state and its decoders: devres,
+ * unwinding in reverse, brings BI down only after the decoders
+ * quiesce, while its slow walk still precedes the HDM state
+ * free.
+ */
+ rc = cxl_bi_setup(port);
+ if (rc)
+ dev_dbg(&port->dev, "BI setup failed rc=%d\n", rc);
+
return devm_cxl_enumerate_decoders(cxlhdm, &info);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_endpoint_decoders_setup, "CXL");
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 80fda8acdf17..96eadbf4a0cb 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -2,12 +2,14 @@
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
#include <linux/units.h>
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/iopoll.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
#include <cxl/pci.h>
#include <linux/aer.h>
+#include <linux/string_choices.h>
#include <cxlpci.h>
#include <cxlmem.h>
#include <cxl.h>
@@ -987,3 +989,474 @@ void devm_cxl_dport_bi_setup(struct cxl_dport *dport)
break;
}
}
+
+/*
+ * BI requires 256B Flit operation on the link. RP/DSP/endpoint must
+ * also have the BI Decoder cap mapped (@bi); for USPs the BI RT cap
+ * is optional per CXL 4.0 8.2.4.26, so absent @bi is allowed.
+ */
+static bool cxl_is_bi_capable(struct pci_dev *pdev, void __iomem *bi)
+{
+ if (!cxl_pci_flit_256(pdev))
+ return false;
+
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_UPSTREAM && !bi) {
+ dev_dbg(&pdev->dev, "No BI Decoder registers.\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* limit any insane timeouts from hw */
+#define CXL_BI_COMMIT_MAXTMO_US (20 * USEC_PER_SEC)
+
+static unsigned long __cxl_bi_get_timeout_us(struct device *dev,
+ unsigned int scale,
+ unsigned int base)
+{
+ static const unsigned long scale_tbl[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
+ };
+
+ if (scale >= ARRAY_SIZE(scale_tbl) || !base) {
+ dev_dbg(dev, "Invalid BI commit timeout: scale=%u base=%u\n",
+ scale, base);
+ return CXL_BI_COMMIT_MAXTMO_US;
+ }
+
+ return scale_tbl[scale] * base;
+}
+
+static int __cxl_bi_wait_commit(struct device *dev, void __iomem *status_reg,
+ u32 committed_bit, u32 err_bit,
+ unsigned int scale, unsigned int base)
+{
+ unsigned long tmo_us, poll_us;
+ ktime_t start;
+ u32 status;
+ int rc;
+
+ tmo_us = min_t(unsigned long, CXL_BI_COMMIT_MAXTMO_US,
+ __cxl_bi_get_timeout_us(dev, scale, base));
+ poll_us = max_t(unsigned long, tmo_us / 10, 1); /* ~10% */
+ start = ktime_get();
+
+ rc = readx_poll_timeout(readl, status_reg, status,
+ status & (committed_bit | err_bit),
+ poll_us, tmo_us);
+ if (rc) {
+ dev_err(dev, "BI-ID commit timed out (%luus)\n", tmo_us);
+ return rc; /* -ETIMEDOUT */
+ }
+
+ if (status & err_bit) {
+ dev_err(dev, "BI-ID commit rejected by hardware\n");
+ return -EIO;
+ }
+
+ dev_dbg(dev, "BI-ID commit wait took %lluus\n",
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ return 0;
+}
+
+/* BI RT only exists on switch upstream ports. */
+static int __cxl_bi_commit_rt(struct device *dev, void __iomem *bi)
+{
+ u32 status, ctrl;
+ unsigned int scale, base;
+
+ if (!FIELD_GET(CXL_BI_RT_CAPS_EXPLICIT_COMMIT_REQ,
+ readl(bi + CXL_BI_RT_CAPS_OFFSET)))
+ return 0;
+
+ ctrl = readl(bi + CXL_BI_RT_CTRL_OFFSET);
+ writel(ctrl & ~CXL_BI_RT_CTRL_BI_COMMIT, bi + CXL_BI_RT_CTRL_OFFSET);
+ writel(ctrl | CXL_BI_RT_CTRL_BI_COMMIT, bi + CXL_BI_RT_CTRL_OFFSET);
+
+ status = readl(bi + CXL_BI_RT_STATUS_OFFSET);
+ scale = FIELD_GET(CXL_BI_RT_STATUS_BI_COMMIT_TM_SCALE, status);
+ base = FIELD_GET(CXL_BI_RT_STATUS_BI_COMMIT_TM_BASE, status);
+
+ return __cxl_bi_wait_commit(dev, bi + CXL_BI_RT_STATUS_OFFSET,
+ CXL_BI_RT_STATUS_BI_COMMITTED,
+ CXL_BI_RT_STATUS_BI_ERR_NOT_COMMITTED,
+ scale, base);
+}
+
+static int __cxl_bi_commit_decoder(struct device *dev, void __iomem *bi)
+{
+ u32 status, ctrl;
+ unsigned int scale, base;
+
+ if (!FIELD_GET(CXL_BI_DECODER_CAPS_EXPLICIT_COMMIT_REQ,
+ readl(bi + CXL_BI_DECODER_CAPS_OFFSET)))
+ return 0;
+
+ ctrl = readl(bi + CXL_BI_DECODER_CTRL_OFFSET);
+ writel(ctrl & ~CXL_BI_DECODER_CTRL_BI_COMMIT,
+ bi + CXL_BI_DECODER_CTRL_OFFSET);
+ writel(ctrl | CXL_BI_DECODER_CTRL_BI_COMMIT,
+ bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ status = readl(bi + CXL_BI_DECODER_STATUS_OFFSET);
+ scale = FIELD_GET(CXL_BI_DECODER_STATUS_BI_COMMIT_TM_SCALE, status);
+ base = FIELD_GET(CXL_BI_DECODER_STATUS_BI_COMMIT_TM_BASE, status);
+
+ return __cxl_bi_wait_commit(dev, bi + CXL_BI_DECODER_STATUS_OFFSET,
+ CXL_BI_DECODER_STATUS_BI_COMMITTED,
+ CXL_BI_DECODER_STATUS_BI_ERR_NOT_COMMITTED,
+ scale, base);
+}
+
+static int cxl_bi_commit_dport(struct cxl_dport *dport)
+{
+ struct cxl_port *port = dport->port;
+ int rc;
+
+ lockdep_assert_held(&port->bi_lock);
+
+ /* root ports never require the explicit commit */
+ if (pci_pcie_type(to_pci_dev(dport->dport_dev)) !=
+ PCI_EXP_TYPE_DOWNSTREAM)
+ return 0;
+
+ rc = __cxl_bi_commit_decoder(dport->dport_dev, dport->regs.bi_decoder);
+ if (rc)
+ return rc;
+
+ if (port->regs.bi_rt)
+ rc = __cxl_bi_commit_rt(&port->dev, port->regs.bi_rt);
+
+ return rc;
+}
+
+/*
+ * Enable BI-ID changes in the given level of the topology.
+ * @direct says the device is connected directly to this dport, which
+ * takes BI Enable; every dport above it takes BI Forward.
+ */
+static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
+{
+ struct cxl_port *port = dport->port;
+ u32 ctrl, value, set, clr;
+ void __iomem *bi;
+ int rc;
+
+ guard(mutex)(&port->bi_lock);
+
+ bi = dport->regs.bi_decoder;
+ if (!bi)
+ return -EINVAL;
+
+ ctrl = readl(bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ /*
+ * First reference through a level already up: firmware brought
+ * it up, or this driver did for a device it let go of while it
+ * still decoded with BI, which a dport re-created since cannot
+ * otherwise know. Either way it is not this driver's to take
+ * down: only a level it brought up from zero is ever cleared.
+ */
+ if (!dport->nr_bi &&
+ (ctrl & (CXL_BI_DECODER_CTRL_BI_FW | CXL_BI_DECODER_CTRL_BI_ENABLE)))
+ dport->bi_held = true;
+
+ set = direct ? CXL_BI_DECODER_CTRL_BI_ENABLE :
+ CXL_BI_DECODER_CTRL_BI_FW;
+ clr = direct ? CXL_BI_DECODER_CTRL_BI_FW :
+ CXL_BI_DECODER_CTRL_BI_ENABLE;
+
+ value = (ctrl | set) & ~clr;
+ if (value != ctrl)
+ writel(value, bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ /* owed per new device below, not per register change */
+ rc = cxl_bi_commit_dport(dport);
+ if (rc) {
+ if (value != ctrl) {
+ /* the undo is a BI-ID change owing its own commit */
+ writel(ctrl, bi + CXL_BI_DECODER_CTRL_OFFSET);
+ cxl_bi_commit_dport(dport);
+ }
+ return rc;
+ }
+ dport->nr_bi++;
+
+ return 0;
+}
+
+/*
+ * Dealloc BI-ID changes in the given level of the topology. Called
+ * once per endpoint that enabled this level: on teardown, or to
+ * unwind a path that failed partway up. @hold marks the level for a
+ * device below that still decodes with BI, so that no teardown
+ * reaching zero clears it.
+ */
+static int cxl_bi_ctrl_dport_disable(struct cxl_dport *dport, bool hold)
+{
+ struct cxl_port *port = dport->port;
+ void __iomem *bi;
+ u32 ctrl;
+
+ guard(mutex)(&port->bi_lock);
+
+ bi = dport->regs.bi_decoder;
+ if (!bi)
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(dport->nr_bi == 0))
+ return -EINVAL;
+
+ if (hold)
+ dport->bi_held = true;
+
+ /* others below still need this level */
+ if (--dport->nr_bi > 0)
+ return 0;
+
+ /* found up, or a device below still decodes with BI */
+ if (dport->bi_held)
+ return 0;
+
+ ctrl = readl(bi + CXL_BI_DECODER_CTRL_OFFSET);
+ writel(ctrl & ~(CXL_BI_DECODER_CTRL_BI_FW |
+ CXL_BI_DECODER_CTRL_BI_ENABLE),
+ bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ return cxl_bi_commit_dport(dport);
+}
+
+static int __cxl_bi_ctrl_endpoint(struct cxl_dev_state *cxlds, bool enable)
+{
+ struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
+ void __iomem *bi = endpoint->regs.bi_decoder;
+ u32 ctrl;
+
+ if (!bi)
+ return -EINVAL;
+
+ ctrl = readl(bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ if (enable) {
+ if (FIELD_GET(CXL_BI_DECODER_CTRL_BI_ENABLE, ctrl)) {
+ if (!cxlds->bi) /* adopt firmware enabled */
+ dev_dbg(cxlds->dev,
+ "adopting firmware-enabled BI\n");
+ goto done;
+ }
+ } else {
+ if (!FIELD_GET(CXL_BI_DECODER_CTRL_BI_ENABLE, ctrl)) {
+ if (!cxlds->bi)
+ return 0;
+ dev_err(cxlds->dev,
+ "BI already disabled in hardware\n");
+ return -EBUSY;
+ }
+ }
+
+ FIELD_MODIFY(CXL_BI_DECODER_CTRL_BI_ENABLE, &ctrl, enable);
+ writel(ctrl, bi + CXL_BI_DECODER_CTRL_OFFSET);
+
+ dev_dbg(cxlds->dev, "BI requests %s\n", str_enabled_disabled(enable));
+done:
+ cxlds->bi = enable;
+ return 0;
+}
+
+static int cxl_bi_ctrl_endpoint_enable(struct cxl_dev_state *cxlds)
+{
+ return __cxl_bi_ctrl_endpoint(cxlds, true);
+}
+
+static int cxl_bi_ctrl_endpoint_disable(struct cxl_dev_state *cxlds)
+{
+ return __cxl_bi_ctrl_endpoint(cxlds, false);
+}
+
+/*
+ * Release the device's BI, then each dport up the path short of @stop,
+ * following the deallocate sequence in the implementation notes of
+ * CXL 4.0 9.14.2. Setup's unwind and teardown alike.
+ *
+ * A decoder that still decodes with its BI bit set needs BI Enable,
+ * the device-wide permission behind it, so the device keeps it. The
+ * references are still released - @nr_bi has to stay true to the
+ * endpoints below - and each level is held instead, so a later
+ * teardown reaching zero leaves it alone. An unwind never holds: the
+ * walk failed before the device's BI was taken up, so no path is left
+ * to keep up for it.
+ */
+static void cxl_bi_release(struct cxl_dev_state *cxlds, struct cxl_dport *stop)
+{
+ struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
+ bool live = cxl_hdm_bi_committed(endpoint);
+ bool hold = live && cxlds->bi;
+ struct cxl_dport *dport_iter;
+ struct cxl_port *port_iter;
+
+ /* only the binding that took up the device's BI releases it */
+ if (cxlds->bi && !live)
+ cxl_bi_ctrl_endpoint_disable(cxlds);
+ cxlds->bi = false;
+
+ /*
+ * Walk the same parent_dport chain that enabled the path. A bus
+ * lookup cannot stand in for it: an ancestor-driven teardown
+ * delists the parent port before this devres action runs.
+ */
+ dport_iter = endpoint->parent_dport;
+ port_iter = dport_iter->port;
+ while (!is_cxl_root(port_iter) && dport_iter != stop) {
+ int rc = cxl_bi_ctrl_dport_disable(dport_iter, hold);
+
+ /* best effort */
+ if (rc)
+ dev_dbg(&port_iter->dev,
+ "BI dport disable failed: %d\n", rc);
+
+ dport_iter = port_iter->parent_dport;
+ port_iter = dport_iter->port;
+ }
+}
+
+/*
+ * devm teardown on endpoint port destruction. Registered before the
+ * decoders, so devres runs it after them: regions are detached and
+ * decoders unregistered by the time BI comes down.
+ */
+static void cxl_bi_dealloc(void *data)
+{
+ struct cxl_port *endpoint = data;
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+
+ cxl_bi_release(cxlmd->cxlds, NULL);
+}
+
+/*
+ * Enable BI on every dport in the path, then on the device itself,
+ * following the allocate sequence in the implementation notes of
+ * CXL 4.0 9.14.2. On failure, unwind only the dports that enabled.
+ */
+static int cxl_bi_enable_path(struct cxl_dev_state *cxlds,
+ struct cxl_port *port, struct cxl_dport *dport)
+{
+ struct cxl_dport *dport_iter;
+ struct cxl_port *port_iter;
+ int rc;
+
+ port_iter = port;
+ dport_iter = dport;
+ while (!is_cxl_root(port_iter)) {
+ rc = cxl_bi_ctrl_dport_enable(dport_iter, dport_iter == dport);
+ if (rc)
+ goto err_rollback;
+
+ dport_iter = port_iter->parent_dport;
+ port_iter = dport_iter->port;
+ }
+
+ rc = cxl_bi_ctrl_endpoint_enable(cxlds);
+ if (rc)
+ goto err_rollback;
+
+ return 0;
+
+err_rollback:
+ /* the failed level, and any above it, took no reference */
+ cxl_bi_release(cxlds, dport_iter);
+ return rc;
+}
+
+/*
+ * An SBR wipes the device's BI Enable; an FLR leaves it alone.
+ * The check is against the hardware, not decoder state: BI is
+ * enabled at probe, so it can be wiped with no decoder ever
+ * committed. A wipe invalidates the software state; BI is never
+ * re-enabled here.
+ */
+void cxl_bi_reset_detected(struct cxl_port *endpoint)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+
+ /*
+ * The BI Decoder mapping belongs to the endpoint port, and an
+ * unbind of that port alone releases it: hold the port while it
+ * is read, and read nothing once the port driver is gone.
+ */
+ guard(device)(&endpoint->dev);
+ if (!endpoint->dev.driver)
+ return;
+
+ if (!cxlds->bi)
+ return;
+
+ if (cxl_bi_decoder_enabled(endpoint))
+ return;
+
+ dev_warn(cxlds->dev, "BI disabled by reset\n");
+ cxlds->bi = false;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_bi_reset_detected, "CXL");
+
+int cxl_bi_setup(struct cxl_port *endpoint)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ struct cxl_dport *dport = endpoint->parent_dport;
+ struct cxl_dport *dport_iter;
+ struct cxl_port *port_iter;
+ int rc;
+
+ if (!dev_is_pci(cxlds->dev))
+ return 0;
+
+ /* BI is VH-only */
+ if (cxlds->rcd)
+ return 0;
+
+ if (!cxl_is_bi_capable(to_pci_dev(cxlds->dev),
+ endpoint->regs.bi_decoder))
+ return 0;
+
+ port_iter = dport->port;
+ dport_iter = dport;
+ while (!is_cxl_root(port_iter)) {
+ /* check rp, dsp */
+ if (!cxl_is_bi_capable(to_pci_dev(dport_iter->dport_dev),
+ dport_iter->regs.bi_decoder)) {
+ dev_dbg(cxlds->dev, "BI not supported by topology\n");
+ return 0;
+ }
+
+ /* check usp */
+ if (dev_is_pci(port_iter->uport_dev) &&
+ pci_pcie_type(to_pci_dev(port_iter->uport_dev)) ==
+ PCI_EXP_TYPE_UPSTREAM) {
+ if (!cxl_is_bi_capable(to_pci_dev(port_iter->uport_dev),
+ NULL)) {
+ dev_dbg(cxlds->dev,
+ "BI not supported by USP\n");
+ return 0;
+ }
+ if (port_iter->reg_map.component_map.bi_rt.valid &&
+ !port_iter->regs.bi_rt) {
+ dev_dbg(cxlds->dev,
+ "BI RT advertised but unmapped\n");
+ return 0;
+ }
+ }
+
+ dport_iter = port_iter->parent_dport;
+ port_iter = dport_iter->port;
+ }
+
+ rc = cxl_bi_enable_path(cxlds, dport->port, dport);
+ if (rc)
+ return rc;
+
+ return devm_add_action_or_reset(&endpoint->dev, cxl_bi_dealloc,
+ endpoint);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_bi_setup, "CXL");
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 3371b2d9ba9a..5c33fcb5240f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -751,6 +751,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
xa_init(&port->dports);
xa_init(&port->endpoints);
xa_init(&port->regions);
+ mutex_init(&port->bi_lock);
port->component_reg_phys = CXL_RESOURCE_NONE;
device_initialize(dev);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 33fadf4dda52..f22478c340bd 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -75,6 +75,7 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
#define CXL_HDM_DECODER0_CTRL_COMMIT_ERROR BIT(11)
#define CXL_HDM_DECODER0_CTRL_HOSTONLY BIT(12)
+#define CXL_HDM_DECODER0_CTRL_BI BIT(13)
#define CXL_HDM_DECODER0_TL_LOW(i) (0x20 * (i) + 0x24)
#define CXL_HDM_DECODER0_TL_HIGH(i) (0x20 * (i) + 0x28)
/* Target list capacity, i.e. max interleave ways for a non-endpoint decoder */
@@ -185,6 +186,31 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
#define CXL_HEADERLOG_TRACE_SIZE SZ_512
#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32))
+/* CXL 4.0 8.2.4.26 CXL BI Route Table Capability Structure */
+#define CXL_BI_RT_CAPS_OFFSET 0x0
+#define CXL_BI_RT_CAPS_EXPLICIT_COMMIT_REQ BIT(0)
+#define CXL_BI_RT_CTRL_OFFSET 0x4
+#define CXL_BI_RT_CTRL_BI_COMMIT BIT(0)
+#define CXL_BI_RT_STATUS_OFFSET 0x8
+#define CXL_BI_RT_STATUS_BI_COMMITTED BIT(0)
+#define CXL_BI_RT_STATUS_BI_ERR_NOT_COMMITTED BIT(1)
+#define CXL_BI_RT_STATUS_BI_COMMIT_TM_SCALE GENMASK(11, 8)
+#define CXL_BI_RT_STATUS_BI_COMMIT_TM_BASE GENMASK(15, 12)
+
+/* CXL 4.0 8.2.4.27 CXL BI Decoder Capability Structure */
+#define CXL_BI_DECODER_CAPS_OFFSET 0x0
+#define CXL_BI_DECODER_CAPS_HDMD_CAP BIT(0)
+#define CXL_BI_DECODER_CAPS_EXPLICIT_COMMIT_REQ BIT(1)
+#define CXL_BI_DECODER_CTRL_OFFSET 0x4
+#define CXL_BI_DECODER_CTRL_BI_FW BIT(0)
+#define CXL_BI_DECODER_CTRL_BI_ENABLE BIT(1)
+#define CXL_BI_DECODER_CTRL_BI_COMMIT BIT(2)
+#define CXL_BI_DECODER_STATUS_OFFSET 0x8
+#define CXL_BI_DECODER_STATUS_BI_COMMITTED BIT(0)
+#define CXL_BI_DECODER_STATUS_BI_ERR_NOT_COMMITTED BIT(1)
+#define CXL_BI_DECODER_STATUS_BI_COMMIT_TM_SCALE GENMASK(11, 8)
+#define CXL_BI_DECODER_STATUS_BI_COMMIT_TM_BASE GENMASK(15, 12)
+
/* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
#define CXLDEV_CAP_ARRAY_OFFSET 0x0
#define CXLDEV_CAP_ARRAY_CAP_ID 0
@@ -584,6 +610,7 @@ struct cxl_dax_region {
* @decoder_ida: allocator for decoder ids
* @reg_map: component and ras register mapping parameters
* @regs: mapped component registers
+ * @bi_lock: serializes BI Decoder/RT state of this port's dports
* @nr_dports: number of entries in @dports
* @hdm_end: track last allocated HDM decoder instance for allocation ordering
* @commit_end: cursor to track highest committed decoder for commit ordering
@@ -606,6 +633,7 @@ struct cxl_port {
struct ida decoder_ida;
struct cxl_register_map reg_map;
struct cxl_component_regs regs;
+ struct mutex bi_lock; /* dport BI state shared below this port */
int nr_dports;
int hdm_end;
int commit_end;
@@ -669,6 +697,8 @@ struct cxl_rcrb_info {
* @coord: access coordinates (bandwidth and latency performance attributes)
* @link_latency: calculated PCIe downstream latency
* @gpf_dvsec: Cached GPF port DVSEC
+ * @nr_bi: number of BI-enabled endpoints below this dport
+ * @bi_held: found up on first use, or kept up for a device below: never cleared
*/
struct cxl_dport {
struct device *dport_dev;
@@ -681,6 +711,8 @@ struct cxl_dport {
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
long link_latency;
int gpf_dvsec;
+ int nr_bi;
+ bool bi_held;
};
/**
@@ -953,6 +985,8 @@ void cxl_coordinates_combine(struct access_coordinate *out,
struct access_coordinate *c2);
bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
+int cxl_bi_setup(struct cxl_port *endpoint);
+void cxl_bi_reset_detected(struct cxl_port *endpoint);
struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port,
struct device *dport_dev);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 3ed7c3e70042..0fa97350fc88 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1117,8 +1117,12 @@ static void cxl_reset_done(struct pci_dev *pdev)
if (!cxlmd->dev.driver)
return;
- if (cxlmd->endpoint &&
- cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) {
+ if (!cxlmd->endpoint)
+ return;
+
+ cxl_bi_reset_detected(cxlmd->endpoint);
+
+ if (cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) {
device_for_each_child(&cxlmd->endpoint->dev, NULL,
cxl_endpoint_decoder_clear_reset_flags);
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 1df75fdefbfa..37a373eeb283 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -195,6 +195,7 @@ struct cxl_dpa_partition {
* @regs: Parsed register blocks
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
+ * @bi: device is BI (Back-Invalidate) enabled
* @media_ready: Indicate whether the device media is usable
* @dpa_res: Overall DPA resource tree for the device
* @part: DPA partition array
@@ -214,6 +215,7 @@ struct cxl_dev_state {
struct cxl_device_regs regs;
int cxl_dvsec;
bool rcd;
+ bool bi;
bool media_ready;
struct resource dpa_res;
struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX];
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 03/10] cxl/hdm: Add BI coherency support for endpoint decoders
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 01/10] cxl: Add BI register probing and port initialization Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 02/10] cxl/pci: Add BI topology enable/disable Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 04/10] cxl: Add HDM-DB region creation Davidlohr Bueso
` (6 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Cache the HDM decoder's Supported Coherency Models on struct cxl_hdm.
A later patch has region attach consult it to verify the HDM supports
the region's coherency type.
For uncommitted endpoint decoders, init_hdm_decoder() defaults
target_type from supported_coherency: Type 3 devices default to
HDM-DB when the HDM is device-coherent-only, HDM-H otherwise.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/hdm.c | 27 ++++++++++++++++-----------
drivers/cxl/cxl.h | 7 ++++++-
drivers/cxl/cxlmem.h | 2 ++
4 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 72be96df57f5..041c86c43d19 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -218,6 +218,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info);
bool cxl_hdm_bi_committed(struct cxl_port *endpoint);
int cxl_port_get_possible_dports(struct cxl_port *port);
+enum cxl_decoder_type cxled_default_type(struct cxl_endpoint_decoder *cxled);
#ifdef CONFIG_CXL_FEATURES
struct cxl_feat_entry *
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index f8fe1b12de3d..0fe86a3e187c 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -90,6 +90,8 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
cxlhdm->iw_cap_mask |= BIT(3) | BIT(6) | BIT(12);
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_16_WAY, hdm_cap))
cxlhdm->iw_cap_mask |= BIT(16);
+ cxlhdm->supported_coherency =
+ FIELD_GET(CXL_HDM_DECODER_SUPPORTED_COHERENCY_MASK, hdm_cap);
}
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
@@ -1054,6 +1056,19 @@ static int cxl_setup_hdm_decoder_from_dvsec(
return 0;
}
+enum cxl_decoder_type cxled_default_type(struct cxl_endpoint_decoder *cxled)
+{
+ struct cxl_dev_state *cxlds = cxled_to_memdev(cxled)->cxlds;
+ struct cxl_port *port = cxled_to_port(cxled);
+ struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
+
+ if (cxlds->type == CXL_DEVTYPE_CLASSMEM &&
+ cxlhdm->supported_coherency != CXL_HDM_DECODER_COHERENCY_DEV)
+ return CXL_DECODER_HOSTONLYMEM;
+
+ return CXL_DECODER_DEVMEM;
+}
+
static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
void __iomem *hdm, int which,
u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
@@ -1117,17 +1132,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
port->commit_end = cxld->id;
} else {
if (cxled) {
- struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct cxl_dev_state *cxlds = cxlmd->cxlds;
-
- /*
- * Default by devtype until a device arrives that needs
- * more precision.
- */
- if (cxlds->type == CXL_DEVTYPE_CLASSMEM)
- cxld->target_type = CXL_DECODER_HOSTONLYMEM;
- else
- cxld->target_type = CXL_DECODER_DEVMEM;
+ cxld->target_type = cxled_default_type(cxled);
} else {
/* To be overridden by region type at commit time */
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index f22478c340bd..176c359bd68c 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -53,7 +53,7 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_BI_RT_CAPABILITY_LENGTH 0xC
#define CXL_BI_DECODER_CAPABILITY_LENGTH 0xC
-/* HDM decoders CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure */
+/* HDM decoders CXL 4.0 8.2.4.20 CXL HDM Decoder Capability Structure */
#define CXL_HDM_DECODER_CAP_OFFSET 0x0
#define CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
@@ -61,6 +61,11 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
#define CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY BIT(11)
#define CXL_HDM_DECODER_INTERLEAVE_16_WAY BIT(12)
+#define CXL_HDM_DECODER_SUPPORTED_COHERENCY_MASK GENMASK(22, 21)
+#define CXL_HDM_DECODER_COHERENCY_UNKNOWN 0x0
+#define CXL_HDM_DECODER_COHERENCY_DEV 0x1
+#define CXL_HDM_DECODER_COHERENCY_HOST 0x2
+#define CXL_HDM_DECODER_COHERENCY_BOTH 0x3
#define CXL_HDM_DECODER_CTRL_OFFSET 0x4
#define CXL_HDM_DECODER_ENABLE BIT(1)
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 35268d1c387c..348e63aef804 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -933,6 +933,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
* @target_count: for switch decoders, max downstream port targets
* @interleave_mask: interleave granularity capability, see check_interleave_cap()
* @iw_cap_mask: bitmask of supported interleave ways, see check_interleave_cap()
+ * @supported_coherency: HDM Decoder Capability supported coherency models
* @port: mapped cxl_port, see devm_cxl_setup_hdm()
*/
struct cxl_hdm {
@@ -941,6 +942,7 @@ struct cxl_hdm {
unsigned int target_count;
unsigned int interleave_mask;
unsigned long iw_cap_mask;
+ unsigned int supported_coherency;
struct cxl_port *port;
};
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 04/10] cxl: Add HDM-DB region creation
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (2 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 03/10] cxl/hdm: Add BI coherency support for endpoint decoders Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 22:24 ` sashiko-bot
2026-11-01 21:51 ` [PATCH v10 05/10] cxl/hdm: Rename decoder coherency flags Davidlohr Bueso
` (5 subsequent siblings)
9 siblings, 1 reply; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
A region created through sysfs inherits its coherency from the chosen
root decoder - HDM-DB if the root has CXL_DECODER_F_BI, otherwise
HDM-H. An auto-assembled region takes its type from the committed
decoder it is built from instead.
cxl_acpi_cfmws_verify() rejects a Window that declares no coherency
model at all (neither Device Coherent nor Host-only Coherent), one that
sets BI together with Host-only Coherent, which the CFMWS definition
calls undefined behavior, and one that sets BI without Device Coherent,
since HDM-DB is defined only as bit[0] and bit[5] together. A BI Window
therefore always exposes device-coherent memory and nothing else.
Introduce the following read-only sysfs ABI.
- decoderX.Y/cap_back_invalidate (root) reports the CFMWS BI
restriction.
- decoderX.Y/back_invalidate (endpoint) reads '1' when configured
for HDM-DB.
cxl_region_attach() rejects endpoints whose device or HDM cannot
serve the region's type; target_type is inherited from cxlr->type
in cxl_rr_assign_decoder(), restored to the endpoint default on
detach unless the decoder is still committed, and to whatever it was
on a failed attach - a refusal may come before any inheritance, for a
decoder another region owns or one firmware committed, and must not
relabel it.
An HDM that reports Unknown coherency support is not rejected.
Supported Coherency Models is how a device declares whether Target
Range Type is writable - Host-only+Device Coherent means RW, a single
model means the bit may be hardwired to it, Unknown declares neither
- so refusing Unknown would also exclude devices that support both
models without saying so.
A Type 3 decoder defaults to host-only, unless its HDM supports only
device-coherent, and inherits device-coherent when it joins an HDM-DB
region, which requires cxlds->bi. A Type 2 decoder defaults to
device-coherent and an HDM-H region refuses it outright, since the
spec reserves HDM-H for Type 3 devices. An HDM-D region is only
assembled from a committed decoder, taking that decoder's type, so no
inheritance is involved.
The HDM Decoder Control BI bit is set at commit time for a
device-coherent decoder in a region under a BI root, endpoint and
switch decoders alike. Whether the device has BI enabled (cxlds->bi)
is checked when an endpoint attaches and again for every target
before a commit programs anything, since a reset in between
invalidates it; the refusal is the same in both places.
A committed decoder cannot inherit a region's type, so its range type
is checked instead: against the window before the region is built,
as the manual path already does through can_create_ram(), and
against the region at attach, where upstream's type mismatch refusal
stays for committed decoders. Committed means CXL_DECODER_F_ENABLE,
not an auto-assembly state: a decoder left committed by a region that
never assembled, or by a locked one, is as committed as one in an
assembled region.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
Documentation/ABI/testing/sysfs-bus-cxl | 22 ++++
drivers/cxl/acpi.c | 20 ++++
drivers/cxl/core/hdm.c | 24 ++++
drivers/cxl/core/port.c | 39 ++++++-
drivers/cxl/core/region.c | 144 +++++++++++++++++++++---
drivers/cxl/cxl.h | 7 ++
include/cxl/cxl.h | 3 +-
7 files changed, 238 insertions(+), 21 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
index 7352dbd70bc7..d5b775bcb9bd 100644
--- a/Documentation/ABI/testing/sysfs-bus-cxl
+++ b/Documentation/ABI/testing/sysfs-bus-cxl
@@ -432,6 +432,28 @@ Description:
current cached value.
+What: /sys/bus/cxl/devices/decoderX.Y/cap_back_invalidate
+Date: October, 2026
+KernelVersion: v7.5
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (RO) When a CXL decoder is of devtype "cxl_decoder_root", '1'
+ indicates the fixed memory window carries the CFMWS
+ Back-Invalidate restriction, so HDM-DB memory may be mapped
+ behind it.
+
+
+What: /sys/bus/cxl/devices/decoderX.Y/back_invalidate
+Date: October, 2026
+KernelVersion: v7.5
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (RO) Shows '1' if this endpoint decoder is currently configured
+ for HDM-DB (device-managed coherency with back-invalidate).
+ The HDM-DB state is inherited from the region the decoder is
+ attached to, which is in turn set from the chosen root
+ decoder's CFMWS BI restriction (see cap_back_invalidate).
+
What: /sys/bus/cxl/devices/decoderX.Y/delete_region
Date: May, 2022
KernelVersion: v6.0
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index b7ce6b59c680..cc7c99d3066d 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -152,6 +152,8 @@ static unsigned long cfmws_to_decoder_flags(int restrictions)
flags |= CXL_DECODER_F_PMEM;
if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
flags |= CXL_DECODER_F_LOCK;
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)
+ flags |= CXL_DECODER_F_BI;
return flags;
}
@@ -198,6 +200,24 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
cfmws->header.length, expected_len);
+ if ((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM) &&
+ (cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)) {
+ dev_err(dev, "CFMWS cannot have both HDM-H and HDM-DB\n");
+ return -EINVAL;
+ }
+
+ if (!(cfmws->restrictions & (ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM))) {
+ dev_err(dev, "CFMWS has no coherency model\n");
+ return -EINVAL;
+ }
+
+ if ((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI) &&
+ !(cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_DEVMEM)) {
+ dev_err(dev, "CFMWS BI requires device-coherent\n");
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0fe86a3e187c..c272fc6faf37 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -790,9 +790,26 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
{
+ bool bi = cxld->target_type == CXL_DECODER_DEVMEM &&
+ cxld->region && cxl_root_decoder_is_bi(cxld->region->cxlrd);
+
u32p_replace_bits(ctrl,
!!(cxld->target_type == CXL_DECODER_HOSTONLYMEM),
CXL_HDM_DECODER0_CTRL_HOSTONLY);
+ u32p_replace_bits(ctrl, bi, CXL_HDM_DECODER0_CTRL_BI);
+
+ /*
+ * Interleave Set Position: the endpoint's position in the
+ * interleave set, RWL for a BI-capable device and reserved
+ * otherwise (CXL 4.0 Table 8-123).
+ */
+ if (bi && is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ u32p_replace_bits(ctrl, cxled->pos,
+ CXL_HDM_DECODER0_CTRL_ISP_MASK);
+ }
}
static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
@@ -1056,6 +1073,13 @@ static int cxl_setup_hdm_decoder_from_dvsec(
return 0;
}
+/*
+ * HDMs that advertise support for both coherency modes
+ * (CXL_HDM_DECODER_COHERENCY_BOTH) default to host-only; the region
+ * attach path switches target_type to device-coherent if the region's
+ * root decoder has the CFMWS BI bit set. Only HDMs that strictly
+ * support device-coherent mode default to HDM-DB.
+ */
enum cxl_decoder_type cxled_default_type(struct cxl_endpoint_decoder *cxled)
{
struct cxl_dev_state *cxlds = cxled_to_memdev(cxled)->cxlds;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 5c33fcb5240f..9d321f90c361 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -132,6 +132,7 @@ CXL_DECODER_FLAG_ATTR(cap_ram, CXL_DECODER_F_RAM);
CXL_DECODER_FLAG_ATTR(cap_type2, CXL_DECODER_F_TYPE2);
CXL_DECODER_FLAG_ATTR(cap_type3, CXL_DECODER_F_TYPE3);
CXL_DECODER_FLAG_ATTR(locked, CXL_DECODER_F_LOCK);
+CXL_DECODER_FLAG_ATTR(cap_back_invalidate, CXL_DECODER_F_BI);
static ssize_t target_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -243,6 +244,26 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(mode);
+static ssize_t back_invalidate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
+ struct cxl_dev_state *cxlds = cxled_to_memdev(cxled)->cxlds;
+ struct cxl_region *cxlr;
+
+ guard(rwsem_read)(&cxl_rwsem.region);
+ /*
+ * An endpoint decoder is HDM-DB when the device has BI enabled
+ * (cxlds->bi) and it is attached to a device-coherent (DEVMEM)
+ * region whose root decoder advertises the CFMWS BI restriction.
+ */
+ cxlr = cxled->cxld.region;
+ return sysfs_emit(buf, "%d\n", cxlds->bi && cxlr &&
+ cxled->cxld.target_type == CXL_DECODER_DEVMEM &&
+ cxl_root_decoder_is_bi(cxlr->cxlrd));
+}
+static DEVICE_ATTR_RO(back_invalidate);
+
static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -339,6 +360,7 @@ static struct attribute *cxl_decoder_root_attrs[] = {
&dev_attr_cap_ram.attr,
&dev_attr_cap_type2.attr,
&dev_attr_cap_type3.attr,
+ &dev_attr_cap_back_invalidate.attr,
&dev_attr_target_list.attr,
&dev_attr_qos_class.attr,
SET_CXL_REGION_ATTR(create_pmem_region)
@@ -349,16 +371,24 @@ static struct attribute *cxl_decoder_root_attrs[] = {
static bool can_create_pmem(struct cxl_root_decoder *cxlrd)
{
- unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM;
+ unsigned long flags = cxlrd->cxlsd.cxld.flags;
+ unsigned long hdm_h, hdm_db;
- return (cxlrd->cxlsd.cxld.flags & flags) == flags;
+ hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM;
+ hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_PMEM;
+
+ return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db;
}
static bool can_create_ram(struct cxl_root_decoder *cxlrd)
{
- unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM;
+ unsigned long flags = cxlrd->cxlsd.cxld.flags;
+ unsigned long hdm_h, hdm_db;
+
+ hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM;
+ hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_RAM;
- return (cxlrd->cxlsd.cxld.flags & flags) == flags;
+ return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db;
}
static umode_t cxl_root_decoder_visible(struct kobject *kobj, struct attribute *a, int n)
@@ -412,6 +442,7 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
static struct attribute *cxl_decoder_endpoint_attrs[] = {
&dev_attr_target_type.attr,
&dev_attr_mode.attr,
+ &dev_attr_back_invalidate.attr,
&dev_attr_dpa_size.attr,
&dev_attr_dpa_resource.attr,
SET_CXL_REGION_ATTR(region)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index a048bd5c06cb..28c61feef06d 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -313,8 +313,25 @@ static int commit_decoder(struct cxl_decoder *cxld)
static int cxl_region_decode_commit(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
+ bool hdm_db;
int i, rc = 0;
+ hdm_db = cxlr->type == CXL_DECODER_DEVMEM &&
+ cxl_root_decoder_is_bi(cxlr->cxlrd);
+
+ /* a reset since attach may have invalidated cxlds->bi */
+ for (i = 0; i < p->nr_targets; i++) {
+ struct cxl_endpoint_decoder *cxled = p->targets[i];
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+
+ if (hdm_db && !cxlmd->cxlds->bi) {
+ dev_err(&cxlr->dev, "%s:%s BI not enabled on device\n",
+ dev_name(&cxlmd->dev),
+ dev_name(&cxled->cxld.dev));
+ return -ENXIO;
+ }
+ }
+
for (i = 0; i < p->nr_targets; i++) {
struct cxl_endpoint_decoder *cxled = p->targets[i];
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
@@ -346,6 +363,21 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
cxled->cxld.reset(&cxled->cxld);
goto err;
}
+
+ /*
+ * A reset after the check above wipes BI Enable, but
+ * cxlds->bi is only refreshed from .reset_done, which may
+ * not have run. Ask the hardware.
+ */
+ if (hdm_db && !cxl_bi_decoder_enabled(cxled_to_port(cxled))) {
+ dev_err(&cxlr->dev,
+ "%s:%s BI disabled by reset during commit\n",
+ dev_name(&cxlmd->dev),
+ dev_name(&cxled->cxld.dev));
+ rc = -ENXIO;
+ i++; /* this target committed, so undo it too */
+ goto err;
+ }
}
return 0;
@@ -1132,16 +1164,11 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr,
}
/*
- * Endpoints should already match the region type, but backstop that
- * assumption with an assertion. Switch-decoders change mapping-type
- * based on what is mapped when they are assigned to a region.
+ * Endpoint decoders inherit their type from cxlr->type; broken
+ * pairings were already rejected by the coherency checks in
+ * cxl_region_attach(). Switch-decoders change mapping-type based
+ * on what is mapped when they are assigned to a region.
*/
- dev_WARN_ONCE(&cxlr->dev,
- port == cxled_to_port(cxled) &&
- cxld->target_type != cxlr->type,
- "%s:%s mismatch decoder type %d -> %d\n",
- dev_name(&cxled_to_memdev(cxled)->dev),
- dev_name(&cxld->dev), cxld->target_type, cxlr->type);
cxld->target_type = cxlr->type;
cxl_rr->decoder = cxld;
return 0;
@@ -1805,6 +1832,7 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd;
+ enum cxl_decoder_type type = cxled->cxld.target_type;
struct cxl_decoder *cxld = &cxlsd->cxld;
int iw = cxld->interleave_ways;
struct cxl_port *iter;
@@ -1830,6 +1858,8 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
for (iter = cxled_to_port(cxled); !is_cxl_root(iter);
iter = to_cxl_port(iter->dev.parent))
cxl_port_detach_region(iter, cxlr, cxled);
+ /* undo cxl_rr_assign_decoder() type inheritance */
+ cxled->cxld.target_type = type;
return rc;
}
@@ -2049,6 +2079,61 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr)
return rc;
}
+/*
+ * Verify the device and HDM are capable of the region's flavor. The
+ * endpoint decoder's target_type is then inherited from cxlr->type
+ * later in cxl_rr_assign_decoder().
+ */
+static int cxl_region_check_coherency(struct cxl_region *cxlr,
+ struct cxl_endpoint_decoder *cxled)
+{
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ bool root_bi = cxl_root_decoder_is_bi(cxlr->cxlrd);
+ struct cxl_hdm *cxlhdm;
+
+ if (cxlr->type == CXL_DECODER_DEVMEM && root_bi && !cxlds->bi) {
+ dev_dbg(&cxlr->dev, "%s:%s BI not enabled on device\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
+ return -ENXIO;
+ }
+
+ if (cxled->state != CXL_DECODER_STATE_AUTO &&
+ cxlr->type == CXL_DECODER_HOSTONLYMEM &&
+ cxlds->type == CXL_DEVTYPE_DEVMEM) {
+ dev_dbg(&cxlr->dev, "%s:%s HDM-H requires a Type 3 device\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
+ return -ENXIO;
+ }
+
+ cxlhdm = dev_get_drvdata(&cxled_to_port(cxled)->dev);
+ if (!cxlhdm)
+ return -ENXIO;
+ if (cxlr->type == CXL_DECODER_HOSTONLYMEM &&
+ cxlhdm->supported_coherency == CXL_HDM_DECODER_COHERENCY_DEV) {
+ dev_dbg(&cxlr->dev, "%s:%s HDM is device-coherent only\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
+ return -ENXIO;
+ }
+ if (cxlr->type == CXL_DECODER_DEVMEM &&
+ cxlhdm->supported_coherency == CXL_HDM_DECODER_COHERENCY_HOST) {
+ dev_dbg(&cxlr->dev, "%s:%s HDM is host-only coherent\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
+ return -ENXIO;
+ }
+
+ /* a committed decoder cannot inherit the region's type */
+ if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) &&
+ cxled->cxld.target_type != cxlr->type) {
+ dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ cxled->cxld.target_type, cxlr->type);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
static int cxl_region_attach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos)
{
@@ -2107,12 +2192,9 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return -ENXIO;
}
- if (cxled->cxld.target_type != cxlr->type) {
- dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n",
- dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
- cxled->cxld.target_type, cxlr->type);
- return -ENXIO;
- }
+ rc = cxl_region_check_coherency(cxlr, cxled);
+ if (rc)
+ return rc;
if (cxled_empty(cxled)) {
dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
@@ -2327,6 +2409,12 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
.start = 0,
.end = -1,
};
+ /*
+ * Undo cxl_rr_assign_decoder() type inheritance, unless the
+ * decoder is still committed and its type is what it decodes.
+ */
+ if (!(cxled->cxld.flags & CXL_DECODER_F_ENABLE))
+ cxled->cxld.target_type = cxled_default_type(cxled);
get_device(&cxlr->dev);
return cxlr;
@@ -2835,6 +2923,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
size_t len, enum cxl_partition_mode mode)
{
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
+ enum cxl_decoder_type target_type;
struct cxl_region *cxlr;
int rc, id;
@@ -2846,7 +2935,15 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock)))
return rc;
- cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM);
+ /*
+ * The CFMWS dictates endpoint coherency: a BI-restricted Window
+ * produces an HDM-DB region; otherwise HDM-H. HDM-D is not an
+ * option here, type2 regions are created by their driver.
+ */
+ target_type = cxl_root_decoder_is_bi(cxlrd) ?
+ CXL_DECODER_DEVMEM : CXL_DECODER_HOSTONLYMEM;
+
+ cxlr = __create_region(cxlrd, mode, id, target_type);
if (IS_ERR(cxlr))
return PTR_ERR(cxlr);
@@ -3794,10 +3891,25 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
struct cxl_dev_state *cxlds = cxlmd->cxlds;
int rc, part = READ_ONCE(cxled->part);
struct cxl_region *cxlr;
+ unsigned long need;
if (part < 0)
return ERR_PTR(-EBUSY);
+ /*
+ * A committed decoder defines the region built from it, so no
+ * attach check can find its range type wrong. Only the window's
+ * restrictions can.
+ */
+ need = cxled->cxld.target_type == CXL_DECODER_DEVMEM ?
+ CXL_DECODER_F_TYPE2 : CXL_DECODER_F_TYPE3;
+ if (!(cxlrd->cxlsd.cxld.flags & need)) {
+ dev_dbg(&cxlrd->cxlsd.cxld.dev,
+ "%s:%s range type not permitted by the window\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
+ return ERR_PTR(-ENXIO);
+ }
+
do {
cxlr = __create_region(cxlrd, cxlds->part[part].mode,
atomic_read(&cxlrd->region_id),
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 176c359bd68c..88c38db26c51 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -81,6 +81,7 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_HDM_DECODER0_CTRL_COMMIT_ERROR BIT(11)
#define CXL_HDM_DECODER0_CTRL_HOSTONLY BIT(12)
#define CXL_HDM_DECODER0_CTRL_BI BIT(13)
+#define CXL_HDM_DECODER0_CTRL_ISP_MASK GENMASK(27, 24)
#define CXL_HDM_DECODER0_TL_LOW(i) (0x20 * (i) + 0x24)
#define CXL_HDM_DECODER0_TL_HIGH(i) (0x20 * (i) + 0x28)
/* Target list capacity, i.e. max interleave ways for a non-endpoint decoder */
@@ -309,6 +310,7 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
#define CXL_DECODER_F_LOCK BIT(4)
#define CXL_DECODER_F_ENABLE BIT(5)
#define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6)
+#define CXL_DECODER_F_BI BIT(7)
#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK)
enum cxl_decoder_type {
@@ -864,6 +866,11 @@ static inline int cxl_root_decoder_autoremove(struct device *host,
{
return cxl_decoder_autoremove(host, &cxlrd->cxlsd.cxld);
}
+
+static inline bool cxl_root_decoder_is_bi(struct cxl_root_decoder *cxlrd)
+{
+ return cxlrd->cxlsd.cxld.flags & CXL_DECODER_F_BI;
+}
int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint);
/**
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 37a373eeb283..c23f7d3a0fdc 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -16,7 +16,8 @@
* mailbox, or other memory-device-standard manageability
* flows.
* @CXL_DEVTYPE_CLASSMEM: Common class definition of a CXL Type-3 device with
- * HDM-H and class-mandatory memory device registers
+ * HDM-H or HDM-DB, and class-mandatory memory device
+ * registers
*/
enum cxl_devtype {
CXL_DEVTYPE_DEVMEM,
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 05/10] cxl/hdm: Rename decoder coherency flags
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (3 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 04/10] cxl: Add HDM-DB region creation Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 06/10] cxl/region: Log the coherency model at region creation Davidlohr Bueso
` (4 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Align with the ACPI CXL Window restriction naming and convert
CXL_DECODER_F_TYPE2/F_TYPE3 to F_DEVMEM/F_HOSTONLY. Type2 and
Type3 coherency models were named prior to Back-Invalidate.
Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/acpi.c | 4 ++--
drivers/cxl/core/port.c | 12 ++++++------
drivers/cxl/core/region.c | 2 +-
drivers/cxl/cxl.h | 4 ++--
4 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index cc7c99d3066d..f2700d327c20 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -143,9 +143,9 @@ static unsigned long cfmws_to_decoder_flags(int restrictions)
unsigned long flags = CXL_DECODER_F_ENABLE;
if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_DEVMEM)
- flags |= CXL_DECODER_F_TYPE2;
+ flags |= CXL_DECODER_F_DEVMEM;
if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM)
- flags |= CXL_DECODER_F_TYPE3;
+ flags |= CXL_DECODER_F_HOSTONLY;
if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
flags |= CXL_DECODER_F_RAM;
if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 9d321f90c361..218c507891a2 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -129,8 +129,8 @@ static DEVICE_ATTR_RO(name)
CXL_DECODER_FLAG_ATTR(cap_pmem, CXL_DECODER_F_PMEM);
CXL_DECODER_FLAG_ATTR(cap_ram, CXL_DECODER_F_RAM);
-CXL_DECODER_FLAG_ATTR(cap_type2, CXL_DECODER_F_TYPE2);
-CXL_DECODER_FLAG_ATTR(cap_type3, CXL_DECODER_F_TYPE3);
+CXL_DECODER_FLAG_ATTR(cap_type2, CXL_DECODER_F_DEVMEM);
+CXL_DECODER_FLAG_ATTR(cap_type3, CXL_DECODER_F_HOSTONLY);
CXL_DECODER_FLAG_ATTR(locked, CXL_DECODER_F_LOCK);
CXL_DECODER_FLAG_ATTR(cap_back_invalidate, CXL_DECODER_F_BI);
@@ -374,8 +374,8 @@ static bool can_create_pmem(struct cxl_root_decoder *cxlrd)
unsigned long flags = cxlrd->cxlsd.cxld.flags;
unsigned long hdm_h, hdm_db;
- hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM;
- hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_PMEM;
+ hdm_h = CXL_DECODER_F_HOSTONLY | CXL_DECODER_F_PMEM;
+ hdm_db = CXL_DECODER_F_DEVMEM | CXL_DECODER_F_BI | CXL_DECODER_F_PMEM;
return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db;
}
@@ -385,8 +385,8 @@ static bool can_create_ram(struct cxl_root_decoder *cxlrd)
unsigned long flags = cxlrd->cxlsd.cxld.flags;
unsigned long hdm_h, hdm_db;
- hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM;
- hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_RAM;
+ hdm_h = CXL_DECODER_F_HOSTONLY | CXL_DECODER_F_RAM;
+ hdm_db = CXL_DECODER_F_DEVMEM | CXL_DECODER_F_BI | CXL_DECODER_F_RAM;
return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db;
}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 28c61feef06d..d369c98824ac 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3902,7 +3902,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
* restrictions can.
*/
need = cxled->cxld.target_type == CXL_DECODER_DEVMEM ?
- CXL_DECODER_F_TYPE2 : CXL_DECODER_F_TYPE3;
+ CXL_DECODER_F_DEVMEM : CXL_DECODER_F_HOSTONLY;
if (!(cxlrd->cxlsd.cxld.flags & need)) {
dev_dbg(&cxlrd->cxlsd.cxld.dev,
"%s:%s range type not permitted by the window\n",
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 88c38db26c51..0397f6b3dbaa 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -305,8 +305,8 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
*/
#define CXL_DECODER_F_RAM BIT(0)
#define CXL_DECODER_F_PMEM BIT(1)
-#define CXL_DECODER_F_TYPE2 BIT(2)
-#define CXL_DECODER_F_TYPE3 BIT(3)
+#define CXL_DECODER_F_DEVMEM BIT(2)
+#define CXL_DECODER_F_HOSTONLY BIT(3)
#define CXL_DECODER_F_LOCK BIT(4)
#define CXL_DECODER_F_ENABLE BIT(5)
#define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6)
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 06/10] cxl/region: Log the coherency model at region creation
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (4 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 05/10] cxl/hdm: Rename decoder coherency flags Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 07/10] cxl/pci: Split BI capability probe from setup Davidlohr Bueso
` (3 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Region assembly emits plenty of debug information - resources,
interleave geometry, target placement - but not the coherency model
the region operates in.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/region.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index d369c98824ac..06a0e9bb892f 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1863,6 +1863,13 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
return rc;
}
+static const char *cxl_coherency_name(enum cxl_decoder_type type, bool bi)
+{
+ if (type == CXL_DECODER_HOSTONLYMEM)
+ return "HDM-H";
+ return bi ? "HDM-DB" : "HDM-D";
+}
+
static int cxl_region_attach_auto(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos)
{
@@ -2864,8 +2871,10 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
return ERR_PTR(rc);
}
- dev_dbg(port->uport_dev, "%s: created %s\n",
- dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
+ dev_dbg(port->uport_dev, "%s: created %s %s\n",
+ dev_name(&cxlrd->cxlsd.cxld.dev),
+ cxl_coherency_name(cxlr->type, cxl_root_decoder_is_bi(cxlrd)),
+ dev_name(dev));
return cxlr;
err:
put_device(dev);
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 07/10] cxl/pci: Split BI capability probe from setup
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (5 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 06/10] cxl/region: Log the coherency model at region creation Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders Davidlohr Bueso
` (2 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Decouple the topology read-only capability verification phase from
cxl_bi_setup() into cxl_bi_probe_capable(), recording the result in
cxlds->bi_capable.
This allows further dealing with auto-committed BI hdm decoders;
having such knowledge upon decoder enumeration time.
No functional change intended.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/pci.c | 40 +++++++++++++++++++++++++++++-----------
drivers/cxl/cxl.h | 1 +
drivers/cxl/port.c | 1 +
include/cxl/cxl.h | 2 ++
4 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 96eadbf4a0cb..aec1edf648bd 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -1400,34 +1400,38 @@ void cxl_bi_reset_detected(struct cxl_port *endpoint)
}
EXPORT_SYMBOL_NS_GPL(cxl_bi_reset_detected, "CXL");
-int cxl_bi_setup(struct cxl_port *endpoint)
+/*
+ * Verify the device and every component in the path up to the root
+ * are BI capable.
+ */
+void cxl_bi_probe_capable(struct cxl_port *endpoint)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
- struct cxl_dport *dport = endpoint->parent_dport;
struct cxl_dport *dport_iter;
struct cxl_port *port_iter;
- int rc;
+
+ cxlds->bi_capable = false;
if (!dev_is_pci(cxlds->dev))
- return 0;
+ return;
/* BI is VH-only */
if (cxlds->rcd)
- return 0;
+ return;
if (!cxl_is_bi_capable(to_pci_dev(cxlds->dev),
endpoint->regs.bi_decoder))
- return 0;
+ return;
- port_iter = dport->port;
- dport_iter = dport;
+ dport_iter = endpoint->parent_dport;
+ port_iter = dport_iter->port;
while (!is_cxl_root(port_iter)) {
/* check rp, dsp */
if (!cxl_is_bi_capable(to_pci_dev(dport_iter->dport_dev),
dport_iter->regs.bi_decoder)) {
dev_dbg(cxlds->dev, "BI not supported by topology\n");
- return 0;
+ return;
}
/* check usp */
@@ -1438,13 +1442,13 @@ int cxl_bi_setup(struct cxl_port *endpoint)
NULL)) {
dev_dbg(cxlds->dev,
"BI not supported by USP\n");
- return 0;
+ return;
}
if (port_iter->reg_map.component_map.bi_rt.valid &&
!port_iter->regs.bi_rt) {
dev_dbg(cxlds->dev,
"BI RT advertised but unmapped\n");
- return 0;
+ return;
}
}
@@ -1452,6 +1456,20 @@ int cxl_bi_setup(struct cxl_port *endpoint)
port_iter = dport_iter->port;
}
+ cxlds->bi_capable = true;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_bi_probe_capable, "CXL");
+
+int cxl_bi_setup(struct cxl_port *endpoint)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dport *dport = endpoint->parent_dport;
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ int rc;
+
+ if (!cxlds->bi_capable)
+ return 0;
+
rc = cxl_bi_enable_path(cxlds, dport->port, dport);
if (rc)
return rc;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 0397f6b3dbaa..a95045b05601 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -997,6 +997,7 @@ void cxl_coordinates_combine(struct access_coordinate *out,
struct access_coordinate *c2);
bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
+void cxl_bi_probe_capable(struct cxl_port *endpoint);
int cxl_bi_setup(struct cxl_port *endpoint);
void cxl_bi_reset_detected(struct cxl_port *endpoint);
struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port,
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index 1a69be0ec7c1..96fade029911 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -173,6 +173,7 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
cxl_endpoint_parse_cdat(port);
devm_cxl_port_bi_setup(port);
+ cxl_bi_probe_capable(port);
get_device(&cxlmd->dev);
rc = devm_add_action_or_reset(&port->dev, schedule_detach, cxlmd);
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index c23f7d3a0fdc..6fa9d913ccdb 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -197,6 +197,7 @@ struct cxl_dpa_partition {
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
* @bi: device is BI (Back-Invalidate) enabled
+ * @bi_capable: device and topology path are BI capable
* @media_ready: Indicate whether the device media is usable
* @dpa_res: Overall DPA resource tree for the device
* @part: DPA partition array
@@ -217,6 +218,7 @@ struct cxl_dev_state {
int cxl_dvsec;
bool rcd;
bool bi;
+ bool bi_capable;
bool media_ready;
struct resource dpa_res;
struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX];
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (6 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 07/10] cxl/pci: Split BI capability probe from setup Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-02 8:21 ` Li Ming
2026-11-01 21:51 ` [PATCH v10 09/10] cxl/test: Add mock BI topology support Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 10/10] cxl/doc: Update maturity map with BI support Davidlohr Bueso
9 siblings, 1 reply; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
An HDM decoder that firmware committed with the BI bit set was
assembled without the bit ever being looked at. Record it, and
refuse what is broken - BI paired with a host-only target range type,
or for an endpoint decoder BI on a path that cannot route BISnp.
Taking over such a path must not re-program what firmware already
did, and a dport's committed state does not say which device it was
committed for, only that firmware committed for something below it.
Two endpoints sharing a level show why that is not enough to go on.
RP Forward set, but for mem0
|
USP
/ \
DSP0 DSP1
| |
mem0 mem1
BI Enable BI Enable
set clear
Adoption is therefore seeded from the endpoint's own BI Enable, since
only a device firmware itself enabled can have had its BI-ID
accounted for above, and carried up the walk. A level is taken as
found, with no register write and no commit, when nothing this driver
routed sits below it, the value this driver would write is already
there, and both the decoder and the switch's route table are
committed - the table carries a commit of its own that firmware may
not have performed. The first level to fall short ends adoption, and
from it up the driver programs and commits as for any other device.
RP Forward, written but never committed
|
USP0 route table committed by the driver
|
DSP0 Forward, written and committed
| ..... adoption ended here .....
USP1 route table already committed
|
DSP1 Enable, taken as found
|
mem0 BI Enable already set by firmware
An adopted level owes no commit either way. Table 8-152 and Table
8-156 owe one per new BI device enabled below the port, and an
adopted level is not seeing one.
On failure the walk unwinds through the same release as teardown. An
adopted level was found up and is held, so the unwind drops its
reference and leaves its register alone; a level this walk brought up
is cleared. Teardown works the same way: every level this driver
brought up is deallocated, unless a decoder below still decodes with
BI and holds it.
A committed decoder is refused when its coherency model does not fit
where it lands: unlike a decoder this driver programs it cannot
inherit a region's model, so its committed BI bit must match the
window's, checked before the region is built and again at attach,
where a second decoder joining the region arrives.
A committed decoder's BI bit is recorded as CXL_DECODER_F_BI. On a
root decoder the same flag is the window's BI restriction, as F_LOCK
already means a fixed window there and Lock On Commit below.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
drivers/cxl/core/hdm.c | 31 ++++++++++++++++++--
drivers/cxl/core/pci.c | 60 +++++++++++++++++++++++++++++++++++++--
drivers/cxl/core/region.c | 32 +++++++++++++--------
drivers/cxl/cxl.h | 3 +-
4 files changed, 108 insertions(+), 18 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index c272fc6faf37..1136f7d1bd5b 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -907,6 +907,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
int id = cxld->id, rc;
+ u32 ctrl;
if (cxld->flags & CXL_DECODER_F_ENABLE)
return 0;
@@ -950,6 +951,10 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
port->commit_end++;
cxld->flags |= CXL_DECODER_F_ENABLE;
+ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+ if (ctrl & CXL_HDM_DECODER0_CTRL_BI)
+ cxld->flags |= CXL_DECODER_F_BI;
+
return 0;
}
@@ -1020,7 +1025,7 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
- cxld->flags &= ~CXL_DECODER_F_ENABLE;
+ cxld->flags &= ~(CXL_DECODER_F_ENABLE | CXL_DECODER_F_BI);
/* Userspace is now responsible for reconfiguring this decoder */
if (is_endpoint_decoder(&cxld->dev)) {
@@ -1140,11 +1145,32 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
cxld->flags |= CXL_DECODER_F_ENABLE;
if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
cxld->flags |= CXL_DECODER_F_LOCK;
+ if (ctrl & CXL_HDM_DECODER0_CTRL_BI)
+ cxld->flags |= CXL_DECODER_F_BI;
+
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
else
cxld->target_type = CXL_DECODER_DEVMEM;
+ if (FIELD_GET(CXL_HDM_DECODER0_CTRL_BI, ctrl)) {
+ struct cxl_dev_state *cxlds = cxled ?
+ cxled_to_memdev(cxled)->cxlds : NULL;
+
+ if (cxld->target_type == CXL_DECODER_HOSTONLYMEM) {
+ dev_warn(&port->dev,
+ "decoder%d.%d: BI with host-only\n",
+ port->id, cxld->id);
+ return -ENXIO;
+ }
+ if (cxlds && !cxlds->bi_capable) {
+ dev_warn(&port->dev,
+ "decoder%d.%d: path not BI capable\n",
+ port->id, cxld->id);
+ return -ENXIO;
+ }
+ }
+
guard(rwsem_write)(&cxl_rwsem.region);
if (cxld->id != cxl_num_decoders_committed(port)) {
dev_warn(&port->dev,
@@ -1423,7 +1449,8 @@ int devm_cxl_endpoint_decoders_setup(struct cxl_port *port)
* Between the port's HDM state and its decoders: devres,
* unwinding in reverse, brings BI down only after the decoders
* quiesce, while its slow walk still precedes the HDM state
- * free.
+ * free. Must also precede region discovery, where HDM-DB
+ * assembly requires cxlds->bi.
*/
rc = cxl_bi_setup(port);
if (rc)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index aec1edf648bd..11e2a97f6567 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -1109,6 +1109,32 @@ static int __cxl_bi_commit_decoder(struct device *dev, void __iomem *bi)
scale, base);
}
+/* Committed, or no explicit commit required */
+static bool __cxl_bi_committed(void __iomem *caps_reg, void __iomem *status_reg,
+ u32 req_bit, u32 committed_bit)
+{
+ if (!(readl(caps_reg) & req_bit))
+ return true;
+
+ return readl(status_reg) & committed_bit;
+}
+
+static bool cxl_bi_rt_committed(void __iomem *bi)
+{
+ return __cxl_bi_committed(bi + CXL_BI_RT_CAPS_OFFSET,
+ bi + CXL_BI_RT_STATUS_OFFSET,
+ CXL_BI_RT_CAPS_EXPLICIT_COMMIT_REQ,
+ CXL_BI_RT_STATUS_BI_COMMITTED);
+}
+
+static bool cxl_bi_decoder_committed(void __iomem *bi)
+{
+ return __cxl_bi_committed(bi + CXL_BI_DECODER_CAPS_OFFSET,
+ bi + CXL_BI_DECODER_STATUS_OFFSET,
+ CXL_BI_DECODER_CAPS_EXPLICIT_COMMIT_REQ,
+ CXL_BI_DECODER_STATUS_BI_COMMITTED);
+}
+
static int cxl_bi_commit_dport(struct cxl_dport *dport)
{
struct cxl_port *port = dport->port;
@@ -1136,7 +1162,8 @@ static int cxl_bi_commit_dport(struct cxl_dport *dport)
* @direct says the device is connected directly to this dport, which
* takes BI Enable; every dport above it takes BI Forward.
*/
-static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
+static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct,
+ bool *adopt)
{
struct cxl_port *port = dport->port;
u32 ctrl, value, set, clr;
@@ -1168,6 +1195,20 @@ static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
CXL_BI_DECODER_CTRL_BI_ENABLE;
value = (ctrl | set) & ~clr;
+
+ /*
+ * Adopt this level as firmware left it: nothing below it that
+ * firmware did not account for, nothing this driver has routed
+ * through it, the value this walk would write already in place,
+ * and the decoder (and route table) committed.
+ */
+ if (*adopt && !dport->nr_bi && value == ctrl &&
+ cxl_bi_decoder_committed(bi) &&
+ (!port->regs.bi_rt || cxl_bi_rt_committed(port->regs.bi_rt)))
+ goto done;
+ /* firmware did not bring this level up, so nothing above may */
+ *adopt = false;
+
if (value != ctrl)
writel(value, bi + CXL_BI_DECODER_CTRL_OFFSET);
@@ -1181,6 +1222,7 @@ static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
}
return rc;
}
+done:
dport->nr_bi++;
return 0;
@@ -1277,7 +1319,9 @@ static int cxl_bi_ctrl_endpoint_disable(struct cxl_dev_state *cxlds)
/*
* Release the device's BI, then each dport up the path short of @stop,
* following the deallocate sequence in the implementation notes of
- * CXL 4.0 9.14.2. Setup's unwind and teardown alike.
+ * CXL 4.0 9.14.2. Setup's unwind and teardown alike: a level the
+ * walk adopted was found up and is held, so the unwind leaves its
+ * register as it found it.
*
* A decoder that still decodes with its BI bit set needs BI Enable,
* the device-wide permission behind it, so the device keeps it. The
@@ -1341,14 +1385,24 @@ static void cxl_bi_dealloc(void *data)
static int cxl_bi_enable_path(struct cxl_dev_state *cxlds,
struct cxl_port *port, struct cxl_dport *dport)
{
+ struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
struct cxl_dport *dport_iter;
struct cxl_port *port_iter;
+ bool adopt;
int rc;
+ /*
+ * Adoption is a path property, not a per-dport one: only when
+ * firmware enabled the device itself does a dport's committed
+ * state cover this device's BI-ID.
+ */
+ adopt = cxl_bi_decoder_enabled(endpoint);
+
port_iter = port;
dport_iter = dport;
while (!is_cxl_root(port_iter)) {
- rc = cxl_bi_ctrl_dport_enable(dport_iter, dport_iter == dport);
+ rc = cxl_bi_ctrl_dport_enable(dport_iter, dport_iter == dport,
+ &adopt);
if (rc)
goto err_rollback;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 06a0e9bb892f..4c92aed1a744 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2129,13 +2129,19 @@ static int cxl_region_check_coherency(struct cxl_region *cxlr,
return -ENXIO;
}
- /* a committed decoder cannot inherit the region's type */
- if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) &&
- cxled->cxld.target_type != cxlr->type) {
- dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n",
- dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
- cxled->cxld.target_type, cxlr->type);
- return -ENXIO;
+ /* a committed decoder cannot inherit the region's flavor */
+ if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) {
+ bool bi = cxled->cxld.flags & CXL_DECODER_F_BI;
+
+ if (cxled->cxld.target_type != cxlr->type || bi != root_bi) {
+ dev_dbg(&cxlr->dev,
+ "%s:%s coherency model mismatch: %s vs %s\n",
+ dev_name(&cxlmd->dev),
+ dev_name(&cxled->cxld.dev),
+ cxl_coherency_name(cxled->cxld.target_type, bi),
+ cxl_coherency_name(cxlr->type, root_bi));
+ return -ENXIO;
+ }
}
return 0;
@@ -3898,6 +3904,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
struct cxl_endpoint_decoder *cxled = ctx->cxled;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ bool bi = cxled->cxld.flags & CXL_DECODER_F_BI;
int rc, part = READ_ONCE(cxled->part);
struct cxl_region *cxlr;
unsigned long need;
@@ -3907,14 +3914,15 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
/*
* A committed decoder defines the region built from it, so no
- * attach check can find its range type wrong. Only the window's
- * restrictions can.
+ * attach check can find its coherency model wrong. Only the
+ * window's restrictions can, on the BI and range type axes.
*/
need = cxled->cxld.target_type == CXL_DECODER_DEVMEM ?
CXL_DECODER_F_DEVMEM : CXL_DECODER_F_HOSTONLY;
- if (!(cxlrd->cxlsd.cxld.flags & need)) {
- dev_dbg(&cxlrd->cxlsd.cxld.dev,
- "%s:%s range type not permitted by the window\n",
+ if (bi != cxl_root_decoder_is_bi(cxlrd) ||
+ !(cxlrd->cxlsd.cxld.flags & need)) {
+ dev_err(&cxlrd->cxlsd.cxld.dev,
+ "%s:%s coherency model not permitted by the window\n",
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
return ERR_PTR(-ENXIO);
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index a95045b05601..90815daffe8e 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -311,7 +311,8 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
#define CXL_DECODER_F_ENABLE BIT(5)
#define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6)
#define CXL_DECODER_F_BI BIT(7)
-#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK)
+#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK | \
+ CXL_DECODER_F_BI)
enum cxl_decoder_type {
CXL_DECODER_DEVMEM = 2,
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 09/10] cxl/test: Add mock BI topology support
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (7 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 10/10] cxl/doc: Update maturity map with BI support Davidlohr Bueso
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Extend the mock topology with a Back-Invalidate path
covering both the type3 memdevs and the type2 accelerator.
Following the framework's convention of substituting software state
for register programming, cxl_bi_probe_capable() gains a --wrap shim
dispatching through cxl_mock_ops, and the mock decoder setup enables
BI in software in place of cxl_bi_setup(). The mock setup mirrors
cxl_bi_enable_path()'s walk - dport nr_bi accounting up to the root,
unwound by a devm action - and the capability check keeps the
VH-only rule.
A single HDM-DB window (BI | DEVMEM | VOLATILE, targeting host
bridge 0) is emitted in every topology mode and parses into a
CXL_DECODER_F_BI root decoder; DEVMEM satisfies can_create_ram().
In type2 mode it coexists with the accelerator's HDM-D window.
Committed BI state is a decoder flag, so the mock sets
CXL_DECODER_F_BI on decoder commit and clears it on reset, and
restores it on saved-decoder replay - letting a committed HDM-DB
region survive a cxl_acpi rebind with no register to emulate.
Endpoints get a page of plain memory standing in for BI Decoder
Control, with BI Enable set, since committing an HDM-DB region reads
it back. The accelerator grows to 1G of capacity so the boot-time
auto region leaves DPA for a BI region.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
tools/testing/cxl/Kbuild | 1 +
tools/testing/cxl/test/accel.c | 2 +-
tools/testing/cxl/test/cxl.c | 149 ++++++++++++++++++++++++++++++++-
tools/testing/cxl/test/mock.c | 13 +++
tools/testing/cxl/test/mock.h | 1 +
5 files changed, 162 insertions(+), 4 deletions(-)
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index e49ce644b4b1..39b4bb2e0404 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -14,6 +14,7 @@ wrap_cxl_core += --wrap=devm_cxl_add_dport_by_dev
wrap_cxl_core += --wrap=devm_cxl_switch_port_decoders_setup
wrap_cxl_core += --wrap=devm_cxl_endpoint_decoders_setup
wrap_cxl_core += --wrap=cxl_endpoint_parse_cdat
+wrap_cxl_core += --wrap=cxl_bi_probe_capable
LDFLAGS_cxl_acpi.o += $(wrap_cxl_core)
LDFLAGS_cxl_pmem.o += $(wrap_cxl_core)
LDFLAGS_cxl_port.o += $(wrap_cxl_core)
diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c
index 8e6f4687ca02..f3800083ea27 100644
--- a/tools/testing/cxl/test/accel.c
+++ b/tools/testing/cxl/test/accel.c
@@ -31,7 +31,7 @@ static int cxl_mock_accel_probe(struct platform_device *pdev)
cxlds = &cxl_accel->cxlds;
cxlds->media_ready = true;
- rc = cxl_set_capacity(cxlds, SZ_512M);
+ rc = cxl_set_capacity(cxlds, SZ_1G);
if (rc)
return rc;
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 3eb1051b1ee8..12fe1c3bdc92 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -191,6 +191,10 @@ static struct {
struct acpi_cedt_cfmws cfmws;
u32 target[3];
} cfmws8;
+ struct {
+ struct acpi_cedt_cfmws cfmws;
+ u32 target[1];
+ } cfmws_bi;
struct {
struct acpi_cedt_cxims cxims;
u64 xormap_list[2];
@@ -374,6 +378,22 @@ static struct {
},
.target = { 0, 1, 2, },
},
+ .cfmws_bi = {
+ .cfmws = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CFMWS,
+ .length = sizeof(mock_cedt.cfmws_bi),
+ },
+ .interleave_ways = 0,
+ .granularity = 4,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_BI |
+ ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
+ .qtg_id = FAKE_QTG_ID,
+ .window_size = SZ_256M * 4UL,
+ },
+ .target = { 0 },
+ },
.cxims0 = {
.cxims = {
.header = {
@@ -535,6 +555,12 @@ static int populate_cedt(void)
window->base_hpa = res->range.start;
}
+ res = alloc_mock_res(mock_cedt.cfmws_bi.cfmws.window_size,
+ max_t(int, SZ_256M, PMD_SIZE));
+ if (!res)
+ return -ENOMEM;
+ mock_cedt.cfmws_bi.cfmws.base_hpa = res->range.start;
+
return 0;
}
@@ -570,12 +596,17 @@ static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
handler_arg(h, arg, end);
}
- if (id == ACPI_CEDT_TYPE_CFMWS)
+ if (id == ACPI_CEDT_TYPE_CFMWS) {
for (i = cfmws_start; i <= cfmws_end; i++) {
h = (union acpi_subtable_headers *) mock_cfmws[i];
end = (unsigned long) h + mock_cfmws[i]->header.length;
handler_arg(h, arg, end);
}
+ /* one HDM-DB window in every topology */
+ h = (union acpi_subtable_headers *)&mock_cedt.cfmws_bi.cfmws;
+ end = (unsigned long)h + mock_cedt.cfmws_bi.cfmws.header.length;
+ handler_arg(h, arg, end);
+ }
if (id == ACPI_CEDT_TYPE_CXIMS)
for (i = 0; i < ARRAY_SIZE(mock_cxims); i++) {
@@ -737,10 +768,43 @@ static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port,
cxlhdm->port = port;
cxlhdm->interleave_mask = ~0U;
cxlhdm->iw_cap_mask = ~0UL;
+
+ /* a page stands in for the endpoint's BI Decoder block, Enable set */
+ if (is_cxl_endpoint(port)) {
+ void __iomem *bi = (void __iomem *)
+ devm_get_free_pages(dev, GFP_KERNEL | __GFP_ZERO, 0);
+
+ if (!bi)
+ return ERR_PTR(-ENOMEM);
+ writel(CXL_BI_DECODER_CTRL_BI_ENABLE,
+ bi + CXL_BI_DECODER_CTRL_OFFSET);
+ port->regs.bi_decoder = bi;
+ }
+
dev_set_drvdata(dev, cxlhdm);
return cxlhdm;
}
+/* HPA-based, so replay after cxl_acpi rebind can re-derive it */
+static bool mock_hpa_is_bi(u64 hpa)
+{
+ struct acpi_cedt_cfmws *bi = &mock_cedt.cfmws_bi.cfmws;
+
+ return hpa >= bi->base_hpa && hpa < bi->base_hpa + bi->window_size;
+}
+
+/* committed BI state is a decoder flag, so no register to emulate */
+static void mock_decoder_set_bi(struct cxl_decoder *cxld, bool bi)
+{
+ if (!is_endpoint_decoder(&cxld->dev))
+ return;
+
+ if (bi)
+ cxld->flags |= CXL_DECODER_F_BI;
+ else
+ cxld->flags &= ~CXL_DECODER_F_BI;
+}
+
struct target_map_ctx {
u32 *target_map;
int index;
@@ -974,6 +1038,7 @@ static int mock_decoder_commit(struct cxl_decoder *cxld)
cxled->state = CXL_DECODER_STATE_AUTO;
}
+ mock_decoder_set_bi(cxld, mock_hpa_is_bi(cxld->hpa_range.start));
cxld_registry_update(cxld);
return 0;
@@ -1003,6 +1068,7 @@ static void mock_decoder_reset(struct cxl_decoder *cxld)
cxled->state = CXL_DECODER_STATE_MANUAL;
cxled->skip = 0;
}
+ mock_decoder_set_bi(cxld, false);
if (decoder_reset_preserve_registry)
dev_dbg(port->uport_dev, "decoder%d: skip registry update\n",
cxld->id);
@@ -1163,8 +1229,13 @@ static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_
else
enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
- if (enabled)
- return !cxld_registry_restore(cxld, td);
+ if (enabled) {
+ if (cxld_registry_restore(cxld, td))
+ return false;
+ mock_decoder_set_bi(cxld,
+ mock_hpa_is_bi(cxld->hpa_range.start));
+ return true;
+ }
init_disabled_mock_decoder(cxld);
return false;
@@ -1523,9 +1594,12 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
return 0;
}
+static int mock_cxl_bi_setup(struct cxl_port *endpoint);
+
static int __mock_cxl_decoders_setup(struct cxl_port *port)
{
struct cxl_hdm *cxlhdm;
+ int rc;
cxlhdm = mock_cxl_setup_hdm(port, NULL);
if (IS_ERR(cxlhdm)) {
@@ -1534,6 +1608,13 @@ static int __mock_cxl_decoders_setup(struct cxl_port *port)
return PTR_ERR(cxlhdm);
}
+ /* as the real setup: BI between the HDM state and the decoders */
+ if (is_cxl_endpoint(port)) {
+ rc = mock_cxl_bi_setup(port);
+ if (rc)
+ dev_dbg(&port->dev, "BI setup failed rc=%d\n", rc);
+ }
+
return mock_cxl_enumerate_decoders(cxlhdm, NULL);
}
@@ -1722,6 +1803,67 @@ mock_region_intersects_soft_reserve(resource_size_t start, size_t size)
return -1;
}
+/*
+ * All-software mirror of the BI enable path: no BI RT registers exist
+ * on mock devices, so capability and enablement are asserted directly
+ * while the dport nr_bi accounting - the part with driver-visible
+ * semantics (shared transit dports, teardown order) - follows the same
+ * walk the real cxl_bi_enable_path() takes.
+ */
+static void mock_cxl_bi_probe_capable(struct cxl_port *endpoint)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+
+ /* BI is VH-only, mirroring cxl_bi_probe_capable() */
+ cxlds->bi_capable = !cxlds->rcd;
+}
+
+static void mock_cxl_bi_dealloc(void *data)
+{
+ struct cxl_port *endpoint = data;
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ struct cxl_dport *dport_iter;
+ struct cxl_port *port_iter;
+
+ cxlds->bi = false;
+ dport_iter = endpoint->parent_dport;
+ port_iter = dport_iter->port;
+ while (port_iter->parent_dport) {
+ scoped_guard(mutex, &port_iter->bi_lock) {
+ if (!WARN_ON_ONCE(dport_iter->nr_bi == 0))
+ dport_iter->nr_bi--;
+ }
+ dport_iter = port_iter->parent_dport;
+ port_iter = dport_iter->port;
+ }
+}
+
+static int mock_cxl_bi_setup(struct cxl_port *endpoint)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ struct cxl_dport *dport_iter;
+ struct cxl_port *port_iter;
+
+ if (!cxlds->bi_capable)
+ return 0;
+
+ dport_iter = endpoint->parent_dport;
+ port_iter = dport_iter->port;
+ while (port_iter->parent_dport) {
+ scoped_guard(mutex, &port_iter->bi_lock)
+ dport_iter->nr_bi++;
+ dport_iter = port_iter->parent_dport;
+ port_iter = dport_iter->port;
+ }
+ cxlds->bi = true;
+
+ return devm_add_action_or_reset(&endpoint->dev, mock_cxl_bi_dealloc,
+ endpoint);
+}
+
static struct cxl_mock_ops cxl_mock_ops = {
.is_mock_adev = is_mock_adev,
.is_mock_bridge = is_mock_bridge,
@@ -1740,6 +1882,7 @@ static struct cxl_mock_ops cxl_mock_ops = {
.walk_hmem_resources = mock_walk_hmem_resources,
.region_intersects = mock_region_intersects,
.region_intersects_soft_reserve = mock_region_intersects_soft_reserve,
+ .cxl_bi_probe_capable = mock_cxl_bi_probe_capable,
.list = LIST_HEAD_INIT(cxl_mock_ops.list),
};
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index 6454b868b122..eabc9bba0fac 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -181,6 +181,19 @@ int __wrap_devm_cxl_endpoint_decoders_setup(struct cxl_port *port)
}
EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_endpoint_decoders_setup, "CXL");
+void __wrap_cxl_bi_probe_capable(struct cxl_port *endpoint)
+{
+ int index;
+ struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+ if (ops && ops->is_mock_port(endpoint->uport_dev))
+ ops->cxl_bi_probe_capable(endpoint);
+ else
+ cxl_bi_probe_capable(endpoint);
+ put_cxl_mock_ops(index);
+}
+EXPORT_SYMBOL_NS_GPL(__wrap_cxl_bi_probe_capable, "CXL");
+
int __wrap_cxl_await_media_ready(struct cxl_dev_state *cxlds)
{
int rc, index;
diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
index 846d7c5d6eaa..c7919c3eb875 100644
--- a/tools/testing/cxl/test/mock.h
+++ b/tools/testing/cxl/test/mock.h
@@ -35,6 +35,7 @@ struct cxl_mock_ops {
unsigned long flags, unsigned long desc);
int (*region_intersects_soft_reserve)(resource_size_t start,
size_t size);
+ void (*cxl_bi_probe_capable)(struct cxl_port *endpoint);
};
int hmem_test_init(void);
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v10 10/10] cxl/doc: Update maturity map with BI support
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
` (8 preceding siblings ...)
2026-11-01 21:51 ` [PATCH v10 09/10] cxl/test: Add mock BI topology support Davidlohr Bueso
@ 2026-11-01 21:51 ` Davidlohr Bueso
9 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-01 21:51 UTC (permalink / raw)
To: dave.jiang
Cc: jic23, alison.schofield, icheng, ming.li, benjamin.cheatham,
linux-cxl, Davidlohr Bueso, Jonathan Cameron
Add the respective Back-Invalidate info to the maturity map document.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
Documentation/driver-api/cxl/maturity-map.rst | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/Documentation/driver-api/cxl/maturity-map.rst b/Documentation/driver-api/cxl/maturity-map.rst
index fefd31899229..6e8d6ec5bdfb 100644
--- a/Documentation/driver-api/cxl/maturity-map.rst
+++ b/Documentation/driver-api/cxl/maturity-map.rst
@@ -63,6 +63,11 @@ in place, but there are several corner cases that are pending closure.
* [0] Decoder target and granularity constraints
+* [1] :ref:`Back-Invalidate (HDM-DB) <back-invalidate>`
+
+ * [3] BI topology enable / disable (endpoint, DSP, USP route table, RP)
+ * [2] Firmware-enabled path adoption
+
* [2] Performance enumeration
* [3] Endpoint CDAT
@@ -170,7 +175,7 @@ Accelerator
-----------
* [0] Accelerator memory enumeration HDM-D (CXL 1.1/2.0 Type-2)
-* [0] Accelerator memory enumeration HDM-DB (CXL 3.0 Type-2)
+* [1] Accelerator memory enumeration HDM-DB (CXL 3.0 Type-2)
* [0] CXL.cache 68b (CXL 2.0)
* [0] CXL.cache 256b Cache IDs (CXL 3.0)
@@ -196,6 +201,15 @@ Details
hiding some standard registers like PCIe Link Status / Capabilities in
the CXL RCRB (Root Complex Register Block).
+.. _back-invalidate:
+
+* **Back-Invalidate**: HDM-DB lets a device snoop the host over the
+ CXL.mem BISnp channel instead of CXL.cache. The kernel enables BI on
+ every port between a device and its root port, requires 256B Flit
+ mode on the path, adopts paths firmware already enabled, and creates
+ HDM-DB regions under CFMWS windows carrying the Back-Invalidate
+ restriction for Type 3 and Type 2 devices alike.
+
.. _background-commands:
* **Background commands**: The CXL background command mechanism is
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v10 04/10] cxl: Add HDM-DB region creation
2026-11-01 21:51 ` [PATCH v10 04/10] cxl: Add HDM-DB region creation Davidlohr Bueso
@ 2026-11-01 22:24 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-11-01 22:24 UTC (permalink / raw)
To: Davidlohr Bueso; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] cxl/acpi: cxl_acpi_cfmws_verify() rejects Host-only + BI windows on QEMU 11.0+
- [Medium] cxl: stale cxlds->bi state on reset in cxl_region_decode_commit()
- [Low] cxl/acpi: unreachable third coherency check in cxl_acpi_cfmws_verify()
--
commit d4a68ccdfa9e0e2b1d3fcb54967d9cb568d07b31
Author: Davidlohr Bueso <dave@stgolabs.net>
cxl: Add HDM-DB region creation
This commit adds support for creating HDM-DB (device-coherent with
back-invalidate) CXL regions via sysfs and auto-assembly, introduces
CFMWS coherency verification in cxl_acpi_cfmws_verify(), and exposes the
cap_back_invalidate and back_invalidate sysfs attributes.
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index b7ce6b59c680b..cc7c99d3066d8 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -152,6 +152,8 @@ static unsigned long cfmws_to_decoder_flags(int restrictions)
> flags |= CXL_DECODER_F_PMEM;
> if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
> flags |= CXL_DECODER_F_LOCK;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)
> + flags |= CXL_DECODER_F_BI;
>
> return flags;
> }
> @@ -198,6 +200,24 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
> dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
> cfmws->header.length, expected_len);
>
> + if ((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM) &&
> + (cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)) {
> + dev_err(dev, "CFMWS cannot have both HDM-H and HDM-DB\n");
> + return -EINVAL;
> + }
[Severity: Medium]
As reported by Alison Schofield on v9 [1], does rejecting the entire CFMWS
window in cxl_acpi_cfmws_verify() when both ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM
and ACPI_CEDT_CFMWS_RESTRICT_BI are set cause a regression on released QEMU
v11.0+ systems?
During cxl_acpi_probe() -> acpi_table_parse_cedt() -> cxl_parse_cfmws() ->
__cxl_parse_cfmws() -> cxl_acpi_cfmws_verify(), returning -EINVAL for this
combination aborts root decoder registration completely. Because released
QEMU versions from v11.0 onward set both restriction bits by default, CXL
windows are rejected at boot and manual HDM-H region creation is lost on
windows where BI was previously ignored.
Could cfmws_to_decoder_flags() withhold CXL_DECODER_F_BI when
ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM is set (or when
ACPI_CEDT_CFMWS_RESTRICT_DEVMEM is not set) instead of failing the entire
window in cxl_acpi_cfmws_verify()?
> +
> + if (!(cfmws->restrictions & (ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
> + ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM))) {
> + dev_err(dev, "CFMWS has no coherency model\n");
> + return -EINVAL;
> + }
> +
> + if ((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI) &&
> + !(cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_DEVMEM)) {
> + dev_err(dev, "CFMWS BI requires device-coherent\n");
> + return -EINVAL;
> + }
[Severity: Low]
Can this third check in cxl_acpi_cfmws_verify() ever be reached?
For (cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI) &&
!(cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_DEVMEM) to evaluate to
true, ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM must be either set or clear:
- If ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM is set, the first check
((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM) &&
(cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)) already returns
-EINVAL.
- If ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM is clear (with
ACPI_CEDT_CFMWS_RESTRICT_DEVMEM also clear), the second check
(!(cfmws->restrictions & (ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM))) already logs "CFMWS has no
coherency model" and returns -EINVAL.
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index a048bd5c06cb6..28c61feef06d2 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -313,8 +313,25 @@ static int commit_decoder(struct cxl_decoder *cxld)
> static int cxl_region_decode_commit(struct cxl_region *cxlr)
> {
> struct cxl_region_params *p = &cxlr->params;
> + bool hdm_db;
> int i, rc = 0;
>
> + hdm_db = cxlr->type == CXL_DECODER_DEVMEM &&
> + cxl_root_decoder_is_bi(cxlr->cxlrd);
> +
> + /* a reset since attach may have invalidated cxlds->bi */
> + for (i = 0; i < p->nr_targets; i++) {
> + struct cxl_endpoint_decoder *cxled = p->targets[i];
> + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> +
> + if (hdm_db && !cxlmd->cxlds->bi) {
> + dev_err(&cxlr->dev, "%s:%s BI not enabled on device\n",
> + dev_name(&cxlmd->dev),
> + dev_name(&cxled->cxld.dev));
> + return -ENXIO;
> + }
> + }
> +
> for (i = 0; i < p->nr_targets; i++) {
> struct cxl_endpoint_decoder *cxled = p->targets[i];
> struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> @@ -346,6 +363,21 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
> cxled->cxld.reset(&cxled->cxld);
> goto err;
> }
> +
> + /*
> + * A reset after the check above wipes BI Enable, but
> + * cxlds->bi is only refreshed from .reset_done, which may
> + * not have run. Ask the hardware.
> + */
> + if (hdm_db && !cxl_bi_decoder_enabled(cxled_to_port(cxled))) {
> + dev_err(&cxlr->dev,
> + "%s:%s BI disabled by reset during commit\n",
> + dev_name(&cxlmd->dev),
> + dev_name(&cxled->cxld.dev));
> + rc = -ENXIO;
> + i++; /* this target committed, so undo it too */
> + goto err;
> + }
[Severity: Medium]
When a reset clears CXL_BI_DECODER_CTRL_BI_ENABLE in hardware without
.reset_done (cxl_bi_reset_detected()) having updated cxlmd->cxlds->bi (for
example, during a concurrent reset before .reset_done runs, a secondary bus
reset that does not invoke .reset_done, or on a Type-2 device driver that
does not call cxl_bi_reset_detected()), doesn't this leave cxlmd->cxlds->bi
stale and transiently commit decoders before rolling them back?
First, in the pre-commit loop at the top of cxl_region_decode_commit(), only
the software flag !cxlmd->cxlds->bi is checked rather than
cxl_bi_decoder_enabled(cxled_to_port(cxled)). If hardware BI Enable was
already cleared while cxlmd->cxlds->bi is still true,
cxl_region_decode_commit() programs and commits the endpoint and switch HDM
decoders in hardware with CXL_HDM_DECODER0_CTRL_BI set before catching the
mismatch in the post-commit check and rolling back via
cxl_region_decode_reset().
Second, when the post-commit check detects
!cxl_bi_decoder_enabled(cxled_to_port(cxled)), it does not clear
cxlmd->cxlds->bi = false. If .reset_done never runs, cxlmd->cxlds->bi
remains true indefinitely: back_invalidate_show() in
drivers/cxl/core/port.c continues to report 1 while the decoder is attached,
cxl_region_check_coherency() continues to allow attaching the endpoint to
HDM-DB regions, subsequent commit_store() calls repeat the hardware commit
and rollback cycle, and cxl_bi_release() attempts to disable BI on teardown.
Should the pre-commit check also verify
cxl_bi_decoder_enabled(cxled_to_port(cxled)), and should cxlmd->cxlds->bi be
updated when hardware BI Enable is found cleared?
[1] https://lore.kernel.org/r/ar3TU4IM_TtcGgwl@aschofie-mobl2.lan
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793567979.git.dave@stgolabs.net?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders
2026-11-01 21:51 ` [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders Davidlohr Bueso
@ 2026-11-02 8:21 ` Li Ming
2026-11-02 20:04 ` Davidlohr Bueso
0 siblings, 1 reply; 14+ messages in thread
From: Li Ming @ 2026-11-02 8:21 UTC (permalink / raw)
To: Davidlohr Bueso, dave.jiang
Cc: jic23, alison.schofield, icheng, benjamin.cheatham, linux-cxl,
Jonathan Cameron
在 2026/11/2 05:51, Davidlohr Bueso 写道:
> An HDM decoder that firmware committed with the BI bit set was
> assembled without the bit ever being looked at. Record it, and
> refuse what is broken - BI paired with a host-only target range type,
> or for an endpoint decoder BI on a path that cannot route BISnp.
>
> Taking over such a path must not re-program what firmware already
> did, and a dport's committed state does not say which device it was
> committed for, only that firmware committed for something below it.
> Two endpoints sharing a level show why that is not enough to go on.
>
> RP Forward set, but for mem0
> |
> USP
> / \
> DSP0 DSP1
> | |
> mem0 mem1
> BI Enable BI Enable
> set clear
>
> Adoption is therefore seeded from the endpoint's own BI Enable, since
> only a device firmware itself enabled can have had its BI-ID
> accounted for above, and carried up the walk. A level is taken as
> found, with no register write and no commit, when nothing this driver
> routed sits below it, the value this driver would write is already
> there, and both the decoder and the switch's route table are
> committed - the table carries a commit of its own that firmware may
> not have performed. The first level to fall short ends adoption, and
> from it up the driver programs and commits as for any other device.
>
> RP Forward, written but never committed
> |
> USP0 route table committed by the driver
> |
> DSP0 Forward, written and committed
> | ..... adoption ended here .....
> USP1 route table already committed
> |
> DSP1 Enable, taken as found
> |
> mem0 BI Enable already set by firmware
>
> An adopted level owes no commit either way. Table 8-152 and Table
> 8-156 owe one per new BI device enabled below the port, and an
> adopted level is not seeing one.
>
> On failure the walk unwinds through the same release as teardown. An
> adopted level was found up and is held, so the unwind drops its
> reference and leaves its register alone; a level this walk brought up
> is cleared. Teardown works the same way: every level this driver
> brought up is deallocated, unless a decoder below still decodes with
> BI and holds it.
>
> A committed decoder is refused when its coherency model does not fit
> where it lands: unlike a decoder this driver programs it cannot
> inherit a region's model, so its committed BI bit must match the
> window's, checked before the region is built and again at attach,
> where a second decoder joining the region arrives.
>
> A committed decoder's BI bit is recorded as CXL_DECODER_F_BI. On a
> root decoder the same flag is the window's BI restriction, as F_LOCK
> already means a fixed window there and Lock On Commit below.
>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
Reviewed-by: Li Ming <ming.li@zohomail.com>
One comment below.
> ---
> drivers/cxl/core/hdm.c | 31 ++++++++++++++++++--
> drivers/cxl/core/pci.c | 60 +++++++++++++++++++++++++++++++++++++--
> drivers/cxl/core/region.c | 32 +++++++++++++--------
> drivers/cxl/cxl.h | 3 +-
> 4 files changed, 108 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index c272fc6faf37..1136f7d1bd5b 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -907,6 +907,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
> struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
> void __iomem *hdm = cxlhdm->regs.hdm_decoder;
> int id = cxld->id, rc;
> + u32 ctrl;
>
> if (cxld->flags & CXL_DECODER_F_ENABLE)
> return 0;
> @@ -950,6 +951,10 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
> port->commit_end++;
> cxld->flags |= CXL_DECODER_F_ENABLE;
>
> + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
> + if (ctrl & CXL_HDM_DECODER0_CTRL_BI)
> + cxld->flags |= CXL_DECODER_F_BI;
> +
IIUC, this part is unrelated auto-committed BI HDM decoder handling,
because above "if (cxld->flags & CXL_DECODER_F_ENABLE)" will return
directly for auto-committed BI HDM decoder, I guess this part should
move to patch #4?
Ming
> return 0;
> }
>
> @@ -1020,7 +1025,7 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
> writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
> writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
>
> - cxld->flags &= ~CXL_DECODER_F_ENABLE;
> + cxld->flags &= ~(CXL_DECODER_F_ENABLE | CXL_DECODER_F_BI);
>
> /* Userspace is now responsible for reconfiguring this decoder */
> if (is_endpoint_decoder(&cxld->dev)) {
> @@ -1140,11 +1145,32 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
> cxld->flags |= CXL_DECODER_F_ENABLE;
> if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
> cxld->flags |= CXL_DECODER_F_LOCK;
> + if (ctrl & CXL_HDM_DECODER0_CTRL_BI)
> + cxld->flags |= CXL_DECODER_F_BI;
> +
> if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
> cxld->target_type = CXL_DECODER_HOSTONLYMEM;
> else
> cxld->target_type = CXL_DECODER_DEVMEM;
>
> + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_BI, ctrl)) {
> + struct cxl_dev_state *cxlds = cxled ?
> + cxled_to_memdev(cxled)->cxlds : NULL;
> +
> + if (cxld->target_type == CXL_DECODER_HOSTONLYMEM) {
> + dev_warn(&port->dev,
> + "decoder%d.%d: BI with host-only\n",
> + port->id, cxld->id);
> + return -ENXIO;
> + }
> + if (cxlds && !cxlds->bi_capable) {
> + dev_warn(&port->dev,
> + "decoder%d.%d: path not BI capable\n",
> + port->id, cxld->id);
> + return -ENXIO;
> + }
> + }
> +
> guard(rwsem_write)(&cxl_rwsem.region);
> if (cxld->id != cxl_num_decoders_committed(port)) {
> dev_warn(&port->dev,
> @@ -1423,7 +1449,8 @@ int devm_cxl_endpoint_decoders_setup(struct cxl_port *port)
> * Between the port's HDM state and its decoders: devres,
> * unwinding in reverse, brings BI down only after the decoders
> * quiesce, while its slow walk still precedes the HDM state
> - * free.
> + * free. Must also precede region discovery, where HDM-DB
> + * assembly requires cxlds->bi.
> */
> rc = cxl_bi_setup(port);
> if (rc)
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index aec1edf648bd..11e2a97f6567 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -1109,6 +1109,32 @@ static int __cxl_bi_commit_decoder(struct device *dev, void __iomem *bi)
> scale, base);
> }
>
> +/* Committed, or no explicit commit required */
> +static bool __cxl_bi_committed(void __iomem *caps_reg, void __iomem *status_reg,
> + u32 req_bit, u32 committed_bit)
> +{
> + if (!(readl(caps_reg) & req_bit))
> + return true;
> +
> + return readl(status_reg) & committed_bit;
> +}
> +
> +static bool cxl_bi_rt_committed(void __iomem *bi)
> +{
> + return __cxl_bi_committed(bi + CXL_BI_RT_CAPS_OFFSET,
> + bi + CXL_BI_RT_STATUS_OFFSET,
> + CXL_BI_RT_CAPS_EXPLICIT_COMMIT_REQ,
> + CXL_BI_RT_STATUS_BI_COMMITTED);
> +}
> +
> +static bool cxl_bi_decoder_committed(void __iomem *bi)
> +{
> + return __cxl_bi_committed(bi + CXL_BI_DECODER_CAPS_OFFSET,
> + bi + CXL_BI_DECODER_STATUS_OFFSET,
> + CXL_BI_DECODER_CAPS_EXPLICIT_COMMIT_REQ,
> + CXL_BI_DECODER_STATUS_BI_COMMITTED);
> +}
> +
> static int cxl_bi_commit_dport(struct cxl_dport *dport)
> {
> struct cxl_port *port = dport->port;
> @@ -1136,7 +1162,8 @@ static int cxl_bi_commit_dport(struct cxl_dport *dport)
> * @direct says the device is connected directly to this dport, which
> * takes BI Enable; every dport above it takes BI Forward.
> */
> -static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
> +static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct,
> + bool *adopt)
> {
> struct cxl_port *port = dport->port;
> u32 ctrl, value, set, clr;
> @@ -1168,6 +1195,20 @@ static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
> CXL_BI_DECODER_CTRL_BI_ENABLE;
>
> value = (ctrl | set) & ~clr;
> +
> + /*
> + * Adopt this level as firmware left it: nothing below it that
> + * firmware did not account for, nothing this driver has routed
> + * through it, the value this walk would write already in place,
> + * and the decoder (and route table) committed.
> + */
> + if (*adopt && !dport->nr_bi && value == ctrl &&
> + cxl_bi_decoder_committed(bi) &&
> + (!port->regs.bi_rt || cxl_bi_rt_committed(port->regs.bi_rt)))
> + goto done;
> + /* firmware did not bring this level up, so nothing above may */
> + *adopt = false;
> +
> if (value != ctrl)
> writel(value, bi + CXL_BI_DECODER_CTRL_OFFSET);
>
> @@ -1181,6 +1222,7 @@ static int cxl_bi_ctrl_dport_enable(struct cxl_dport *dport, bool direct)
> }
> return rc;
> }
> +done:
> dport->nr_bi++;
>
> return 0;
> @@ -1277,7 +1319,9 @@ static int cxl_bi_ctrl_endpoint_disable(struct cxl_dev_state *cxlds)
> /*
> * Release the device's BI, then each dport up the path short of @stop,
> * following the deallocate sequence in the implementation notes of
> - * CXL 4.0 9.14.2. Setup's unwind and teardown alike.
> + * CXL 4.0 9.14.2. Setup's unwind and teardown alike: a level the
> + * walk adopted was found up and is held, so the unwind leaves its
> + * register as it found it.
> *
> * A decoder that still decodes with its BI bit set needs BI Enable,
> * the device-wide permission behind it, so the device keeps it. The
> @@ -1341,14 +1385,24 @@ static void cxl_bi_dealloc(void *data)
> static int cxl_bi_enable_path(struct cxl_dev_state *cxlds,
> struct cxl_port *port, struct cxl_dport *dport)
> {
> + struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
> struct cxl_dport *dport_iter;
> struct cxl_port *port_iter;
> + bool adopt;
> int rc;
>
> + /*
> + * Adoption is a path property, not a per-dport one: only when
> + * firmware enabled the device itself does a dport's committed
> + * state cover this device's BI-ID.
> + */
> + adopt = cxl_bi_decoder_enabled(endpoint);
> +
> port_iter = port;
> dport_iter = dport;
> while (!is_cxl_root(port_iter)) {
> - rc = cxl_bi_ctrl_dport_enable(dport_iter, dport_iter == dport);
> + rc = cxl_bi_ctrl_dport_enable(dport_iter, dport_iter == dport,
> + &adopt);
> if (rc)
> goto err_rollback;
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 06a0e9bb892f..4c92aed1a744 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2129,13 +2129,19 @@ static int cxl_region_check_coherency(struct cxl_region *cxlr,
> return -ENXIO;
> }
>
> - /* a committed decoder cannot inherit the region's type */
> - if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) &&
> - cxled->cxld.target_type != cxlr->type) {
> - dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n",
> - dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
> - cxled->cxld.target_type, cxlr->type);
> - return -ENXIO;
> + /* a committed decoder cannot inherit the region's flavor */
> + if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) {
> + bool bi = cxled->cxld.flags & CXL_DECODER_F_BI;
> +
> + if (cxled->cxld.target_type != cxlr->type || bi != root_bi) {
> + dev_dbg(&cxlr->dev,
> + "%s:%s coherency model mismatch: %s vs %s\n",
> + dev_name(&cxlmd->dev),
> + dev_name(&cxled->cxld.dev),
> + cxl_coherency_name(cxled->cxld.target_type, bi),
> + cxl_coherency_name(cxlr->type, root_bi));
> + return -ENXIO;
> + }
> }
>
> return 0;
> @@ -3898,6 +3904,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
> struct cxl_endpoint_decoder *cxled = ctx->cxled;
> struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> + bool bi = cxled->cxld.flags & CXL_DECODER_F_BI;
> int rc, part = READ_ONCE(cxled->part);
> struct cxl_region *cxlr;
> unsigned long need;
> @@ -3907,14 +3914,15 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>
> /*
> * A committed decoder defines the region built from it, so no
> - * attach check can find its range type wrong. Only the window's
> - * restrictions can.
> + * attach check can find its coherency model wrong. Only the
> + * window's restrictions can, on the BI and range type axes.
> */
> need = cxled->cxld.target_type == CXL_DECODER_DEVMEM ?
> CXL_DECODER_F_DEVMEM : CXL_DECODER_F_HOSTONLY;
> - if (!(cxlrd->cxlsd.cxld.flags & need)) {
> - dev_dbg(&cxlrd->cxlsd.cxld.dev,
> - "%s:%s range type not permitted by the window\n",
> + if (bi != cxl_root_decoder_is_bi(cxlrd) ||
> + !(cxlrd->cxlsd.cxld.flags & need)) {
> + dev_err(&cxlrd->cxlsd.cxld.dev,
> + "%s:%s coherency model not permitted by the window\n",
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
> return ERR_PTR(-ENXIO);
> }
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a95045b05601..90815daffe8e 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -311,7 +311,8 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
> #define CXL_DECODER_F_ENABLE BIT(5)
> #define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6)
> #define CXL_DECODER_F_BI BIT(7)
> -#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK)
> +#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK | \
> + CXL_DECODER_F_BI)
>
> enum cxl_decoder_type {
> CXL_DECODER_DEVMEM = 2,
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders
2026-11-02 8:21 ` Li Ming
@ 2026-11-02 20:04 ` Davidlohr Bueso
0 siblings, 0 replies; 14+ messages in thread
From: Davidlohr Bueso @ 2026-11-02 20:04 UTC (permalink / raw)
To: Li Ming
Cc: dave.jiang, jic23, alison.schofield, icheng, benjamin.cheatham,
linux-cxl, Jonathan Cameron
On Mon, 02 Nov 2026, Li Ming wrote:
>>@@ -907,6 +907,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>> struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
>> void __iomem *hdm = cxlhdm->regs.hdm_decoder;
>> int id = cxld->id, rc;
>>+ u32 ctrl;
>> if (cxld->flags & CXL_DECODER_F_ENABLE)
>> return 0;
>>@@ -950,6 +951,10 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>> port->commit_end++;
>> cxld->flags |= CXL_DECODER_F_ENABLE;
>>+ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
>>+ if (ctrl & CXL_HDM_DECODER0_CTRL_BI)
>>+ cxld->flags |= CXL_DECODER_F_BI;
>>+
>
>IIUC, this part is unrelated auto-committed BI HDM decoder handling,
>because above "if (cxld->flags & CXL_DECODER_F_ENABLE)" will return
>directly for auto-committed BI HDM decoder, I guess this part should
>move to patch #4?
That's fair. Since Alison's comment for caching this read was in this patch,
I just went ahead and did it all here for the endpoint. Patch 4 introduces
the flag for the root decoder, and this patch extends its use for BI
endpoints (much like how we do it for the LOCK equivalent).
I'll move the non strictly auto-commit CXL_DECODER_F_BI users out of
this patch and into patch 4 - which is basically this and the reset.
Thanks,
Davidlohr
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-11-02 20:05 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 21:50 [PATCH v10 00/10] cxl: Support Back-Invalidate Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 01/10] cxl: Add BI register probing and port initialization Davidlohr Bueso
2026-11-01 21:50 ` [PATCH v10 02/10] cxl/pci: Add BI topology enable/disable Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 03/10] cxl/hdm: Add BI coherency support for endpoint decoders Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 04/10] cxl: Add HDM-DB region creation Davidlohr Bueso
2026-11-01 22:24 ` sashiko-bot
2026-11-01 21:51 ` [PATCH v10 05/10] cxl/hdm: Rename decoder coherency flags Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 06/10] cxl/region: Log the coherency model at region creation Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 07/10] cxl/pci: Split BI capability probe from setup Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 08/10] cxl: Handle auto-committed BI hdm decoders Davidlohr Bueso
2026-11-02 8:21 ` Li Ming
2026-11-02 20:04 ` Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 09/10] cxl/test: Add mock BI topology support Davidlohr Bueso
2026-11-01 21:51 ` [PATCH v10 10/10] cxl/doc: Update maturity map with BI support Davidlohr Bueso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox