PCI hotplug resource assignment: hpmmiosize, realloc and BAR placement
PCI hotplug resource assignment: hpmmiosize, realloc and BAR placement
How Linux assigns bridge windows and BARs to a Thunderbolt-attached GPU — BIOS-built tunnels versus kernel re-enumeration, pci=realloc and the hpmmio sizes, why thunderbolt.host_reset makes the card re-enter as a hot-added device with a 256 MB BAR, why a rebuilt bridge path can keep memory decoding off, and how to verify each step.
name: linux-pcie-hotplug-bar-allocation title: Linux PCIe resource allocation for Thunderbolt/USB4 eGPUs — firmware tunnels vs kernel re-enumeration, bridge windows, BARs, COMMAND bits, ReBAR description: >- Expert reference for why a Thunderbolt/USB4-attached GPU comes up with an unusable BAR (0xffffffff MMIO reads, 256 MB BAR instead of 16 GB, bridges with Mem-/BusMaster-) after Linux re-enumerates a firmware-built PCIe tunnel, and what each kernel parameter (pci=realloc/realloc=off, hpmmiosize, hpmmioprefsize, hpiosize, hpbussize, assign-busses, nocrs, hpmemsize, pcie_ports=native/compat, pcie_port_pm, pcie_aspm, iommu=pt, thunderbolt.host_reset, thunderbolt.clx) actually does, with kernel-source citations. TRIGGER: eGPU BAR/bridge-window failures, “failed to assign”, “no space”, “can’t claim”, Mem decode off after hotplug, ReBAR over Thunderbolt, host_reset regressions, kernel 6.x-7.x resource-fitting changes. SKIP: GPU driver bugs unrelated to enumeration; desktop-slot ReBAR with no Thunderbolt; Windows/macOS. verified-as-of: 2026-09-24
Linux PCIe resource allocation for Thunderbolt/USB4-attached GPUs
Verified-as-of: 2026-09-24. Web-research only; nothing on the reference machine was probed for this document.
Every claim is tagged [SOURCED <url>] (text or code seen at that URL) or [INFERRED] (reasoning from sourced
facts; not independently verified). Kernel code quoted from torvalds/linux master as fetched on 2026-09-24.
Anchor case (worked example)
NUC 15 Pro (Arrow Lake-P), Ubuntu 26.04.1, kernel 7.0.0-34, RTX 5080 in Razer Core X V2 over TB4. Enclosure switch
reports Intel 8086:5786, which is the JHL9586 “Barlow Ridge Hub 80G” (Thunderbolt 5 / USB4 v2 hub)
[SOURCED https://ratatoskr.run/linux-usb/2026/07/17208566/t (search-result summary of the JHL9580 RFC thread; the
ID→name mapping was not re-read from the thread body — treat as probable)].
Failing configuration: pci=realloc,hpmmioprefsize=32G,hpmmiosize=512M with default thunderbolt.host_reset=1.
Observed (as reported on this box): kernel rebuilt the tunnel ~1.4 s into boot; afterwards the enclosure bridges had
Mem decode OFF; GPU BAR0 read 0xffffffff although config space answered; BAR1 came up 256 MB despite 16 GB ReBAR
support; bridge 03:00.0 prefetch window ~16.7 GB, non-prefetch 65 MB.
Working configuration: thunderbolt.host_reset=0 pci=realloc=off pcie_ports=native pcie_port_pm=off pcie_aspm=off thunderbolt.clx=0 iommu=pt plus a post-bolt service doing setpci -s <each bridge> COMMAND=0x0006:0x0006 before
modprobe nvidia.
The rest of this document explains why each of those knobs matters, what is load-bearing, and what is cargo.
Core Concepts
1. Two enumerators: firmware at POST vs. the kernel at hotplug time
- A Thunderbolt/USB4 connection manager (CM) “is an entity running on the host router responsible for enumerating routers and establishing tunnels”. PCs traditionally ship a firmware CM (Thunderbolt 3 / early USB4); the Linux driver “supports both and can detect at runtime which connection manager implementation is to be used” [SOURCED https://www.kernel.org/doc/html/latest/admin-guide/thunderbolt.html].
- When the BIOS builds the PCIe tunnel at POST, the BIOS also sizes the bridge windows and assigns BARs — and, on
ReBAR-capable firmware, exercises ReBAR so the GPU gets its full-size BAR. A boot-present device therefore inherits
BIOS-chosen windows; the kernel only claims them (
pci_claim_resource) unless told to reallocate [SOURCED https://ratatoskr.run/linux-pci/2026/03/14618943/t — “thunderbolt.host_reset=0 … preserves BIOS’s PCIe tunnel and BAR assignments from POST (where the BIOS does exercise ReBAR)”]. - When the kernel (re)builds the tunnel — because the driver reset the host router, because you authorized the device
post-boot via
bolt, or because you replugged — the GPU appears as a hot-added device under apciehpslot.pciehp_configure_device()“enumerate[s] PCI devices below a hotplug bridge and add[s] them to the system”:pci_scan_slot()→ for each bridgepci_hp_add_bridge()→pci_assign_unassigned_bridge_resources(bridge)→pcie_bus_configure_settings()→pci_bus_add_devices()[SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/hotplug/pciehp_pci.c]. - In that hot-add path “the PCI core reads the BAR at its power-on size, sizes the bridge windows to match and assigns addresses, all before any driver binds — and the ReBAR capability is not consulted at any point in that path” [SOURCED https://ratatoskr.run/linux-pci/2026/09/17551273/t]. This is the single most important fact in this document: kernel hot-add enumeration never uses Resizable BAR; only firmware POST enumeration (or a later driver/sysfs resize) does.
2. How hotplug bridge windows are sized (drivers/pci/setup-bus.c)
- For a bridge with
is_hotplug_bridge, the sizing code adds “additional” (optional) space from the tunables:additional_io_size = pci_hotplug_io_size; additional_mmio_size = pci_hotplug_mmio_size; additional_mmio_pref_size = pci_hotplug_mmio_pref_size;[SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/setup-bus.c]. - Defaults:
DEFAULT_HOTPLUG_IO_SIZE (256),DEFAULT_HOTPLUG_MMIO_SIZE (2*1024*1024),DEFAULT_HOTPLUG_MMIO_PREF_SIZE (2*1024*1024),DEFAULT_HOTPLUG_BUS_SIZE 1[SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/pci.c]. pbus_size_mem(): “If there’s a 64-bit prefetchable MMIO window, put all 64-bit prefetchable resources into it and place 32-bit prefetchable memory into the non-prefetchable window” [SOURCED setup-bus.c above]. GPU VRAM BARs are 64-bit prefetchable, so they live in the prefetchable window; that is whyhpmmioprefsize(nothpmmiosize) is the knob people reach for.- Retry loop when things do not fit: “First try will not touch PCI bridge res. Second and later try will clear small
leaf bridge res. Will stop till to the max depth if can not find good one” and
pci_bus_release_bridge_resources(): “Try to release PCI bridge resources from leaf bridge, so we can allocate a larger window later” [SOURCED setup-bus.c above]. This is thepci=reallocmachinery. - The hotplug reservation is applied to every hotplug bridge, including the three empty downstream ports of a Thunderbolt/USB4 hub. Those empty ports therefore each get a speculative large prefetch window, which “pins” the parent window and starves the one port that actually has a GPU behind it [SOURCED https://forum.linuxfoundation.org/discussion/870568/make-the-linux-kernel-rebar-over-thunderbolt-friendly — root port 96 GB pref; populated bridge 384 MB; “three empty sibling bridges: 32GB each”; and https://ratatoskr.run/linux-pci/2026/03/14618943/t].
3. Why a hot-added GPU ends up with a 256 MB BAR
- ReBAR-capable GPUs power on with a small default BAR (commonly 256 MB) and advertise larger sizes in the Resizable
BAR capability; the kernel hot-add path reads the power-on size (§1). Growing it later requires a resize, which
must release and re-grow every bridge window above the GPU. That fails when siblings pin the windows:
“the resize walks upwards in the PCI hierarchy and tries to free any bridge window it encounters … but any other
device under any of those bridge windows are not and they pin their bridge windows in-place” (a PCI developer)
[SOURCED https://ratatoskr.run/linux-pci/2026/03/14618943/t]. Kernel message when this happens:
bridge window [mem …64bit pref]: was not released (still contains assigned resources)[SOURCED same thread and https://ratatoskr.run/linux-pci/2026/04/8893590/t]. - Consequence for the anchor case: with
host_reset=1the tunnel is rebuilt by the kernel, so the RTX 5080 is a hot-added device and its BAR1 comes up at the power-on 256 MB regardless ofhpmmioprefsize=32G[INFERRED from §1 + §2; matches the reported symptom].
4. BAR0 reading 0xffffffff while config space works
- Config reads use configuration cycles; MMIO reads use memory cycles that must be forwarded by every bridge above the
device. A bridge whose COMMAND register has Memory Space Enable clear (
Mem-inlspci -vv) does not forward memory transactions; the read completes as an unsupported request and the CPU sees all-ones. So “config space answers but BAR0 reads 0xffffffff” is the textbook signature of an upstream bridge with Mem decode off, or of a BAR that is not within its parent window [INFERRED — PCI/PCIe architectural behaviour; consistent with the anchor case’s simultaneous “bridges had Mem decode OFF”].
5. Bus numbers are pre-reserved too
hpbussize=nn: “The minimum amount of additional bus numbers reserved for buses below a hotplug bridge. Default is 1” [SOURCED https://lkml.iu.edu/hypermail/linux/kernel/1910.2/07833.html (doc diff context)]. Bus numbers are reserved up front inpci_scan_child_bus_extend();pci_hp_add_bridge()fails if that pool is exhausted [SOURCED https://lobste.rs/s/p7iaq6/linux_evening_thunderbolt_musings (commentary; secondary)]. A TB5 hub + GPU enclosure + internal switch consumes several buses per tier.
Kernel Parameter Reference
Exact semantics from Documentation/admin-guide/kernel-parameters.txt and the source. “Effect here” = relevance to
the anchor symptom set; those judgements are [INFERRED] unless tagged otherwise.
| Parameter | Exact semantics (source) | Side effects / caveats | Effect on the anchor case |
|---|---|---|---|
pci=realloc / pci=realloc=on |
“Enable/disable reallocating PCI bridge resources if allocations done by BIOS are too small to accommodate resources required by all child devices. off: Turn realloc off; on: Turn realloc on” [SOURCED wording reproduced in https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/9.2_release_notes/kernel_parameters_changes and https://www.kernel.org/doc/html/v4.16/admin-guide/kernel-parameters.html; canonical file https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt]. Internally pci_realloc_enable states: “-1: undefined, will auto detect later / 0: disabled by user / 1: disabled by auto detect / 2: enabled by user / 3: enabled by auto detect”; auto-enables when unassigned SR-IOV resources exist [SOURCED setup-bus.c]. |
Releases BIOS bridge windows and re-runs the fitting loop (leaf first, then whole subtree). Can shrink or move windows the BIOS sized generously. Maintainer note: “Risks: May worsen resource fit or exhaust iomem space” [SOURCED https://ratatoskr.run/linux-pci/2026/03/14618943/t]. | Harmful when the goal is to keep BIOS-built ReBAR allocations: realloc lets the kernel undo the BIOS windows. Reports: “pci=realloc=on alone — no effect on populated bridge” [SOURCED LF forum]; “pci=realloc — no effect on pre-populated bridges” [SOURCED Sept-2026 thread]. |
pci=realloc=off |
“Do not reconfigure PCI device resources” / “off: Turn realloc off” [SOURCED same]. Sets pci_realloc_enable = 0 (disabled by user). |
Kernel keeps BIOS/firmware bridge windows and BARs for boot-present devices; hot-added devices are still assigned by the normal hotplug path. | Load-bearing with host_reset=0: keeps the POST-time (ReBAR-exercised) allocation intact. |
pci=hpmmioprefsize=nn[KMG] |
“The fixed amount of bus space which is reserved for hotplug bridge’s MMIO_PREF window. Default size is 2 megabytes.” Parsed as pci_hotplug_mmio_pref_size = memparse(...) [SOURCED https://lkml.iu.edu/hypermail/linux/kernel/1910.2/07833.html]. |
Applied to every hotplug bridge, including empty TB hub ports, so a big value multiplies: 32 G × 4 ports on one hub. Can cause the parent to fail to fit: “pci=hpmmioprefsize=32G — worsened situation; GPU disappeared entirely” [SOURCED https://ratatoskr.run/linux-pci/2026/09/17551273/t]; “cannot fit 0x1000000000 required for 0000:94:01.0 bridging” [SOURCED https://ratatoskr.run/linux-pci/2026/04/8893590/t]. |
Does not make a hot-added GPU’s BAR bigger (hot-add never resizes BARs, §1). Only useful if you later resize via sysfs/driver and need headroom — and even then siblings pin windows (§3). |
pci=hpmmiosize=nn[KMG] |
“The fixed amount of bus space which is reserved for hotplug bridge’s MMIO window. Default size is 2 megabytes.” [SOURCED same]. | Non-prefetchable 32-bit window; must fit below 4 GB with everything else. | Irrelevant to VRAM BARs (64-bit pref). A 512 M value is harmless but pointless for this symptom. |
pci=hpmemsize=nn[KMG] |
“reserved for hotplug bridge’s MMIO and MMIO_PREF window. Default size is 2 megabytes.” Sets both pci_hotplug_mmio_size and pci_hotplug_mmio_pref_size [SOURCED same]. |
Legacy knob kept “to prevent disruptions to existing users”. | Prefer the two specific knobs. |
pci=hpiosize=nn[KMG] |
“The fixed amount of bus space which is reserved for hotplug bridge’s IO window. Default size is 256 bytes.” [SOURCED same]. | I/O port space is 64 KB total; large values exhaust it. | GPUs need at most a small legacy VGA I/O BAR; not relevant to BAR0/BAR1. |
pci=hpbussize=nn |
“The minimum amount of additional bus numbers reserved for buses below a hotplug bridge. Default is 1” [SOURCED same]. | Too small → pci_hp_add_bridge() “No bus number available for hot-added bridge” [SOURCED https://bbs.archlinux.org/viewtopic.php?id=253050 title; lobste.rs commentary]. Too large → bus numbers (max 256/segment) run out for other root ports. |
Relevant for deep TB5 hub + enclosure + GPU-internal-switch topologies; a working Ubuntu 26.04 config used hpbussize=0x33 [SOURCED https://discourse.ubuntu.com/t/external-gpu-rtx-6000-pro-on-ubuntu-25-10-26-04/77130]. |
pci=assign-busses |
“Assign all PCI bus numbers ourselves, overriding whatever the firmware provided” [SOURCED https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html]. | Discards firmware bus numbering; reported “counterproductive” for a TB eGPU [SOURCED https://ratatoskr.run/linux-usb/2026/08/17378667/t]. | Anti-pattern here. |
pci=nocrs |
“[X86] Ignore PCI host bridge windows from ACPI” (_CRS) [SOURCED RH/kernel.org v4.16 wording above]. |
Kernel invents host-bridge windows instead of using ACPI; can overlap platform reserved ranges. Reported: “system failed to boot; NVMe probe returned -ENOMEM” [SOURCED https://ratatoskr.run/linux-pci/2026/09/17551273/t]; “sometimes broke booting entirely” [SOURCED Ubuntu discourse]. | Anti-pattern; last resort only when _CRS is provably wrong. |
pcie_ports=native |
“Use native PCIe services for PME, AER, and DPC, even if the platform doesn’t give the OS permission to use them. This may cause conflicts if the platform also tries to use these services.” compat: “Disable native PCIe services (PME, AER, DPC, PCIe hotplug).” dpc-native: “Use native PCIe service for DPC only.” [SOURCED https://www.spinics.net/lists/linux-pci/msg88329.html (doc diff)]; code: pcie_ports_native = true “use the PCIe services regardless of whether the platform has given us permission” [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/pcie/portdrv.c]. |
Forces Linux ownership of hotplug/AER/DPC when _OSC withholds it; may fight SMM/firmware-first handling. |
Relevant only if pciehp was not getting native control (check `dmesg |
pcie_port_pm=off |
“PCIe port power management handling: off — Disable power management of all PCIe ports, force — Forcibly enable power management of all PCIe ports”. Default allows D3 for ports on systems with BIOS date ≥ 2015 (pci_bridge_d3_possible()) [SOURCED https://github.com/torvalds/linux/commit/9d26d3a8f1b0c442339a235f9508bdad8af91043]. |
Keeps the TB root port / tunnel bridges in D0; avoids runtime-suspend/D3cold transitions of the bridges over the GPU. | Stability aid (link drops, Xid 79) rather than a BAR fix [INFERRED]. |
pcie_aspm=off |
Current doc: “off — Don’t touch ASPM configuration at all. Leave any configuration done by firmware unchanged.” The older text “Disable ASPM” was “wrong” [SOURCED https://patchew.org/linux/[email protected]/]. | It does not disable ASPM; it stops Linux from managing it. To actually force links out of L0s/L1 use pcie_aspm.policy=performance (used in working Ubuntu 26.04 configs) [SOURCED Ubuntu discourse; Titan Ridge thread: “pcie_aspm.policy=performance … necessary to avoid losing hotplug support”]. |
Likely inert for BAR assignment; may leave firmware ASPM on. If ASPM-induced link errors are suspected, pcie_aspm.policy=performance is the effective knob [INFERRED]. |
iommu=pt |
“pt: Use passthrough mode by default (Equivalent to iommu.passthrough=1)”; nopt the reverse [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/Documentation/admin-guide/kernel-parameters.txt]. |
Identity-maps DMA for trusted devices. Thunderbolt-attached devices are marked untrusted and Intel IOMMU keeps them in a translated DMA domain even under pt (the driver refuses identity mapping for untrusted devices) [INFERRED — from memory of device_def_domain_type() in drivers/iommu/intel/iommu.c; verify before relying]. Kernel DMA protection is exposed at /sys/bus/thunderbolt/devices/domainX/iommu_dma_protection [SOURCED thunderbolt.rst]. |
Probably no effect on the eGPU’s mappings or BARs; harmless. |
thunderbolt.host_reset=0 |
static bool host_reset = true; module_param(host_reset, bool, 0444); MODULE_PARM_DESC(host_reset, "reset USB4 host router (default: true)"); Passed as tb_domain_add(tb, host_reset) → tb->cm_ops->start(tb, reset) [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/nhi.c, …/domain.c]. See next section. |
Keeps firmware-built tunnels (and their BIOS-assigned windows/BARs) alive across driver probe. Works for cold-plug only: “if the eGPU is power-cycled at runtime, the new tunnel gets the 256 MB default” [SOURCED https://ratatoskr.run/linux-pci/2026/03/14618943/t]. | The load-bearing fix. |
thunderbolt.clx=0 |
module_param_named(clx, clx_enabled, bool, 0444); MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)"); — CL0s/CL1/CL2 link states; not enabled on pre-Alder-Lake Intel USB4; skipped for inter-domain and unbonded links [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/clx.c]. |
Keeps the USB4 link in CL0 (no lane low-power). | Stability/latency aid only; unrelated to PCI resource assignment [INFERRED]. |
thunderbolt.dyndbg=+p |
Standard dynamic-debug enable for the module (used with host_reset=false in field reports) [SOURCED https://lkml.iu.edu/hypermail/linux/kernel/2405.0/03998.html (search summary)]. |
Verbose tunnel/CM logging. | Diagnostic only. |
Thunderbolt host_reset and tunnel rebuild
What the driver does at probe
nhi_probe()callsnhi_reset(nhi): “Reset only v2 and later routers” (REG_CAPS_VERSION >= 2); if!host_resetit logs “skipping host router reset”; on timeout it warns “timeout resetting host router” [SOURCED nhi.c]. Origin: “USB4 v2 added a bit that can be used to reset the host router so the kernel uses this to trigger reset when the driver probes. This resets the already connected topology as well but doing this simplifies things a lot if the link is already set to asymmetric, and a module parameter was added to prevent this in case of problems” (Thunderbolt maintainer, June 2023) [SOURCED https://patches.linaro.org/project/linux-usb/patch/[email protected]/ (search summary)].tb_domain_add(tb, host_reset)→ software CMtb_start(tb, reset):
with the comment: “Boot firmware might have created tunnels of its own. Since we cannot be sure they are usable for us, tear them down and reset the ports to handle it as new hotplug for USB4 v1 routers (for USB4 v2 and beyond we already do host reset).” When not resetting,if (reset && tb_switch_is_usb4(tb->root_switch)) { discover = false; if (usb4_switch_version(tb->root_switch) == 1) tb_switch_reset(tb->root_switch); }tb_discover_tunnels(tb)adopts the firmware tunnels and marks their routerssw->boot = true[SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/tb.c].- Origin of the v1 tear-down: commit 59a54c5f3dbd “thunderbolt: Reset topology created by the boot firmware” (the commit author): “Boot firmware (typically BIOS) might have created tunnels of its own. The tunnel configuration that it does might be sub-optimal … In addition there is an issue on some AMD based systems where the BIOS does not allocate enough PCIe resources for future topology extension. By resetting the USB4 topology the PCIe links will be reset as well allowing Linux to re-allocate. This aligns the behavior with Windows Connection Manager.” [SOURCED https://github.com/torvalds/linux/commit/59a54c5f3dbd]. Follow-up “thunderbolt: Reset only non-USB4 host routers in resume” (2024-01-31, Fixes: 59a54c5f3dbd) limits the resume reset to Apple/Intel non-USB4 hosts [SOURCED https://git.zx2c4.com/linux-rng/commit/drivers/thunderbolt?id=8cf9926c537ce8b0c7783afebe752e084765d553].
What the reset does to an existing PCIe tunnel
- The PCIe tunnel is torn down; the PCIe link at the tunnelled downstream port drops;
pciehpsees a surprise removal and then a re-add; the GPU and enclosure bridges are re-created as hot-added devices and go through the hotplug sizing path of §Core-Concepts-1/2, with the GPU BAR at its power-on size and onlyhp*reservations for windows [INFERRED from the tb.c comment “handle it as new hotplug” + pciehp code]. Field reports confirm the shape: the regression thread “[REGRESSION] Thunderbolt Host Reset Change Causes eGPU Disconnection from 6.8.7=>6.8.8” — AMD RX 7600 “falls off PCIe bus entirely”, RTX 5060 crashes post-login; the maintainer’s reply: hot-removal expectations are inherent to the USB4 bus and “GPU driver readiness varies”; workaroundthunderbolt.host_reset=0[SOURCED https://ratatoskr.run/linux-usb/2026/08/17480231/t]. Note the thread lists affected kernels 6.18.45, 7.0.10, 7.1.6-7.1.9 — this is still the default behaviour in the 7.x series. - The ~1.4 s timing in the anchor case is the driver probe:
thunderboltloads early from the initramfs, resets the host router / root switch, and the tunnel is rebuilt by the software CM [INFERRED]. - On the Arrow Lake-P NUC the integrated TB4 host router is a USB4 v1 router, so the operative reset is the
tb_switch_reset()branch rather than the NHIREG_RESETbit; the8086:5786Barlow Ridge hub in the enclosure is v2 but it is not the host router [INFERRED — the NUC’s NHI CAPS version was not verified; either branch yields the same tunnel tear-down]. - Counter-example worth knowing: on hosts where firmware never builds a PCIe tunnel at POST (some Barlow Ridge TB5
ports, some Lunar Lake laptops),
host_reset=0does nothing because there is no BIOS allocation to preserve, and the host reset can be the only thing that ever assigns GPU BARs [SOURCED https://github.com/minisforum-docs/MS-02-Ultra/issues/32; https://ratatoskr.run/linux-pci/2026/09/17551273/t]. the maintainer’s framing: “Typically there is just certain amount of resources allocated for each PCIe root port that gets tunneled … most of the vendors don’t actually allow it to be changed” [SOURCED Sept-2026 thread].
Bridge COMMAND registers (Mem decode / Bus Master)
How the kernel normally sets them
- Drivers must call
pci_enable_device(): “Initialize device before it’s used by a driver. Ask low-level code to enable I/O and memory. Wake up the device if it was suspended.”pci_set_master()“Enables bus-mastering on the device and calls pcibios_set_master() to do the needed arch specific settings” — it “will enable DMA by setting the bus master bit in the PCI_COMMAND register” and “fixes the latency timer value if it’s set to something bogus by the BIOS” [SOURCED https://www.kernel.org/doc/html/latest/driver-api/pci/pci.html; https://www.kernel.org/doc/html/latest/PCI/pci.html]. - The COMMAND bits are actually written by
pci_enable_resources(): it iterates all resources (device BARs and bridge windows), and for each withr->parentset ORsPCI_COMMAND_IO/PCI_COMMAND_MEMORY, then logsenabling device (%04x -> %04x)and writesPCI_COMMAND. For device BARs (i < PCI_BRIDGE_RESOURCES) anIORESOURCE_UNSETor unclaimed (!r->parent) BAR aborts with “not assigned; can’t enable device” / “not claimed; can’t enable device” (-EINVAL). For bridge windows an unclaimed window is silently skipped — the Memory bit is simply not set [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/setup-res.c]. - Bridges are enabled lazily and recursively:
pci_enable_bridge()“Current we enable bridges after bus scan and assign resources … [instead] enable bridge as needed basis” — it recurses to the upstream bridge, thenpci_enable_device(dev)(logging “Error enabling bridge (%d), continuing” on failure) andpci_set_master(dev); called frompci_enable_device_flags()for the device’s upstream bridge (Yinghai Lu, 2013) [SOURCED https://lkml.iu.edu/hypermail/linux/kernel/1306.0/00031.html]. The PCIe port driver also enables each port itself:/* Enable PCI Express port device */ status = pci_enable_device(dev); … pci_set_master(dev);[SOURCED portdrv.c above].
Why bridges can come up with Mem decode / Bus Master cleared
Ranked by plausibility for the anchor case; all [INFERRED] from the sourced mechanics above unless noted:
- Bridge window not claimed/assigned. After re-enumeration, if a bridge’s prefetch window could not be fitted
(
bridge window … cannot fit,can't claim; no compatible bridge window,no space),r->parentis NULL andpci_enable_resources()never setsPCI_COMMAND_MEMORYon that bridge — while the base/limit registers may still hold stale (BIOS or kernel-provisional) values. MMIO through it returns all-ones. The anchor case’s odd 16.7 GB prefetch window on03:00.0alongside a 256 MB GPU BAR is consistent with a partially failed fit. - Bridge windows kept from BIOS but not claimable. With
host_reset=0 realloc=off, BIOS windows are left in the registers; ifpci_claim_resource()fails for one of them (parent window mismatch vs_CRS, or overlap), the kernel keeps the register values but refuses to enable decode.setpci COMMAND=0x0006:0x0006then “works” because the hardware windows are in fact correct and only the software gate was missing. This is the most likely reason the post-boltsetpciservice was still needed with the otherwise-correct configuration. - Enable-count already positive.
pci_enable_bridge()skipspci_enable_device()whenpci_is_enabled(dev)is already true and only re-asserts Bus Master if!dev->is_busmaster. If something cleared COMMAND after portdrv enabled the port (a secondary-bus reset does not clear it, but a power-cycle of the enclosure or D3cold of a bridge without a restore path can), nothing re-writes the Memory bit for that bridge.pcie_port_pm=offreduces this class. - Bolt authorization timing. With security level
user/secure, “the connected device must be authorized by the user before PCIe tunnels are created (e.g the PCIe device appears)” [SOURCED thunderbolt.rst]; the tunnel — and the whole enumeration — happens whenboltauthorizes, so a udev/systemd ordering afterboltis the right hook.
setpci mask syntax: each value is “either a hexadecimal number or an expression of type data:mask”; COMMAND is the
“word-sized command register” at offset 4 [SOURCED https://man7.org/linux/man-pages/man8/setpci.8.html].
COMMAND=0x0006:0x0006 sets bit1 (Memory Space Enable) and bit2 (Bus Master Enable) and leaves the rest untouched.
It is a symptomatic fix: it does not make an unassigned window valid; verify base/limit first (see Verification).
Resizable BAR
- Firmware side. POST enumeration on ReBAR/“Above 4G Decoding”-enabled firmware programs the large BAR; CSM must be
off and 64-bit MMIO above 4 GB enabled [SOURCED https://github.com/xCuri0/ReBarUEFI README and vendor guides —
secondary sources].
thunderbolt.host_reset=0“preserves the BIOS’s PCIe tunnel and BAR assignments from POST (where the BIOS does exercise ReBAR)” [SOURCED LF forum / linux-pci ReBAR thread]. - Kernel side.
pci_resize_resource(): “Reconfigure resno to size and re-run resource assignment algorithm with the new size”;pci_rebar_get_possible_sizes(): “Get the possible sizes of a resizable BAR as bitmask” [SOURCED driver-api/pci]. sysfs:/sys/bus/pci/devices/…/resourceN_resize— reading gives “a bitmap of available resource sizes” (bit0 = 1 MB, bit1 = 2 MB, … size = 2^(bit+20)); writing a bit number sets that size; “all PCI drivers must be unbound from the device” first; peers may need soft removal; VGA resize removes console drivers; success is not guaranteed [SOURCED https://raw.githubusercontent.com/torvalds/linux/master/Documentation/ABI/testing/sysfs-bus-pci]. Example from a working Ubuntu 26.04 setup:echo 17 > …/resource1_resize(2^(17+20) = 128 GB) [SOURCED Ubuntu discourse]. - Driver side. amdgpu resizes at probe and logs “Not enough PCI address space for a large BAR” / “Problem resizing
BAR0 (-16)” on failure [SOURCED ReBAR thread; amdgpu regression thread]; NVIDIA open driver needs
options nvidia NVreg_EnableResizableBar=1[SOURCED Ubuntu discourse]. - Why it fails over Thunderbolt. Hot-add never consults ReBAR (§1); a later resize must release every window up to
the root port, and empty sibling hotplug ports pin them (“was not released (still contains assigned resources)”).
Documented workaround: “remove those sibling devices first, and then attempt the resize through sysfs and rescan”
(a PCI developer) — i.e.
echo 1 > /sys/bus/pci/devices/<empty-port>/removefor each empty downstream port, then writeresourceN_resize, thenecho 1 > /sys/bus/pci/rescan[SOURCED https://ratatoskr.run/linux-pci/2026/04/8893590/t; https://ratatoskr.run/linux-pci/2026/03/14618943/t]. Manualsetpcirewrites of type-1 base/limit registers (0x24/0x28/0x2c) have also been used to consolidate windows before resizing, at the cost of CMOS resets when it goes wrong [SOURCED Ubuntu discourse]. - Upstream status (2026-09). A PCI developer: “I am already looking into resizable BAR aware resource fitting, hopefully we’ll get there in this year … It’s a very complex change due to fallbacks that have to be put into place” [SOURCED ReBAR thread]. A competing “PCI: Reserve prefetchable window for ReBAR / demote small BARs” patch (Geramy Loveless, 2026-08-28) drew objections that ReBAR register values “often doesn’t reflect the actual needed space but rather the maximum the HW address logic can resolve” (Christian König) and that a “naive approach like this will surely” break without fallbacks (a PCI developer) [SOURCED https://ratatoskr.run/lkml/2026/08/17476778/t]. The 2020 “movable BARs” series (Sergei Miroshnichenko, v9) never landed [SOURCED Sept-2026 thread].
Changes in kernels 6.x-7.x that matter here
| Version | Change | Source |
|---|---|---|
| 5.5 | pci=hpmmiosize / pci=hpmmioprefsize split out of hpmemsize (Nicholas Johnson) |
[SOURCED https://lkml.iu.edu/hypermail/linux/kernel/1910.2/07833.html] |
| 6.6 (posted 2023-06) | USB4 v2 NHI host-router reset at probe + thunderbolt.host_reset param (Thunderbolt maintainer) |
[SOURCED linaro patchwork summary] |
| 6.9 / backported to 6.8.8 | tb_start() tears down firmware tunnels on USB4 v1 hosts (59a54c5f3dbd) — the cause of the 6.8.7→6.8.8 eGPU regression reports; host_reset=0 is the documented workaround |
[SOURCED github commit; regression thread] |
| 6.9 | Resume-path reset limited to non-USB4 hosts (8cf9926c537c) | [SOURCED zx2c4 mirror] |
| 6.18 (series posted 2025-08) | “PCI: Bridge window selection improvements” — unified pbus_select_window(), preserves window type flags, common pci_enable_resources() |
[SOURCED https://lwn.net/Articles/1034828/] — merge version [INFERRED] |
| 6.18.20 / 7.0-rc5 | Regression from “PCI: Stop over-estimating bridge window size” (3958bf16): amdgpu “Problem resizing BAR0 (-16)” over TB; fixed by “PCI: Prevent shrinking bridge window from its required size” (dc4b4d04) in 7.1-rc1 | [SOURCED https://ratatoskr.run/linux-pci/2026/03/7140240/t] |
| 7.1 (pull 2026-04-15) | “Avoid shrinking bridge windows to fit in the initial Root Port window; fixes one problem with devices with large BARs connected via switches, e.g., Thunderbolt”; place small resources before large; pass full free extent to resource_alignf; alignment fix for windows >1 MB |
[SOURCED https://ratatoskr.run/lkml/2026/04/3515371/t] |
| 7.3 (applied 2026-07-22) | “PCI: Do not add hotplug reservation for intermediate bridges”: size = max(size + children_add_size, min_size) so nested TB topologies stop multiplying the hp* reservation at every tier |
[SOURCED https://ratatoskr.run/lkml/2026/07/17285367/t] |
| pending | ReBAR-aware resource fitting (a PCI developer); “don’t assign bridge windows that have no real resources underneath” post-pass | [SOURCED ReBAR thread] |
Implication for the anchor box on 7.0.0-34: it has the 6.18-era window-selection rework but not the 7.1 “avoid shrinking” fix nor the 7.3 nested-reservation fix; both are directly about large BARs behind Thunderbolt switches. Re-testing on ≥7.1 (and ideally 7.3) before adding more workarounds is justified [INFERRED].
Verification checklist
Run as root. Replace BB:DD.F with the bridge/GPU BDF; lspci -t shows the chain root port → host-side TB bridge →
hub upstream → hub downstream → enclosure switch → GPU.
- Topology and driver ownership
lspci -tv— “Show a tree-like diagram containing all buses, bridges, devices” [SOURCED lspci(8)].dmesg | grep -iE 'thunderbolt|tb_|connection manager'— expect “using software connection manager” or “using firmware connection manager” (Titan-Ridge-class hosts use firmware CM: “it’s not the TB driver that creates the tunnels”) [SOURCED https://ratatoskr.run/linux-usb/2026/08/17378667/t]; withhost_reset=0expect “skipping host router reset” (v2 hosts) [SOURCED nhi.c].cat /sys/bus/thunderbolt/devices/*/authorizedandboltctl list— device must be authorized before the PCIe tunnel exists [SOURCED thunderbolt.rst].cat /sys/bus/thunderbolt/devices/domain0/iommu_dma_protection[SOURCED thunderbolt.rst].
- Resource assignment
dmesg | grep -E "BAR [0-9]+|bridge window|can't (assign|claim)|no space|failed to assign|not claimed|cannot fit|was not released"Exact strings:%s %pR: assigned,can't assign; no space,can't assign; bogus alignment,failed to assign,can't claim; no address assigned,can't claim; no compatible bridge window,can't claim; address conflict with …,not assigned; can't enable device,not claimed; can't enable device,enabling device (0000 -> 0002)[SOURCED setup-res.c];bridge window … cannot fit …,… was not released (still contains assigned resources)[SOURCED linux-pci threads].cat /proc/iomem— “Memory map” of physical ranges; confirm the GPU’s BARs are nested inside every bridge window above them and the root port window [SOURCED https://www.kernel.org/doc/html/latest/filesystems/proc.html].lspci -vvs <GPU>—Region 1: Memory at … (64-bit, prefetchable) [size=16G];[virtual]or[disabled]markers indicate unassigned/undecoded regions [INFERRED — lspci output conventions, not quoted from the man page].cat /sys/bus/pci/devices/0000:<GPU>/resource1_resize— bitmap of supported sizes [SOURCED sysfs-bus-pci].
- COMMAND registers
setpci -s BB:DD.F COMMANDfor the GPU and each bridge; want bit1 (0x2 Memory) and bit2 (0x4 Bus Master) set.lspci -vvs BB:DD.F | grep -E 'Control:|Memory behind|Prefetchable memory behind'—Mem+ BusMaster+and sane base/limit ranges [SOURCED setpci(8), lspci(8) for option semantics; field names INFERRED].- Before forcing bits: confirm
Prefetchable memory behind bridge:on each bridge encloses the GPU BAR; if it is[disabled]/empty, forcingMem+will not help.
- Link health —
lspci -vvs <GPU> | grep -E 'LnkSta|LnkCap'; USB4 v1 caps at 16 GT/s x4 (~3.8 GB/s observed) [SOURCED Sept-2026 thread; Ubuntu discourse]; AER storms at boot are a host-reset side effect on some platforms [SOURCED MS-02-Ultra issue].
Decision guide for this symptom set
Symptoms: eGPU enumerates; BAR reads 0xffffffff; bridges Mem-; BAR1 256 MB; possibly large odd prefetch window.
- Does firmware build a PCIe tunnel at POST? (
host_reset=0boot → is the GPU present inlspcibeforeboltauthorizes anything, with a large BAR?)- Yes → use
thunderbolt.host_reset=0 pci=realloc=off. Do not addhpmmioprefsize/realloc: they only apply to kernel-built windows and can make the fit worse. Accept that runtime replug will fall back to 256 MB (that is the documented limitation) [SOURCED ReBAR thread]. - No (no tunnel/BARs until the kernel builds them) →
host_reset=0is inert; you are in the hot-add path. Options: (a) resize via sysfs after removing empty sibling ports, then rescan [SOURCED Ilpo]; (b) modesthpmmioprefsize(e.g. 2-4× the target BAR, not 32 G) plushpbussizesized to the topology; (c) upgrade to ≥7.1 / 7.3 for the window-fitting fixes [SOURCED 7.1 pull; 7.3 patch].
- Yes → use
- Bridges
Mem-although windows look right → checkdmesgforcan't claim/not claimedon those bridges. If the base/limit values enclose the GPU BAR,setpci -s <bridge> COMMAND=0x0006:0x0006afterboltauthorization and beforemodprobe nvidiais a legitimate stop-gap; file thecan't claimline upstream (linux-pci) because it is a fitting bug, not a hardware fault [INFERRED]. - Bridges
Mem-and windows[disabled]/absent → the fit failed;setpcion COMMAND cannot help. Go back to step 1 or remove sibling ports and rescan. - Stability knobs (
pcie_port_pm=off,thunderbolt.clx=0,pcie_aspm.policy=performance, Link Control 2 “auto speed disable”setpci -s <TB bridge> CAP_EXP+30.w=0020:0020) address link drops/Xid 79 after the GPU works; they are not part of the BAR fix [SOURCED Ubuntu discourse for the LnkCtl2 trick; rest INFERRED]. - Keep
pcie_ports=nativeonly ifdmesgshows_OSCdenying native hotplug/AER control and hotplug events were being missed; otherwise drop it to avoid firmware conflicts [SOURCED kernel-parameters wording].
Minimal recommended command line for the anchor box, derived from the above:
thunderbolt.host_reset=0 pci=realloc=off (+ pcie_port_pm=off if link drops recur) — then prove or disprove the
need for each extra knob one at a time.
Anti-patterns
pci=hpmmioprefsize=32Gon a multi-port TB5 hub. Multiplies per empty port, starves the populated port, and can make the GPU vanish (“can’t claim; no compatible bridge window”) [SOURCED Sept-2026 thread; LF forum].pci=realloctogether withthunderbolt.host_reset=0. Contradictory: one preserves BIOS windows, the other invites the kernel to release and refit them.pci=nocrs. Broke boot (NVMe -ENOMEM) in a 2026 Lunar Lake report [SOURCED Sept-2026 thread].pci=assign-busses. “Counterproductive” for TB eGPUs [SOURCED Titan Ridge thread].- Believing
pcie_aspm=offdisables ASPM. It leaves firmware ASPM untouched [SOURCED Bjorn Helgaas 2024 doc fix]. - Forcing
COMMAND=0x0006on a bridge whose window is unassigned. Enables decode of a bogus range; can hang the bus. Always check base/limit first. - Expecting
host_reset=0to survive runtime replug/power-cycle. New tunnel → hot-add path → 256 MB [SOURCED ReBAR thread]. - Writing
resourceN_resizewith the driver bound. ABI requires all drivers unbound [SOURCED sysfs-bus-pci]. - “BIOS Assist Mode.” A Thunderbolt maintainer: “a workaround for early Windows systems … should not be used in any modern systems” [SOURCED Sept-2026 thread].
Sources
Primary (kernel tree / kernel.org)
- https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html — pci=, pcie_ports= (partial fetch)
- https://raw.githubusercontent.com/torvalds/linux/master/Documentation/admin-guide/kernel-parameters.txt — iommu=pt/nopt
- https://www.kernel.org/doc/html/latest/admin-guide/thunderbolt.html — CM types, authorization, DMA protection
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/nhi.c — host_reset, nhi_reset()
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/domain.c — tb_domain_add(tb, reset)
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/tb.c — tb_start() reset/discover
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/thunderbolt/clx.c — clx param
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/setup-bus.c — hotplug sizing, realloc, release loop
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/pci.c — DEFAULT_HOTPLUG_* defines
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/setup-res.c — pci_enable_resources(), dmesg strings
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/pcie/portdrv.c — port enable, pcie_ports= parsing
- https://raw.githubusercontent.com/torvalds/linux/master/drivers/pci/hotplug/pciehp_pci.c — pciehp_configure_device()
- https://raw.githubusercontent.com/torvalds/linux/master/Documentation/ABI/testing/sysfs-bus-pci — resourceN_resize
- https://www.kernel.org/doc/html/latest/driver-api/pci/pci.html and https://www.kernel.org/doc/html/latest/PCI/pci.html — pci_enable_device/pci_set_master/pci_resize_resource
- https://www.kernel.org/doc/html/latest/filesystems/proc.html — /proc/iomem
- https://github.com/torvalds/linux/commit/59a54c5f3dbd — Reset topology created by the boot firmware
- https://git.zx2c4.com/linux-rng/commit/drivers/thunderbolt?id=8cf9926c537ce8b0c7783afebe752e084765d553 — resume reset fix
- https://github.com/torvalds/linux/commit/9d26d3a8f1b0c442339a235f9508bdad8af91043 — pcie_port_pm=, pci_bridge_d3_possible()
- https://patches.linaro.org/project/linux-usb/patch/[email protected]/ — USB4 v2 host reset (summary only; page not fetched)
LKML / linux-pci / linux-usb (2019-2026)
- https://lkml.iu.edu/hypermail/linux/kernel/1910.2/07833.html — hpmmiosize/hpmmioprefsize doc + pci_setup() diff
- https://lkml.iu.edu/hypermail/linux/kernel/1306.0/00031.html — pci_enable_bridge() origin
- https://www.spinics.net/lists/linux-pci/msg88329.html — pcie_ports= doc text (dpc-native patch)
- https://patchew.org/linux/[email protected]/ — pcie_aspm=off semantics
- https://ratatoskr.run/linux-pci/2026/03/14618943/t — “ReBAR over Thunderbolt” (Mar 2026; a PCI developer replies)
- https://ratatoskr.run/linux-pci/2026/09/17551273/t — “Resizable BAR never considered for hot-added USB4 GPU” (Sept 2026; a Thunderbolt maintainer reply)
- https://ratatoskr.run/linux-usb/2026/08/17480231/t — “[REGRESSION] Thunderbolt Host Reset Change …” (Aug 2026)
- https://ratatoskr.run/linux-pci/2026/04/8893590/t — “USB4v2 BAR resizing problems” (Apr 2026)
- https://ratatoskr.run/linux-pci/2026/03/7140240/t — amdgpu TB regression from “Stop over-estimating bridge window size”
- https://ratatoskr.run/linux-usb/2026/08/17378667/t — Titan Ridge firmware-CM tunnel failure (assign-busses, aspm policy)
- https://ratatoskr.run/lkml/2026/04/3515371/t — [GIT PULL] PCI changes for v7.1
- https://ratatoskr.run/lkml/2026/07/17285367/t — “Do not add hotplug reservation for intermediate bridges” (v7.3)
- https://ratatoskr.run/lkml/2026/08/17476778/t — “Reserve prefetchable window for ReBAR” RFC discussion
- https://lwn.net/Articles/1034828/ — “PCI: Bridge window selection improvements”
Community / vendor
- https://forum.linuxfoundation.org/discussion/870568/make-the-linux-kernel-rebar-over-thunderbolt-friendly — Framework 13 + Core X V2 + RTX 5060 Ti, 6.17
- https://discourse.ubuntu.com/t/external-gpu-rtx-6000-pro-on-ubuntu-25-10-26-04/77130 — Ubuntu 26.04, host_reset=0, hpbussize, resize, LnkCtl2 trick
- https://github.com/minisforum-docs/MS-02-Ultra/issues/32 — Arrow Lake + Barlow Ridge: host reset is the only BAR-assigning event
- https://forum.level1techs.com/t/thunderbolt-egpu-on-linux-cant-allocate-bar-space/200785 — “BAR 15: no space for” examples
- https://lobste.rs/s/p7iaq6/linux_evening_thunderbolt_musings — hpbussize commentary
- https://egpu.io/forums/thunderbolt-linux-setup/state-of-egpu-hot-plug-for-nvidia/ — egpu.io (listed by search; body not fetched)
- https://man7.org/linux/man-pages/man8/setpci.8.html, https://man7.org/linux/man-pages/man8/lspci.8.html
- https://github.com/xCuri0/ReBarUEFI — Above-4G / 64-bit BAR firmware notes (secondary)