AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/25 02:50 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "CXL_BUS",
    "CXL_PCI",
    "CXL_REGION",
    "HOTPLUG_PCI_PCIE",
    "PCIE_DPC",
    "MEMORY_HOTPLUG"
  ],
  "FocusSymbols": [
    "cxl_offline_memory",
    "cxl_port_recommit_decoders",
    "cxl_port_save_hdm_state",
    "cxl_port_put_hdm_state",
    "cxl_enable_hdm",
    "cxl_set_mem_enable",
    "cxl_region_invalidate_memregion",
    "__pci_bridge_secondary_bus_reset",
    "pci_bridge_secondary_bus_reset",
    "pci_dpc_containment_active"
  ],
  "KMSANReasoning": "The patch introduces logic to handle Secondary Bus Reset (SBR) for CXL Downstream Ports, which involves saving and restoring hardware registers, offlining memory blocks, and unbinding drivers. All newly allocated data structures (`hdm_state`, `state`) are allocated using zero-initializing functions (`kzalloc_obj`, `kzalloc_flex`). Local variables (`ctx`, `regions`) are explicitly initialized. No new complex structures are exposed to user space. The only user-space interface added is a sysfs attribute (`cxl_unmask_sbr`) which reads/writes a single bitfield. There is no risk of uninitialized memory being used in control flow or leaked to user space. Standard KASAN and LOCKDEP are sufficient to detect any potential memory corruption, out-of-bounds accesses, or locking issues.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch implements Secondary Bus Reset (SBR) support for CXL Downstream Ports. It introduces a new sysfs attribute `cxl_unmask_sbr` to allow unmasking SBR, and implements logic to disable/enable CXL regions during SBR. It also handles Downstream Port Containment (DPC) by unbinding regions instead of offlining them. The changes modify core PCI reset logic and CXL region management, which are reachable via sysfs and PCI error recovery in virtualized environments like QEMU.",
  "WorthFuzzing": true
}

1/1 2026/08/25 02:50 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 106959a31a9fda18580851af5ce38f8d304e1fa9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Aug 25 02:50:40 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci\nindex b767db2c52cb7..7b0314da6f4d6 100644\n--- a/Documentation/ABI/testing/sysfs-bus-pci\n+++ b/Documentation/ABI/testing/sysfs-bus-pci\n@@ -174,6 +174,22 @@ Description:\n \t\tsimiliar to writing 1 to their individual \"reset\" file, so use\n \t\twith caution.\n \n+What:\t\t/sys/bus/pci/devices/.../cxl_unmask_sbr\n+Date:\t\tAugust 2026\n+Contact:\tlinux-pci@vger.kernel.org\n+Description:\n+\t\tThis is visible only for a CXL Downstream Port, that is a Root\n+\t\tPort or a Downstream Switch Port that publishes the CXL\n+\t\tExtensions DVSEC for Ports.  Such a Port ignores the Secondary\n+\t\tBus Reset bit in its Bridge Control register unless its\n+\t\t\"Unmask SBR\" bit is set, and system firmware leaves that bit\n+\t\tclear by default.\n+\n+\t\tWriting 1 allows the kernel to set \"Unmask SBR\" while it\n+\t\tresets the Port's secondary bus, so that the reset reaches the\n+\t\tcomponent below the Port.  While this reads 0, a bus reset of\n+\t\tthe Port fails instead.\n+\n What:\t\t/sys/bus/pci/devices/.../vpd\n Date:\t\tFebruary 2008\n Contact:\tBen Hutchings \u003cbwh@kernel.org\u003e\ndiff --git a/drivers/base/memory.c b/drivers/base/memory.c\nindex bcfe2d9f4adbd..ce89b251d34ac 100644\n--- a/drivers/base/memory.c\n+++ b/drivers/base/memory.c\n@@ -1018,6 +1018,42 @@ int walk_memory_blocks(unsigned long start, unsigned long size,\n \treturn ret;\n }\n \n+static int cxl_offline_memory_block(struct memory_block *mem, void *arg)\n+{\n+\tint rc = device_offline(\u0026mem-\u003edev);\n+\n+\t/* device_offline() returns a positive value when already offline. */\n+\tif (rc \u003e 0)\n+\t\treturn 0;\n+\n+\treturn rc;\n+}\n+\n+/**\n+ * cxl_offline_memory - offline the memory blocks spanning a physical range\n+ * @start: start of the range, memory-block aligned\n+ * @size: size of the range, a multiple of the memory block size\n+ *\n+ * Offline every memory block in [start, start + size). The blocks are offlined\n+ * but not removed, so the range can be brought back online afterward. The\n+ * caller owns the range and this performs no validation on it.\n+ *\n+ * Return: 0 on success, negative errno if a block cannot be offlined.\n+ *\n+ * Context: process context. Sleeps and takes the memory hotplug lock.\n+ */\n+int cxl_offline_memory(u64 start, u64 size)\n+{\n+\tint rc;\n+\n+\tlock_device_hotplug();\n+\trc = walk_memory_blocks(start, size, NULL, cxl_offline_memory_block);\n+\tunlock_device_hotplug();\n+\n+\treturn rc;\n+}\n+EXPORT_SYMBOL_NS_GPL(cxl_offline_memory, \"CXL_MHP\");\n+\n struct for_each_memory_block_cb_data {\n \twalk_memory_blocks_func_t func;\n \tvoid *arg;\ndiff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile\nindex ce7213818d3c0..4d4538182d81b 100644\n--- a/drivers/cxl/core/Makefile\n+++ b/drivers/cxl/core/Makefile\n@@ -15,7 +15,7 @@ cxl_core-y += hdm.o\n cxl_core-y += pmu.o\n cxl_core-y += cdat.o\n cxl_core-$(CONFIG_TRACING) += trace.o\n-cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o\n+cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o dport_sbr.o\n cxl_core-$(CONFIG_CXL_MCE) += mce.o\n cxl_core-$(CONFIG_CXL_FEATURES) += features.o\n cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o\ndiff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h\nindex 07555ae638594..b250fa3461847 100644\n--- a/drivers/cxl/core/core.h\n+++ b/drivers/cxl/core/core.h\n@@ -13,6 +13,9 @@ extern const struct device_type cxl_pmu_type;\n \n extern struct attribute_group cxl_base_attribute_group;\n \n+struct cxl_port *find_cxl_port(struct device *dport_dev,\n+\t\t\t       struct cxl_dport **dport);\n+\n enum cxl_detach_mode {\n \tDETACH_ONLY,\n \tDETACH_INVALIDATE,\n@@ -53,6 +56,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,\n int devm_cxl_add_dax_region(struct cxl_region *cxlr);\n int devm_cxl_add_pmem_region(struct cxl_region *cxlr);\n void kill_regions(struct cxl_root_decoder *cxlrd);\n+int cxl_region_invalidate_memregion(struct cxl_region *cxlr);\n+struct pci_cxl_sbr_region_ops;\n+extern const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops;\n \n #else\n static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,\n@@ -213,6 +219,28 @@ int cxl_gpf_port_setup(struct cxl_dport *dport);\n struct cxl_hdm;\n int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,\n \t\t\tstruct cxl_endpoint_dvsec_info *info);\n+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl);\n+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val);\n+/**\n+ * struct cxl_hdm_state - one CXL port's HDM decoder programming, saved\n+ * @global_ctrl: CXL HDM Decoder Global Control\n+ * @nr_ctrl: number of entries in @ctrl\n+ * @ctrl: CXL HDM Decoder n Control, indexed by decoder id\n+ *\n+ * Holds the fields of those two registers that the driver does not model, read\n+ * before a reset and written back after it. Instances are held in an xarray\n+ * keyed by the \u0026struct cxl_port they were read from.\n+ */\n+struct cxl_hdm_state {\n+\tu32 global_ctrl;\n+\tint nr_ctrl;\n+\tu32 ctrl[];\n+};\n+\n+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state);\n+void cxl_port_put_hdm_state(struct xarray *hdm_state);\n+int cxl_port_recommit_decoders(struct cxl_port *port,\n+\t\t\t       struct xarray *hdm_state);\n int cxl_port_get_possible_dports(struct cxl_port *port);\n \n #ifdef CONFIG_CXL_FEATURES\ndiff --git a/drivers/cxl/core/dport_sbr.c b/drivers/cxl/core/dport_sbr.c\nnew file mode 100644\nindex 0000000000000..823b63012f45b\n--- /dev/null\n+++ b/drivers/cxl/core/dport_sbr.c\n@@ -0,0 +1,374 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/* Copyright(c) 2026 Intel Corporation. */\n+\n+#include \u003clinux/memregion.h\u003e\n+#include \u003clinux/memory_hotplug.h\u003e\n+#include \u003clinux/memory.h\u003e\n+#include \u003clinux/device.h\u003e\n+#include \u003clinux/pci.h\u003e\n+#include \u003ccxl.h\u003e\n+#include \"core.h\"\n+\n+/*\n+ * cxl_region_unbind - take a region out of service ahead of a reset\n+ * @cxlr: region routed through the CXL Downstream Port being reset\n+ *\n+ * Unbind the region driver, which tears down everything built on the region:\n+ * the dax region device, its dax device and the driver bound to it. An SBR\n+ * zeroes the downstream bus number, so a region left bound would decode to a\n+ * device in reset.\n+ *\n+ * The memory the region hosts is left as it is. A caller that reaches a live\n+ * device offlines it first; see cxl_region_disable().\n+ *\n+ * Context: process context. Driver unbind sleeps, so this cannot run in atomic\n+ * context.\n+ */\n+static void cxl_region_unbind(struct cxl_region *cxlr)\n+{\n+\tstruct cxl_region_params *p = \u0026cxlr-\u003eparams;\n+\n+\tdevice_release_driver(\u0026cxlr-\u003edev);\n+\tdev_dbg(\u0026cxlr-\u003edev, \"%s: region unbound before reset, HPA %pr\\n\",\n+\t\t__func__, p-\u003eres);\n+}\n+\n+/*\n+ * cxl_region_disable - make a region inactive ahead of a Secondary Bus Reset\n+ * @cxlr: region routed through the CXL Downstream Port being reset\n+ *\n+ * Offline the memory blocks the region owns and unbind its driver. An SBR\n+ * zeroes the downstream bus number, so a region left live as System RAM would\n+ * be accessed while the device is in reset. On offline failure return the error\n+ * so the caller aborts the reset; the memory is never force-removed.\n+ *\n+ * Context: process context. Offlining and driver unbind sleep and take the\n+ * memory hotplug lock, so this cannot run in atomic context.\n+ */\n+static int cxl_region_disable(struct cxl_region *cxlr)\n+{\n+\tstruct cxl_region_params *p = \u0026cxlr-\u003eparams;\n+\tunsigned long block_size;\n+\tu64 start, end;\n+\tint rc;\n+\n+\t/*\n+\t * Per CXL r4.0 sec 9.13.1 an Interleave Set has a Base HPA and a Size\n+\t * that are multiples of 256 MB, while a memory block spans up to 2 GB.\n+\t * A block overlapping either end of the range therefore also covers\n+\t * memory outside this region, so round the range inward to block\n+\t * granularity as dax_kmem did when it onlined the range. Offlining a\n+\t * straddling block would migrate pages that the reset does not affect.\n+\t */\n+\tblock_size = memory_block_size_bytes();\n+\tstart = ALIGN(p-\u003eres-\u003estart, block_size);\n+\tend = ALIGN_DOWN(p-\u003eres-\u003eend + 1, block_size);\n+\tif (start \u003e= end) {\n+\t\tdev_dbg(\u0026cxlr-\u003edev, \"%s: HPA %pr spans no whole memory block, no System RAM to offline\\n\",\n+\t\t\t__func__, p-\u003eres);\n+\t} else {\n+\t\trc = cxl_offline_memory(start, end - start);\n+\t\tif (rc) {\n+\t\t\tdev_warn(\u0026cxlr-\u003edev, \"offline System RAM failed before reset: %d\\n\",\n+\t\t\t\t rc);\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\trc = cxl_region_invalidate_memregion(cxlr);\n+\tif (rc) {\n+\t\tdev_warn(\u0026cxlr-\u003edev, \"CPU cache invalidate failed before reset: %d\\n\",\n+\t\t\t rc);\n+\t\treturn rc;\n+\t}\n+\n+\tcxl_region_unbind(cxlr);\n+\tdev_dbg(\u0026cxlr-\u003edev, \"%s: System RAM offline, region disabled before reset, HPA %pr\\n\",\n+\t\t__func__, p-\u003eres);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * cxl_region_enable - restore a region after a Secondary Bus Reset\n+ * @cxlr: region disabled by cxl_region_disable() before the reset\n+ *\n+ * Rebind the region driver. The System RAM is left offline; bringing it back\n+ * online is a separate administrative step.\n+ */\n+static void cxl_region_enable(struct cxl_region *cxlr)\n+{\n+\tstruct cxl_region_params *p = \u0026cxlr-\u003eparams;\n+\n+\tif (device_attach(\u0026cxlr-\u003edev) \u003c 0) {\n+\t\tdev_dbg(\u0026cxlr-\u003edev, \"driver re-attach failed after reset\\n\");\n+\t\treturn;\n+\t}\n+\n+\tdev_dbg(\u0026cxlr-\u003edev, \"%s: region re-enabled after reset, HPA %pr, IW %d, IG %d\\n\",\n+\t\t__func__, p-\u003eres, p-\u003einterleave_ways, p-\u003einterleave_granularity);\n+}\n+\n+/*\n+ * Collect the regions with a member endpoint routed through @dport_pci, the\n+ * CXL Downstream Port about to be reset. cxl_rwsem.region keeps the topology\n+ * stable for the duration of the walk only. Each collected region is pinned\n+ * with get_device() so the object survives after the lock is dropped, since\n+ * cxl_region_disable()/cxl_region_enable() run with the rwsem released (they\n+ * unbind and rebind the region driver). Hence snapshot the set first.\n+ */\n+static int cxl_sbr_collect_regions(struct pci_dev *dport_pci,\n+\t\t\t\t   struct xarray *regions)\n+{\n+\tstruct cxl_region_ref *cxl_rr;\n+\tstruct cxl_dport *dport;\n+\tunsigned long index;\n+\tint count = 0;\n+\tint rc;\n+\n+\tstruct cxl_port *port __free(put_cxl_port) =\n+\t\tfind_cxl_port(\u0026dport_pci-\u003edev, \u0026dport);\n+\tif (!port) {\n+\t\tpci_dbg(dport_pci, \"no CXL port found for reset dport\\n\");\n+\t\treturn 0;\n+\t}\n+\n+\tguard(rwsem_read)(\u0026cxl_rwsem.region);\n+\txa_for_each(\u0026port-\u003eregions, index, cxl_rr) {\n+\t\tstruct cxl_region *cxlr = cxl_rr-\u003eregion;\n+\t\tstruct cxl_ep *ep;\n+\t\tunsigned long ep_index;\n+\n+\t\t/* Skip unless a region endpoint sits below the reset dport. */\n+\t\txa_for_each(\u0026cxl_rr-\u003eendpoints, ep_index, ep)\n+\t\t\tif (ep-\u003edport == dport)\n+\t\t\t\tbreak;\n+\t\tif (!ep) {\n+\t\t\tdev_dbg(\u0026cxlr-\u003edev, \"%s: no endpoint below %s, region excluded\\n\",\n+\t\t\t\t__func__, dev_name(dport-\u003edport_dev));\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tget_device(\u0026cxlr-\u003edev);\n+\t\trc = xa_insert(regions, (unsigned long)cxlr, cxlr, GFP_KERNEL);\n+\t\tif (rc) {\n+\t\t\tput_device(\u0026cxlr-\u003edev);\n+\t\t\treturn rc;\n+\t\t}\n+\t\tdev_dbg(\u0026cxlr-\u003edev, \"%s: endpoint below %s, region collected\\n\",\n+\t\t\t__func__, dev_name(dport-\u003edport_dev));\n+\t\tcount++;\n+\t}\n+\n+\tdev_dbg(\u0026port-\u003edev, \"%d region(s) routed through %s\\n\", count,\n+\t\tdev_name(dport-\u003edport_dev));\n+\treturn 0;\n+}\n+\n+static void cxl_sbr_put_regions(struct xarray *regions)\n+{\n+\tstruct cxl_region *cxlr;\n+\tunsigned long index;\n+\n+\txa_for_each(regions, index, cxlr)\n+\t\tput_device(\u0026cxlr-\u003edev);\n+\txa_destroy(regions);\n+}\n+\n+/*\n+ * The reset cleared the HDM Decoder registers of every CXL component below\n+ * @dport_pci, so restore them from the settings the driver holds and from\n+ * @hdm_state, the register fields the driver does not model, saved before the\n+ * reset. Takes cxl_rwsem.region for read, which cxl_port_recommit_decoders()\n+ * requires. The caller has already disabled the regions, so nothing reaches the\n+ * decoders being reprogrammed.\n+ */\n+static void cxl_sbr_recommit_decoders(struct pci_dev *dport_pci,\n+\t\t\t\t      struct xarray *hdm_state)\n+{\n+\tstruct cxl_dport *dport;\n+\tint rc;\n+\n+\tstruct cxl_port *port __free(put_cxl_port) =\n+\t\tfind_cxl_port(\u0026dport_pci-\u003edev, \u0026dport);\n+\tif (!port) {\n+\t\tpci_dbg(dport_pci, \"no CXL port owns this Downstream Port\\n\");\n+\t\treturn;\n+\t}\n+\n+\tpci_dbg(dport_pci, \"restoring HDM decode below %s\\n\", dev_name(\u0026port-\u003edev));\n+\n+\tguard(rwsem_read)(\u0026cxl_rwsem.region);\n+\trc = cxl_port_recommit_decoders(port, hdm_state);\n+\tif (rc)\n+\t\tpci_warn(dport_pci, \"HDM decode restore failed: %d\\n\", rc);\n+}\n+\n+/*\n+ * The HDM decoder control registers the reset is about to clear, held from the\n+ * disable to the enable of one Downstream Port and indexed by that Port's\n+ * struct pci_dev, so resets of different Ports do not share an entry.\n+ */\n+static DEFINE_XARRAY(cxl_sbr_hdm_state);\n+\n+static void cxl_sbr_drop_hdm_state(struct pci_dev *dport_pci)\n+{\n+\tstruct xarray *hdm_state;\n+\n+\thdm_state = xa_erase(\u0026cxl_sbr_hdm_state, (unsigned long)dport_pci);\n+\tif (!hdm_state)\n+\t\treturn;\n+\n+\tcxl_port_put_hdm_state(hdm_state);\n+\tkfree(hdm_state);\n+}\n+\n+/*\n+ * Record the control registers of every port below @dport_pci before the reset\n+ * clears them. cxl_sbr_enable_regions() consumes the set and drops it.\n+ */\n+static int cxl_sbr_save_hdm_state(struct pci_dev *dport_pci)\n+{\n+\tstruct xarray *hdm_state;\n+\tstruct cxl_dport *dport;\n+\tint rc;\n+\n+\tstruct cxl_port *port __free(put_cxl_port) =\n+\t\tfind_cxl_port(\u0026dport_pci-\u003edev, \u0026dport);\n+\tif (!port)\n+\t\treturn 0;\n+\n+\thdm_state = kzalloc_obj(*hdm_state);\n+\tif (!hdm_state)\n+\t\treturn -ENOMEM;\n+\n+\txa_init(hdm_state);\n+\n+\tscoped_guard(rwsem_read, \u0026cxl_rwsem.region)\n+\t\trc = cxl_port_save_hdm_state(port, hdm_state);\n+\n+\tif (!rc)\n+\t\trc = xa_insert(\u0026cxl_sbr_hdm_state, (unsigned long)dport_pci,\n+\t\t\t       hdm_state, GFP_KERNEL);\n+\tif (rc) {\n+\t\tcxl_port_put_hdm_state(hdm_state);\n+\t\tkfree(hdm_state);\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Disable the regions routed through the Downstream Port being reset. On\n+ * failure re-enable the regions already disabled and return the error so the\n+ * PCI core aborts the reset with the topology unchanged.\n+ */\n+static int cxl_sbr_disable_regions(struct pci_dev *dport_pci)\n+{\n+\tstruct cxl_region *cxlr;\n+\tstruct xarray regions;\n+\tunsigned long index;\n+\tint rc;\n+\n+\trc = cxl_sbr_save_hdm_state(dport_pci);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\txa_init(\u0026regions);\n+\n+\trc = cxl_sbr_collect_regions(dport_pci, \u0026regions);\n+\tif (rc)\n+\t\tgoto out;\n+\n+\txa_for_each(\u0026regions, index, cxlr) {\n+\t\trc = cxl_region_disable(cxlr);\n+\t\tif (rc)\n+\t\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * On failure restore every collected region and return the error so the\n+\t * PCI core aborts the reset before touching the hardware. Re-enabling a\n+\t * region left untouched is a no-op, so enabling the whole set also\n+\t * recovers the region whose offline failed midway.\n+\t */\n+\tif (rc) {\n+\t\tdev_dbg(\u0026dport_pci-\u003edev, \"%s: disable failed (%d), re-enabling collected regions and aborting reset\\n\",\n+\t\t\t__func__, rc);\n+\t\txa_for_each(\u0026regions, index, cxlr)\n+\t\t\tcxl_region_enable(cxlr);\n+\t}\n+\n+out:\n+\tcxl_sbr_put_regions(\u0026regions);\n+\t/* No enable_regions() call follows an aborted reset, so drop the set. */\n+\tif (rc)\n+\t\tcxl_sbr_drop_hdm_state(dport_pci);\n+\treturn rc;\n+}\n+\n+/*\n+ * Unbind the regions routed through the Downstream Port being reset, leaving\n+ * their memory online. Used on the DPC recovery path, where dpc_reset_link()\n+ * clears DPC Trigger Status and enters the reset without waiting for the link,\n+ * so the device may still be unreachable and the page migration that an offline\n+ * performs would have no device to read from.\n+ *\n+ * Unbinding cannot fail, so unlike cxl_sbr_disable_regions() this never aborts\n+ * the reset. The memory stays online across the reset with no region decoding\n+ * it; cxl_sbr_enable_regions() reprograms the decoders on the way out.\n+ */\n+static void cxl_sbr_unbind_regions(struct pci_dev *dport_pci)\n+{\n+\tstruct cxl_region *cxlr;\n+\tstruct xarray regions;\n+\tunsigned long index;\n+\n+\tif (cxl_sbr_save_hdm_state(dport_pci))\n+\t\tpci_warn(dport_pci, \"HDM state not saved, decode will not be restored\\n\");\n+\n+\txa_init(\u0026regions);\n+\n+\tcxl_sbr_collect_regions(dport_pci, \u0026regions);\n+\n+\txa_for_each(\u0026regions, index, cxlr)\n+\t\tcxl_region_unbind(cxlr);\n+\n+\tcxl_sbr_put_regions(\u0026regions);\n+}\n+\n+/*\n+ * Re-enable the regions disabled by cxl_sbr_disable_regions(). Restore the HDM\n+ * decode first: a region cannot serve memory through decoders that are not\n+ * programmed, so its driver must not re-attach before they are.\n+ */\n+static void cxl_sbr_enable_regions(struct pci_dev *dport_pci)\n+{\n+\tstruct xarray *hdm_state;\n+\tstruct cxl_region *cxlr;\n+\tstruct xarray regions;\n+\tunsigned long index;\n+\n+\txa_init(\u0026regions);\n+\n+\tcxl_sbr_collect_regions(dport_pci, \u0026regions);\n+\n+\thdm_state = xa_load(\u0026cxl_sbr_hdm_state, (unsigned long)dport_pci);\n+\tif (hdm_state)\n+\t\tcxl_sbr_recommit_decoders(dport_pci, hdm_state);\n+\telse\n+\t\tpci_warn(dport_pci, \"no saved HDM state, decode not restored\\n\");\n+\n+\txa_for_each(\u0026regions, index, cxlr)\n+\t\tcxl_region_enable(cxlr);\n+\n+\tcxl_sbr_put_regions(\u0026regions);\n+\tcxl_sbr_drop_hdm_state(dport_pci);\n+}\n+\n+const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops = {\n+\t.disable_regions = cxl_sbr_disable_regions,\n+\t.unbind_regions = cxl_sbr_unbind_regions,\n+\t.enable_regions = cxl_sbr_enable_regions,\n+};\ndiff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c\nindex 0c80b76a5f9b4..9839d2592d206 100644\n--- a/drivers/cxl/core/hdm.c\n+++ b/drivers/cxl/core/hdm.c\n@@ -849,6 +849,347 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)\n \treturn 0;\n }\n \n+/**\n+ * cxl_decoder_recommit - reprogram @cxld's HDM decoder registers and commit\n+ * @cxld: decoder to reprogram from its cached settings\n+ * @ctrl: CXL HDM Decoder n Control value to restore under the cached settings\n+ *\n+ * A reset of an upstream link clears the HDM decoder registers of every\n+ * component below it, dropping Committed while the driver still holds the\n+ * settings that were in effect. Restore those settings and commit.\n+ *\n+ * setup_hw_decoder() rewrites only Interleave Granularity, Interleave Ways and\n+ * Target Range Type, so the rest of the control register would come from a read\n+ * of the reset defaults. Per CXL r4.0 sec 8.2.4.20.7 Table 8-123 that register\n+ * also holds BI, UIO, Upstream Interleave Granularity, Upstream Interleave Ways\n+ * and Lock On Commit, none of which the driver models, and sec 8.2.4.20.12 makes\n+ * device operation undefined if a device that requires BI is committed without\n+ * it. Write @ctrl first so those fields are in place, with Commit masked off\n+ * until setup_hw_decoder() has written the range.\n+ *\n+ * A decoder that hardware still reports Committed kept its programming across\n+ * the reset and needs no work. A decoder with no -\u003ecommit is driven through the\n+ * DVSEC ranges or is a passthrough decoder, and has no registers to program.\n+ *\n+ * Section 8.2.4.20.13 requires the traffic targeting @cxld to be quiesced while\n+ * it is reprogrammed, which the caller owns. @cxld decodes nothing until the\n+ * commit completes.\n+ *\n+ * Return: 0 on success or if @cxld needs no reprogramming, negative errno if the\n+ * commit times out or if the hardware reports a commit error.\n+ */\n+static int cxl_decoder_recommit(struct cxl_decoder *cxld, u32 ctrl)\n+{\n+\tstruct cxl_port *port = to_cxl_port(cxld-\u003edev.parent);\n+\tstruct cxl_hdm *cxlhdm = dev_get_drvdata(\u0026port-\u003edev);\n+\tvoid __iomem *hdm = cxlhdm-\u003eregs.hdm_decoder;\n+\tu32 hw_ctrl;\n+\tint rc;\n+\n+\tif ((cxld-\u003eflags \u0026 CXL_DECODER_F_ENABLE) == 0)\n+\t\treturn 0;\n+\n+\tif (!cxld-\u003ecommit)\n+\t\treturn 0;\n+\n+\thw_ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld-\u003eid));\n+\tif (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, hw_ctrl)) {\n+\t\tdev_dbg(\u0026cxld-\u003edev, \"%s: still committed, no reprogram needed\\n\",\n+\t\t\t__func__);\n+\t\treturn 0;\n+\t}\n+\n+\twritel(ctrl \u0026 ~(CXL_HDM_DECODER0_CTRL_COMMIT |\n+\t\t\tCXL_HDM_DECODER0_CTRL_COMMITTED |\n+\t\t\tCXL_HDM_DECODER0_CTRL_COMMIT_ERROR),\n+\t       hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld-\u003eid));\n+\n+\tscoped_guard(rwsem_read, \u0026cxl_rwsem.dpa)\n+\t\tsetup_hw_decoder(cxld, hdm);\n+\n+\trc = cxld_await_commit(hdm, cxld-\u003eid);\n+\tif (rc) {\n+\t\tdev_warn(\u0026cxld-\u003edev, \"%s: failed to commit decoder: %d\\n\",\n+\t\t\t __func__, rc);\n+\t\treturn rc;\n+\t}\n+\n+\tdev_dbg(\u0026cxld-\u003edev, \"%s: reprogrammed HPA %#llx-%#llx\\n\",\n+\t\t__func__, cxld-\u003ehpa_range.start, cxld-\u003ehpa_range.end);\n+\n+\treturn 0;\n+}\n+\n+static int __cxl_endpoint_decoder_is_emulated(struct device *dev, void *data)\n+{\n+\tif (!is_endpoint_decoder(dev))\n+\t\treturn 0;\n+\n+\treturn !to_cxl_decoder(dev)-\u003ecommit;\n+}\n+\n+/* Only the DVSEC setup path leaves -\u003ecommit NULL. */\n+static bool cxl_endpoint_decoders_are_emulated(struct cxl_port *endpoint)\n+{\n+\treturn device_for_each_child(\u0026endpoint-\u003edev, NULL,\n+\t\t\t\t     __cxl_endpoint_decoder_is_emulated);\n+}\n+\n+struct cxl_recommit_ctx {\n+\tconst struct cxl_hdm_state *state;\n+\tint *first_rc;\n+};\n+\n+static int __cxl_port_recommit_decoder(struct device *dev, void *data)\n+{\n+\tstruct cxl_recommit_ctx *ctx = data;\n+\tstruct cxl_decoder *cxld;\n+\tint rc;\n+\n+\tif (!(is_switch_decoder(dev) || is_endpoint_decoder(dev)))\n+\t\treturn 0;\n+\n+\tcxld = to_cxl_decoder(dev);\n+\n+\tif (cxld-\u003eid \u003e= ctx-\u003estate-\u003enr_ctrl) {\n+\t\tdev_warn(\u0026cxld-\u003edev, \"%s: no saved control register\\n\",\n+\t\t\t __func__);\n+\t\tif (!*ctx-\u003efirst_rc)\n+\t\t\t*ctx-\u003efirst_rc = -ENODATA;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Reprogram every decoder the walk reaches. Stopping at the first\n+\t * failure would leave the rest of the path decoding nothing, so record\n+\t * the first error and continue.\n+\t */\n+\trc = cxl_decoder_recommit(cxld, ctx-\u003estate-\u003ectrl[cxld-\u003eid]);\n+\tif (rc \u0026\u0026 !*ctx-\u003efirst_rc)\n+\t\t*ctx-\u003efirst_rc = rc;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Restore CXL.mem decode on @cxlmd before any of its decoders is committed. A\n+ * reset clears the endpoint's HDM Decoder Global Control and the DVSEC CXL\n+ * Control, and per CXL r4.0 sec 8.2.4.20.2 Table 8-118 a device decodes CXL.mem\n+ * with the DVSEC range registers while HDM Decoder Enable is clear. Committing\n+ * a decoder in that state does not establish the route. An endpoint with no HDM\n+ * decoder registers is driven through the DVSEC ranges and has nothing to\n+ * enable. So is an endpoint whose decoders are emulated from those ranges, and\n+ * setting HDM Decoder Enable there would switch it to decoders locked against\n+ * reprogramming.\n+ *\n+ * @global_ctrl is the Global Control value to enable decode in. That register\n+ * also holds Poison On Decode Error Enable, which the driver does not model, so\n+ * the caller supplies the value it saved rather than one read back after the\n+ * reset.\n+ */\n+static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,\n+\t\t\t\t\t  u32 global_ctrl)\n+{\n+\tstruct cxl_port *endpoint = cxlmd-\u003eendpoint;\n+\tstruct cxl_hdm *cxlhdm = dev_get_drvdata(\u0026endpoint-\u003edev);\n+\tint rc;\n+\n+\tif (!cxlhdm || !cxlhdm-\u003eregs.hdm_decoder)\n+\t\treturn 0;\n+\n+\tif (cxl_endpoint_decoders_are_emulated(endpoint))\n+\t\treturn 0;\n+\n+\tcxl_enable_hdm(cxlhdm, global_ctrl);\n+\n+\trc = cxl_set_mem_enable(cxlmd-\u003ecxlds, PCI_DVSEC_CXL_MEM_ENABLE);\n+\tif (rc \u003c 0)\n+\t\treturn rc;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * cxl_port_recommit_decoders - reprogram the HDM decoders below @port\n+ * @port: CXL port whose downstream decoders to reprogram\n+ * @hdm_state: saved \u0026struct cxl_hdm_state per port, keyed by \u0026struct cxl_port\n+ *\n+ * Reprogram the HDM decoders below @port that lost their programming. Every\n+ * endpoint beneath @port is restored along its whole path, from the endpoint up\n+ * to the last port below @port. A decoder that hardware still reports committed\n+ * is left untouched.\n+ *\n+ * Per CXL r4.0 sec 8.2.4.20.13 decoder m must be committed before decoder m+1\n+ * while reprogramming, so let device_for_each_child() visit each port's decoders\n+ * in instance order. Each path is walked from the endpoint upward, the order\n+ * cxl_region_decode_commit() uses.\n+ *\n+ * The endpoints are reprogrammed one after another, so an interleaved HPA range\n+ * decodes through only part of its interleave set until the last member is\n+ * done. Per CXL r4.0 sec 8.2.4.20.13 software owns quiescing the traffic that\n+ * targets a decoder being reprogrammed: a read that no decoder positively\n+ * decodes returns all 1s or poison, and per Table 8-118 such a write is\n+ * dropped. Nothing here can detect a stray access, so the caller carries that\n+ * duty.\n+ *\n+ * Context: caller must hold @cxl_rwsem.region to keep the topology and the\n+ * switch decoder target lists stable across the walk, and must have quiesced\n+ * every access to the HPA ranges decoded below @port.\n+ *\n+ * A port with no entry in @hdm_state was not saved, so its decoders are left\n+ * alone rather than committed with whatever the reset left in the fields the\n+ * driver does not model.\n+ *\n+ * Return: 0 on success, negative errno of the first decoder that failed or\n+ * -ENODATA if a port on the path has no saved state.\n+ */\n+int cxl_port_recommit_decoders(struct cxl_port *port, struct xarray *hdm_state)\n+{\n+\tstruct cxl_ep *port_ep;\n+\tunsigned long index;\n+\tint first_rc = 0;\n+\n+\tlockdep_assert_held(\u0026cxl_rwsem.region);\n+\n+\txa_for_each(\u0026port-\u003eendpoints, index, port_ep) {\n+\t\tstruct cxl_memdev *cxlmd = to_cxl_memdev(port_ep-\u003eep);\n+\t\tstruct cxl_hdm_state *state;\n+\t\tstruct cxl_port *iter;\n+\t\tint rc;\n+\n+\t\tif (IS_ERR_OR_NULL(cxlmd-\u003eendpoint))\n+\t\t\tcontinue;\n+\n+\t\tstate = xa_load(hdm_state, (unsigned long)cxlmd-\u003eendpoint);\n+\t\tif (!state) {\n+\t\t\tdev_warn(\u0026cxlmd-\u003edev, \"%s: no saved HDM state\\n\",\n+\t\t\t\t __func__);\n+\t\t\tif (!first_rc)\n+\t\t\t\tfirst_rc = -ENODATA;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\trc = cxl_endpoint_enable_hdm_decode(cxlmd, state-\u003eglobal_ctrl);\n+\t\tif (rc) {\n+\t\t\tdev_warn(\u0026cxlmd-\u003edev,\n+\t\t\t\t \"%s: failed to enable HDM decode: %d\\n\",\n+\t\t\t\t __func__, rc);\n+\t\t\tif (!first_rc)\n+\t\t\t\tfirst_rc = rc;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Walk from the endpoint up to @port so a decoder is committed\n+\t\t * only after the decoder it routes to. @port is the last parent\n+\t\t * visited by the walk, and it is excluded.\n+\t\t */\n+\t\tfor (iter = cxlmd-\u003eendpoint; iter \u0026\u0026 iter != port;\n+\t\t     iter = parent_port_of(iter)) {\n+\t\t\tstruct cxl_recommit_ctx ctx = {\n+\t\t\t\t.state = xa_load(hdm_state, (unsigned long)iter),\n+\t\t\t\t.first_rc = \u0026first_rc,\n+\t\t\t};\n+\n+\t\t\tif (!ctx.state) {\n+\t\t\t\tdev_warn(\u0026iter-\u003edev, \"%s: no saved HDM state\\n\",\n+\t\t\t\t\t __func__);\n+\t\t\t\tif (!first_rc)\n+\t\t\t\t\tfirst_rc = -ENODATA;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tdevice_for_each_child(\u0026iter-\u003edev, \u0026ctx,\n+\t\t\t\t\t      __cxl_port_recommit_decoder);\n+\t\t}\n+\t}\n+\n+\treturn first_rc;\n+}\n+\n+/**\n+ * cxl_port_save_hdm_state - record the HDM decoder control registers below @port\n+ * @port: CXL port whose downstream decoders to record\n+ * @hdm_state: xarray to fill, one entry per port, keyed by \u0026struct cxl_port\n+ *\n+ * Read the CXL HDM Decoder Global Control and every CXL HDM Decoder n Control\n+ * register of the ports below @port. Those hold the fields\n+ * cxl_port_recommit_decoders() cannot rebuild from the driver's cached settings,\n+ * so they have to be read while the registers still hold them.\n+ *\n+ * The set of ports is the same one cxl_port_recommit_decoders() walks. A port\n+ * with no HDM decoder registers has nothing to record and gets no entry.\n+ *\n+ * Context: caller must hold @cxl_rwsem.region.\n+ *\n+ * Return: 0 on success, negative errno if an entry cannot be allocated or\n+ * inserted.\n+ */\n+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state)\n+{\n+\tstruct cxl_ep *port_ep;\n+\tunsigned long index;\n+\n+\tlockdep_assert_held(\u0026cxl_rwsem.region);\n+\n+\txa_for_each(\u0026port-\u003eendpoints, index, port_ep) {\n+\t\tstruct cxl_memdev *cxlmd = to_cxl_memdev(port_ep-\u003eep);\n+\t\tstruct cxl_port *iter;\n+\n+\t\tif (IS_ERR_OR_NULL(cxlmd-\u003eendpoint))\n+\t\t\tcontinue;\n+\n+\t\tfor (iter = cxlmd-\u003eendpoint; iter \u0026\u0026 iter != port;\n+\t\t     iter = parent_port_of(iter)) {\n+\t\t\tstruct cxl_hdm *cxlhdm = dev_get_drvdata(\u0026iter-\u003edev);\n+\t\t\tstruct cxl_hdm_state *state;\n+\t\t\tvoid __iomem *hdm;\n+\t\t\tint rc;\n+\n+\t\t\tif (xa_load(hdm_state, (unsigned long)iter))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (!cxlhdm || !cxlhdm-\u003eregs.hdm_decoder)\n+\t\t\t\tcontinue;\n+\n+\t\t\thdm = cxlhdm-\u003eregs.hdm_decoder;\n+\t\t\tstate = kzalloc_flex(*state, ctrl,\n+\t\t\t\t\t     cxlhdm-\u003edecoder_count);\n+\t\t\tif (!state)\n+\t\t\t\treturn -ENOMEM;\n+\n+\t\t\tstate-\u003eglobal_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);\n+\t\t\tstate-\u003enr_ctrl = cxlhdm-\u003edecoder_count;\n+\t\t\tfor (int i = 0; i \u003c state-\u003enr_ctrl; i++)\n+\t\t\t\tstate-\u003ectrl[i] =\n+\t\t\t\t\treadl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));\n+\n+\t\t\trc = xa_insert(hdm_state, (unsigned long)iter, state,\n+\t\t\t\t       GFP_KERNEL);\n+\t\t\tif (rc) {\n+\t\t\t\tkfree(state);\n+\t\t\t\treturn rc;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * cxl_port_put_hdm_state - release a set filled by cxl_port_save_hdm_state()\n+ * @hdm_state: xarray to empty\n+ */\n+void cxl_port_put_hdm_state(struct xarray *hdm_state)\n+{\n+\tstruct cxl_hdm_state *state;\n+\tunsigned long index;\n+\n+\txa_for_each(hdm_state, index, state)\n+\t\tkfree(state);\n+\txa_destroy(hdm_state);\n+}\n+\n static int commit_reap(struct device *dev, void *data)\n {\n \tstruct cxl_port *port = to_cxl_port(dev-\u003eparent);\ndiff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c\nindex e4338fd7e01b4..a7a2b84293e9c 100644\n--- a/drivers/cxl/core/pci.c\n+++ b/drivers/cxl/core/pci.c\n@@ -179,7 +179,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds)\n }\n EXPORT_SYMBOL_NS_GPL(cxl_await_media_ready, \"CXL\");\n \n-static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)\n+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)\n {\n \tstruct pci_dev *pdev = to_pci_dev(cxlds-\u003edev);\n \tint d = cxlds-\u003ecxl_dvsec;\n@@ -247,14 +247,23 @@ static void disable_hdm(void *_cxlhdm)\n \t       hdm + CXL_HDM_DECODER_CTRL_OFFSET);\n }\n \n-static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm)\n+/*\n+ * @global_ctrl is the CXL HDM Decoder Global Control value to enable decode in.\n+ * A caller restoring decode after a reset passes the value it saved, so the\n+ * fields the driver does not model are not left at their reset defaults.\n+ */\n+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl)\n {\n \tvoid __iomem *hdm = cxlhdm-\u003eregs.hdm_decoder;\n-\tu32 global_ctrl;\n \n-\tglobal_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);\n \twritel(global_ctrl | CXL_HDM_DECODER_ENABLE,\n \t       hdm + CXL_HDM_DECODER_CTRL_OFFSET);\n+}\n+\n+static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm,\n+\t\t\t       u32 global_ctrl)\n+{\n+\tcxl_enable_hdm(cxlhdm, global_ctrl);\n \n \treturn devm_add_action_or_reset(host, disable_hdm, cxlhdm);\n }\n@@ -398,7 +407,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,\n \t * enable and use the HDM Decoder Capability registers.\n \t */\n \tif (!info-\u003emem_enabled) {\n-\t\trc = devm_cxl_enable_hdm(\u0026port-\u003edev, cxlhdm);\n+\t\trc = devm_cxl_enable_hdm(\u0026port-\u003edev, cxlhdm, global_ctrl);\n \t\tif (rc)\n \t\t\treturn rc;\n \ndiff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c\nindex 1215ee4f40351..66a6d513843e5 100644\n--- a/drivers/cxl/core/port.c\n+++ b/drivers/cxl/core/port.c\n@@ -1392,8 +1392,8 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)\n \treturn NULL;\n }\n \n-static struct cxl_port *find_cxl_port(struct device *dport_dev,\n-\t\t\t\t      struct cxl_dport **dport)\n+struct cxl_port *find_cxl_port(struct device *dport_dev,\n+\t\t\t       struct cxl_dport **dport)\n {\n \tstruct cxl_find_port_ctx ctx = {\n \t\t.dport_dev = dport_dev,\ndiff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c\nindex 99fb00949c2fa..568a906f3b50f 100644\n--- a/drivers/cxl/core/ras.c\n+++ b/drivers/cxl/core/ras.c\n@@ -323,6 +323,16 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,\n \t\t}\n \t\treturn PCI_ERS_RESULT_CAN_RECOVER;\n \tcase pci_channel_io_frozen:\n+\t\t/*\n+\t\t * A Port on the path in DPC means dpc_reset_link() is about to\n+\t\t * reset the link, and that path takes the CXL regions out of\n+\t\t * service and restores the HDM decode itself. Keep the memdev\n+\t\t * driver bound so the endpoint and its decoders are still there\n+\t\t * to restore.\n+\t\t */\n+\t\tif (pci_dpc_containment_active(pdev))\n+\t\t\treturn PCI_ERS_RESULT_NEED_RESET;\n+\n \t\tdev_warn(\u0026pdev-\u003edev,\n \t\t\t \"%s: frozen state error detected, disable CXL.mem\\n\",\n \t\t\t dev_name(dev));\ndiff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c\nindex 1e211542b6b64..d1dd4924fba17 100644\n--- a/drivers/cxl/core/region.c\n+++ b/drivers/cxl/core/region.c\n@@ -12,6 +12,7 @@\n #include \u003clinux/idr.h\u003e\n #include \u003clinux/memory-tiers.h\u003e\n #include \u003clinux/string_choices.h\u003e\n+#include \u003clinux/pci.h\u003e\n #include \u003ccxlmem.h\u003e\n #include \u003ccxl.h\u003e\n #include \"core.h\"\n@@ -222,7 +223,7 @@ static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port,\n \treturn xa_load(\u0026port-\u003eregions, (unsigned long)cxlr);\n }\n \n-static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)\n+int cxl_region_invalidate_memregion(struct cxl_region *cxlr)\n {\n \tif (!cpu_cache_has_invalidate_memregion()) {\n \t\tif (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) {\n@@ -4263,14 +4264,17 @@ static struct cxl_driver cxl_region_driver = {\n \n int cxl_region_init(void)\n {\n+\tpci_cxl_set_sbr_region_ops(\u0026cxl_sbr_region_ops);\n \treturn cxl_driver_register(\u0026cxl_region_driver);\n }\n \n void cxl_region_exit(void)\n {\n \tcxl_driver_unregister(\u0026cxl_region_driver);\n+\tpci_cxl_set_sbr_region_ops(NULL);\n }\n \n MODULE_IMPORT_NS(\"CXL\");\n MODULE_IMPORT_NS(\"DEVMEM\");\n+MODULE_IMPORT_NS(\"CXL_MHP\");\n MODULE_ALIAS_CXL(CXL_DEVICE_REGION);\ndiff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c\nindex 5ec0b245a69bd..ba1cae6ab4fae 100644\n--- a/drivers/pci/pci-sysfs.c\n+++ b/drivers/pci/pci-sysfs.c\n@@ -573,6 +573,33 @@ static ssize_t reset_subordinate_store(struct device *dev,\n }\n static DEVICE_ATTR_WO(reset_subordinate);\n \n+static ssize_t cxl_unmask_sbr_show(struct device *dev,\n+\t\t\t\t   struct device_attribute *attr, char *buf)\n+{\n+\tstruct pci_dev *pdev = to_pci_dev(dev);\n+\n+\treturn sysfs_emit(buf, \"%u\\n\", pdev-\u003ecxl_unmask_sbr);\n+}\n+\n+static ssize_t cxl_unmask_sbr_store(struct device *dev,\n+\t\t\t\t    struct device_attribute *attr,\n+\t\t\t\t    const char *buf, size_t count)\n+{\n+\tstruct pci_dev *pdev = to_pci_dev(dev);\n+\tunsigned long val;\n+\n+\tif (!capable(CAP_SYS_ADMIN))\n+\t\treturn -EPERM;\n+\n+\tif (kstrtoul(buf, 0, \u0026val) \u003c 0)\n+\t\treturn -EINVAL;\n+\n+\tpdev-\u003ecxl_unmask_sbr = !!val;\n+\n+\treturn count;\n+}\n+static DEVICE_ATTR_RW(cxl_unmask_sbr);\n+\n #if defined(CONFIG_PM) \u0026\u0026 defined(CONFIG_ACPI)\n static ssize_t d3cold_allowed_store(struct device *dev,\n \t\t\t\t    struct device_attribute *attr,\n@@ -650,6 +677,7 @@ static struct attribute *pci_bridge_attrs[] = {\n \t\u0026dev_attr_subordinate_bus_number.attr,\n \t\u0026dev_attr_secondary_bus_number.attr,\n \t\u0026dev_attr_reset_subordinate.attr,\n+\t\u0026dev_attr_cxl_unmask_sbr.attr,\n \tNULL,\n };\n \n@@ -1824,6 +1852,9 @@ static umode_t pci_bridge_attrs_are_visible(struct kobject *kobj,\n \tstruct device *dev = kobj_to_dev(kobj);\n \tstruct pci_dev *pdev = to_pci_dev(dev);\n \n+\tif (a == \u0026dev_attr_cxl_unmask_sbr.attr \u0026\u0026 !is_cxl_dport(pdev))\n+\t\treturn 0;\n+\n \tif (pci_is_bridge(pdev))\n \t\treturn a-\u003emode;\n \ndiff --git a/drivers/pci/pci.c b/drivers/pci/pci.c\nindex 77b17b13ee615..08873ea3957b2 100644\n--- a/drivers/pci/pci.c\n+++ b/drivers/pci/pci.c\n@@ -23,6 +23,7 @@\n #include \u003clinux/module.h\u003e\n #include \u003clinux/spinlock.h\u003e\n #include \u003clinux/string.h\u003e\n+#include \u003clinux/string_choices.h\u003e\n #include \u003clinux/log2.h\u003e\n #include \u003clinux/logic_pio.h\u003e\n #include \u003clinux/device.h\u003e\n@@ -4844,21 +4845,235 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)\n \tpci_reset_secondary_bus(dev);\n }\n \n+/*\n+ * Registered by the CXL core to disable and re-enable the regions mapped\n+ * through a CXL Downstream Port across a Secondary Bus Reset. NULL whenever\n+ * the CXL region code is absent: not built, or built as a module not loaded.\n+ */\n+static const struct pci_cxl_sbr_region_ops *cxl_sbr_region_ops;\n+\n+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops)\n+{\n+\tcxl_sbr_region_ops = ops;\n+}\n+EXPORT_SYMBOL_GPL(pci_cxl_set_sbr_region_ops);\n+\n+struct cxl_sbr_ctx {\n+\tu16 port_ctl;\n+\tu16 acs_ctrl;\n+\tu16 command;\n+};\n+\n+bool is_cxl_dport(struct pci_dev *dev)\n+{\n+\treturn pcie_is_cxl(dev) \u0026\u0026 pcie_downstream_port(dev);\n+}\n+\n+u16 cxl_port_dvsec(struct pci_dev *dev)\n+{\n+\treturn pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,\n+\t\t\t\t\t PCI_DVSEC_CXL_PORT);\n+}\n+\n+static int cxl_sbr_prepare(struct pci_dev *bridge, u16 dvsec,\n+\t\t\t   struct cxl_sbr_ctx *ctx,\n+\t\t\t   enum cxl_sbr_region_action action)\n+{\n+\tint rc;\n+\n+\tif (action == CXL_SBR_OFFLINE_AND_UNBIND \u0026\u0026 !cxl_sbr_allowed(bridge)) {\n+\t\tpci_info(bridge, \"SBR masked, write 1 to cxl_unmask_sbr to allow a bus reset\\n\");\n+\t\treturn -ENOTTY;\n+\t}\n+\n+\t/*\n+\t * CXL_SBR_UNBIND: the link is already down, so offlining the regions'\n+\t * memory would take the reads that page migration performs as a machine\n+\t * check. Per PCIe r7.0 sec 2.9.3 the Port answers a Non-Posted Request\n+\t * with an Unsupported Request or Completer Abort completion while it is\n+\t * in DPC. Unbinding never fails, so the reset always goes ahead.\n+\t *\n+\t * CXL_SBR_OFFLINE_AND_UNBIND: the device is reachable, so offline the\n+\t * memory first and abort the reset before touching hardware if that\n+\t * fails.\n+\t */\n+\tif (cxl_sbr_region_ops \u0026\u0026 action == CXL_SBR_UNBIND) {\n+\t\tcxl_sbr_region_ops-\u003eunbind_regions(bridge);\n+\t} else if (cxl_sbr_region_ops) {\n+\t\trc = cxl_sbr_region_ops-\u003edisable_regions(bridge);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\t/* CXL r4.0 sec 8.1.5.2, Table 8-32: set Unmask SBR so the Port issues Hot Reset. */\n+\tpci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, \u0026ctx-\u003eport_ctl);\n+\tpci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,\n+\t\t\t      ctx-\u003eport_ctl | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);\n+\n+\tpci_read_config_word(bridge, PCI_COMMAND, \u0026ctx-\u003ecommand);\n+\tpci_clear_master(bridge);\n+\n+\t/* CXL r4.0 sec 8.1.5.1: Disable ACS SV bit before SBR */\n+\tif (bridge-\u003eacs_cap) {\n+\t\tpci_read_config_word(bridge, bridge-\u003eacs_cap + PCI_ACS_CTRL, \u0026ctx-\u003eacs_ctrl);\n+\t\tpci_dbg(bridge, \"%s: ACS SV %s\\n\", __func__,\n+\t\t\tstr_enabled_disabled(ctx-\u003eacs_ctrl \u0026 PCI_ACS_SV));\n+\t\tpci_write_config_word(bridge, bridge-\u003eacs_cap + PCI_ACS_CTRL,\n+\t\t\t\t      ctx-\u003eacs_ctrl \u0026 ~PCI_ACS_SV);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * CXL r4.0 sec 8.1.5.1, Table 8-31: the Port sets PM Init Complete within\n+ * 100 ms of link-up. Restoring ACS Source Validation before then makes the\n+ * Port reject the downstream Component's Requester-Bus-0 IP2PM message, so\n+ * poll for completion before restoring config.\n+ */\n+static bool cxl_port_pm_init_is_complete(struct pci_dev *bridge, u16 dvsec)\n+{\n+\tunsigned long start = jiffies;\n+\tunsigned long timeout = start + msecs_to_jiffies(100);\n+\tu16 status;\n+\n+\tdo {\n+\t\tpci_read_config_word(bridge,\n+\t\t\t\t     dvsec + PCI_DVSEC_CXL_PORT_EXT_STATUS,\n+\t\t\t\t     \u0026status);\n+\t\tif (!PCI_POSSIBLE_ERROR(status) \u0026\u0026\n+\t\t    (status \u0026 PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP)) {\n+\t\t\tpci_dbg(bridge, \"%s: PM Init Complete set after %u ms, ext status %#06x\\n\",\n+\t\t\t\t__func__, jiffies_to_msecs(jiffies - start), status);\n+\t\t\treturn true;\n+\t\t}\n+\t\tmsleep(10);\n+\t} while (time_before(jiffies, timeout));\n+\n+\tpci_warn(bridge, \"%s: PM Init Complete not set after %u ms, ext status %#06x\\n\",\n+\t\t __func__, jiffies_to_msecs(jiffies - start), status);\n+\n+\treturn false;\n+}\n+\n+static int cxl_sbr_restore_config_space(struct pci_dev *dev, void *userdata)\n+{\n+\tpci_restore_config_space(dev);\n+\tpci_dbg(dev, \"%s: config space restored\\n\", __func__);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * The CXL region ops that run next read the HDM Decoders through a Base Address\n+ * Register the reset returned to its initialization value, so restore the\n+ * header of every device below @bridge first. Restoring also re-captures each\n+ * Bus Number before the Port's ACS Source Validation comes back: a device that\n+ * has completed no Type 0 Configuration Write since the reset sources Requests\n+ * with Bus 0, which the Port rejects as an ACS Violation.\n+ *\n+ * Only the header is restored. The capability state each caller saved is its own\n+ * to replay, and the -\u003ereset_done() callbacks pci_dev_restore() invokes must\n+ * fire once, from the caller that owns the reset.\n+ */\n+static void cxl_sbr_restore_subordinate(struct pci_dev *bridge)\n+{\n+\tif (!bridge-\u003esubordinate)\n+\t\treturn;\n+\n+\t/* Parents before children: a child answers once its parent forwards. */\n+\tpci_walk_bus(bridge-\u003esubordinate, cxl_sbr_restore_config_space, NULL);\n+}\n+\n+static void cxl_sbr_complete(struct pci_dev *bridge, u16 dvsec,\n+\t\t\t     const struct cxl_sbr_ctx *ctx)\n+{\n+\tu16 val;\n+\n+\t/* CXL r4.0 sec 8.1.5.1: wait for PM Init before restoring ACS SV. */\n+\tif (!cxl_port_pm_init_is_complete(bridge, dvsec))\n+\t\tpci_warn(bridge,\n+\t\t\t \"restoring ACS Source Validation before PM Init complete; Port may reject the Component's bus 0 traffic\\n\");\n+\n+\tcxl_sbr_restore_subordinate(bridge);\n+\n+\t/* CXL r4.0 sec 8.1.5.1: Re-enable ACS SV bit after SBR if it was enabled before */\n+\tif (bridge-\u003eacs_cap \u0026\u0026 (ctx-\u003eacs_ctrl \u0026 PCI_ACS_SV)) {\n+\t\tpci_read_config_word(bridge, bridge-\u003eacs_cap + PCI_ACS_CTRL, \u0026val);\n+\t\tpci_write_config_word(bridge, bridge-\u003eacs_cap + PCI_ACS_CTRL,\n+\t\t\t\t      val | PCI_ACS_SV);\n+\t\tpci_dbg(bridge, \"%s: ACS SV bit set\\n\", __func__);\n+\t} else {\n+\t\tpci_dbg(bridge, \"%s: ACS SV bit not set (was not enabled before the SBR)\\n\",\n+\t\t\t__func__);\n+\t}\n+\n+\tif (ctx-\u003ecommand \u0026 PCI_COMMAND_MASTER)\n+\t\tpci_set_master(bridge);\n+\n+\tif (!(ctx-\u003eport_ctl \u0026 PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)) {\n+\t\tpci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, \u0026val);\n+\t\tpci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,\n+\t\t\t\t      val \u0026 ~PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);\n+\t}\n+\n+\tif (cxl_sbr_region_ops)\n+\t\tcxl_sbr_region_ops-\u003eenable_regions(bridge);\n+}\n+\n+/*\n+ * __pci_bridge_secondary_bus_reset - assert Secondary Bus Reset on a bridge\n+ * @dev: bridge device\n+ * @action: what to do with the CXL regions reached through @dev\n+ *\n+ * See pci_bridge_secondary_bus_reset(). Pass CXL_SBR_UNBIND when the link is\n+ * already down, which leaves the regions' memory online because offlining it\n+ * needs a reachable device.\n+ */\n+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\n+\t\t\t\t     enum cxl_sbr_region_action action)\n+{\n+\tstruct cxl_sbr_ctx ctx = {};\n+\tu16 dvsec = 0;\n+\tint rc;\n+\n+\tif (!dev-\u003eblock_cfg_access)\n+\t\tpci_warn_once(dev, \"unlocked secondary bus reset via: %pS\\n\",\n+\t\t\t      __builtin_return_address(0));\n+\n+\tif (is_cxl_dport(dev))\n+\t\tdvsec = cxl_port_dvsec(dev);\n+\tif (dvsec) {\n+\t\trc = cxl_sbr_prepare(dev, dvsec, \u0026ctx, action);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\tpcibios_reset_secondary_bus(dev);\n+\n+\trc = pci_bridge_wait_for_secondary_bus(dev, \"bus reset\");\n+\n+\tif (dvsec)\n+\t\tcxl_sbr_complete(dev, dvsec, \u0026ctx);\n+\n+\treturn rc;\n+}\n+\n /**\n  * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge.\n  * @dev: Bridge device\n  *\n  * Use the bridge control register to assert reset on the secondary bus.\n  * Devices on the secondary bus are left in power-on state.\n+ *\n+ * When @dev is a CXL Downstream Port, clear ACS Source Validation and Bus\n+ * Master Enable across the reset, per the workaround in CXL r4.0 sec 8.1.5.1,\n+ * so that Port Power Management Initialization completes at link-up. The\n+ * bits stay cleared until the secondary bus is back, then are restored.\n  */\n int pci_bridge_secondary_bus_reset(struct pci_dev *dev)\n {\n-\tif (!dev-\u003eblock_cfg_access)\n-\t\tpci_warn_once(dev, \"unlocked secondary bus reset via: %pS\\n\",\n-\t\t\t      __builtin_return_address(0));\n-\tpcibios_reset_secondary_bus(dev);\n-\n-\treturn pci_bridge_wait_for_secondary_bus(dev, \"bus reset\");\n+\treturn __pci_bridge_secondary_bus_reset(dev, CXL_SBR_OFFLINE_AND_UNBIND);\n }\n EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);\n \n@@ -4904,12 +5119,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)\n \treturn pci_reset_hotplug_slot(dev-\u003eslot-\u003ehotplug, probe);\n }\n \n-static u16 cxl_port_dvsec(struct pci_dev *dev)\n-{\n-\treturn pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,\n-\t\t\t\t\t PCI_DVSEC_CXL_PORT);\n-}\n-\n static bool cxl_sbr_masked(struct pci_dev *dev)\n {\n \tu16 dvsec, reg;\n@@ -4934,6 +5143,27 @@ static bool cxl_sbr_masked(struct pci_dev *dev)\n \treturn true;\n }\n \n+/*\n+ * cxl_sbr_allowed - whether an SBR of a CXL Downstream Port may go ahead\n+ * @dev: Downstream Port to test\n+ *\n+ * Per CXL r4.0 sec 8.1.5.2 Table 8-32 the SBR bit in a CXL Port's Bridge\n+ * Control register has no effect while the Port's Unmask SBR bit is clear, and\n+ * sec 9.12.3 says System Firmware may leave it clear \"to prevent CXL-unaware\n+ * PCIe software from resetting the device and the link\". A Port that already\n+ * has it set needs no further permission; otherwise unmasking it takes the\n+ * administrator's consent, given by writing 1 to the Port's cxl_unmask_sbr.\n+ *\n+ * Return: true if the reset paths may unmask and generate an SBR of @dev.\n+ */\n+bool cxl_sbr_allowed(struct pci_dev *dev)\n+{\n+\tif (!cxl_port_dvsec(dev))\n+\t\treturn false;\n+\n+\treturn dev-\u003ecxl_unmask_sbr || !cxl_sbr_masked(dev);\n+}\n+\n static int pci_reset_bus_function(struct pci_dev *dev, bool probe)\n {\n \tstruct pci_dev *bridge = pci_upstream_bridge(dev);\ndiff --git a/drivers/pci/pci.h b/drivers/pci/pci.h\nindex 4469e1a77f3c1..bea05acc58f0d 100644\n--- a/drivers/pci/pci.h\n+++ b/drivers/pci/pci.h\n@@ -233,6 +233,22 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,\n bool pci_reset_supported(struct pci_dev *dev);\n void pci_init_reset_methods(struct pci_dev *dev);\n int pci_bridge_secondary_bus_reset(struct pci_dev *dev);\n+\n+/*\n+ * What to do with the CXL regions reached through a Downstream Port before it\n+ * is reset. Offlining their memory needs a reachable device, so a Port whose\n+ * link is already down only unbinds and leaves the memory online.\n+ */\n+enum cxl_sbr_region_action {\n+\tCXL_SBR_OFFLINE_AND_UNBIND,\n+\tCXL_SBR_UNBIND,\n+};\n+\n+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\n+\t\t\t\t     enum cxl_sbr_region_action action);\n+bool is_cxl_dport(struct pci_dev *dev);\n+u16 cxl_port_dvsec(struct pci_dev *dev);\n+bool cxl_sbr_allowed(struct pci_dev *dev);\n int pci_bus_error_reset(struct pci_dev *dev);\n int pci_try_reset_bridge(struct pci_dev *bridge);\n \ndiff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c\nindex 2b779bd1d861b..ad74086bf82c7 100644\n--- a/drivers/pci/pcie/dpc.c\n+++ b/drivers/pci/pcie/dpc.c\n@@ -127,6 +127,44 @@ bool pci_dpc_recovered(struct pci_dev *pdev)\n }\n #endif /* CONFIG_HOTPLUG_PCI_PCIE */\n \n+/**\n+ * pci_dpc_containment_active - whether a Port above @pdev is contained by DPC\n+ * @pdev: PCI device below the Port\n+ *\n+ * Per PCIe r7.0 sec 2.9.3 the Port's LTSSM stays in the Disabled state while\n+ * DPC Trigger Status is set, and dpc_reset_link() clears that bit only after\n+ * pcie_do_recovery() has broadcast error_detected. A -\u003eerror_detected()\n+ * callback can therefore use this to tell a DPC containment from any other\n+ * frozen-channel error, and to know that the link is about to be reset.\n+ *\n+ * The Port that triggered is on the path to @pdev, because the broadcast walks\n+ * that Port's subordinate bus, so test every bridge above @pdev.\n+ *\n+ * Return: true if a Port on the path to @pdev has DPC Trigger Status set.\n+ */\n+bool pci_dpc_containment_active(struct pci_dev *pdev)\n+{\n+\tstruct pci_dev *bridge;\n+\n+\tfor (bridge = pci_upstream_bridge(pdev); bridge;\n+\t     bridge = pci_upstream_bridge(bridge)) {\n+\t\tu16 status;\n+\n+\t\tif (!bridge-\u003edpc_cap)\n+\t\t\tcontinue;\n+\n+\t\tpci_read_config_word(bridge,\n+\t\t\t\t     bridge-\u003edpc_cap + PCI_EXP_DPC_STATUS,\n+\t\t\t\t     \u0026status);\n+\t\tif (!PCI_POSSIBLE_ERROR(status) \u0026\u0026\n+\t\t    (status \u0026 PCI_EXP_DPC_STATUS_TRIGGER))\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+EXPORT_SYMBOL_GPL(pci_dpc_containment_active);\n+\n static int dpc_wait_rp_inactive(struct pci_dev *pdev)\n {\n \tunsigned long timeout = jiffies + HZ;\n@@ -149,6 +187,7 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)\n {\n \tpci_ers_result_t ret;\n \tu16 cap;\n+\tint rc;\n \n \tset_bit(PCI_DPC_RECOVERING, \u0026pdev-\u003epriv_flags);\n \n@@ -174,7 +213,12 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)\n \tpci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,\n \t\t\t      PCI_EXP_DPC_STATUS_TRIGGER);\n \n-\tif (pci_bridge_wait_for_secondary_bus(pdev, \"DPC\")) {\n+\tif (is_cxl_dport(pdev) \u0026\u0026 cxl_port_dvsec(pdev))\n+\t\trc = __pci_bridge_secondary_bus_reset(pdev, CXL_SBR_UNBIND);\n+\telse\n+\t\trc = pci_bridge_wait_for_secondary_bus(pdev, \"DPC\");\n+\n+\tif (rc) {\n \t\tclear_bit(PCI_DPC_RECOVERED, \u0026pdev-\u003epriv_flags);\n \t\tret = PCI_ERS_RESULT_DISCONNECT;\n \t} else {\ndiff --git a/include/linux/aer.h b/include/linux/aer.h\nindex df0f5c382286f..7ac029f01c25c 100644\n--- a/include/linux/aer.h\n+++ b/include/linux/aer.h\n@@ -66,6 +66,15 @@ static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; }\n static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }\n #endif\n \n+#if defined(CONFIG_PCIE_DPC)\n+bool pci_dpc_containment_active(struct pci_dev *pdev);\n+#else\n+static inline bool pci_dpc_containment_active(struct pci_dev *pdev)\n+{\n+\treturn false;\n+}\n+#endif\n+\n void pci_print_aer(struct pci_dev *dev, int aer_severity,\n \t\t    struct aer_capability_regs *aer);\n int cper_severity_to_aer(int cper_severity);\ndiff --git a/include/linux/memory.h b/include/linux/memory.h\nindex 463dc02f6cff0..5dc0b96fb004a 100644\n--- a/include/linux/memory.h\n+++ b/include/linux/memory.h\n@@ -149,6 +149,10 @@ static inline unsigned long memory_block_advised_max_size(void)\n {\n \treturn 0;\n }\n+static inline int cxl_offline_memory(u64 start, u64 size)\n+{\n+\treturn 0;\n+}\n #else /* CONFIG_MEMORY_HOTPLUG */\n extern int register_memory_notifier(struct notifier_block *nb);\n extern void unregister_memory_notifier(struct notifier_block *nb);\n@@ -167,6 +171,7 @@ typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *);\n extern int walk_memory_blocks(unsigned long start, unsigned long size,\n \t\t\t      void *arg, walk_memory_blocks_func_t func);\n extern int for_each_memory_block(void *arg, walk_memory_blocks_func_t func);\n+int cxl_offline_memory(u64 start, u64 size);\n \n extern int memory_group_register_static(int nid, unsigned long max_pages);\n extern int memory_group_register_dynamic(int nid, unsigned long unit_pages);\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex 64b308b6e61c1..bc0f652eff804 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -485,6 +485,7 @@ struct pci_dev {\n \tunsigned int\tshpc_managed:1;\t\t/* SHPC owned by shpchp */\n \tunsigned int\tis_thunderbolt:1;\t/* Thunderbolt controller */\n \tunsigned int\tis_cxl:1;               /* Compute Express Link (CXL) */\n+\tunsigned int\tcxl_unmask_sbr:1;\t/* SBR unmask allowed by user */\n \t/*\n \t * Devices marked being untrusted are the ones that can potentially\n \t * execute DMA attacks and similar. They are typically connected\n@@ -1606,6 +1607,21 @@ int devm_request_pci_bus_resources(struct device *dev,\n /* Temporary until new and working PCI SBR API in place */\n int pci_bridge_secondary_bus_reset(struct pci_dev *dev);\n \n+/**\n+ * struct pci_cxl_sbr_region_ops - CXL region callbacks for a bus reset\n+ * @disable_regions: disable the regions below @dport, 0 or errno\n+ * @unbind_regions: unbind the drivers of the regions below @dport, leaving\n+ *\t\t    their memory online, for a link already contained\n+ * @enable_regions: re-enable the regions below @dport\n+ */\n+struct pci_cxl_sbr_region_ops {\n+\tint (*disable_regions)(struct pci_dev *dport);\n+\tvoid (*unbind_regions)(struct pci_dev *dport);\n+\tvoid (*enable_regions)(struct pci_dev *dport);\n+};\n+\n+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops);\n+\n #define __pci_bus_for_each_res0(bus, res, ...)\t\t\t\t\\\n \tfor (unsigned int __b = 0;\t\t\t\t\t\\\n \t     (res = pci_bus_resource_n(bus, __b)) || __b \u003c PCI_BRIDGE_RESOURCE_NUM; \\\ndiff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h\nindex facaa324bd86a..0eaa34db93cef 100644\n--- a/include/uapi/linux/pci_regs.h\n+++ b/include/uapi/linux/pci_regs.h\n@@ -1371,6 +1371,8 @@\n \n /* CXL r4.0, 8.1.5: Extensions DVSEC for Ports */\n #define PCI_DVSEC_CXL_PORT\t\t\t\t3\n+#define  PCI_DVSEC_CXL_PORT_EXT_STATUS\t\t\t0x0a\n+#define   PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP\t0x00000001\n #define  PCI_DVSEC_CXL_PORT_CTL\t\t\t\t0x0c\n #define   PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR\t\t0x00000001\n \ndiff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild\nindex 2be1df80fcc93..f18d41b299fec 100644\n--- a/tools/testing/cxl/Kbuild\n+++ b/tools/testing/cxl/Kbuild\n@@ -63,7 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/hdm.o\n cxl_core-y += $(CXL_CORE_SRC)/pmu.o\n cxl_core-y += $(CXL_CORE_SRC)/cdat.o\n cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o\n-cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o\n+cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o $(CXL_CORE_SRC)/dport_sbr.o\n cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o\n cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o\n cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o\n"
}

2/1 2026/08/25 02:50 agent patch-evaluator
3m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "CXL_BUS",
    "CXL_PCI",
    "CXL_REGION",
    "HOTPLUG_PCI_PCIE",
    "PCIE_DPC",
    "MEMORY_HOTPLUG"
  ],
  "FocusSymbols": [
    "cxl_offline_memory",
    "cxl_port_recommit_decoders",
    "cxl_port_save_hdm_state",
    "cxl_port_put_hdm_state",
    "cxl_enable_hdm",
    "cxl_set_mem_enable",
    "cxl_region_invalidate_memregion",
    "__pci_bridge_secondary_bus_reset",
    "pci_bridge_secondary_bus_reset",
    "pci_dpc_containment_active"
  ],
  "Reasoning": "The patch implements Secondary Bus Reset (SBR) support for CXL Downstream Ports. It introduces a new sysfs attribute `cxl_unmask_sbr` to allow unmasking SBR, and implements logic to disable/enable CXL regions during SBR. It also handles Downstream Port Containment (DPC) by unbinding regions instead of offlining them. The changes modify core PCI reset logic and CXL region management, which are reachable via sysfs and PCI error recovery in virtualized environments like QEMU.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 106959a31a9fda18580851af5ce38f8d304e1fa9
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Aug 25 02:50:40 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index b767db2c52cb7..7b0314da6f4d6 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -174,6 +174,22 @@ Description:
 		similiar to writing 1 to their individual "reset" file, so use
 		with caution.
 
+What:		/sys/bus/pci/devices/.../cxl_unmask_sbr
+Date:		August 2026
+Contact:	linux-pci@vger.kernel.org
+Description:
+		This is visible only for a CXL Downstream Port, that is a Root
+		Port or a Downstream Switch Port that publishes the CXL
+		Extensions DVSEC for Ports.  Such a Port ignores the Secondary
+		Bus Reset bit in its Bridge Control register unless its
+		"Unmask SBR" bit is set, and system firmware leaves that bit
+		clear by default.
+
+		Writing 1 allows the kernel to set "Unmask SBR" while it
+		resets the Port's secondary bus, so that the reset reaches the
+		component below the Port.  While this reads 0, a bus reset of
+		the Port fails instead.
+
 What:		/sys/bus/pci/devices/.../vpd
 Date:		February 2008
 Contact:	Ben Hutchings <bwh@kernel.org>
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index bcfe2d9f4adbd..ce89b251d34ac 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -1018,6 +1018,42 @@ int walk_memory_blocks(unsigned long start, unsigned long size,
 	return ret;
 }
 
+static int cxl_offline_memory_block(struct memory_block *mem, void *arg)
+{
+	int rc = device_offline(&mem->dev);
+
+	/* device_offline() returns a positive value when already offline. */
+	if (rc > 0)
+		return 0;
+
+	return rc;
+}
+
+/**
+ * cxl_offline_memory - offline the memory blocks spanning a physical range
+ * @start: start of the range, memory-block aligned
+ * @size: size of the range, a multiple of the memory block size
+ *
+ * Offline every memory block in [start, start + size). The blocks are offlined
+ * but not removed, so the range can be brought back online afterward. The
+ * caller owns the range and this performs no validation on it.
+ *
+ * Return: 0 on success, negative errno if a block cannot be offlined.
+ *
+ * Context: process context. Sleeps and takes the memory hotplug lock.
+ */
+int cxl_offline_memory(u64 start, u64 size)
+{
+	int rc;
+
+	lock_device_hotplug();
+	rc = walk_memory_blocks(start, size, NULL, cxl_offline_memory_block);
+	unlock_device_hotplug();
+
+	return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_offline_memory, "CXL_MHP");
+
 struct for_each_memory_block_cb_data {
 	walk_memory_blocks_func_t func;
 	void *arg;
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index ce7213818d3c0..4d4538182d81b 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -15,7 +15,7 @@ cxl_core-y += hdm.o
 cxl_core-y += pmu.o
 cxl_core-y += cdat.o
 cxl_core-$(CONFIG_TRACING) += trace.o
-cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o
+cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o dport_sbr.o
 cxl_core-$(CONFIG_CXL_MCE) += mce.o
 cxl_core-$(CONFIG_CXL_FEATURES) += features.o
 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 07555ae638594..b250fa3461847 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -13,6 +13,9 @@ extern const struct device_type cxl_pmu_type;
 
 extern struct attribute_group cxl_base_attribute_group;
 
+struct cxl_port *find_cxl_port(struct device *dport_dev,
+			       struct cxl_dport **dport);
+
 enum cxl_detach_mode {
 	DETACH_ONLY,
 	DETACH_INVALIDATE,
@@ -53,6 +56,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 int devm_cxl_add_dax_region(struct cxl_region *cxlr);
 int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
 void kill_regions(struct cxl_root_decoder *cxlrd);
+int cxl_region_invalidate_memregion(struct cxl_region *cxlr);
+struct pci_cxl_sbr_region_ops;
+extern const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops;
 
 #else
 static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -213,6 +219,28 @@ int cxl_gpf_port_setup(struct cxl_dport *dport);
 struct cxl_hdm;
 int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
 			struct cxl_endpoint_dvsec_info *info);
+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl);
+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val);
+/**
+ * struct cxl_hdm_state - one CXL port's HDM decoder programming, saved
+ * @global_ctrl: CXL HDM Decoder Global Control
+ * @nr_ctrl: number of entries in @ctrl
+ * @ctrl: CXL HDM Decoder n Control, indexed by decoder id
+ *
+ * Holds the fields of those two registers that the driver does not model, read
+ * before a reset and written back after it. Instances are held in an xarray
+ * keyed by the &struct cxl_port they were read from.
+ */
+struct cxl_hdm_state {
+	u32 global_ctrl;
+	int nr_ctrl;
+	u32 ctrl[];
+};
+
+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state);
+void cxl_port_put_hdm_state(struct xarray *hdm_state);
+int cxl_port_recommit_decoders(struct cxl_port *port,
+			       struct xarray *hdm_state);
 int cxl_port_get_possible_dports(struct cxl_port *port);
 
 #ifdef CONFIG_CXL_FEATURES
diff --git a/drivers/cxl/core/dport_sbr.c b/drivers/cxl/core/dport_sbr.c
new file mode 100644
index 0000000000000..823b63012f45b
--- /dev/null
+++ b/drivers/cxl/core/dport_sbr.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2026 Intel Corporation. */
+
+#include <linux/memregion.h>
+#include <linux/memory_hotplug.h>
+#include <linux/memory.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <cxl.h>
+#include "core.h"
+
+/*
+ * cxl_region_unbind - take a region out of service ahead of a reset
+ * @cxlr: region routed through the CXL Downstream Port being reset
+ *
+ * Unbind the region driver, which tears down everything built on the region:
+ * the dax region device, its dax device and the driver bound to it. An SBR
+ * zeroes the downstream bus number, so a region left bound would decode to a
+ * device in reset.
+ *
+ * The memory the region hosts is left as it is. A caller that reaches a live
+ * device offlines it first; see cxl_region_disable().
+ *
+ * Context: process context. Driver unbind sleeps, so this cannot run in atomic
+ * context.
+ */
+static void cxl_region_unbind(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+
+	device_release_driver(&cxlr->dev);
+	dev_dbg(&cxlr->dev, "%s: region unbound before reset, HPA %pr\n",
+		__func__, p->res);
+}
+
+/*
+ * cxl_region_disable - make a region inactive ahead of a Secondary Bus Reset
+ * @cxlr: region routed through the CXL Downstream Port being reset
+ *
+ * Offline the memory blocks the region owns and unbind its driver. An SBR
+ * zeroes the downstream bus number, so a region left live as System RAM would
+ * be accessed while the device is in reset. On offline failure return the error
+ * so the caller aborts the reset; the memory is never force-removed.
+ *
+ * Context: process context. Offlining and driver unbind sleep and take the
+ * memory hotplug lock, so this cannot run in atomic context.
+ */
+static int cxl_region_disable(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+	unsigned long block_size;
+	u64 start, end;
+	int rc;
+
+	/*
+	 * Per CXL r4.0 sec 9.13.1 an Interleave Set has a Base HPA and a Size
+	 * that are multiples of 256 MB, while a memory block spans up to 2 GB.
+	 * A block overlapping either end of the range therefore also covers
+	 * memory outside this region, so round the range inward to block
+	 * granularity as dax_kmem did when it onlined the range. Offlining a
+	 * straddling block would migrate pages that the reset does not affect.
+	 */
+	block_size = memory_block_size_bytes();
+	start = ALIGN(p->res->start, block_size);
+	end = ALIGN_DOWN(p->res->end + 1, block_size);
+	if (start >= end) {
+		dev_dbg(&cxlr->dev, "%s: HPA %pr spans no whole memory block, no System RAM to offline\n",
+			__func__, p->res);
+	} else {
+		rc = cxl_offline_memory(start, end - start);
+		if (rc) {
+			dev_warn(&cxlr->dev, "offline System RAM failed before reset: %d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	rc = cxl_region_invalidate_memregion(cxlr);
+	if (rc) {
+		dev_warn(&cxlr->dev, "CPU cache invalidate failed before reset: %d\n",
+			 rc);
+		return rc;
+	}
+
+	cxl_region_unbind(cxlr);
+	dev_dbg(&cxlr->dev, "%s: System RAM offline, region disabled before reset, HPA %pr\n",
+		__func__, p->res);
+
+	return 0;
+}
+
+/*
+ * cxl_region_enable - restore a region after a Secondary Bus Reset
+ * @cxlr: region disabled by cxl_region_disable() before the reset
+ *
+ * Rebind the region driver. The System RAM is left offline; bringing it back
+ * online is a separate administrative step.
+ */
+static void cxl_region_enable(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+
+	if (device_attach(&cxlr->dev) < 0) {
+		dev_dbg(&cxlr->dev, "driver re-attach failed after reset\n");
+		return;
+	}
+
+	dev_dbg(&cxlr->dev, "%s: region re-enabled after reset, HPA %pr, IW %d, IG %d\n",
+		__func__, p->res, p->interleave_ways, p->interleave_granularity);
+}
+
+/*
+ * Collect the regions with a member endpoint routed through @dport_pci, the
+ * CXL Downstream Port about to be reset. cxl_rwsem.region keeps the topology
+ * stable for the duration of the walk only. Each collected region is pinned
+ * with get_device() so the object survives after the lock is dropped, since
+ * cxl_region_disable()/cxl_region_enable() run with the rwsem released (they
+ * unbind and rebind the region driver). Hence snapshot the set first.
+ */
+static int cxl_sbr_collect_regions(struct pci_dev *dport_pci,
+				   struct xarray *regions)
+{
+	struct cxl_region_ref *cxl_rr;
+	struct cxl_dport *dport;
+	unsigned long index;
+	int count = 0;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port) {
+		pci_dbg(dport_pci, "no CXL port found for reset dport\n");
+		return 0;
+	}
+
+	guard(rwsem_read)(&cxl_rwsem.region);
+	xa_for_each(&port->regions, index, cxl_rr) {
+		struct cxl_region *cxlr = cxl_rr->region;
+		struct cxl_ep *ep;
+		unsigned long ep_index;
+
+		/* Skip unless a region endpoint sits below the reset dport. */
+		xa_for_each(&cxl_rr->endpoints, ep_index, ep)
+			if (ep->dport == dport)
+				break;
+		if (!ep) {
+			dev_dbg(&cxlr->dev, "%s: no endpoint below %s, region excluded\n",
+				__func__, dev_name(dport->dport_dev));
+			continue;
+		}
+
+		get_device(&cxlr->dev);
+		rc = xa_insert(regions, (unsigned long)cxlr, cxlr, GFP_KERNEL);
+		if (rc) {
+			put_device(&cxlr->dev);
+			return rc;
+		}
+		dev_dbg(&cxlr->dev, "%s: endpoint below %s, region collected\n",
+			__func__, dev_name(dport->dport_dev));
+		count++;
+	}
+
+	dev_dbg(&port->dev, "%d region(s) routed through %s\n", count,
+		dev_name(dport->dport_dev));
+	return 0;
+}
+
+static void cxl_sbr_put_regions(struct xarray *regions)
+{
+	struct cxl_region *cxlr;
+	unsigned long index;
+
+	xa_for_each(regions, index, cxlr)
+		put_device(&cxlr->dev);
+	xa_destroy(regions);
+}
+
+/*
+ * The reset cleared the HDM Decoder registers of every CXL component below
+ * @dport_pci, so restore them from the settings the driver holds and from
+ * @hdm_state, the register fields the driver does not model, saved before the
+ * reset. Takes cxl_rwsem.region for read, which cxl_port_recommit_decoders()
+ * requires. The caller has already disabled the regions, so nothing reaches the
+ * decoders being reprogrammed.
+ */
+static void cxl_sbr_recommit_decoders(struct pci_dev *dport_pci,
+				      struct xarray *hdm_state)
+{
+	struct cxl_dport *dport;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port) {
+		pci_dbg(dport_pci, "no CXL port owns this Downstream Port\n");
+		return;
+	}
+
+	pci_dbg(dport_pci, "restoring HDM decode below %s\n", dev_name(&port->dev));
+
+	guard(rwsem_read)(&cxl_rwsem.region);
+	rc = cxl_port_recommit_decoders(port, hdm_state);
+	if (rc)
+		pci_warn(dport_pci, "HDM decode restore failed: %d\n", rc);
+}
+
+/*
+ * The HDM decoder control registers the reset is about to clear, held from the
+ * disable to the enable of one Downstream Port and indexed by that Port's
+ * struct pci_dev, so resets of different Ports do not share an entry.
+ */
+static DEFINE_XARRAY(cxl_sbr_hdm_state);
+
+static void cxl_sbr_drop_hdm_state(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+
+	hdm_state = xa_erase(&cxl_sbr_hdm_state, (unsigned long)dport_pci);
+	if (!hdm_state)
+		return;
+
+	cxl_port_put_hdm_state(hdm_state);
+	kfree(hdm_state);
+}
+
+/*
+ * Record the control registers of every port below @dport_pci before the reset
+ * clears them. cxl_sbr_enable_regions() consumes the set and drops it.
+ */
+static int cxl_sbr_save_hdm_state(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+	struct cxl_dport *dport;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port)
+		return 0;
+
+	hdm_state = kzalloc_obj(*hdm_state);
+	if (!hdm_state)
+		return -ENOMEM;
+
+	xa_init(hdm_state);
+
+	scoped_guard(rwsem_read, &cxl_rwsem.region)
+		rc = cxl_port_save_hdm_state(port, hdm_state);
+
+	if (!rc)
+		rc = xa_insert(&cxl_sbr_hdm_state, (unsigned long)dport_pci,
+			       hdm_state, GFP_KERNEL);
+	if (rc) {
+		cxl_port_put_hdm_state(hdm_state);
+		kfree(hdm_state);
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Disable the regions routed through the Downstream Port being reset. On
+ * failure re-enable the regions already disabled and return the error so the
+ * PCI core aborts the reset with the topology unchanged.
+ */
+static int cxl_sbr_disable_regions(struct pci_dev *dport_pci)
+{
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+	int rc;
+
+	rc = cxl_sbr_save_hdm_state(dport_pci);
+	if (rc)
+		return rc;
+
+	xa_init(&regions);
+
+	rc = cxl_sbr_collect_regions(dport_pci, &regions);
+	if (rc)
+		goto out;
+
+	xa_for_each(&regions, index, cxlr) {
+		rc = cxl_region_disable(cxlr);
+		if (rc)
+			break;
+	}
+
+	/*
+	 * On failure restore every collected region and return the error so the
+	 * PCI core aborts the reset before touching the hardware. Re-enabling a
+	 * region left untouched is a no-op, so enabling the whole set also
+	 * recovers the region whose offline failed midway.
+	 */
+	if (rc) {
+		dev_dbg(&dport_pci->dev, "%s: disable failed (%d), re-enabling collected regions and aborting reset\n",
+			__func__, rc);
+		xa_for_each(&regions, index, cxlr)
+			cxl_region_enable(cxlr);
+	}
+
+out:
+	cxl_sbr_put_regions(&regions);
+	/* No enable_regions() call follows an aborted reset, so drop the set. */
+	if (rc)
+		cxl_sbr_drop_hdm_state(dport_pci);
+	return rc;
+}
+
+/*
+ * Unbind the regions routed through the Downstream Port being reset, leaving
+ * their memory online. Used on the DPC recovery path, where dpc_reset_link()
+ * clears DPC Trigger Status and enters the reset without waiting for the link,
+ * so the device may still be unreachable and the page migration that an offline
+ * performs would have no device to read from.
+ *
+ * Unbinding cannot fail, so unlike cxl_sbr_disable_regions() this never aborts
+ * the reset. The memory stays online across the reset with no region decoding
+ * it; cxl_sbr_enable_regions() reprograms the decoders on the way out.
+ */
+static void cxl_sbr_unbind_regions(struct pci_dev *dport_pci)
+{
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+
+	if (cxl_sbr_save_hdm_state(dport_pci))
+		pci_warn(dport_pci, "HDM state not saved, decode will not be restored\n");
+
+	xa_init(&regions);
+
+	cxl_sbr_collect_regions(dport_pci, &regions);
+
+	xa_for_each(&regions, index, cxlr)
+		cxl_region_unbind(cxlr);
+
+	cxl_sbr_put_regions(&regions);
+}
+
+/*
+ * Re-enable the regions disabled by cxl_sbr_disable_regions(). Restore the HDM
+ * decode first: a region cannot serve memory through decoders that are not
+ * programmed, so its driver must not re-attach before they are.
+ */
+static void cxl_sbr_enable_regions(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+
+	xa_init(&regions);
+
+	cxl_sbr_collect_regions(dport_pci, &regions);
+
+	hdm_state = xa_load(&cxl_sbr_hdm_state, (unsigned long)dport_pci);
+	if (hdm_state)
+		cxl_sbr_recommit_decoders(dport_pci, hdm_state);
+	else
+		pci_warn(dport_pci, "no saved HDM state, decode not restored\n");
+
+	xa_for_each(&regions, index, cxlr)
+		cxl_region_enable(cxlr);
+
+	cxl_sbr_put_regions(&regions);
+	cxl_sbr_drop_hdm_state(dport_pci);
+}
+
+const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops = {
+	.disable_regions = cxl_sbr_disable_regions,
+	.unbind_regions = cxl_sbr_unbind_regions,
+	.enable_regions = cxl_sbr_enable_regions,
+};
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0c80b76a5f9b4..9839d2592d206 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -849,6 +849,347 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
 	return 0;
 }
 
+/**
+ * cxl_decoder_recommit - reprogram @cxld's HDM decoder registers and commit
+ * @cxld: decoder to reprogram from its cached settings
+ * @ctrl: CXL HDM Decoder n Control value to restore under the cached settings
+ *
+ * A reset of an upstream link clears the HDM decoder registers of every
+ * component below it, dropping Committed while the driver still holds the
+ * settings that were in effect. Restore those settings and commit.
+ *
+ * setup_hw_decoder() rewrites only Interleave Granularity, Interleave Ways and
+ * Target Range Type, so the rest of the control register would come from a read
+ * of the reset defaults. Per CXL r4.0 sec 8.2.4.20.7 Table 8-123 that register
+ * also holds BI, UIO, Upstream Interleave Granularity, Upstream Interleave Ways
+ * and Lock On Commit, none of which the driver models, and sec 8.2.4.20.12 makes
+ * device operation undefined if a device that requires BI is committed without
+ * it. Write @ctrl first so those fields are in place, with Commit masked off
+ * until setup_hw_decoder() has written the range.
+ *
+ * A decoder that hardware still reports Committed kept its programming across
+ * the reset and needs no work. A decoder with no ->commit is driven through the
+ * DVSEC ranges or is a passthrough decoder, and has no registers to program.
+ *
+ * Section 8.2.4.20.13 requires the traffic targeting @cxld to be quiesced while
+ * it is reprogrammed, which the caller owns. @cxld decodes nothing until the
+ * commit completes.
+ *
+ * Return: 0 on success or if @cxld needs no reprogramming, negative errno if the
+ * commit times out or if the hardware reports a commit error.
+ */
+static int cxl_decoder_recommit(struct cxl_decoder *cxld, u32 ctrl)
+{
+	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+	struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
+	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
+	u32 hw_ctrl;
+	int rc;
+
+	if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
+		return 0;
+
+	if (!cxld->commit)
+		return 0;
+
+	hw_ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
+	if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, hw_ctrl)) {
+		dev_dbg(&cxld->dev, "%s: still committed, no reprogram needed\n",
+			__func__);
+		return 0;
+	}
+
+	writel(ctrl & ~(CXL_HDM_DECODER0_CTRL_COMMIT |
+			CXL_HDM_DECODER0_CTRL_COMMITTED |
+			CXL_HDM_DECODER0_CTRL_COMMIT_ERROR),
+	       hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
+
+	scoped_guard(rwsem_read, &cxl_rwsem.dpa)
+		setup_hw_decoder(cxld, hdm);
+
+	rc = cxld_await_commit(hdm, cxld->id);
+	if (rc) {
+		dev_warn(&cxld->dev, "%s: failed to commit decoder: %d\n",
+			 __func__, rc);
+		return rc;
+	}
+
+	dev_dbg(&cxld->dev, "%s: reprogrammed HPA %#llx-%#llx\n",
+		__func__, cxld->hpa_range.start, cxld->hpa_range.end);
+
+	return 0;
+}
+
+static int __cxl_endpoint_decoder_is_emulated(struct device *dev, void *data)
+{
+	if (!is_endpoint_decoder(dev))
+		return 0;
+
+	return !to_cxl_decoder(dev)->commit;
+}
+
+/* Only the DVSEC setup path leaves ->commit NULL. */
+static bool cxl_endpoint_decoders_are_emulated(struct cxl_port *endpoint)
+{
+	return device_for_each_child(&endpoint->dev, NULL,
+				     __cxl_endpoint_decoder_is_emulated);
+}
+
+struct cxl_recommit_ctx {
+	const struct cxl_hdm_state *state;
+	int *first_rc;
+};
+
+static int __cxl_port_recommit_decoder(struct device *dev, void *data)
+{
+	struct cxl_recommit_ctx *ctx = data;
+	struct cxl_decoder *cxld;
+	int rc;
+
+	if (!(is_switch_decoder(dev) || is_endpoint_decoder(dev)))
+		return 0;
+
+	cxld = to_cxl_decoder(dev);
+
+	if (cxld->id >= ctx->state->nr_ctrl) {
+		dev_warn(&cxld->dev, "%s: no saved control register\n",
+			 __func__);
+		if (!*ctx->first_rc)
+			*ctx->first_rc = -ENODATA;
+		return 0;
+	}
+
+	/*
+	 * Reprogram every decoder the walk reaches. Stopping at the first
+	 * failure would leave the rest of the path decoding nothing, so record
+	 * the first error and continue.
+	 */
+	rc = cxl_decoder_recommit(cxld, ctx->state->ctrl[cxld->id]);
+	if (rc && !*ctx->first_rc)
+		*ctx->first_rc = rc;
+
+	return 0;
+}
+
+/*
+ * Restore CXL.mem decode on @cxlmd before any of its decoders is committed. A
+ * reset clears the endpoint's HDM Decoder Global Control and the DVSEC CXL
+ * Control, and per CXL r4.0 sec 8.2.4.20.2 Table 8-118 a device decodes CXL.mem
+ * with the DVSEC range registers while HDM Decoder Enable is clear. Committing
+ * a decoder in that state does not establish the route. An endpoint with no HDM
+ * decoder registers is driven through the DVSEC ranges and has nothing to
+ * enable. So is an endpoint whose decoders are emulated from those ranges, and
+ * setting HDM Decoder Enable there would switch it to decoders locked against
+ * reprogramming.
+ *
+ * @global_ctrl is the Global Control value to enable decode in. That register
+ * also holds Poison On Decode Error Enable, which the driver does not model, so
+ * the caller supplies the value it saved rather than one read back after the
+ * reset.
+ */
+static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,
+					  u32 global_ctrl)
+{
+	struct cxl_port *endpoint = cxlmd->endpoint;
+	struct cxl_hdm *cxlhdm = dev_get_drvdata(&endpoint->dev);
+	int rc;
+
+	if (!cxlhdm || !cxlhdm->regs.hdm_decoder)
+		return 0;
+
+	if (cxl_endpoint_decoders_are_emulated(endpoint))
+		return 0;
+
+	cxl_enable_hdm(cxlhdm, global_ctrl);
+
+	rc = cxl_set_mem_enable(cxlmd->cxlds, PCI_DVSEC_CXL_MEM_ENABLE);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+/**
+ * cxl_port_recommit_decoders - reprogram the HDM decoders below @port
+ * @port: CXL port whose downstream decoders to reprogram
+ * @hdm_state: saved &struct cxl_hdm_state per port, keyed by &struct cxl_port
+ *
+ * Reprogram the HDM decoders below @port that lost their programming. Every
+ * endpoint beneath @port is restored along its whole path, from the endpoint up
+ * to the last port below @port. A decoder that hardware still reports committed
+ * is left untouched.
+ *
+ * Per CXL r4.0 sec 8.2.4.20.13 decoder m must be committed before decoder m+1
+ * while reprogramming, so let device_for_each_child() visit each port's decoders
+ * in instance order. Each path is walked from the endpoint upward, the order
+ * cxl_region_decode_commit() uses.
+ *
+ * The endpoints are reprogrammed one after another, so an interleaved HPA range
+ * decodes through only part of its interleave set until the last member is
+ * done. Per CXL r4.0 sec 8.2.4.20.13 software owns quiescing the traffic that
+ * targets a decoder being reprogrammed: a read that no decoder positively
+ * decodes returns all 1s or poison, and per Table 8-118 such a write is
+ * dropped. Nothing here can detect a stray access, so the caller carries that
+ * duty.
+ *
+ * Context: caller must hold @cxl_rwsem.region to keep the topology and the
+ * switch decoder target lists stable across the walk, and must have quiesced
+ * every access to the HPA ranges decoded below @port.
+ *
+ * A port with no entry in @hdm_state was not saved, so its decoders are left
+ * alone rather than committed with whatever the reset left in the fields the
+ * driver does not model.
+ *
+ * Return: 0 on success, negative errno of the first decoder that failed or
+ * -ENODATA if a port on the path has no saved state.
+ */
+int cxl_port_recommit_decoders(struct cxl_port *port, struct xarray *hdm_state)
+{
+	struct cxl_ep *port_ep;
+	unsigned long index;
+	int first_rc = 0;
+
+	lockdep_assert_held(&cxl_rwsem.region);
+
+	xa_for_each(&port->endpoints, index, port_ep) {
+		struct cxl_memdev *cxlmd = to_cxl_memdev(port_ep->ep);
+		struct cxl_hdm_state *state;
+		struct cxl_port *iter;
+		int rc;
+
+		if (IS_ERR_OR_NULL(cxlmd->endpoint))
+			continue;
+
+		state = xa_load(hdm_state, (unsigned long)cxlmd->endpoint);
+		if (!state) {
+			dev_warn(&cxlmd->dev, "%s: no saved HDM state\n",
+				 __func__);
+			if (!first_rc)
+				first_rc = -ENODATA;
+			continue;
+		}
+
+		rc = cxl_endpoint_enable_hdm_decode(cxlmd, state->global_ctrl);
+		if (rc) {
+			dev_warn(&cxlmd->dev,
+				 "%s: failed to enable HDM decode: %d\n",
+				 __func__, rc);
+			if (!first_rc)
+				first_rc = rc;
+			continue;
+		}
+
+		/*
+		 * Walk from the endpoint up to @port so a decoder is committed
+		 * only after the decoder it routes to. @port is the last parent
+		 * visited by the walk, and it is excluded.
+		 */
+		for (iter = cxlmd->endpoint; iter && iter != port;
+		     iter = parent_port_of(iter)) {
+			struct cxl_recommit_ctx ctx = {
+				.state = xa_load(hdm_state, (unsigned long)iter),
+				.first_rc = &first_rc,
+			};
+
+			if (!ctx.state) {
+				dev_warn(&iter->dev, "%s: no saved HDM state\n",
+					 __func__);
+				if (!first_rc)
+					first_rc = -ENODATA;
+				continue;
+			}
+
+			device_for_each_child(&iter->dev, &ctx,
+					      __cxl_port_recommit_decoder);
+		}
+	}
+
+	return first_rc;
+}
+
+/**
+ * cxl_port_save_hdm_state - record the HDM decoder control registers below @port
+ * @port: CXL port whose downstream decoders to record
+ * @hdm_state: xarray to fill, one entry per port, keyed by &struct cxl_port
+ *
+ * Read the CXL HDM Decoder Global Control and every CXL HDM Decoder n Control
+ * register of the ports below @port. Those hold the fields
+ * cxl_port_recommit_decoders() cannot rebuild from the driver's cached settings,
+ * so they have to be read while the registers still hold them.
+ *
+ * The set of ports is the same one cxl_port_recommit_decoders() walks. A port
+ * with no HDM decoder registers has nothing to record and gets no entry.
+ *
+ * Context: caller must hold @cxl_rwsem.region.
+ *
+ * Return: 0 on success, negative errno if an entry cannot be allocated or
+ * inserted.
+ */
+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state)
+{
+	struct cxl_ep *port_ep;
+	unsigned long index;
+
+	lockdep_assert_held(&cxl_rwsem.region);
+
+	xa_for_each(&port->endpoints, index, port_ep) {
+		struct cxl_memdev *cxlmd = to_cxl_memdev(port_ep->ep);
+		struct cxl_port *iter;
+
+		if (IS_ERR_OR_NULL(cxlmd->endpoint))
+			continue;
+
+		for (iter = cxlmd->endpoint; iter && iter != port;
+		     iter = parent_port_of(iter)) {
+			struct cxl_hdm *cxlhdm = dev_get_drvdata(&iter->dev);
+			struct cxl_hdm_state *state;
+			void __iomem *hdm;
+			int rc;
+
+			if (xa_load(hdm_state, (unsigned long)iter))
+				continue;
+
+			if (!cxlhdm || !cxlhdm->regs.hdm_decoder)
+				continue;
+
+			hdm = cxlhdm->regs.hdm_decoder;
+			state = kzalloc_flex(*state, ctrl,
+					     cxlhdm->decoder_count);
+			if (!state)
+				return -ENOMEM;
+
+			state->global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+			state->nr_ctrl = cxlhdm->decoder_count;
+			for (int i = 0; i < state->nr_ctrl; i++)
+				state->ctrl[i] =
+					readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
+
+			rc = xa_insert(hdm_state, (unsigned long)iter, state,
+				       GFP_KERNEL);
+			if (rc) {
+				kfree(state);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cxl_port_put_hdm_state - release a set filled by cxl_port_save_hdm_state()
+ * @hdm_state: xarray to empty
+ */
+void cxl_port_put_hdm_state(struct xarray *hdm_state)
+{
+	struct cxl_hdm_state *state;
+	unsigned long index;
+
+	xa_for_each(hdm_state, index, state)
+		kfree(state);
+	xa_destroy(hdm_state);
+}
+
 static int commit_reap(struct device *dev, void *data)
 {
 	struct cxl_port *port = to_cxl_port(dev->parent);
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index e4338fd7e01b4..a7a2b84293e9c 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -179,7 +179,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_await_media_ready, "CXL");
 
-static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
 {
 	struct pci_dev *pdev = to_pci_dev(cxlds->dev);
 	int d = cxlds->cxl_dvsec;
@@ -247,14 +247,23 @@ static void disable_hdm(void *_cxlhdm)
 	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 }
 
-static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm)
+/*
+ * @global_ctrl is the CXL HDM Decoder Global Control value to enable decode in.
+ * A caller restoring decode after a reset passes the value it saved, so the
+ * fields the driver does not model are not left at their reset defaults.
+ */
+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl)
 {
 	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
-	u32 global_ctrl;
 
-	global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 	writel(global_ctrl | CXL_HDM_DECODER_ENABLE,
 	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+}
+
+static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm,
+			       u32 global_ctrl)
+{
+	cxl_enable_hdm(cxlhdm, global_ctrl);
 
 	return devm_add_action_or_reset(host, disable_hdm, cxlhdm);
 }
@@ -398,7 +407,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
 	 * enable and use the HDM Decoder Capability registers.
 	 */
 	if (!info->mem_enabled) {
-		rc = devm_cxl_enable_hdm(&port->dev, cxlhdm);
+		rc = devm_cxl_enable_hdm(&port->dev, cxlhdm, global_ctrl);
 		if (rc)
 			return rc;
 
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 1215ee4f40351..66a6d513843e5 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1392,8 +1392,8 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
 	return NULL;
 }
 
-static struct cxl_port *find_cxl_port(struct device *dport_dev,
-				      struct cxl_dport **dport)
+struct cxl_port *find_cxl_port(struct device *dport_dev,
+			       struct cxl_dport **dport)
 {
 	struct cxl_find_port_ctx ctx = {
 		.dport_dev = dport_dev,
diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
index 99fb00949c2fa..568a906f3b50f 100644
--- a/drivers/cxl/core/ras.c
+++ b/drivers/cxl/core/ras.c
@@ -323,6 +323,16 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
 		}
 		return PCI_ERS_RESULT_CAN_RECOVER;
 	case pci_channel_io_frozen:
+		/*
+		 * A Port on the path in DPC means dpc_reset_link() is about to
+		 * reset the link, and that path takes the CXL regions out of
+		 * service and restores the HDM decode itself. Keep the memdev
+		 * driver bound so the endpoint and its decoders are still there
+		 * to restore.
+		 */
+		if (pci_dpc_containment_active(pdev))
+			return PCI_ERS_RESULT_NEED_RESET;
+
 		dev_warn(&pdev->dev,
 			 "%s: frozen state error detected, disable CXL.mem\n",
 			 dev_name(dev));
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1e211542b6b64..d1dd4924fba17 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -12,6 +12,7 @@
 #include <linux/idr.h>
 #include <linux/memory-tiers.h>
 #include <linux/string_choices.h>
+#include <linux/pci.h>
 #include <cxlmem.h>
 #include <cxl.h>
 #include "core.h"
@@ -222,7 +223,7 @@ static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port,
 	return xa_load(&port->regions, (unsigned long)cxlr);
 }
 
-static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
+int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
 {
 	if (!cpu_cache_has_invalidate_memregion()) {
 		if (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) {
@@ -4263,14 +4264,17 @@ static struct cxl_driver cxl_region_driver = {
 
 int cxl_region_init(void)
 {
+	pci_cxl_set_sbr_region_ops(&cxl_sbr_region_ops);
 	return cxl_driver_register(&cxl_region_driver);
 }
 
 void cxl_region_exit(void)
 {
 	cxl_driver_unregister(&cxl_region_driver);
+	pci_cxl_set_sbr_region_ops(NULL);
 }
 
 MODULE_IMPORT_NS("CXL");
 MODULE_IMPORT_NS("DEVMEM");
+MODULE_IMPORT_NS("CXL_MHP");
 MODULE_ALIAS_CXL(CXL_DEVICE_REGION);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 5ec0b245a69bd..ba1cae6ab4fae 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -573,6 +573,33 @@ static ssize_t reset_subordinate_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(reset_subordinate);
 
+static ssize_t cxl_unmask_sbr_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	return sysfs_emit(buf, "%u\n", pdev->cxl_unmask_sbr);
+}
+
+static ssize_t cxl_unmask_sbr_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	unsigned long val;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (kstrtoul(buf, 0, &val) < 0)
+		return -EINVAL;
+
+	pdev->cxl_unmask_sbr = !!val;
+
+	return count;
+}
+static DEVICE_ATTR_RW(cxl_unmask_sbr);
+
 #if defined(CONFIG_PM) && defined(CONFIG_ACPI)
 static ssize_t d3cold_allowed_store(struct device *dev,
 				    struct device_attribute *attr,
@@ -650,6 +677,7 @@ static struct attribute *pci_bridge_attrs[] = {
 	&dev_attr_subordinate_bus_number.attr,
 	&dev_attr_secondary_bus_number.attr,
 	&dev_attr_reset_subordinate.attr,
+	&dev_attr_cxl_unmask_sbr.attr,
 	NULL,
 };
 
@@ -1824,6 +1852,9 @@ static umode_t pci_bridge_attrs_are_visible(struct kobject *kobj,
 	struct device *dev = kobj_to_dev(kobj);
 	struct pci_dev *pdev = to_pci_dev(dev);
 
+	if (a == &dev_attr_cxl_unmask_sbr.attr && !is_cxl_dport(pdev))
+		return 0;
+
 	if (pci_is_bridge(pdev))
 		return a->mode;
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 77b17b13ee615..08873ea3957b2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -23,6 +23,7 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
+#include <linux/string_choices.h>
 #include <linux/log2.h>
 #include <linux/logic_pio.h>
 #include <linux/device.h>
@@ -4844,21 +4845,235 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
 	pci_reset_secondary_bus(dev);
 }
 
+/*
+ * Registered by the CXL core to disable and re-enable the regions mapped
+ * through a CXL Downstream Port across a Secondary Bus Reset. NULL whenever
+ * the CXL region code is absent: not built, or built as a module not loaded.
+ */
+static const struct pci_cxl_sbr_region_ops *cxl_sbr_region_ops;
+
+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops)
+{
+	cxl_sbr_region_ops = ops;
+}
+EXPORT_SYMBOL_GPL(pci_cxl_set_sbr_region_ops);
+
+struct cxl_sbr_ctx {
+	u16 port_ctl;
+	u16 acs_ctrl;
+	u16 command;
+};
+
+bool is_cxl_dport(struct pci_dev *dev)
+{
+	return pcie_is_cxl(dev) && pcie_downstream_port(dev);
+}
+
+u16 cxl_port_dvsec(struct pci_dev *dev)
+{
+	return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
+					 PCI_DVSEC_CXL_PORT);
+}
+
+static int cxl_sbr_prepare(struct pci_dev *bridge, u16 dvsec,
+			   struct cxl_sbr_ctx *ctx,
+			   enum cxl_sbr_region_action action)
+{
+	int rc;
+
+	if (action == CXL_SBR_OFFLINE_AND_UNBIND && !cxl_sbr_allowed(bridge)) {
+		pci_info(bridge, "SBR masked, write 1 to cxl_unmask_sbr to allow a bus reset\n");
+		return -ENOTTY;
+	}
+
+	/*
+	 * CXL_SBR_UNBIND: the link is already down, so offlining the regions'
+	 * memory would take the reads that page migration performs as a machine
+	 * check. Per PCIe r7.0 sec 2.9.3 the Port answers a Non-Posted Request
+	 * with an Unsupported Request or Completer Abort completion while it is
+	 * in DPC. Unbinding never fails, so the reset always goes ahead.
+	 *
+	 * CXL_SBR_OFFLINE_AND_UNBIND: the device is reachable, so offline the
+	 * memory first and abort the reset before touching hardware if that
+	 * fails.
+	 */
+	if (cxl_sbr_region_ops && action == CXL_SBR_UNBIND) {
+		cxl_sbr_region_ops->unbind_regions(bridge);
+	} else if (cxl_sbr_region_ops) {
+		rc = cxl_sbr_region_ops->disable_regions(bridge);
+		if (rc)
+			return rc;
+	}
+
+	/* CXL r4.0 sec 8.1.5.2, Table 8-32: set Unmask SBR so the Port issues Hot Reset. */
+	pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, &ctx->port_ctl);
+	pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+			      ctx->port_ctl | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);
+
+	pci_read_config_word(bridge, PCI_COMMAND, &ctx->command);
+	pci_clear_master(bridge);
+
+	/* CXL r4.0 sec 8.1.5.1: Disable ACS SV bit before SBR */
+	if (bridge->acs_cap) {
+		pci_read_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL, &ctx->acs_ctrl);
+		pci_dbg(bridge, "%s: ACS SV %s\n", __func__,
+			str_enabled_disabled(ctx->acs_ctrl & PCI_ACS_SV));
+		pci_write_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL,
+				      ctx->acs_ctrl & ~PCI_ACS_SV);
+	}
+
+	return 0;
+}
+
+/*
+ * CXL r4.0 sec 8.1.5.1, Table 8-31: the Port sets PM Init Complete within
+ * 100 ms of link-up. Restoring ACS Source Validation before then makes the
+ * Port reject the downstream Component's Requester-Bus-0 IP2PM message, so
+ * poll for completion before restoring config.
+ */
+static bool cxl_port_pm_init_is_complete(struct pci_dev *bridge, u16 dvsec)
+{
+	unsigned long start = jiffies;
+	unsigned long timeout = start + msecs_to_jiffies(100);
+	u16 status;
+
+	do {
+		pci_read_config_word(bridge,
+				     dvsec + PCI_DVSEC_CXL_PORT_EXT_STATUS,
+				     &status);
+		if (!PCI_POSSIBLE_ERROR(status) &&
+		    (status & PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP)) {
+			pci_dbg(bridge, "%s: PM Init Complete set after %u ms, ext status %#06x\n",
+				__func__, jiffies_to_msecs(jiffies - start), status);
+			return true;
+		}
+		msleep(10);
+	} while (time_before(jiffies, timeout));
+
+	pci_warn(bridge, "%s: PM Init Complete not set after %u ms, ext status %#06x\n",
+		 __func__, jiffies_to_msecs(jiffies - start), status);
+
+	return false;
+}
+
+static int cxl_sbr_restore_config_space(struct pci_dev *dev, void *userdata)
+{
+	pci_restore_config_space(dev);
+	pci_dbg(dev, "%s: config space restored\n", __func__);
+
+	return 0;
+}
+
+/*
+ * The CXL region ops that run next read the HDM Decoders through a Base Address
+ * Register the reset returned to its initialization value, so restore the
+ * header of every device below @bridge first. Restoring also re-captures each
+ * Bus Number before the Port's ACS Source Validation comes back: a device that
+ * has completed no Type 0 Configuration Write since the reset sources Requests
+ * with Bus 0, which the Port rejects as an ACS Violation.
+ *
+ * Only the header is restored. The capability state each caller saved is its own
+ * to replay, and the ->reset_done() callbacks pci_dev_restore() invokes must
+ * fire once, from the caller that owns the reset.
+ */
+static void cxl_sbr_restore_subordinate(struct pci_dev *bridge)
+{
+	if (!bridge->subordinate)
+		return;
+
+	/* Parents before children: a child answers once its parent forwards. */
+	pci_walk_bus(bridge->subordinate, cxl_sbr_restore_config_space, NULL);
+}
+
+static void cxl_sbr_complete(struct pci_dev *bridge, u16 dvsec,
+			     const struct cxl_sbr_ctx *ctx)
+{
+	u16 val;
+
+	/* CXL r4.0 sec 8.1.5.1: wait for PM Init before restoring ACS SV. */
+	if (!cxl_port_pm_init_is_complete(bridge, dvsec))
+		pci_warn(bridge,
+			 "restoring ACS Source Validation before PM Init complete; Port may reject the Component's bus 0 traffic\n");
+
+	cxl_sbr_restore_subordinate(bridge);
+
+	/* CXL r4.0 sec 8.1.5.1: Re-enable ACS SV bit after SBR if it was enabled before */
+	if (bridge->acs_cap && (ctx->acs_ctrl & PCI_ACS_SV)) {
+		pci_read_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL, &val);
+		pci_write_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL,
+				      val | PCI_ACS_SV);
+		pci_dbg(bridge, "%s: ACS SV bit set\n", __func__);
+	} else {
+		pci_dbg(bridge, "%s: ACS SV bit not set (was not enabled before the SBR)\n",
+			__func__);
+	}
+
+	if (ctx->command & PCI_COMMAND_MASTER)
+		pci_set_master(bridge);
+
+	if (!(ctx->port_ctl & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)) {
+		pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, &val);
+		pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+				      val & ~PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);
+	}
+
+	if (cxl_sbr_region_ops)
+		cxl_sbr_region_ops->enable_regions(bridge);
+}
+
+/*
+ * __pci_bridge_secondary_bus_reset - assert Secondary Bus Reset on a bridge
+ * @dev: bridge device
+ * @action: what to do with the CXL regions reached through @dev
+ *
+ * See pci_bridge_secondary_bus_reset(). Pass CXL_SBR_UNBIND when the link is
+ * already down, which leaves the regions' memory online because offlining it
+ * needs a reachable device.
+ */
+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,
+				     enum cxl_sbr_region_action action)
+{
+	struct cxl_sbr_ctx ctx = {};
+	u16 dvsec = 0;
+	int rc;
+
+	if (!dev->block_cfg_access)
+		pci_warn_once(dev, "unlocked secondary bus reset via: %pS\n",
+			      __builtin_return_address(0));
+
+	if (is_cxl_dport(dev))
+		dvsec = cxl_port_dvsec(dev);
+	if (dvsec) {
+		rc = cxl_sbr_prepare(dev, dvsec, &ctx, action);
+		if (rc)
+			return rc;
+	}
+
+	pcibios_reset_secondary_bus(dev);
+
+	rc = pci_bridge_wait_for_secondary_bus(dev, "bus reset");
+
+	if (dvsec)
+		cxl_sbr_complete(dev, dvsec, &ctx);
+
+	return rc;
+}
+
 /**
  * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge.
  * @dev: Bridge device
  *
  * Use the bridge control register to assert reset on the secondary bus.
  * Devices on the secondary bus are left in power-on state.
+ *
+ * When @dev is a CXL Downstream Port, clear ACS Source Validation and Bus
+ * Master Enable across the reset, per the workaround in CXL r4.0 sec 8.1.5.1,
+ * so that Port Power Management Initialization completes at link-up. The
+ * bits stay cleared until the secondary bus is back, then are restored.
  */
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev)
 {
-	if (!dev->block_cfg_access)
-		pci_warn_once(dev, "unlocked secondary bus reset via: %pS\n",
-			      __builtin_return_address(0));
-	pcibios_reset_secondary_bus(dev);
-
-	return pci_bridge_wait_for_secondary_bus(dev, "bus reset");
+	return __pci_bridge_secondary_bus_reset(dev, CXL_SBR_OFFLINE_AND_UNBIND);
 }
 EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);
 
@@ -4904,12 +5119,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)
 	return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
 }
 
-static u16 cxl_port_dvsec(struct pci_dev *dev)
-{
-	return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
-					 PCI_DVSEC_CXL_PORT);
-}
-
 static bool cxl_sbr_masked(struct pci_dev *dev)
 {
 	u16 dvsec, reg;
@@ -4934,6 +5143,27 @@ static bool cxl_sbr_masked(struct pci_dev *dev)
 	return true;
 }
 
+/*
+ * cxl_sbr_allowed - whether an SBR of a CXL Downstream Port may go ahead
+ * @dev: Downstream Port to test
+ *
+ * Per CXL r4.0 sec 8.1.5.2 Table 8-32 the SBR bit in a CXL Port's Bridge
+ * Control register has no effect while the Port's Unmask SBR bit is clear, and
+ * sec 9.12.3 says System Firmware may leave it clear "to prevent CXL-unaware
+ * PCIe software from resetting the device and the link". A Port that already
+ * has it set needs no further permission; otherwise unmasking it takes the
+ * administrator's consent, given by writing 1 to the Port's cxl_unmask_sbr.
+ *
+ * Return: true if the reset paths may unmask and generate an SBR of @dev.
+ */
+bool cxl_sbr_allowed(struct pci_dev *dev)
+{
+	if (!cxl_port_dvsec(dev))
+		return false;
+
+	return dev->cxl_unmask_sbr || !cxl_sbr_masked(dev);
+}
+
 static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
 {
 	struct pci_dev *bridge = pci_upstream_bridge(dev);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4469e1a77f3c1..bea05acc58f0d 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -233,6 +233,22 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
 bool pci_reset_supported(struct pci_dev *dev);
 void pci_init_reset_methods(struct pci_dev *dev);
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
+
+/*
+ * What to do with the CXL regions reached through a Downstream Port before it
+ * is reset. Offlining their memory needs a reachable device, so a Port whose
+ * link is already down only unbinds and leaves the memory online.
+ */
+enum cxl_sbr_region_action {
+	CXL_SBR_OFFLINE_AND_UNBIND,
+	CXL_SBR_UNBIND,
+};
+
+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,
+				     enum cxl_sbr_region_action action);
+bool is_cxl_dport(struct pci_dev *dev);
+u16 cxl_port_dvsec(struct pci_dev *dev);
+bool cxl_sbr_allowed(struct pci_dev *dev);
 int pci_bus_error_reset(struct pci_dev *dev);
 int pci_try_reset_bridge(struct pci_dev *bridge);
 
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index 2b779bd1d861b..ad74086bf82c7 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -127,6 +127,44 @@ bool pci_dpc_recovered(struct pci_dev *pdev)
 }
 #endif /* CONFIG_HOTPLUG_PCI_PCIE */
 
+/**
+ * pci_dpc_containment_active - whether a Port above @pdev is contained by DPC
+ * @pdev: PCI device below the Port
+ *
+ * Per PCIe r7.0 sec 2.9.3 the Port's LTSSM stays in the Disabled state while
+ * DPC Trigger Status is set, and dpc_reset_link() clears that bit only after
+ * pcie_do_recovery() has broadcast error_detected. A ->error_detected()
+ * callback can therefore use this to tell a DPC containment from any other
+ * frozen-channel error, and to know that the link is about to be reset.
+ *
+ * The Port that triggered is on the path to @pdev, because the broadcast walks
+ * that Port's subordinate bus, so test every bridge above @pdev.
+ *
+ * Return: true if a Port on the path to @pdev has DPC Trigger Status set.
+ */
+bool pci_dpc_containment_active(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+
+	for (bridge = pci_upstream_bridge(pdev); bridge;
+	     bridge = pci_upstream_bridge(bridge)) {
+		u16 status;
+
+		if (!bridge->dpc_cap)
+			continue;
+
+		pci_read_config_word(bridge,
+				     bridge->dpc_cap + PCI_EXP_DPC_STATUS,
+				     &status);
+		if (!PCI_POSSIBLE_ERROR(status) &&
+		    (status & PCI_EXP_DPC_STATUS_TRIGGER))
+			return true;
+	}
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(pci_dpc_containment_active);
+
 static int dpc_wait_rp_inactive(struct pci_dev *pdev)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -149,6 +187,7 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
 	pci_ers_result_t ret;
 	u16 cap;
+	int rc;
 
 	set_bit(PCI_DPC_RECOVERING, &pdev->priv_flags);
 
@@ -174,7 +213,12 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 	pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
 			      PCI_EXP_DPC_STATUS_TRIGGER);
 
-	if (pci_bridge_wait_for_secondary_bus(pdev, "DPC")) {
+	if (is_cxl_dport(pdev) && cxl_port_dvsec(pdev))
+		rc = __pci_bridge_secondary_bus_reset(pdev, CXL_SBR_UNBIND);
+	else
+		rc = pci_bridge_wait_for_secondary_bus(pdev, "DPC");
+
+	if (rc) {
 		clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
 		ret = PCI_ERS_RESULT_DISCONNECT;
 	} else {
diff --git a/include/linux/aer.h b/include/linux/aer.h
index df0f5c382286f..7ac029f01c25c 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -66,6 +66,15 @@ static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; }
 static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }
 #endif
 
+#if defined(CONFIG_PCIE_DPC)
+bool pci_dpc_containment_active(struct pci_dev *pdev);
+#else
+static inline bool pci_dpc_containment_active(struct pci_dev *pdev)
+{
+	return false;
+}
+#endif
+
 void pci_print_aer(struct pci_dev *dev, int aer_severity,
 		    struct aer_capability_regs *aer);
 int cper_severity_to_aer(int cper_severity);
diff --git a/include/linux/memory.h b/include/linux/memory.h
index 463dc02f6cff0..5dc0b96fb004a 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -149,6 +149,10 @@ static inline unsigned long memory_block_advised_max_size(void)
 {
 	return 0;
 }
+static inline int cxl_offline_memory(u64 start, u64 size)
+{
+	return 0;
+}
 #else /* CONFIG_MEMORY_HOTPLUG */
 extern int register_memory_notifier(struct notifier_block *nb);
 extern void unregister_memory_notifier(struct notifier_block *nb);
@@ -167,6 +171,7 @@ typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *);
 extern int walk_memory_blocks(unsigned long start, unsigned long size,
 			      void *arg, walk_memory_blocks_func_t func);
 extern int for_each_memory_block(void *arg, walk_memory_blocks_func_t func);
+int cxl_offline_memory(u64 start, u64 size);
 
 extern int memory_group_register_static(int nid, unsigned long max_pages);
 extern int memory_group_register_dynamic(int nid, unsigned long unit_pages);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 64b308b6e61c1..bc0f652eff804 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -485,6 +485,7 @@ struct pci_dev {
 	unsigned int	shpc_managed:1;		/* SHPC owned by shpchp */
 	unsigned int	is_thunderbolt:1;	/* Thunderbolt controller */
 	unsigned int	is_cxl:1;               /* Compute Express Link (CXL) */
+	unsigned int	cxl_unmask_sbr:1;	/* SBR unmask allowed by user */
 	/*
 	 * Devices marked being untrusted are the ones that can potentially
 	 * execute DMA attacks and similar. They are typically connected
@@ -1606,6 +1607,21 @@ int devm_request_pci_bus_resources(struct device *dev,
 /* Temporary until new and working PCI SBR API in place */
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
 
+/**
+ * struct pci_cxl_sbr_region_ops - CXL region callbacks for a bus reset
+ * @disable_regions: disable the regions below @dport, 0 or errno
+ * @unbind_regions: unbind the drivers of the regions below @dport, leaving
+ *		    their memory online, for a link already contained
+ * @enable_regions: re-enable the regions below @dport
+ */
+struct pci_cxl_sbr_region_ops {
+	int (*disable_regions)(struct pci_dev *dport);
+	void (*unbind_regions)(struct pci_dev *dport);
+	void (*enable_regions)(struct pci_dev *dport);
+};
+
+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops);
+
 #define __pci_bus_for_each_res0(bus, res, ...)				\
 	for (unsigned int __b = 0;					\
 	     (res = pci_bus_resource_n(bus, __b)) || __b < PCI_BRIDGE_RESOURCE_NUM; \
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index facaa324bd86a..0eaa34db93cef 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -1371,6 +1371,8 @@
 
 /* CXL r4.0, 8.1.5: Extensions DVSEC for Ports */
 #define PCI_DVSEC_CXL_PORT				3
+#define  PCI_DVSEC_CXL_PORT_EXT_STATUS			0x0a
+#define   PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP	0x00000001
 #define  PCI_DVSEC_CXL_PORT_CTL				0x0c
 #define   PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR		0x00000001
 
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 2be1df80fcc93..f18d41b299fec 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -63,7 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/hdm.o
 cxl_core-y += $(CXL_CORE_SRC)/pmu.o
 cxl_core-y += $(CXL_CORE_SRC)/cdat.o
 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
-cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o
+cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o $(CXL_CORE_SRC)/dport_sbr.o
 cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
 cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o

3/2 2026/08/25 02:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22398
							output: 24
							thoughts: 523
						

Thoughts:
**Fuzzing Plan for Secondary Bus Reset (SBR) Support in CXL**

Okay, so we've got a new patch adding Secondary Bus Reset (SBR) support for CXL Downstream Ports. It's got a new sysfs attribute, `cxl_unmask_sbr`, which immediately triggers some fuzzing thoughts. This is *interesting*, it touches pretty core areas: PCI, CXL, memory management... potentially a lot of surface for bugs.

The changes are distributed, which is good for looking at coverage. It's modifying `drivers/cxl/core/dport_sbr.c` (that's a new one!), `drivers/cxl/core/hdm.c`, `drivers/pci/pci.c`, `drivers/pci/pcie/dpc.c`, so it's reaching across the layers. This touches on PCI resets and CXL region management.

Now, the important question: how realistic is fuzzing here? We have CXL emulation in QEMU, and that makes it very reasonable to fuzz. CXL devices can be attached, which means we can hit these new code paths. Given the new sysfs attributes and the PCI reset logic, there's certainly a chance to find something.

Let's focus the fuzzing effort. We need a list of targets. Let's see... we have sysfs entries:
*   `cxl_unmask_sbr_store` and `cxl_unmask_sbr_show` in `drivers/pci/pci-sysfs.c`. Easy to target.
*   Functions in `drivers/cxl/core/dport_sbr.c`: `cxl_sbr_disable_regions`, `cxl_sbr_unbind_regions`, and `cxl_sbr_enable_regions`. They are static, but they are registered via `cxl_sbr_region_ops` and called from `pci.c`, so it is definitely worth fuzzing these functions.
*   `__pci_bridge_secondary_bus_reset` and `pci_bridge_secondary_bus_reset` in `drivers/pci/pci.c`. The core reset logic. These are critical.
*   `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state` in `drivers/cxl/core/hdm.c`. These are worth a look, especially with reset logic.
*   `cxl_offline_memory` in `drivers/base/memory.c`. Memory management!

Now, the configuration requirements. We need to make sure we have the correct build environment. We'll need `CONFIG_CXL_BUS`, `CONFIG_CXL_PCI`, `CONFIG_CXL_REGION`, `CONFIG_PCI`, `CONFIG_HOTPLUG_PCI_PCIE`, and `CONFIG_PCIE_DPC`. Let's confirm these with `grepper` and double-check, but these are the areas that are being touched so it makes sense.



4/2 2026/08/25 02:50 tool grepper
0m Args:
{
  "Expression": "cxl_unmask_sbr_store"
}

Results:
{
  "Output": "drivers/pci/pci-sysfs.c=576=static ssize_t cxl_unmask_sbr_show(struct device *dev,\n--\ndrivers/pci/pci-sysfs.c-583-\ndrivers/pci/pci-sysfs.c:584:static ssize_t cxl_unmask_sbr_store(struct device *dev,\ndrivers/pci/pci-sysfs.c-585-\t\t\t\t    struct device_attribute *attr,\n"
}

5/2 2026/08/25 02:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23763
							output: 17
							thoughts: 18
						

Thoughts:
Here's my take on the thought process, as if it were my own:

**CXL Configuration Hunt**

Alright, time to get to the heart of this CXL configuration question. My initial thought is to zero in on the `drivers/cxl/Kconfig` file. That's the logical starting point for any kernel configuration related to a specific hardware or driver, especially one as cutting-edge as CXL. I expect to find the relevant Kconfig options in there. It's the standard place to define the build-time configurations that will determine if, how, and with what features CXL support is compiled into the kernel. From here, I can find the precise options I need to examine for more insight. Let's see what's in there.



6/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "config CXL"
}

Results:
{
  "Output": "drivers/cxl/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/cxl/Kconfig:2:menuconfig CXL_BUS\ndrivers/cxl/Kconfig-3-\ttristate \"CXL (Compute Express Link) Devices Support\"\n--\ndrivers/cxl/Kconfig=20=if CXL_BUS\ndrivers/cxl/Kconfig-21-\ndrivers/cxl/Kconfig:22:config CXL_PCI\ndrivers/cxl/Kconfig-23-\ttristate \"PCI manageability\"\n--\ndrivers/cxl/Kconfig-39-\ndrivers/cxl/Kconfig:40:config CXL_MEM_RAW_COMMANDS\ndrivers/cxl/Kconfig-41-\tbool \"RAW Command Interface for Memory Devices\"\n--\ndrivers/cxl/Kconfig-57-\ndrivers/cxl/Kconfig:58:config CXL_ACPI\ndrivers/cxl/Kconfig-59-\ttristate \"CXL ACPI: Platform Support\"\n--\ndrivers/cxl/Kconfig-78-\ndrivers/cxl/Kconfig:79:config CXL_PMEM\ndrivers/cxl/Kconfig-80-\ttristate \"CXL PMEM: Persistent Memory Support\"\n--\ndrivers/cxl/Kconfig-91-\ndrivers/cxl/Kconfig:92:config CXL_MEM\ndrivers/cxl/Kconfig-93-\ttristate \"CXL: Memory Expansion\"\n--\ndrivers/cxl/Kconfig-106-\ndrivers/cxl/Kconfig:107:config CXL_FEATURES\ndrivers/cxl/Kconfig-108-\tbool \"CXL: Features\"\n--\ndrivers/cxl/Kconfig-117-\ndrivers/cxl/Kconfig:118:config CXL_EDAC_MEM_FEATURES\ndrivers/cxl/Kconfig-119-\tbool \"CXL: EDAC Memory Features\"\n--\ndrivers/cxl/Kconfig-132-\ndrivers/cxl/Kconfig:133:config CXL_EDAC_SCRUB\ndrivers/cxl/Kconfig-134-\tbool \"Enable CXL Patrol Scrub Control (Patrol Read)\"\n--\ndrivers/cxl/Kconfig-150-\ndrivers/cxl/Kconfig:151:config CXL_EDAC_ECS\ndrivers/cxl/Kconfig-152-\tbool \"Enable CXL Error Check Scrub (Repair)\"\n--\ndrivers/cxl/Kconfig-167-\ndrivers/cxl/Kconfig:168:config CXL_EDAC_MEM_REPAIR\ndrivers/cxl/Kconfig-169-\tbool \"Enable CXL Memory Repair\"\n--\ndrivers/cxl/Kconfig-188-\ndrivers/cxl/Kconfig:189:config CXL_PORT\ndrivers/cxl/Kconfig-190-\tdefault CXL_BUS\n--\ndrivers/cxl/Kconfig-192-\ndrivers/cxl/Kconfig:193:config CXL_SUSPEND\ndrivers/cxl/Kconfig-194-\tdef_bool y\n--\ndrivers/cxl/Kconfig-196-\ndrivers/cxl/Kconfig:197:config CXL_REGION\ndrivers/cxl/Kconfig-198-\tbool \"CXL: Region Support\"\n--\ndrivers/cxl/Kconfig-214-\ndrivers/cxl/Kconfig:215:config CXL_REGION_INVALIDATION_TEST\ndrivers/cxl/Kconfig-216-\tbool \"CXL: Region Cache Management Bypass (TEST)\"\n--\ndrivers/cxl/Kconfig-232-\ndrivers/cxl/Kconfig:233:config CXL_MCE\ndrivers/cxl/Kconfig-234-\tdef_bool y\n--\ndrivers/cxl/Kconfig-236-\ndrivers/cxl/Kconfig:237:config CXL_RAS\ndrivers/cxl/Kconfig-238-\tdef_bool y\n--\ndrivers/cxl/Kconfig-240-\ndrivers/cxl/Kconfig:241:config CXL_ATL\ndrivers/cxl/Kconfig-242-\tdef_bool y\n--\ndrivers/perf/Kconfig=292=source \"drivers/perf/amlogic/Kconfig\"\ndrivers/perf/Kconfig-293-\ndrivers/perf/Kconfig:294:config CXL_PMU\ndrivers/perf/Kconfig-295-\ttristate \"CXL Performance Monitoring Unit\"\n"
}

7/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25093
							output: 25
							thoughts: 127
						

Thoughts:
**Analyzing the CXL Driver Changes**

Okay, so the changes are specifically within `drivers/cxl/core/dport_sbr.c`. That file's built only when `CONFIG_CXL_REGION` is enabled, which is a key piece of information. The `Makefile` line clearly shows the dependency: `cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o dport_sbr.o`. So, to trigger this build and utilize the modifications, `CXL_REGION` needs to be defined.

Beyond that, I should double-check the project dependencies. This likely involves digging around for other dependent config options; `CXL_BUS` and `CXL_PCI` might be relevant and worth a quick sanity check to ensure I don't miss something. I'll need to confirm any interactions with those.

As a related item, I should also check how this interacts with the PCI layer. `__pci_bridge_secondary_bus_reset` looks interesting and possibly relevant to any bus-level activities involved. I'll take a look at `drivers/pci/pci.c` to understand that better.



8/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "__pci_bridge_secondary_bus_reset"
}

Results:
{
  "Output": "drivers/pci/pci.c=4988=static void cxl_sbr_complete(struct pci_dev *bridge, u16 dvsec,\n--\ndrivers/pci/pci.c-5024-/*\ndrivers/pci/pci.c:5025: * __pci_bridge_secondary_bus_reset - assert Secondary Bus Reset on a bridge\ndrivers/pci/pci.c-5026- * @dev: bridge device\n--\ndrivers/pci/pci.c-5032- */\ndrivers/pci/pci.c:5033:int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\ndrivers/pci/pci.c-5034-\t\t\t\t     enum cxl_sbr_region_action action)\n--\ndrivers/pci/pci.c=5074=int pci_bridge_secondary_bus_reset(struct pci_dev *dev)\ndrivers/pci/pci.c-5075-{\ndrivers/pci/pci.c:5076:\treturn __pci_bridge_secondary_bus_reset(dev, CXL_SBR_OFFLINE_AND_UNBIND);\ndrivers/pci/pci.c-5077-}\n--\ndrivers/pci/pci.h=242=enum cxl_sbr_region_action {\n--\ndrivers/pci/pci.h-246-\ndrivers/pci/pci.h:247:int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\ndrivers/pci/pci.h-248-\t\t\t\t     enum cxl_sbr_region_action action);\n--\ndrivers/pci/pcie/dpc.c=186=pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)\n--\ndrivers/pci/pcie/dpc.c-216-\tif (is_cxl_dport(pdev) \u0026\u0026 cxl_port_dvsec(pdev))\ndrivers/pci/pcie/dpc.c:217:\t\trc = __pci_bridge_secondary_bus_reset(pdev, CXL_SBR_UNBIND);\ndrivers/pci/pcie/dpc.c-218-\telse\n"
}

9/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26102
							output: 20
							thoughts: 873
						

Thoughts:
**My Current Assessment of Key Functions and Exported Symbols**

Okay, here's what I've been digging into. My focus is on these functions: `__pci_bridge_secondary_bus_reset`, `cxl_offline_memory`, `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, `cxl_region_invalidate_memregion`, `cxl_unmask_sbr_store`, `cxl_unmask_sbr_show`, and `pci_dpc_containment_active`. I want to understand their scope, visibility, and potential interactions.

First, I confirmed that `cxl_offline_memory` *is* exported using `EXPORT_SYMBOL_NS_GPL` within the "CXL_MHP" namespace. Good. Now, `cxl_port_recommit_decoders`... it's defined within `drivers/cxl/core/hdm.c`, but it's *not* explicitly exported. However, it *is* called from a function in `dport_sbr.c` which, importantly, is within the same `cxl_core` module. That’s probably fine.

I also checked if `cxl_sbr_region_ops` is in use, and yes, it is via `pci_cxl_set_sbr_region_ops` which is called from `cxl_region_init`. That’s good to know.

I then noted that `cxl_sbr_allowed` and `is_cxl_dport` are *not* exported.

Now, `pci_dpc_containment_active` is indeed exported, which is important for understanding how DPC is managed.

So, just to summarize, these are the functions I'm concentrating on right now:
`cxl_offline_memory`
`cxl_port_recommit_decoders`
`cxl_port_save_hdm_state`
`cxl_port_put_hdm_state`
`cxl_enable_hdm`
`cxl_set_mem_enable`
`cxl_region_invalidate_memregion`
`__pci_bridge_secondary_bus_reset`
`pci_dpc_containment_active`
`cxl_unmask_sbr_store`
`cxl_unmask_sbr_show`

Next, I looked at the static status of some of these. `cxl_unmask_sbr_store` is defined as static within `drivers/pci/pci-sysfs.c`. Also, I'll use `pci_bridge_secondary_bus_reset` instead of `__pci_bridge_secondary_bus_reset` because the latter is not static and is defined and declared in `drivers/pci/pci.c` and `drivers/pci/pci.h`.

I then checked if the rest of the functions are static and none of them are: `cxl_offline_memory`, `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, `cxl_region_invalidate_memregion`, and `pci_dpc_containment_active`.

Finally, just a quick sanity check on related configs: I'm keeping an eye on `CXL_BUS`, `CXL_PCI`, `CXL_REGION`, `HOTPLUG_PCI_PCIE`, and `PCIE_DPC`.  I'll want to verify `PCIE_DPC` in `drivers/pci/pcie/Kconfig` to confirm its settings. This should inform the configuration context.



10/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "config PCIE_DPC"
}

Results:
{
  "Output": "drivers/pci/pcie/Kconfig=117=config PCIE_PME\n--\ndrivers/pci/pcie/Kconfig-120-\ndrivers/pci/pcie/Kconfig:121:config PCIE_DPC\ndrivers/pci/pcie/Kconfig-122-\tbool \"PCI Express Downstream Port Containment support\"\n"
}

11/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27985
							output: 23
							thoughts: 26
						

Thoughts:
Okay, here's my summary, presented as a stream of thought:

**Checking the PCIe Infrastructure**

Right, *PCIE_DPC*... yes, that's definitely a thing. Now, where are we with hotplug support for PCIe? I need to quickly refresh my mental map of the kernel code. Let's dig into *HOTPLUG_PCI_PCIE*. Time to get the details.



12/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "config HOTPLUG_PCI_PCIE"
}

Results:
{
  "Output": "drivers/pci/pcie/Kconfig=5=config PCIEPORTBUS\n--\ndrivers/pci/pcie/Kconfig-15-#\ndrivers/pci/pcie/Kconfig:16:config HOTPLUG_PCI_PCIE\ndrivers/pci/pcie/Kconfig-17-\tbool \"PCI Express Hotplug driver\"\n"
}

13/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28219
							output: 21
							thoughts: 46
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Navigating the PCI/CXL Memory Landscape**

Alright, so `HOTPLUG_PCI_PCIE` is definitely a thing, I knew I'd seen that one kicking around. Now, I'm thinking about how the `cxl_offline_memory` function is located within `drivers/base/memory.c`. That placement is significant. Since we're dealing with hotplugging memory with CXL involved, I need to check how `MEMORY_HOTPLUG` is being handled in relation to this. The dependency is likely complex, and understanding the interplay between those two is critical for a complete picture. Gotta make sure those settings are correctly defined and interacted for a reliable system.



14/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "config MEMORY_HOTPLUG"
}

Results:
{
  "Output": "mm/Kconfig=519=config ARCH_ENABLE_MEMORY_HOTPLUG\n--\nmm/Kconfig-522-# eventually, we can have this option just 'select SPARSEMEM'\nmm/Kconfig:523:menuconfig MEMORY_HOTPLUG\nmm/Kconfig-524-\tbool \"Memory hotplug\"\n"
}

15/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28550
							output: 20
							thoughts: 47
						

Thoughts:
**My Analysis of Memory Hotplug in CXL Environments**

Okay, so I'm thinking about memory hotplug and the potential implications within a CXL context. First, I need to acknowledge that yes, the concept of `MEMORY_HOTPLUG` is definitely a thing, and it's relevant here. Now, I'm going to dig into the relationship between `cxl_offline_memory` and `MEMORY_HOTPLUG`. My immediate thought is, "Is `cxl_offline_memory` contingent upon `MEMORY_HOTPLUG` being compiled into the kernel?" I need to verify whether the `cxl_offline_memory` function, specifically, is only compiled if `MEMORY_HOTPLUG` is defined. To do that, I'm going to check the source code in `drivers/base/memory.c`. Let's see what the preprocessor directives reveal. I need to understand this dependency precisely to ensure the correct operation and configuration of hotplug features.



16/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "cxl_offline_memory"
}

Results:
{
  "Output": "drivers/base/memory.c=996=int walk_memory_blocks(unsigned long start, unsigned long size,\n--\ndrivers/base/memory.c-1020-\ndrivers/base/memory.c:1021:static int cxl_offline_memory_block(struct memory_block *mem, void *arg)\ndrivers/base/memory.c-1022-{\n--\ndrivers/base/memory.c-1032-/**\ndrivers/base/memory.c:1033: * cxl_offline_memory - offline the memory blocks spanning a physical range\ndrivers/base/memory.c-1034- * @start: start of the range, memory-block aligned\n--\ndrivers/base/memory.c-1044- */\ndrivers/base/memory.c:1045:int cxl_offline_memory(u64 start, u64 size)\ndrivers/base/memory.c-1046-{\n--\ndrivers/base/memory.c-1049-\tlock_device_hotplug();\ndrivers/base/memory.c:1050:\trc = walk_memory_blocks(start, size, NULL, cxl_offline_memory_block);\ndrivers/base/memory.c-1051-\tunlock_device_hotplug();\n--\ndrivers/base/memory.c-1054-}\ndrivers/base/memory.c:1055:EXPORT_SYMBOL_NS_GPL(cxl_offline_memory, \"CXL_MHP\");\ndrivers/base/memory.c-1056-\n--\ndrivers/cxl/core/dport_sbr.c=48=static int cxl_region_disable(struct cxl_region *cxlr)\n--\ndrivers/cxl/core/dport_sbr.c-69-\t} else {\ndrivers/cxl/core/dport_sbr.c:70:\t\trc = cxl_offline_memory(start, end - start);\ndrivers/cxl/core/dport_sbr.c-71-\t\tif (rc) {\n--\ninclude/linux/memory.h=148=static inline unsigned long memory_block_advised_max_size(void)\n--\ninclude/linux/memory.h-151-}\ninclude/linux/memory.h:152:static inline int cxl_offline_memory(u64 start, u64 size)\ninclude/linux/memory.h-153-{\n--\ninclude/linux/memory.h=173=extern int for_each_memory_block(void *arg, walk_memory_blocks_func_t func);\ninclude/linux/memory.h:174:int cxl_offline_memory(u64 start, u64 size);\ninclude/linux/memory.h-175-\n"
}

17/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29517
							output: 28
							thoughts: 24
						

Thoughts:
Here's my take on the thought process, as if it were my own:

**Concerning Memory Hotplug and Compilation**

Okay, so I'm looking at this `drivers/base/memory.c` file and thinking about its compilation. The key here is definitely `CONFIG_MEMORY_HOTPLUG`. My brain immediately links this to the hot-add/removal of RAM in the system, which I know impacts memory management, obviously. So, if that configuration option is enabled, this `memory.c` file *must* be included in the build process. I need to keep this in mind as I start to investigate the memory management framework. If I'm working on a system where hotplug is supported, I know this will be a crucial file. Conversely, if hotplug is not enabled, then I need to consider that, depending on the kernel's configuration, some parts of memory management may differ. This is likely responsible for initialization and management of hot-pluggable memory regions, and probably how the kernel reacts to dynamic changes in the available RAM. This is good to know, and the implications of this need to be kept in the forefront of my mind.



18/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "memory\\.o",
  "PathPrefix": "drivers/base/Makefile"
}

Results:
{
  "Output": "drivers/base/Makefile=15=obj-$(CONFIG_NUMA)\t+= node.o\ndrivers/base/Makefile:16:obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o\ndrivers/base/Makefile-17-ifeq ($(CONFIG_SYSFS),y)\n"
}

19/2 2026/08/25 02:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29894
							output: 19
							thoughts: 41
						

Thoughts:
**My Reasoning on CXL and Memory Hotplug**

Okay, so I'm thinking about `cxl_offline_memory`. My immediate thought is, yes, we definitely need `MEMORY_HOTPLUG` enabled for that to work. No question there. Now, just to be absolutely sure, I should double-check the dependencies. Let's quickly see if `CXL_REGION` itself hinges on `MEMORY_HOTPLUG`. I want to make certain everything is correctly configured, so I don't run into any surprises later.



20/2 2026/08/25 02:51 tool grepper
0m Args:
{
  "Expression": "config CXL_REGION"
}

Results:
{
  "Output": "drivers/cxl/Kconfig=193=config CXL_SUSPEND\n--\ndrivers/cxl/Kconfig-196-\ndrivers/cxl/Kconfig:197:config CXL_REGION\ndrivers/cxl/Kconfig-198-\tbool \"CXL: Region Support\"\n--\ndrivers/cxl/Kconfig-214-\ndrivers/cxl/Kconfig:215:config CXL_REGION_INVALIDATION_TEST\ndrivers/cxl/Kconfig-216-\tbool \"CXL: Region Cache Management Bypass (TEST)\"\n"
}

21/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30219
							output: 35
							thoughts: 20
						

Thoughts:
Here's my summary, as though I'm processing the `drivers/cxl/Kconfig` file around the `CXL_REGION` configuration option:

**Diving into CXL Region Configuration**

Okay, let's get right into `drivers/cxl/Kconfig`. I need to understand how the `CXL_REGION` configuration option is set up, its dependencies, and what implications it has. I'm going to start by just scanning around that keyword. I know the general CXL landscape, so I'm hoping this will be a quick refresher and help me pinpoint any changes or nuances in this specific implementation. I'm looking for where the option is *defined*, where it's *used*, and most importantly, what other options or features it's linked to. Is it dependent on the underlying hardware capabilities? Is there a nested structure of configuration options involved, like the memory and device discovery portions? Are there any specific drivers tied to it? My goal here is to establish the scope of this option – what parts of the system are affected when I enable or disable `CXL_REGION`? Understanding these linkages will be crucial for debugging or modifying CXL-related functionalities down the line. I want to build a mental map of this part of the kernel configuration system, and this is the starting point.



22/2 2026/08/25 02:52 tool read-file
0m Args:
{
  "File": "drivers/cxl/Kconfig",
  "FirstLine": 190,
  "LineCount": 20
}

Results:
{
  "Contents": " 190:\t\tdefault CXL_BUS\n 191:\t\ttristate\n 192:\t\n 193:\tconfig CXL_SUSPEND\n 194:\t\tdef_bool y\n 195:\t\tdepends on SUSPEND \u0026\u0026 CXL_MEM\n 196:\t\n 197:\tconfig CXL_REGION\n 198:\t\tbool \"CXL: Region Support\"\n 199:\t\tdefault CXL_BUS\n 200:\t\t# For MAX_PHYSMEM_BITS\n 201:\t\tdepends on SPARSEMEM\n 202:\t\tselect MEMREGION\n 203:\t\tselect GET_FREE_REGION\n 204:\t\thelp\n 205:\t\t  Enable the CXL core to enumerate and provision CXL regions. A CXL\n 206:\t\t  region is defined by one or more CXL expanders that decode a given\n 207:\t\t  system-physical address range. For CXL regions established by\n 208:\t\t  platform-firmware this option enables memory error handling to\n 209:\t\t  identify the devices participating in a given interleaved memory\n"
}

23/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30842
							output: 19
							thoughts: 64
						

Thoughts:
**My Assessment of the CXL Memory Hotplug Scenario**

Okay, so the `cxl_offline_memory` function itself isn't directly tied to `MEMORY_HOTPLUG`, but the implementation is basically a placeholder, a stub, if the hotplug feature isn't enabled. That means if I really want to test the *actual* functionality of `cxl_offline_memory`, I'm going to need to have `MEMORY_HOTPLUG` activated. Otherwise, I'm not getting a true evaluation of what it does. Time to move on to looking at `CXL_PORT`.



24/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "config CXL_PORT"
}

Results:
{
  "Output": "drivers/cxl/Kconfig=168=config CXL_EDAC_MEM_REPAIR\n--\ndrivers/cxl/Kconfig-188-\ndrivers/cxl/Kconfig:189:config CXL_PORT\ndrivers/cxl/Kconfig-190-\tdefault CXL_BUS\n"
}

25/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31142
							output: 19
							thoughts: 10
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:

**Investigating the CXL_BUS**

Alright, time to dig in. Let's make sure we're on the same page and start by checking out `CXL_BUS`. It's the logical starting point, isn't it? Gotta understand the foundation before we go deeper. Let's see what's happening there first. We need to ascertain that the base level functionality is stable before we can move on to analyzing any potential anomalies at a higher level.



26/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "config CXL_BUS"
}

Results:
{
  "Output": "drivers/cxl/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/cxl/Kconfig:2:menuconfig CXL_BUS\ndrivers/cxl/Kconfig-3-\ttristate \"CXL (Compute Express Link) Devices Support\"\n"
}

27/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31376
							output: 19
							thoughts: 10
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm deep in my domain of expertise:

**Checking CXL_PCI**

Right, let's dive in. The phrase "Let's check `CXL_PCI`" just triggered a mental checklist. My brain immediately starts running through a list of things related to Compute Express Link (CXL) over PCI-Express (PCIe), specifically the aspects where they intersect. This could mean a few things, depending on the context.

*   **Configuration:** Am I looking at register mappings, memory window configurations, or perhaps a specific boot process using PCIe to initialize the CXL fabric? It could be a hardware design review, or debugging an issue where the CXL link isn't coming up as expected.
*   **Protocol analysis:** Is it time to analyze a trace, using tools like a protocol analyzer? Is there a CXL transaction flowing over a PCIe physical link? Maybe I need to look for specific error conditions that manifest at the PCIe level when CXL is involved.
*   **Performance:** Am I benchmarking a CXL-attached device and trying to isolate performance bottlenecks? Are we seeing PCIe-related delays that are impacting the overall CXL performance? PCIe has a direct and significant influence on overall CXL performance.
*   **Driver/Firmware:** I'm also considering potential interactions with the host driver or device firmware. Perhaps I need to ensure they are correctly setting up the PCIe configuration space for CXL devices. Could be driver compatibility, or misconfiguration in device discovery.
*   **Interoperability:** "Checking `CXL_PCI`" could also suggest an interoperability test, especially if there's a mix of CXL and PCIe devices in a system. I have to verify that both protocols are playing nicely together, and that they're both functioning as expected. It also might be an issue with PCIe link training.

So, the first thing I need to do is establish *why* I'm checking it. The term itself is a breadcrumb that brings me to all the crucial areas. And the questions I need to answer are: Where is the problem? And where is the solution?



28/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "config CXL_PCI"
}

Results:
{
  "Output": "drivers/cxl/Kconfig=20=if CXL_BUS\ndrivers/cxl/Kconfig-21-\ndrivers/cxl/Kconfig:22:config CXL_PCI\ndrivers/cxl/Kconfig-23-\ttristate \"PCI manageability\"\n"
}

29/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31942
							output: 24
							thoughts: 298
						

Thoughts:
**My Initial Assessment of the CXL Driver Landscape**

Okay, so I'm looking at these configurations: `CXL_BUS`, `CXL_PCI`, `CXL_REGION`, `HOTPLUG_PCI_PCIE`, `PCIE_DPC`, and `MEMORY_HOTPLUG`. My initial goal is to understand how these relate to potential security vulnerabilities, particularly in the context of device hotplugging and memory management.

Right, let's start with `pci_bridge_secondary_bus_reset`. I know that it lives in `drivers/pci/pci.c`, which makes sense. I see that the core functionality is within `__pci_bridge_secondary_bus_reset`. Good, I'll keep that in mind.

Now, let's look into the CXL-specific functions, specifically, `cxl_unmask_sbr_store`. Hmmm, it's static, so it's not a direct focus for exploitation as an exported symbol. However, it's a sysfs attribute, so fuzzing the sysfs interface is definitely a valid attack vector to reach it.

I need to quickly check for other externally callable functions. Let's see... I've got: `cxl_offline_memory`, `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, `cxl_region_invalidate_memregion`, and finally, `__pci_bridge_secondary_bus_reset` and `pci_dpc_containment_active`.

Let's quickly check on `cxl_port_recommit_decoders` to verify if it's static. Gotta make sure nothing is missed!



30/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "cxl_port_recommit_decoders"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=241=void cxl_port_put_hdm_state(struct xarray *hdm_state);\ndrivers/cxl/core/core.h:242:int cxl_port_recommit_decoders(struct cxl_port *port,\ndrivers/cxl/core/core.h-243-\t\t\t       struct xarray *hdm_state);\n--\ndrivers/cxl/core/dport_sbr.c=168=static void cxl_sbr_put_regions(struct xarray *regions)\n--\ndrivers/cxl/core/dport_sbr.c-181- * @hdm_state, the register fields the driver does not model, saved before the\ndrivers/cxl/core/dport_sbr.c:182: * reset. Takes cxl_rwsem.region for read, which cxl_port_recommit_decoders()\ndrivers/cxl/core/dport_sbr.c-183- * requires. The caller has already disabled the regions, so nothing reaches the\n--\ndrivers/cxl/core/dport_sbr.c=186=static void cxl_sbr_recommit_decoders(struct pci_dev *dport_pci,\n--\ndrivers/cxl/core/dport_sbr.c-201-\tguard(rwsem_read)(\u0026cxl_rwsem.region);\ndrivers/cxl/core/dport_sbr.c:202:\trc = cxl_port_recommit_decoders(port, hdm_state);\ndrivers/cxl/core/dport_sbr.c-203-\tif (rc)\n--\ndrivers/cxl/core/hdm.c=990=static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,\n--\ndrivers/cxl/core/hdm.c-1012-/**\ndrivers/cxl/core/hdm.c:1013: * cxl_port_recommit_decoders - reprogram the HDM decoders below @port\ndrivers/cxl/core/hdm.c-1014- * @port: CXL port whose downstream decoders to reprogram\n--\ndrivers/cxl/core/hdm.c-1045- */\ndrivers/cxl/core/hdm.c:1046:int cxl_port_recommit_decoders(struct cxl_port *port, struct xarray *hdm_state)\ndrivers/cxl/core/hdm.c-1047-{\n--\ndrivers/cxl/core/hdm.c-1116- * register of the ports below @port. Those hold the fields\ndrivers/cxl/core/hdm.c:1117: * cxl_port_recommit_decoders() cannot rebuild from the driver's cached settings,\ndrivers/cxl/core/hdm.c-1118- * so they have to be read while the registers still hold them.\ndrivers/cxl/core/hdm.c-1119- *\ndrivers/cxl/core/hdm.c:1120: * The set of ports is the same one cxl_port_recommit_decoders() walks. A port\ndrivers/cxl/core/hdm.c-1121- * with no HDM decoder registers has nothing to record and gets no entry.\n"
}

31/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 33509
							output: 25
							thoughts: 23
						

Thoughts:
Okay, here's my attempt at summarizing those thoughts from a first-person, expert perspective, aiming for a fulsome rendering:

**My Immediate Assessment**

Alright, the core concept here is that whatever we're dealing with isn't a fixed, unchanging entity. That's the first and most crucial takeaway. The system I'm working with has inherent dynamism. This isn't just a snapshot; it's a moving target, or at least a system susceptible to change. We need to bear that in mind.

Now, the next thing that pops into my head is a verification task. The phrase "Let's verify `cxl_port_save_hdm_state`" has taken root. I'm immediately shifting gears to something practical, something specific. It sounds like there's a particular function or module - `cxl_port_save_hdm_state` - that needs careful examination. My suspicion is that this function plays a critical role in managing the current state of a CXL port, presumably related to HDM (Host-managed Device Memory) state, or some related function. It may involve saving or restoring, hence "save." Whether it's the whole of the process is of little consequence, for the moment. My mind is already considering it in the scope of potential implications and edge cases. I'm focusing on the necessity of confirming that this portion of code functions as expected. It's a key point, and, thus, requires immediate attention. It needs a good going-over, from the ground up, to be absolutely sure it's doing its job reliably. The verification is likely linked to the understanding that there may be non-static issues to the current system.



32/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "cxl_port_save_hdm_state"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=234=struct cxl_hdm_state {\n--\ndrivers/cxl/core/core.h-239-\ndrivers/cxl/core/core.h:240:int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state);\ndrivers/cxl/core/core.h-241-void cxl_port_put_hdm_state(struct xarray *hdm_state);\n--\ndrivers/cxl/core/dport_sbr.c=230=static int cxl_sbr_save_hdm_state(struct pci_dev *dport_pci)\n--\ndrivers/cxl/core/dport_sbr.c-247-\tscoped_guard(rwsem_read, \u0026cxl_rwsem.region)\ndrivers/cxl/core/dport_sbr.c:248:\t\trc = cxl_port_save_hdm_state(port, hdm_state);\ndrivers/cxl/core/dport_sbr.c-249-\n--\ndrivers/cxl/core/hdm.c=1046=int cxl_port_recommit_decoders(struct cxl_port *port, struct xarray *hdm_state)\n--\ndrivers/cxl/core/hdm.c-1110-/**\ndrivers/cxl/core/hdm.c:1111: * cxl_port_save_hdm_state - record the HDM decoder control registers below @port\ndrivers/cxl/core/hdm.c-1112- * @port: CXL port whose downstream decoders to record\n--\ndrivers/cxl/core/hdm.c-1127- */\ndrivers/cxl/core/hdm.c:1128:int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state)\ndrivers/cxl/core/hdm.c-1129-{\n--\ndrivers/cxl/core/hdm.c-1179-/**\ndrivers/cxl/core/hdm.c:1180: * cxl_port_put_hdm_state - release a set filled by cxl_port_save_hdm_state()\ndrivers/cxl/core/hdm.c-1181- * @hdm_state: xarray to empty\n"
}

33/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34530
							output: 25
							thoughts: 23
						

Thoughts:
Okay, here's my attempt at summarizing those thoughts from a first-person, expert perspective, aiming for a fulsome rendering:

**Verification of `cxl_port_put_hdm_state` – It's Not Set in Stone**

Alright, so the first thing that pops into my head is that this isn't a fixed situation. Things are *always* dynamic, especially in the CXL world. There's a constant interplay of states, dependencies, and potential race conditions. We can't just assume anything; assumptions are the enemy. The context around the function `cxl_port_put_hdm_state` is the key.

This brings me to the core of the matter: I need to verify `cxl_port_put_hdm_state`. I can't just take it at face value. I need to dig into the surrounding code, understand how this state is *set*, how it's *used*, and the potential consequences of getting it wrong. I need to be asking questions like:

*   What are the preconditions for calling this function?
*   What happens if the HDM state is corrupted, or changed at the wrong time?
*   Is it atomic? Is there a mutex or other form of locking protecting the state?
*   How is the HDM state represented internally? What does it *mean*?
*   Is there an existing set of tests for it? If not, I'm going to want to write some.

This also means that I should not be assuming this is the only operation to impact a port's HDM state, and I want to verify that there are no additional interactions with this state.
I'm not just going to run the function and hope for the best. I'm going to approach this with the methodical rigor this field demands.



34/2 2026/08/25 02:52 tool grepper
0m Args:
{
  "Expression": "cxl_port_put_hdm_state"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=240=int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state);\ndrivers/cxl/core/core.h:241:void cxl_port_put_hdm_state(struct xarray *hdm_state);\ndrivers/cxl/core/core.h-242-int cxl_port_recommit_decoders(struct cxl_port *port,\n--\ndrivers/cxl/core/dport_sbr.c=214=static void cxl_sbr_drop_hdm_state(struct pci_dev *dport_pci)\n--\ndrivers/cxl/core/dport_sbr.c-221-\ndrivers/cxl/core/dport_sbr.c:222:\tcxl_port_put_hdm_state(hdm_state);\ndrivers/cxl/core/dport_sbr.c-223-\tkfree(hdm_state);\n--\ndrivers/cxl/core/dport_sbr.c=230=static int cxl_sbr_save_hdm_state(struct pci_dev *dport_pci)\n--\ndrivers/cxl/core/dport_sbr.c-253-\tif (rc) {\ndrivers/cxl/core/dport_sbr.c:254:\t\tcxl_port_put_hdm_state(hdm_state);\ndrivers/cxl/core/dport_sbr.c-255-\t\tkfree(hdm_state);\n--\ndrivers/cxl/core/hdm.c=1128=int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state)\n--\ndrivers/cxl/core/hdm.c-1179-/**\ndrivers/cxl/core/hdm.c:1180: * cxl_port_put_hdm_state - release a set filled by cxl_port_save_hdm_state()\ndrivers/cxl/core/hdm.c-1181- * @hdm_state: xarray to empty\ndrivers/cxl/core/hdm.c-1182- */\ndrivers/cxl/core/hdm.c:1183:void cxl_port_put_hdm_state(struct xarray *hdm_state)\ndrivers/cxl/core/hdm.c-1184-{\n"
}

35/2 2026/08/25 02:52 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35597
							output: 21
							thoughts: 19
						

Thoughts:
Okay, here's my interpretation of those thoughts, summarized from a first-person perspective, assuming I'm operating within a technical context and am highly familiar with the subject matter:

**Navigating the Landscape of Dynamic Systems**

Okay, so I'm thinking about this system, and the first thing that jumps out is that it's *not* a fixed thing. I need to remember that. It's not a static entity that I can just analyze once and assume I have the whole picture. Change is the only constant here. Things shift, evolve, and the implications of this need to be constantly considered.

And now, I think I need to start with verification. Let's make sure that `cxl_enable_hdm` is actually set correctly. That's the baseline, the fundamental thing to check *first*. It's a critical configuration, so let's verify that's enabled.



36/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "cxl_enable_hdm"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=220=int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,\ndrivers/cxl/core/core.h-221-\t\t\tstruct cxl_endpoint_dvsec_info *info);\ndrivers/cxl/core/core.h:222:void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl);\ndrivers/cxl/core/core.h-223-int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val);\n--\ndrivers/cxl/core/hdm.c=990=static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,\n--\ndrivers/cxl/core/hdm.c-1002-\ndrivers/cxl/core/hdm.c:1003:\tcxl_enable_hdm(cxlhdm, global_ctrl);\ndrivers/cxl/core/hdm.c-1004-\n--\ndrivers/cxl/core/pci.c=239=static void disable_hdm(void *_cxlhdm)\n--\ndrivers/cxl/core/pci.c-254- */\ndrivers/cxl/core/pci.c:255:void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl)\ndrivers/cxl/core/pci.c-256-{\n--\ndrivers/cxl/core/pci.c-262-\ndrivers/cxl/core/pci.c:263:static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm,\ndrivers/cxl/core/pci.c-264-\t\t\t       u32 global_ctrl)\ndrivers/cxl/core/pci.c-265-{\ndrivers/cxl/core/pci.c:266:\tcxl_enable_hdm(cxlhdm, global_ctrl);\ndrivers/cxl/core/pci.c-267-\n--\ndrivers/cxl/core/pci.c=376=int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,\n--\ndrivers/cxl/core/pci.c-409-\tif (!info-\u003emem_enabled) {\ndrivers/cxl/core/pci.c:410:\t\trc = devm_cxl_enable_hdm(\u0026port-\u003edev, cxlhdm, global_ctrl);\ndrivers/cxl/core/pci.c-411-\t\tif (rc)\n"
}

37/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 36550
							output: 22
							thoughts: 20
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Navigating the Dynamic Landscape of Computation**

This isn't a fixed state. The situation is in flux, and I need to keep that in mind as I approach this. The context isn't rigid; it's fluid. It requires active investigation.

Right, let's drill down into a crucial function in this context: `cxl_set_mem_enable`. Before I go any further I'll want to take a look and make sure this function does what I expect. I'll need to verify its behavior, particularly within the specific CXL environment that I am focusing on. Understanding its internal mechanisms and its interaction with other components is paramount.



38/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "cxl_set_mem_enable"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=222=void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl);\ndrivers/cxl/core/core.h:223:int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val);\ndrivers/cxl/core/core.h-224-/**\n--\ndrivers/cxl/core/hdm.c=990=static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,\n--\ndrivers/cxl/core/hdm.c-1004-\ndrivers/cxl/core/hdm.c:1005:\trc = cxl_set_mem_enable(cxlmd-\u003ecxlds, PCI_DVSEC_CXL_MEM_ENABLE);\ndrivers/cxl/core/hdm.c-1006-\tif (rc \u003c 0)\n--\ndrivers/cxl/core/pci.c=180=EXPORT_SYMBOL_NS_GPL(cxl_await_media_ready, \"CXL\");\ndrivers/cxl/core/pci.c-181-\ndrivers/cxl/core/pci.c:182:int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)\ndrivers/cxl/core/pci.c-183-{\n--\ndrivers/cxl/core/pci.c=205=static void clear_mem_enable(void *cxlds)\ndrivers/cxl/core/pci.c-206-{\ndrivers/cxl/core/pci.c:207:\tcxl_set_mem_enable(cxlds, 0);\ndrivers/cxl/core/pci.c-208-}\n--\ndrivers/cxl/core/pci.c=210=static int devm_cxl_enable_mem(struct device *host, struct cxl_dev_state *cxlds)\n--\ndrivers/cxl/core/pci.c-213-\ndrivers/cxl/core/pci.c:214:\trc = cxl_set_mem_enable(cxlds, PCI_DVSEC_CXL_MEM_ENABLE);\ndrivers/cxl/core/pci.c-215-\tif (rc \u003c 0)\n"
}

39/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37361
							output: 23
							thoughts: 21
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Verifying Invalidation**

Right, it's never as simple as a snapshot, is it? We can't just assume anything about the memory region state. Things are constantly changing; that's the core of it. I need to make sure I'm absolutely clear on the behavior.

I need to dive into `cxl_region_invalidate_memregion` again. I have a feeling there might be a subtle nuance in its handling of certain access patterns. Is it truly comprehensive in invalidating all potential cache lines or TLB entries related to that region? Or are there edge cases? Memory coherency is always my primary concern, so I must know, beyond a doubt, that it's functioning as expected. I need to refresh my memory on the critical sections and synchronization primitives used there. Let’s make certain.



40/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "cxl_region_invalidate_memregion"
}

Results:
{
  "Output": "drivers/cxl/core/core.h=58=void kill_regions(struct cxl_root_decoder *cxlrd);\ndrivers/cxl/core/core.h:59:int cxl_region_invalidate_memregion(struct cxl_region *cxlr);\ndrivers/cxl/core/core.h-60-struct pci_cxl_sbr_region_ops;\n--\ndrivers/cxl/core/dport_sbr.c=48=static int cxl_region_disable(struct cxl_region *cxlr)\n--\ndrivers/cxl/core/dport_sbr.c-77-\ndrivers/cxl/core/dport_sbr.c:78:\trc = cxl_region_invalidate_memregion(cxlr);\ndrivers/cxl/core/dport_sbr.c-79-\tif (rc) {\n--\ndrivers/cxl/core/region.c=220=static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port,\n--\ndrivers/cxl/core/region.c-225-\ndrivers/cxl/core/region.c:226:int cxl_region_invalidate_memregion(struct cxl_region *cxlr)\ndrivers/cxl/core/region.c-227-{\n--\ndrivers/cxl/core/region.c=247=static void cxl_region_decode_reset(struct cxl_region *cxlr, int count)\n--\ndrivers/cxl/core/region.c-259-\t */\ndrivers/cxl/core/region.c:260:\tcxl_region_invalidate_memregion(cxlr);\ndrivers/cxl/core/region.c-261-\n--\ndrivers/cxl/core/region.c=376=static int __commit(struct cxl_region *cxlr)\n--\ndrivers/cxl/core/region.c-396-\t */\ndrivers/cxl/core/region.c:397:\trc = cxl_region_invalidate_memregion(cxlr);\ndrivers/cxl/core/region.c-398-\tif (rc)\n"
}

41/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38156
							output: 24
							thoughts: 22
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary:

**The Dynamic Nature of the Beast**

Right, so this isn't a fixed situation. Things are fluid, they change. That's the core of what's running through my mind. And given that, the immediate next step is clear. We need to verify the state of `pci_dpc_containment_active`. That's the key variable, the critical setting, that will tell us where we are and what we need to address. It's the lynchpin, the indicator that determines our next steps.



42/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "pci_dpc_containment_active"
}

Results:
{
  "Output": "drivers/cxl/core/ras.c=291=pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,\n--\ndrivers/cxl/core/ras.c-332-\t\t */\ndrivers/cxl/core/ras.c:333:\t\tif (pci_dpc_containment_active(pdev))\ndrivers/cxl/core/ras.c-334-\t\t\treturn PCI_ERS_RESULT_NEED_RESET;\n--\ndrivers/pci/pcie/dpc.c=103=bool pci_dpc_recovered(struct pci_dev *pdev)\n--\ndrivers/pci/pcie/dpc.c-130-/**\ndrivers/pci/pcie/dpc.c:131: * pci_dpc_containment_active - whether a Port above @pdev is contained by DPC\ndrivers/pci/pcie/dpc.c-132- * @pdev: PCI device below the Port\n--\ndrivers/pci/pcie/dpc.c-144- */\ndrivers/pci/pcie/dpc.c:145:bool pci_dpc_containment_active(struct pci_dev *pdev)\ndrivers/pci/pcie/dpc.c-146-{\n--\ndrivers/pci/pcie/dpc.c-165-}\ndrivers/pci/pcie/dpc.c:166:EXPORT_SYMBOL_GPL(pci_dpc_containment_active);\ndrivers/pci/pcie/dpc.c-167-\n--\ninclude/linux/aer.h=66=static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }\n--\ninclude/linux/aer.h-69-#if defined(CONFIG_PCIE_DPC)\ninclude/linux/aer.h:70:bool pci_dpc_containment_active(struct pci_dev *pdev);\ninclude/linux/aer.h-71-#else\ninclude/linux/aer.h:72:static inline bool pci_dpc_containment_active(struct pci_dev *pdev)\ninclude/linux/aer.h-73-{\n"
}

43/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38913
							output: 24
							thoughts: 155
						

Thoughts:
**My Assessment of the Exported Symbols**

Okay, so I'm looking at a list of exported symbols, and my immediate thought is to understand their context. I see several symbols prefixed with "cxl_", clearly related to CXL (Compute Express Link) functionality, including memory management, port operations, and HDM (Hardware-Defined Memory) state manipulation: `cxl_offline_memory`, `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, and `cxl_region_invalidate_memregion`. These all seem pretty standard for dealing with CXL memory and interconnects.

Then there's `__pci_bridge_secondary_bus_reset`, `pci_bridge_secondary_bus_reset` and `pci_dpc_containment_active`, which are a bit more general, but still potentially relevant in a system that's likely dealing with PCI devices within the CXL context. The presence of the double underscore, `__pci_bridge_secondary_bus_reset`, is usually a clue. It's often an internal version of a public symbol, so I'm going to want to confirm if the latter, `pci_bridge_secondary_bus_reset` is, in fact, exported. That's my immediate next step. I need to confirm whether it's available for other parts of the system to utilize, and to understand how it interacts with the CXL elements.



44/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "pci_bridge_secondary_bus_reset"
}

Results:
{
  "Output": "drivers/infiniband/hw/hfi1/pcie.c=797=static int trigger_sbr(struct hfi1_devdata *dd)\n--\ndrivers/infiniband/hw/hfi1/pcie.c-821-\t */\ndrivers/infiniband/hw/hfi1/pcie.c:822:\treturn pci_bridge_secondary_bus_reset(dev-\u003ebus-\u003eself);\ndrivers/infiniband/hw/hfi1/pcie.c-823-}\n--\ndrivers/pci/hotplug/pciehp_hpc.c=939=int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe)\n--\ndrivers/pci/hotplug/pciehp_hpc.c-951-\ndrivers/pci/hotplug/pciehp_hpc.c:952:\trc = pci_bridge_secondary_bus_reset(ctrl-\u003epcie-\u003eport);\ndrivers/pci/hotplug/pciehp_hpc.c-953-\n--\ndrivers/pci/hotplug/pnv_php.c=622=static int pnv_php_reset_slot(struct hotplug_slot *slot, bool probe)\n--\ndrivers/pci/hotplug/pnv_php.c-639-\ndrivers/pci/hotplug/pnv_php.c:640:\tpci_bridge_secondary_bus_reset(bridge);\ndrivers/pci/hotplug/pnv_php.c-641-\n--\ndrivers/pci/pci.c=4988=static void cxl_sbr_complete(struct pci_dev *bridge, u16 dvsec,\n--\ndrivers/pci/pci.c-5024-/*\ndrivers/pci/pci.c:5025: * __pci_bridge_secondary_bus_reset - assert Secondary Bus Reset on a bridge\ndrivers/pci/pci.c-5026- * @dev: bridge device\n--\ndrivers/pci/pci.c-5028- *\ndrivers/pci/pci.c:5029: * See pci_bridge_secondary_bus_reset(). Pass CXL_SBR_UNBIND when the link is\ndrivers/pci/pci.c-5030- * already down, which leaves the regions' memory online because offlining it\n--\ndrivers/pci/pci.c-5032- */\ndrivers/pci/pci.c:5033:int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\ndrivers/pci/pci.c-5034-\t\t\t\t     enum cxl_sbr_region_action action)\n--\ndrivers/pci/pci.c-5062-/**\ndrivers/pci/pci.c:5063: * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge.\ndrivers/pci/pci.c-5064- * @dev: Bridge device\n--\ndrivers/pci/pci.c-5073- */\ndrivers/pci/pci.c:5074:int pci_bridge_secondary_bus_reset(struct pci_dev *dev)\ndrivers/pci/pci.c-5075-{\ndrivers/pci/pci.c:5076:\treturn __pci_bridge_secondary_bus_reset(dev, CXL_SBR_OFFLINE_AND_UNBIND);\ndrivers/pci/pci.c-5077-}\ndrivers/pci/pci.c:5078:EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);\ndrivers/pci/pci.c-5079-\ndrivers/pci/pci.c=5080=static int pci_parent_bus_reset(struct pci_dev *dev, bool probe)\n--\ndrivers/pci/pci.c-5094-\ndrivers/pci/pci.c:5095:\treturn pci_bridge_secondary_bus_reset(dev-\u003ebus-\u003eself);\ndrivers/pci/pci.c-5096-}\n--\ndrivers/pci/pci.c=5816=static int pci_bus_reset(struct pci_bus *bus, bool probe)\n--\ndrivers/pci/pci.c-5829-\ndrivers/pci/pci.c:5830:\tret = pci_bridge_secondary_bus_reset(bus-\u003eself);\ndrivers/pci/pci.c-5831-\n--\ndrivers/pci/pci.c=5843=static int pci_try_reset_bus(struct pci_bus *bus)\n--\ndrivers/pci/pci.c-5853-\t\tmight_sleep();\ndrivers/pci/pci.c:5854:\t\trc = pci_bridge_secondary_bus_reset(bus-\u003eself);\ndrivers/pci/pci.c-5855-\t\tpci_bus_restore_locked(bus);\n--\ndrivers/pci/pci.h=234=void pci_init_reset_methods(struct pci_dev *dev);\ndrivers/pci/pci.h:235:int pci_bridge_secondary_bus_reset(struct pci_dev *dev);\ndrivers/pci/pci.h-236-\n--\ndrivers/pci/pci.h=242=enum cxl_sbr_region_action {\n--\ndrivers/pci/pci.h-246-\ndrivers/pci/pci.h:247:int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,\ndrivers/pci/pci.h-248-\t\t\t\t     enum cxl_sbr_region_action action);\n--\ndrivers/pci/pcie/dpc.c=186=pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)\n--\ndrivers/pci/pcie/dpc.c-216-\tif (is_cxl_dport(pdev) \u0026\u0026 cxl_port_dvsec(pdev))\ndrivers/pci/pcie/dpc.c:217:\t\trc = __pci_bridge_secondary_bus_reset(pdev, CXL_SBR_UNBIND);\ndrivers/pci/pcie/dpc.c-218-\telse\n--\ninclude/linux/pci.h=1604=int devm_request_pci_bus_resources(struct device *dev,\n--\ninclude/linux/pci.h-1607-/* Temporary until new and working PCI SBR API in place */\ninclude/linux/pci.h:1608:int pci_bridge_secondary_bus_reset(struct pci_dev *dev);\ninclude/linux/pci.h-1609-\n"
}

45/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 41195
							output: 21
							thoughts: 246
						

Thoughts:
**My Configuration Verification**

Okay, so I've confirmed that `pci_bridge_secondary_bus_reset` is indeed exported. Good. Now, let's run through the configuration settings. I need to make sure I've got this straight.

Right, so the key configurations are: `CXL_BUS`, `CXL_PCI`, `CXL_REGION`, `HOTPLUG_PCI_PCIE`, `PCIE_DPC`, and `MEMORY_HOTPLUG`. I'll just confirm each one is precisely how it appears in the configuration.

Alright, `MEMORY_HOTPLUG` is the exact name, no leading `CONFIG_`. Got it. Next, `CXL_REGION`... also the exact name. And `CXL_BUS`... good. `CXL_PCI` is the exact name too, and `HOTPLUG_PCI_PCIE`... nailed it. And finally `PCIE_DPC`, exact. And let's not forget the basics, `PCI` is precise as well.

Wait a second, `MEMORY_HOTPLUG` sometimes depends on `SPARSEMEM`, so I'd better double check those dependencies now. Always good to dot the i's and cross the t's, even when I think I have it covered.



46/2 2026/08/25 02:53 tool grepper
0m Args:
{
  "Expression": "config MEMORY_HOTPLUG"
}

Results:
{
  "Output": "mm/Kconfig=519=config ARCH_ENABLE_MEMORY_HOTPLUG\n--\nmm/Kconfig-522-# eventually, we can have this option just 'select SPARSEMEM'\nmm/Kconfig:523:menuconfig MEMORY_HOTPLUG\nmm/Kconfig-524-\tbool \"Memory hotplug\"\n"
}

47/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 41838
							output: 32
							thoughts: 18
						

Thoughts:
Here's my attempt at summarizing my thought process, as if I were analyzing the Kconfig files:

**Diving into the Memory Hotplug Configuration**

Okay, so I'm starting by looking at `mm/Kconfig` around the `MEMORY_HOTPLUG` option. My initial thought is to understand how this feature is defined, what dependencies it has, and what other options might be relevant to its configuration. I'm probably looking for clues about the scope of the feature – does it relate only to specific architectures, memory types, or hardware configurations? I'm anticipating finding dependencies on other kernel features or hardware support. Is it a core capability, or more of an optional add-on? I will be looking to get an overview of the configuration options around hotplug, and how the kernel handles the addition or removal of memory. From a configuration standpoint, what kinds of things can I enable or disable?



48/2 2026/08/25 02:53 tool read-file
0m Args:
{
  "File": "mm/Kconfig",
  "FirstLine": 520,
  "LineCount": 20
}

Results:
{
  "Contents": " 520:\t\tbool\n 521:\t\n 522:\t# eventually, we can have this option just 'select SPARSEMEM'\n 523:\tmenuconfig MEMORY_HOTPLUG\n 524:\t\tbool \"Memory hotplug\"\n 525:\t\tselect MEMORY_ISOLATION\n 526:\t\tdepends on SPARSEMEM_VMEMMAP\n 527:\t\tdepends on ARCH_ENABLE_MEMORY_HOTPLUG\n 528:\t\tdepends on 64BIT\n 529:\t\tselect NUMA_KEEP_MEMINFO if NUMA\n 530:\t\n 531:\tif MEMORY_HOTPLUG\n 532:\t\n 533:\tchoice\n 534:\t\tprompt \"Memory Hotplug Default Online Type\"\n 535:\t\tdefault MHP_DEFAULT_ONLINE_TYPE_OFFLINE\n 536:\t\thelp\n 537:\t\t  Default memory type for hotplugged memory.\n 538:\t\n 539:\t\t  This option sets the default policy setting for memory hotplug\n"
}

49/2 2026/08/25 02:53 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 42357
							output: 311
							thoughts: 678
						

Thoughts:
**My Analysis of CXL Downstream Port SBR Implementation**

Okay, let's break this down. I'm focusing on the implementation of Secondary Bus Reset (SBR) for CXL Downstream Ports. The first thing I note is that `MEMORY_HOTPLUG` is a key factor here, and it depends on `SPARSEMEM_VMEMMAP`, `ARCH_ENABLE_MEMORY_HOTPLUG`, and, of course, a 64-bit architecture. I only need to consider direct dependencies, so that is enough. `CXL_REGION` is also important, as it selects `MEMREGION` and `GET_FREE_REGION`.

I've identified these configurations as being particularly relevant: `CXL_BUS`, `CXL_PCI`, `CXL_REGION`, `HOTPLUG_PCI_PCIE`, `PCIE_DPC`, and `MEMORY_HOTPLUG`.

Now, let's look at the symbols. The ones that stick out are: `cxl_offline_memory`, `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, `cxl_region_invalidate_memregion`, `__pci_bridge_secondary_bus_reset`, `pci_bridge_secondary_bus_reset`, and `pci_dpc_containment_active`.

Let's quickly check export status. Hmm, looks like `__pci_bridge_secondary_bus_reset` isn't exported, but it's not static, so it *could* be used within the module. Good. `pci_bridge_secondary_bus_reset` is exported. That's good to know.

Now, let's check `cxl_offline_memory`. Ah, it is exported as `EXPORT_SYMBOL_NS_GPL(cxl_offline_memory, "CXL_MHP");`. The rest of the `cxl_*` functions: `cxl_port_recommit_decoders`, `cxl_port_save_hdm_state`, `cxl_port_put_hdm_state`, `cxl_enable_hdm`, `cxl_set_mem_enable`, and `cxl_region_invalidate_memregion` are *not* exported, but also aren't static. Finally, `pci_dpc_containment_active` is exported.

So, summing it up, the patch implements SBR specifically for CXL Downstream Ports. It adds sysfs attributes, that's interesting. It also implements callbacks that handle the enabling and disabling of CXL regions during the Secondary Bus Reset, which could be critical for data integrity. The DPC is handled, too, but it's unbinding instead of offlining. That's a key detail to watch for. This functionality is accessible via sysfs and PCI error recovery, making it even more interesting. It will definitely be worth fuzzing these new interfaces.



50/2 2026/08/25 02:54 tool set-results
0m Args:
{
  "EnableConfigs": [
    "CXL_BUS",
    "CXL_PCI",
    "CXL_REGION",
    "HOTPLUG_PCI_PCIE",
    "PCIE_DPC",
    "MEMORY_HOTPLUG"
  ],
  "FocusSymbols": [
    "cxl_offline_memory",
    "cxl_port_recommit_decoders",
    "cxl_port_save_hdm_state",
    "cxl_port_put_hdm_state",
    "cxl_enable_hdm",
    "cxl_set_mem_enable",
    "cxl_region_invalidate_memregion",
    "__pci_bridge_secondary_bus_reset",
    "pci_bridge_secondary_bus_reset",
    "pci_dpc_containment_active"
  ],
  "Reasoning": "The patch implements Secondary Bus Reset (SBR) support for CXL Downstream Ports. It introduces a new sysfs attribute `cxl_unmask_sbr` to allow unmasking SBR, and implements logic to disable/enable CXL regions during SBR. It also handles Downstream Port Containment (DPC) by unbinding regions instead of offlining them. The changes modify core PCI reset logic and CXL region management, which are reachable via sysfs and PCI error recovery in virtualized environments like QEMU.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "CXL_BUS",
    "CXL_PCI",
    "CXL_REGION",
    "HOTPLUG_PCI_PCIE",
    "PCIE_DPC",
    "MEMORY_HOTPLUG"
  ],
  "FocusSymbols": [
    "cxl_offline_memory",
    "cxl_port_recommit_decoders",
    "cxl_port_save_hdm_state",
    "cxl_port_put_hdm_state",
    "cxl_enable_hdm",
    "cxl_set_mem_enable",
    "cxl_region_invalidate_memregion",
    "__pci_bridge_secondary_bus_reset",
    "pci_bridge_secondary_bus_reset",
    "pci_dpc_containment_active"
  ],
  "Reasoning": "The patch implements Secondary Bus Reset (SBR) support for CXL Downstream Ports. It introduces a new sysfs attribute `cxl_unmask_sbr` to allow unmasking SBR, and implements logic to disable/enable CXL regions during SBR. It also handles Downstream Port Containment (DPC) by unbinding regions instead of offlining them. The changes modify core PCI reset logic and CXL region management, which are reachable via sysfs and PCI error recovery in virtualized environments like QEMU.",
  "WorthFuzzing": true
}

51/1 2026/08/25 02:54 action If
1m Args:
{
  "WorthFuzzing": true
}

Results:
null

52/2 2026/08/25 02:54 agent kmsan-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces logic to handle Secondary Bus Reset (SBR) for CXL Downstream Ports, which involves saving and restoring hardware registers, offlining memory blocks, and unbinding drivers. All newly allocated data structures (`hdm_state`, `state`) are allocated using zero-initializing functions (`kzalloc_obj`, `kzalloc_flex`). Local variables (`ctx`, `regions`) are explicitly initialized. No new complex structures are exposed to user space. The only user-space interface added is a sysfs attribute (`cxl_unmask_sbr`) which reads/writes a single bitfield. There is no risk of uninitialized memory being used in control flow or leaked to user space. Standard KASAN and LOCKDEP are sufficient to detect any potential memory corruption, out-of-bounds accesses, or locking issues.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 106959a31a9fda18580851af5ce38f8d304e1fa9
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Aug 25 02:50:40 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index b767db2c52cb7..7b0314da6f4d6 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -174,6 +174,22 @@ Description:
 		similiar to writing 1 to their individual "reset" file, so use
 		with caution.
 
+What:		/sys/bus/pci/devices/.../cxl_unmask_sbr
+Date:		August 2026
+Contact:	linux-pci@vger.kernel.org
+Description:
+		This is visible only for a CXL Downstream Port, that is a Root
+		Port or a Downstream Switch Port that publishes the CXL
+		Extensions DVSEC for Ports.  Such a Port ignores the Secondary
+		Bus Reset bit in its Bridge Control register unless its
+		"Unmask SBR" bit is set, and system firmware leaves that bit
+		clear by default.
+
+		Writing 1 allows the kernel to set "Unmask SBR" while it
+		resets the Port's secondary bus, so that the reset reaches the
+		component below the Port.  While this reads 0, a bus reset of
+		the Port fails instead.
+
 What:		/sys/bus/pci/devices/.../vpd
 Date:		February 2008
 Contact:	Ben Hutchings <bwh@kernel.org>
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index bcfe2d9f4adbd..ce89b251d34ac 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -1018,6 +1018,42 @@ int walk_memory_blocks(unsigned long start, unsigned long size,
 	return ret;
 }
 
+static int cxl_offline_memory_block(struct memory_block *mem, void *arg)
+{
+	int rc = device_offline(&mem->dev);
+
+	/* device_offline() returns a positive value when already offline. */
+	if (rc > 0)
+		return 0;
+
+	return rc;
+}
+
+/**
+ * cxl_offline_memory - offline the memory blocks spanning a physical range
+ * @start: start of the range, memory-block aligned
+ * @size: size of the range, a multiple of the memory block size
+ *
+ * Offline every memory block in [start, start + size). The blocks are offlined
+ * but not removed, so the range can be brought back online afterward. The
+ * caller owns the range and this performs no validation on it.
+ *
+ * Return: 0 on success, negative errno if a block cannot be offlined.
+ *
+ * Context: process context. Sleeps and takes the memory hotplug lock.
+ */
+int cxl_offline_memory(u64 start, u64 size)
+{
+	int rc;
+
+	lock_device_hotplug();
+	rc = walk_memory_blocks(start, size, NULL, cxl_offline_memory_block);
+	unlock_device_hotplug();
+
+	return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_offline_memory, "CXL_MHP");
+
 struct for_each_memory_block_cb_data {
 	walk_memory_blocks_func_t func;
 	void *arg;
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index ce7213818d3c0..4d4538182d81b 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -15,7 +15,7 @@ cxl_core-y += hdm.o
 cxl_core-y += pmu.o
 cxl_core-y += cdat.o
 cxl_core-$(CONFIG_TRACING) += trace.o
-cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o
+cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o dport_sbr.o
 cxl_core-$(CONFIG_CXL_MCE) += mce.o
 cxl_core-$(CONFIG_CXL_FEATURES) += features.o
 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 07555ae638594..b250fa3461847 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -13,6 +13,9 @@ extern const struct device_type cxl_pmu_type;
 
 extern struct attribute_group cxl_base_attribute_group;
 
+struct cxl_port *find_cxl_port(struct device *dport_dev,
+			       struct cxl_dport **dport);
+
 enum cxl_detach_mode {
 	DETACH_ONLY,
 	DETACH_INVALIDATE,
@@ -53,6 +56,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 int devm_cxl_add_dax_region(struct cxl_region *cxlr);
 int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
 void kill_regions(struct cxl_root_decoder *cxlrd);
+int cxl_region_invalidate_memregion(struct cxl_region *cxlr);
+struct pci_cxl_sbr_region_ops;
+extern const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops;
 
 #else
 static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -213,6 +219,28 @@ int cxl_gpf_port_setup(struct cxl_dport *dport);
 struct cxl_hdm;
 int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
 			struct cxl_endpoint_dvsec_info *info);
+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl);
+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val);
+/**
+ * struct cxl_hdm_state - one CXL port's HDM decoder programming, saved
+ * @global_ctrl: CXL HDM Decoder Global Control
+ * @nr_ctrl: number of entries in @ctrl
+ * @ctrl: CXL HDM Decoder n Control, indexed by decoder id
+ *
+ * Holds the fields of those two registers that the driver does not model, read
+ * before a reset and written back after it. Instances are held in an xarray
+ * keyed by the &struct cxl_port they were read from.
+ */
+struct cxl_hdm_state {
+	u32 global_ctrl;
+	int nr_ctrl;
+	u32 ctrl[];
+};
+
+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state);
+void cxl_port_put_hdm_state(struct xarray *hdm_state);
+int cxl_port_recommit_decoders(struct cxl_port *port,
+			       struct xarray *hdm_state);
 int cxl_port_get_possible_dports(struct cxl_port *port);
 
 #ifdef CONFIG_CXL_FEATURES
diff --git a/drivers/cxl/core/dport_sbr.c b/drivers/cxl/core/dport_sbr.c
new file mode 100644
index 0000000000000..823b63012f45b
--- /dev/null
+++ b/drivers/cxl/core/dport_sbr.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2026 Intel Corporation. */
+
+#include <linux/memregion.h>
+#include <linux/memory_hotplug.h>
+#include <linux/memory.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <cxl.h>
+#include "core.h"
+
+/*
+ * cxl_region_unbind - take a region out of service ahead of a reset
+ * @cxlr: region routed through the CXL Downstream Port being reset
+ *
+ * Unbind the region driver, which tears down everything built on the region:
+ * the dax region device, its dax device and the driver bound to it. An SBR
+ * zeroes the downstream bus number, so a region left bound would decode to a
+ * device in reset.
+ *
+ * The memory the region hosts is left as it is. A caller that reaches a live
+ * device offlines it first; see cxl_region_disable().
+ *
+ * Context: process context. Driver unbind sleeps, so this cannot run in atomic
+ * context.
+ */
+static void cxl_region_unbind(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+
+	device_release_driver(&cxlr->dev);
+	dev_dbg(&cxlr->dev, "%s: region unbound before reset, HPA %pr\n",
+		__func__, p->res);
+}
+
+/*
+ * cxl_region_disable - make a region inactive ahead of a Secondary Bus Reset
+ * @cxlr: region routed through the CXL Downstream Port being reset
+ *
+ * Offline the memory blocks the region owns and unbind its driver. An SBR
+ * zeroes the downstream bus number, so a region left live as System RAM would
+ * be accessed while the device is in reset. On offline failure return the error
+ * so the caller aborts the reset; the memory is never force-removed.
+ *
+ * Context: process context. Offlining and driver unbind sleep and take the
+ * memory hotplug lock, so this cannot run in atomic context.
+ */
+static int cxl_region_disable(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+	unsigned long block_size;
+	u64 start, end;
+	int rc;
+
+	/*
+	 * Per CXL r4.0 sec 9.13.1 an Interleave Set has a Base HPA and a Size
+	 * that are multiples of 256 MB, while a memory block spans up to 2 GB.
+	 * A block overlapping either end of the range therefore also covers
+	 * memory outside this region, so round the range inward to block
+	 * granularity as dax_kmem did when it onlined the range. Offlining a
+	 * straddling block would migrate pages that the reset does not affect.
+	 */
+	block_size = memory_block_size_bytes();
+	start = ALIGN(p->res->start, block_size);
+	end = ALIGN_DOWN(p->res->end + 1, block_size);
+	if (start >= end) {
+		dev_dbg(&cxlr->dev, "%s: HPA %pr spans no whole memory block, no System RAM to offline\n",
+			__func__, p->res);
+	} else {
+		rc = cxl_offline_memory(start, end - start);
+		if (rc) {
+			dev_warn(&cxlr->dev, "offline System RAM failed before reset: %d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	rc = cxl_region_invalidate_memregion(cxlr);
+	if (rc) {
+		dev_warn(&cxlr->dev, "CPU cache invalidate failed before reset: %d\n",
+			 rc);
+		return rc;
+	}
+
+	cxl_region_unbind(cxlr);
+	dev_dbg(&cxlr->dev, "%s: System RAM offline, region disabled before reset, HPA %pr\n",
+		__func__, p->res);
+
+	return 0;
+}
+
+/*
+ * cxl_region_enable - restore a region after a Secondary Bus Reset
+ * @cxlr: region disabled by cxl_region_disable() before the reset
+ *
+ * Rebind the region driver. The System RAM is left offline; bringing it back
+ * online is a separate administrative step.
+ */
+static void cxl_region_enable(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+
+	if (device_attach(&cxlr->dev) < 0) {
+		dev_dbg(&cxlr->dev, "driver re-attach failed after reset\n");
+		return;
+	}
+
+	dev_dbg(&cxlr->dev, "%s: region re-enabled after reset, HPA %pr, IW %d, IG %d\n",
+		__func__, p->res, p->interleave_ways, p->interleave_granularity);
+}
+
+/*
+ * Collect the regions with a member endpoint routed through @dport_pci, the
+ * CXL Downstream Port about to be reset. cxl_rwsem.region keeps the topology
+ * stable for the duration of the walk only. Each collected region is pinned
+ * with get_device() so the object survives after the lock is dropped, since
+ * cxl_region_disable()/cxl_region_enable() run with the rwsem released (they
+ * unbind and rebind the region driver). Hence snapshot the set first.
+ */
+static int cxl_sbr_collect_regions(struct pci_dev *dport_pci,
+				   struct xarray *regions)
+{
+	struct cxl_region_ref *cxl_rr;
+	struct cxl_dport *dport;
+	unsigned long index;
+	int count = 0;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port) {
+		pci_dbg(dport_pci, "no CXL port found for reset dport\n");
+		return 0;
+	}
+
+	guard(rwsem_read)(&cxl_rwsem.region);
+	xa_for_each(&port->regions, index, cxl_rr) {
+		struct cxl_region *cxlr = cxl_rr->region;
+		struct cxl_ep *ep;
+		unsigned long ep_index;
+
+		/* Skip unless a region endpoint sits below the reset dport. */
+		xa_for_each(&cxl_rr->endpoints, ep_index, ep)
+			if (ep->dport == dport)
+				break;
+		if (!ep) {
+			dev_dbg(&cxlr->dev, "%s: no endpoint below %s, region excluded\n",
+				__func__, dev_name(dport->dport_dev));
+			continue;
+		}
+
+		get_device(&cxlr->dev);
+		rc = xa_insert(regions, (unsigned long)cxlr, cxlr, GFP_KERNEL);
+		if (rc) {
+			put_device(&cxlr->dev);
+			return rc;
+		}
+		dev_dbg(&cxlr->dev, "%s: endpoint below %s, region collected\n",
+			__func__, dev_name(dport->dport_dev));
+		count++;
+	}
+
+	dev_dbg(&port->dev, "%d region(s) routed through %s\n", count,
+		dev_name(dport->dport_dev));
+	return 0;
+}
+
+static void cxl_sbr_put_regions(struct xarray *regions)
+{
+	struct cxl_region *cxlr;
+	unsigned long index;
+
+	xa_for_each(regions, index, cxlr)
+		put_device(&cxlr->dev);
+	xa_destroy(regions);
+}
+
+/*
+ * The reset cleared the HDM Decoder registers of every CXL component below
+ * @dport_pci, so restore them from the settings the driver holds and from
+ * @hdm_state, the register fields the driver does not model, saved before the
+ * reset. Takes cxl_rwsem.region for read, which cxl_port_recommit_decoders()
+ * requires. The caller has already disabled the regions, so nothing reaches the
+ * decoders being reprogrammed.
+ */
+static void cxl_sbr_recommit_decoders(struct pci_dev *dport_pci,
+				      struct xarray *hdm_state)
+{
+	struct cxl_dport *dport;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port) {
+		pci_dbg(dport_pci, "no CXL port owns this Downstream Port\n");
+		return;
+	}
+
+	pci_dbg(dport_pci, "restoring HDM decode below %s\n", dev_name(&port->dev));
+
+	guard(rwsem_read)(&cxl_rwsem.region);
+	rc = cxl_port_recommit_decoders(port, hdm_state);
+	if (rc)
+		pci_warn(dport_pci, "HDM decode restore failed: %d\n", rc);
+}
+
+/*
+ * The HDM decoder control registers the reset is about to clear, held from the
+ * disable to the enable of one Downstream Port and indexed by that Port's
+ * struct pci_dev, so resets of different Ports do not share an entry.
+ */
+static DEFINE_XARRAY(cxl_sbr_hdm_state);
+
+static void cxl_sbr_drop_hdm_state(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+
+	hdm_state = xa_erase(&cxl_sbr_hdm_state, (unsigned long)dport_pci);
+	if (!hdm_state)
+		return;
+
+	cxl_port_put_hdm_state(hdm_state);
+	kfree(hdm_state);
+}
+
+/*
+ * Record the control registers of every port below @dport_pci before the reset
+ * clears them. cxl_sbr_enable_regions() consumes the set and drops it.
+ */
+static int cxl_sbr_save_hdm_state(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+	struct cxl_dport *dport;
+	int rc;
+
+	struct cxl_port *port __free(put_cxl_port) =
+		find_cxl_port(&dport_pci->dev, &dport);
+	if (!port)
+		return 0;
+
+	hdm_state = kzalloc_obj(*hdm_state);
+	if (!hdm_state)
+		return -ENOMEM;
+
+	xa_init(hdm_state);
+
+	scoped_guard(rwsem_read, &cxl_rwsem.region)
+		rc = cxl_port_save_hdm_state(port, hdm_state);
+
+	if (!rc)
+		rc = xa_insert(&cxl_sbr_hdm_state, (unsigned long)dport_pci,
+			       hdm_state, GFP_KERNEL);
+	if (rc) {
+		cxl_port_put_hdm_state(hdm_state);
+		kfree(hdm_state);
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Disable the regions routed through the Downstream Port being reset. On
+ * failure re-enable the regions already disabled and return the error so the
+ * PCI core aborts the reset with the topology unchanged.
+ */
+static int cxl_sbr_disable_regions(struct pci_dev *dport_pci)
+{
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+	int rc;
+
+	rc = cxl_sbr_save_hdm_state(dport_pci);
+	if (rc)
+		return rc;
+
+	xa_init(&regions);
+
+	rc = cxl_sbr_collect_regions(dport_pci, &regions);
+	if (rc)
+		goto out;
+
+	xa_for_each(&regions, index, cxlr) {
+		rc = cxl_region_disable(cxlr);
+		if (rc)
+			break;
+	}
+
+	/*
+	 * On failure restore every collected region and return the error so the
+	 * PCI core aborts the reset before touching the hardware. Re-enabling a
+	 * region left untouched is a no-op, so enabling the whole set also
+	 * recovers the region whose offline failed midway.
+	 */
+	if (rc) {
+		dev_dbg(&dport_pci->dev, "%s: disable failed (%d), re-enabling collected regions and aborting reset\n",
+			__func__, rc);
+		xa_for_each(&regions, index, cxlr)
+			cxl_region_enable(cxlr);
+	}
+
+out:
+	cxl_sbr_put_regions(&regions);
+	/* No enable_regions() call follows an aborted reset, so drop the set. */
+	if (rc)
+		cxl_sbr_drop_hdm_state(dport_pci);
+	return rc;
+}
+
+/*
+ * Unbind the regions routed through the Downstream Port being reset, leaving
+ * their memory online. Used on the DPC recovery path, where dpc_reset_link()
+ * clears DPC Trigger Status and enters the reset without waiting for the link,
+ * so the device may still be unreachable and the page migration that an offline
+ * performs would have no device to read from.
+ *
+ * Unbinding cannot fail, so unlike cxl_sbr_disable_regions() this never aborts
+ * the reset. The memory stays online across the reset with no region decoding
+ * it; cxl_sbr_enable_regions() reprograms the decoders on the way out.
+ */
+static void cxl_sbr_unbind_regions(struct pci_dev *dport_pci)
+{
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+
+	if (cxl_sbr_save_hdm_state(dport_pci))
+		pci_warn(dport_pci, "HDM state not saved, decode will not be restored\n");
+
+	xa_init(&regions);
+
+	cxl_sbr_collect_regions(dport_pci, &regions);
+
+	xa_for_each(&regions, index, cxlr)
+		cxl_region_unbind(cxlr);
+
+	cxl_sbr_put_regions(&regions);
+}
+
+/*
+ * Re-enable the regions disabled by cxl_sbr_disable_regions(). Restore the HDM
+ * decode first: a region cannot serve memory through decoders that are not
+ * programmed, so its driver must not re-attach before they are.
+ */
+static void cxl_sbr_enable_regions(struct pci_dev *dport_pci)
+{
+	struct xarray *hdm_state;
+	struct cxl_region *cxlr;
+	struct xarray regions;
+	unsigned long index;
+
+	xa_init(&regions);
+
+	cxl_sbr_collect_regions(dport_pci, &regions);
+
+	hdm_state = xa_load(&cxl_sbr_hdm_state, (unsigned long)dport_pci);
+	if (hdm_state)
+		cxl_sbr_recommit_decoders(dport_pci, hdm_state);
+	else
+		pci_warn(dport_pci, "no saved HDM state, decode not restored\n");
+
+	xa_for_each(&regions, index, cxlr)
+		cxl_region_enable(cxlr);
+
+	cxl_sbr_put_regions(&regions);
+	cxl_sbr_drop_hdm_state(dport_pci);
+}
+
+const struct pci_cxl_sbr_region_ops cxl_sbr_region_ops = {
+	.disable_regions = cxl_sbr_disable_regions,
+	.unbind_regions = cxl_sbr_unbind_regions,
+	.enable_regions = cxl_sbr_enable_regions,
+};
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0c80b76a5f9b4..9839d2592d206 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -849,6 +849,347 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
 	return 0;
 }
 
+/**
+ * cxl_decoder_recommit - reprogram @cxld's HDM decoder registers and commit
+ * @cxld: decoder to reprogram from its cached settings
+ * @ctrl: CXL HDM Decoder n Control value to restore under the cached settings
+ *
+ * A reset of an upstream link clears the HDM decoder registers of every
+ * component below it, dropping Committed while the driver still holds the
+ * settings that were in effect. Restore those settings and commit.
+ *
+ * setup_hw_decoder() rewrites only Interleave Granularity, Interleave Ways and
+ * Target Range Type, so the rest of the control register would come from a read
+ * of the reset defaults. Per CXL r4.0 sec 8.2.4.20.7 Table 8-123 that register
+ * also holds BI, UIO, Upstream Interleave Granularity, Upstream Interleave Ways
+ * and Lock On Commit, none of which the driver models, and sec 8.2.4.20.12 makes
+ * device operation undefined if a device that requires BI is committed without
+ * it. Write @ctrl first so those fields are in place, with Commit masked off
+ * until setup_hw_decoder() has written the range.
+ *
+ * A decoder that hardware still reports Committed kept its programming across
+ * the reset and needs no work. A decoder with no ->commit is driven through the
+ * DVSEC ranges or is a passthrough decoder, and has no registers to program.
+ *
+ * Section 8.2.4.20.13 requires the traffic targeting @cxld to be quiesced while
+ * it is reprogrammed, which the caller owns. @cxld decodes nothing until the
+ * commit completes.
+ *
+ * Return: 0 on success or if @cxld needs no reprogramming, negative errno if the
+ * commit times out or if the hardware reports a commit error.
+ */
+static int cxl_decoder_recommit(struct cxl_decoder *cxld, u32 ctrl)
+{
+	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+	struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
+	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
+	u32 hw_ctrl;
+	int rc;
+
+	if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
+		return 0;
+
+	if (!cxld->commit)
+		return 0;
+
+	hw_ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
+	if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, hw_ctrl)) {
+		dev_dbg(&cxld->dev, "%s: still committed, no reprogram needed\n",
+			__func__);
+		return 0;
+	}
+
+	writel(ctrl & ~(CXL_HDM_DECODER0_CTRL_COMMIT |
+			CXL_HDM_DECODER0_CTRL_COMMITTED |
+			CXL_HDM_DECODER0_CTRL_COMMIT_ERROR),
+	       hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
+
+	scoped_guard(rwsem_read, &cxl_rwsem.dpa)
+		setup_hw_decoder(cxld, hdm);
+
+	rc = cxld_await_commit(hdm, cxld->id);
+	if (rc) {
+		dev_warn(&cxld->dev, "%s: failed to commit decoder: %d\n",
+			 __func__, rc);
+		return rc;
+	}
+
+	dev_dbg(&cxld->dev, "%s: reprogrammed HPA %#llx-%#llx\n",
+		__func__, cxld->hpa_range.start, cxld->hpa_range.end);
+
+	return 0;
+}
+
+static int __cxl_endpoint_decoder_is_emulated(struct device *dev, void *data)
+{
+	if (!is_endpoint_decoder(dev))
+		return 0;
+
+	return !to_cxl_decoder(dev)->commit;
+}
+
+/* Only the DVSEC setup path leaves ->commit NULL. */
+static bool cxl_endpoint_decoders_are_emulated(struct cxl_port *endpoint)
+{
+	return device_for_each_child(&endpoint->dev, NULL,
+				     __cxl_endpoint_decoder_is_emulated);
+}
+
+struct cxl_recommit_ctx {
+	const struct cxl_hdm_state *state;
+	int *first_rc;
+};
+
+static int __cxl_port_recommit_decoder(struct device *dev, void *data)
+{
+	struct cxl_recommit_ctx *ctx = data;
+	struct cxl_decoder *cxld;
+	int rc;
+
+	if (!(is_switch_decoder(dev) || is_endpoint_decoder(dev)))
+		return 0;
+
+	cxld = to_cxl_decoder(dev);
+
+	if (cxld->id >= ctx->state->nr_ctrl) {
+		dev_warn(&cxld->dev, "%s: no saved control register\n",
+			 __func__);
+		if (!*ctx->first_rc)
+			*ctx->first_rc = -ENODATA;
+		return 0;
+	}
+
+	/*
+	 * Reprogram every decoder the walk reaches. Stopping at the first
+	 * failure would leave the rest of the path decoding nothing, so record
+	 * the first error and continue.
+	 */
+	rc = cxl_decoder_recommit(cxld, ctx->state->ctrl[cxld->id]);
+	if (rc && !*ctx->first_rc)
+		*ctx->first_rc = rc;
+
+	return 0;
+}
+
+/*
+ * Restore CXL.mem decode on @cxlmd before any of its decoders is committed. A
+ * reset clears the endpoint's HDM Decoder Global Control and the DVSEC CXL
+ * Control, and per CXL r4.0 sec 8.2.4.20.2 Table 8-118 a device decodes CXL.mem
+ * with the DVSEC range registers while HDM Decoder Enable is clear. Committing
+ * a decoder in that state does not establish the route. An endpoint with no HDM
+ * decoder registers is driven through the DVSEC ranges and has nothing to
+ * enable. So is an endpoint whose decoders are emulated from those ranges, and
+ * setting HDM Decoder Enable there would switch it to decoders locked against
+ * reprogramming.
+ *
+ * @global_ctrl is the Global Control value to enable decode in. That register
+ * also holds Poison On Decode Error Enable, which the driver does not model, so
+ * the caller supplies the value it saved rather than one read back after the
+ * reset.
+ */
+static int cxl_endpoint_enable_hdm_decode(struct cxl_memdev *cxlmd,
+					  u32 global_ctrl)
+{
+	struct cxl_port *endpoint = cxlmd->endpoint;
+	struct cxl_hdm *cxlhdm = dev_get_drvdata(&endpoint->dev);
+	int rc;
+
+	if (!cxlhdm || !cxlhdm->regs.hdm_decoder)
+		return 0;
+
+	if (cxl_endpoint_decoders_are_emulated(endpoint))
+		return 0;
+
+	cxl_enable_hdm(cxlhdm, global_ctrl);
+
+	rc = cxl_set_mem_enable(cxlmd->cxlds, PCI_DVSEC_CXL_MEM_ENABLE);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+/**
+ * cxl_port_recommit_decoders - reprogram the HDM decoders below @port
+ * @port: CXL port whose downstream decoders to reprogram
+ * @hdm_state: saved &struct cxl_hdm_state per port, keyed by &struct cxl_port
+ *
+ * Reprogram the HDM decoders below @port that lost their programming. Every
+ * endpoint beneath @port is restored along its whole path, from the endpoint up
+ * to the last port below @port. A decoder that hardware still reports committed
+ * is left untouched.
+ *
+ * Per CXL r4.0 sec 8.2.4.20.13 decoder m must be committed before decoder m+1
+ * while reprogramming, so let device_for_each_child() visit each port's decoders
+ * in instance order. Each path is walked from the endpoint upward, the order
+ * cxl_region_decode_commit() uses.
+ *
+ * The endpoints are reprogrammed one after another, so an interleaved HPA range
+ * decodes through only part of its interleave set until the last member is
+ * done. Per CXL r4.0 sec 8.2.4.20.13 software owns quiescing the traffic that
+ * targets a decoder being reprogrammed: a read that no decoder positively
+ * decodes returns all 1s or poison, and per Table 8-118 such a write is
+ * dropped. Nothing here can detect a stray access, so the caller carries that
+ * duty.
+ *
+ * Context: caller must hold @cxl_rwsem.region to keep the topology and the
+ * switch decoder target lists stable across the walk, and must have quiesced
+ * every access to the HPA ranges decoded below @port.
+ *
+ * A port with no entry in @hdm_state was not saved, so its decoders are left
+ * alone rather than committed with whatever the reset left in the fields the
+ * driver does not model.
+ *
+ * Return: 0 on success, negative errno of the first decoder that failed or
+ * -ENODATA if a port on the path has no saved state.
+ */
+int cxl_port_recommit_decoders(struct cxl_port *port, struct xarray *hdm_state)
+{
+	struct cxl_ep *port_ep;
+	unsigned long index;
+	int first_rc = 0;
+
+	lockdep_assert_held(&cxl_rwsem.region);
+
+	xa_for_each(&port->endpoints, index, port_ep) {
+		struct cxl_memdev *cxlmd = to_cxl_memdev(port_ep->ep);
+		struct cxl_hdm_state *state;
+		struct cxl_port *iter;
+		int rc;
+
+		if (IS_ERR_OR_NULL(cxlmd->endpoint))
+			continue;
+
+		state = xa_load(hdm_state, (unsigned long)cxlmd->endpoint);
+		if (!state) {
+			dev_warn(&cxlmd->dev, "%s: no saved HDM state\n",
+				 __func__);
+			if (!first_rc)
+				first_rc = -ENODATA;
+			continue;
+		}
+
+		rc = cxl_endpoint_enable_hdm_decode(cxlmd, state->global_ctrl);
+		if (rc) {
+			dev_warn(&cxlmd->dev,
+				 "%s: failed to enable HDM decode: %d\n",
+				 __func__, rc);
+			if (!first_rc)
+				first_rc = rc;
+			continue;
+		}
+
+		/*
+		 * Walk from the endpoint up to @port so a decoder is committed
+		 * only after the decoder it routes to. @port is the last parent
+		 * visited by the walk, and it is excluded.
+		 */
+		for (iter = cxlmd->endpoint; iter && iter != port;
+		     iter = parent_port_of(iter)) {
+			struct cxl_recommit_ctx ctx = {
+				.state = xa_load(hdm_state, (unsigned long)iter),
+				.first_rc = &first_rc,
+			};
+
+			if (!ctx.state) {
+				dev_warn(&iter->dev, "%s: no saved HDM state\n",
+					 __func__);
+				if (!first_rc)
+					first_rc = -ENODATA;
+				continue;
+			}
+
+			device_for_each_child(&iter->dev, &ctx,
+					      __cxl_port_recommit_decoder);
+		}
+	}
+
+	return first_rc;
+}
+
+/**
+ * cxl_port_save_hdm_state - record the HDM decoder control registers below @port
+ * @port: CXL port whose downstream decoders to record
+ * @hdm_state: xarray to fill, one entry per port, keyed by &struct cxl_port
+ *
+ * Read the CXL HDM Decoder Global Control and every CXL HDM Decoder n Control
+ * register of the ports below @port. Those hold the fields
+ * cxl_port_recommit_decoders() cannot rebuild from the driver's cached settings,
+ * so they have to be read while the registers still hold them.
+ *
+ * The set of ports is the same one cxl_port_recommit_decoders() walks. A port
+ * with no HDM decoder registers has nothing to record and gets no entry.
+ *
+ * Context: caller must hold @cxl_rwsem.region.
+ *
+ * Return: 0 on success, negative errno if an entry cannot be allocated or
+ * inserted.
+ */
+int cxl_port_save_hdm_state(struct cxl_port *port, struct xarray *hdm_state)
+{
+	struct cxl_ep *port_ep;
+	unsigned long index;
+
+	lockdep_assert_held(&cxl_rwsem.region);
+
+	xa_for_each(&port->endpoints, index, port_ep) {
+		struct cxl_memdev *cxlmd = to_cxl_memdev(port_ep->ep);
+		struct cxl_port *iter;
+
+		if (IS_ERR_OR_NULL(cxlmd->endpoint))
+			continue;
+
+		for (iter = cxlmd->endpoint; iter && iter != port;
+		     iter = parent_port_of(iter)) {
+			struct cxl_hdm *cxlhdm = dev_get_drvdata(&iter->dev);
+			struct cxl_hdm_state *state;
+			void __iomem *hdm;
+			int rc;
+
+			if (xa_load(hdm_state, (unsigned long)iter))
+				continue;
+
+			if (!cxlhdm || !cxlhdm->regs.hdm_decoder)
+				continue;
+
+			hdm = cxlhdm->regs.hdm_decoder;
+			state = kzalloc_flex(*state, ctrl,
+					     cxlhdm->decoder_count);
+			if (!state)
+				return -ENOMEM;
+
+			state->global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+			state->nr_ctrl = cxlhdm->decoder_count;
+			for (int i = 0; i < state->nr_ctrl; i++)
+				state->ctrl[i] =
+					readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
+
+			rc = xa_insert(hdm_state, (unsigned long)iter, state,
+				       GFP_KERNEL);
+			if (rc) {
+				kfree(state);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cxl_port_put_hdm_state - release a set filled by cxl_port_save_hdm_state()
+ * @hdm_state: xarray to empty
+ */
+void cxl_port_put_hdm_state(struct xarray *hdm_state)
+{
+	struct cxl_hdm_state *state;
+	unsigned long index;
+
+	xa_for_each(hdm_state, index, state)
+		kfree(state);
+	xa_destroy(hdm_state);
+}
+
 static int commit_reap(struct device *dev, void *data)
 {
 	struct cxl_port *port = to_cxl_port(dev->parent);
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index e4338fd7e01b4..a7a2b84293e9c 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -179,7 +179,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_await_media_ready, "CXL");
 
-static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
+int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
 {
 	struct pci_dev *pdev = to_pci_dev(cxlds->dev);
 	int d = cxlds->cxl_dvsec;
@@ -247,14 +247,23 @@ static void disable_hdm(void *_cxlhdm)
 	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 }
 
-static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm)
+/*
+ * @global_ctrl is the CXL HDM Decoder Global Control value to enable decode in.
+ * A caller restoring decode after a reset passes the value it saved, so the
+ * fields the driver does not model are not left at their reset defaults.
+ */
+void cxl_enable_hdm(struct cxl_hdm *cxlhdm, u32 global_ctrl)
 {
 	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
-	u32 global_ctrl;
 
-	global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
 	writel(global_ctrl | CXL_HDM_DECODER_ENABLE,
 	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+}
+
+static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm,
+			       u32 global_ctrl)
+{
+	cxl_enable_hdm(cxlhdm, global_ctrl);
 
 	return devm_add_action_or_reset(host, disable_hdm, cxlhdm);
 }
@@ -398,7 +407,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
 	 * enable and use the HDM Decoder Capability registers.
 	 */
 	if (!info->mem_enabled) {
-		rc = devm_cxl_enable_hdm(&port->dev, cxlhdm);
+		rc = devm_cxl_enable_hdm(&port->dev, cxlhdm, global_ctrl);
 		if (rc)
 			return rc;
 
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 1215ee4f40351..66a6d513843e5 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1392,8 +1392,8 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
 	return NULL;
 }
 
-static struct cxl_port *find_cxl_port(struct device *dport_dev,
-				      struct cxl_dport **dport)
+struct cxl_port *find_cxl_port(struct device *dport_dev,
+			       struct cxl_dport **dport)
 {
 	struct cxl_find_port_ctx ctx = {
 		.dport_dev = dport_dev,
diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
index 99fb00949c2fa..568a906f3b50f 100644
--- a/drivers/cxl/core/ras.c
+++ b/drivers/cxl/core/ras.c
@@ -323,6 +323,16 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
 		}
 		return PCI_ERS_RESULT_CAN_RECOVER;
 	case pci_channel_io_frozen:
+		/*
+		 * A Port on the path in DPC means dpc_reset_link() is about to
+		 * reset the link, and that path takes the CXL regions out of
+		 * service and restores the HDM decode itself. Keep the memdev
+		 * driver bound so the endpoint and its decoders are still there
+		 * to restore.
+		 */
+		if (pci_dpc_containment_active(pdev))
+			return PCI_ERS_RESULT_NEED_RESET;
+
 		dev_warn(&pdev->dev,
 			 "%s: frozen state error detected, disable CXL.mem\n",
 			 dev_name(dev));
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1e211542b6b64..d1dd4924fba17 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -12,6 +12,7 @@
 #include <linux/idr.h>
 #include <linux/memory-tiers.h>
 #include <linux/string_choices.h>
+#include <linux/pci.h>
 #include <cxlmem.h>
 #include <cxl.h>
 #include "core.h"
@@ -222,7 +223,7 @@ static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port,
 	return xa_load(&port->regions, (unsigned long)cxlr);
 }
 
-static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
+int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
 {
 	if (!cpu_cache_has_invalidate_memregion()) {
 		if (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) {
@@ -4263,14 +4264,17 @@ static struct cxl_driver cxl_region_driver = {
 
 int cxl_region_init(void)
 {
+	pci_cxl_set_sbr_region_ops(&cxl_sbr_region_ops);
 	return cxl_driver_register(&cxl_region_driver);
 }
 
 void cxl_region_exit(void)
 {
 	cxl_driver_unregister(&cxl_region_driver);
+	pci_cxl_set_sbr_region_ops(NULL);
 }
 
 MODULE_IMPORT_NS("CXL");
 MODULE_IMPORT_NS("DEVMEM");
+MODULE_IMPORT_NS("CXL_MHP");
 MODULE_ALIAS_CXL(CXL_DEVICE_REGION);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 5ec0b245a69bd..ba1cae6ab4fae 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -573,6 +573,33 @@ static ssize_t reset_subordinate_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(reset_subordinate);
 
+static ssize_t cxl_unmask_sbr_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	return sysfs_emit(buf, "%u\n", pdev->cxl_unmask_sbr);
+}
+
+static ssize_t cxl_unmask_sbr_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	unsigned long val;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (kstrtoul(buf, 0, &val) < 0)
+		return -EINVAL;
+
+	pdev->cxl_unmask_sbr = !!val;
+
+	return count;
+}
+static DEVICE_ATTR_RW(cxl_unmask_sbr);
+
 #if defined(CONFIG_PM) && defined(CONFIG_ACPI)
 static ssize_t d3cold_allowed_store(struct device *dev,
 				    struct device_attribute *attr,
@@ -650,6 +677,7 @@ static struct attribute *pci_bridge_attrs[] = {
 	&dev_attr_subordinate_bus_number.attr,
 	&dev_attr_secondary_bus_number.attr,
 	&dev_attr_reset_subordinate.attr,
+	&dev_attr_cxl_unmask_sbr.attr,
 	NULL,
 };
 
@@ -1824,6 +1852,9 @@ static umode_t pci_bridge_attrs_are_visible(struct kobject *kobj,
 	struct device *dev = kobj_to_dev(kobj);
 	struct pci_dev *pdev = to_pci_dev(dev);
 
+	if (a == &dev_attr_cxl_unmask_sbr.attr && !is_cxl_dport(pdev))
+		return 0;
+
 	if (pci_is_bridge(pdev))
 		return a->mode;
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 77b17b13ee615..08873ea3957b2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -23,6 +23,7 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
+#include <linux/string_choices.h>
 #include <linux/log2.h>
 #include <linux/logic_pio.h>
 #include <linux/device.h>
@@ -4844,21 +4845,235 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
 	pci_reset_secondary_bus(dev);
 }
 
+/*
+ * Registered by the CXL core to disable and re-enable the regions mapped
+ * through a CXL Downstream Port across a Secondary Bus Reset. NULL whenever
+ * the CXL region code is absent: not built, or built as a module not loaded.
+ */
+static const struct pci_cxl_sbr_region_ops *cxl_sbr_region_ops;
+
+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops)
+{
+	cxl_sbr_region_ops = ops;
+}
+EXPORT_SYMBOL_GPL(pci_cxl_set_sbr_region_ops);
+
+struct cxl_sbr_ctx {
+	u16 port_ctl;
+	u16 acs_ctrl;
+	u16 command;
+};
+
+bool is_cxl_dport(struct pci_dev *dev)
+{
+	return pcie_is_cxl(dev) && pcie_downstream_port(dev);
+}
+
+u16 cxl_port_dvsec(struct pci_dev *dev)
+{
+	return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
+					 PCI_DVSEC_CXL_PORT);
+}
+
+static int cxl_sbr_prepare(struct pci_dev *bridge, u16 dvsec,
+			   struct cxl_sbr_ctx *ctx,
+			   enum cxl_sbr_region_action action)
+{
+	int rc;
+
+	if (action == CXL_SBR_OFFLINE_AND_UNBIND && !cxl_sbr_allowed(bridge)) {
+		pci_info(bridge, "SBR masked, write 1 to cxl_unmask_sbr to allow a bus reset\n");
+		return -ENOTTY;
+	}
+
+	/*
+	 * CXL_SBR_UNBIND: the link is already down, so offlining the regions'
+	 * memory would take the reads that page migration performs as a machine
+	 * check. Per PCIe r7.0 sec 2.9.3 the Port answers a Non-Posted Request
+	 * with an Unsupported Request or Completer Abort completion while it is
+	 * in DPC. Unbinding never fails, so the reset always goes ahead.
+	 *
+	 * CXL_SBR_OFFLINE_AND_UNBIND: the device is reachable, so offline the
+	 * memory first and abort the reset before touching hardware if that
+	 * fails.
+	 */
+	if (cxl_sbr_region_ops && action == CXL_SBR_UNBIND) {
+		cxl_sbr_region_ops->unbind_regions(bridge);
+	} else if (cxl_sbr_region_ops) {
+		rc = cxl_sbr_region_ops->disable_regions(bridge);
+		if (rc)
+			return rc;
+	}
+
+	/* CXL r4.0 sec 8.1.5.2, Table 8-32: set Unmask SBR so the Port issues Hot Reset. */
+	pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, &ctx->port_ctl);
+	pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+			      ctx->port_ctl | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);
+
+	pci_read_config_word(bridge, PCI_COMMAND, &ctx->command);
+	pci_clear_master(bridge);
+
+	/* CXL r4.0 sec 8.1.5.1: Disable ACS SV bit before SBR */
+	if (bridge->acs_cap) {
+		pci_read_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL, &ctx->acs_ctrl);
+		pci_dbg(bridge, "%s: ACS SV %s\n", __func__,
+			str_enabled_disabled(ctx->acs_ctrl & PCI_ACS_SV));
+		pci_write_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL,
+				      ctx->acs_ctrl & ~PCI_ACS_SV);
+	}
+
+	return 0;
+}
+
+/*
+ * CXL r4.0 sec 8.1.5.1, Table 8-31: the Port sets PM Init Complete within
+ * 100 ms of link-up. Restoring ACS Source Validation before then makes the
+ * Port reject the downstream Component's Requester-Bus-0 IP2PM message, so
+ * poll for completion before restoring config.
+ */
+static bool cxl_port_pm_init_is_complete(struct pci_dev *bridge, u16 dvsec)
+{
+	unsigned long start = jiffies;
+	unsigned long timeout = start + msecs_to_jiffies(100);
+	u16 status;
+
+	do {
+		pci_read_config_word(bridge,
+				     dvsec + PCI_DVSEC_CXL_PORT_EXT_STATUS,
+				     &status);
+		if (!PCI_POSSIBLE_ERROR(status) &&
+		    (status & PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP)) {
+			pci_dbg(bridge, "%s: PM Init Complete set after %u ms, ext status %#06x\n",
+				__func__, jiffies_to_msecs(jiffies - start), status);
+			return true;
+		}
+		msleep(10);
+	} while (time_before(jiffies, timeout));
+
+	pci_warn(bridge, "%s: PM Init Complete not set after %u ms, ext status %#06x\n",
+		 __func__, jiffies_to_msecs(jiffies - start), status);
+
+	return false;
+}
+
+static int cxl_sbr_restore_config_space(struct pci_dev *dev, void *userdata)
+{
+	pci_restore_config_space(dev);
+	pci_dbg(dev, "%s: config space restored\n", __func__);
+
+	return 0;
+}
+
+/*
+ * The CXL region ops that run next read the HDM Decoders through a Base Address
+ * Register the reset returned to its initialization value, so restore the
+ * header of every device below @bridge first. Restoring also re-captures each
+ * Bus Number before the Port's ACS Source Validation comes back: a device that
+ * has completed no Type 0 Configuration Write since the reset sources Requests
+ * with Bus 0, which the Port rejects as an ACS Violation.
+ *
+ * Only the header is restored. The capability state each caller saved is its own
+ * to replay, and the ->reset_done() callbacks pci_dev_restore() invokes must
+ * fire once, from the caller that owns the reset.
+ */
+static void cxl_sbr_restore_subordinate(struct pci_dev *bridge)
+{
+	if (!bridge->subordinate)
+		return;
+
+	/* Parents before children: a child answers once its parent forwards. */
+	pci_walk_bus(bridge->subordinate, cxl_sbr_restore_config_space, NULL);
+}
+
+static void cxl_sbr_complete(struct pci_dev *bridge, u16 dvsec,
+			     const struct cxl_sbr_ctx *ctx)
+{
+	u16 val;
+
+	/* CXL r4.0 sec 8.1.5.1: wait for PM Init before restoring ACS SV. */
+	if (!cxl_port_pm_init_is_complete(bridge, dvsec))
+		pci_warn(bridge,
+			 "restoring ACS Source Validation before PM Init complete; Port may reject the Component's bus 0 traffic\n");
+
+	cxl_sbr_restore_subordinate(bridge);
+
+	/* CXL r4.0 sec 8.1.5.1: Re-enable ACS SV bit after SBR if it was enabled before */
+	if (bridge->acs_cap && (ctx->acs_ctrl & PCI_ACS_SV)) {
+		pci_read_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL, &val);
+		pci_write_config_word(bridge, bridge->acs_cap + PCI_ACS_CTRL,
+				      val | PCI_ACS_SV);
+		pci_dbg(bridge, "%s: ACS SV bit set\n", __func__);
+	} else {
+		pci_dbg(bridge, "%s: ACS SV bit not set (was not enabled before the SBR)\n",
+			__func__);
+	}
+
+	if (ctx->command & PCI_COMMAND_MASTER)
+		pci_set_master(bridge);
+
+	if (!(ctx->port_ctl & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)) {
+		pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, &val);
+		pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+				      val & ~PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR);
+	}
+
+	if (cxl_sbr_region_ops)
+		cxl_sbr_region_ops->enable_regions(bridge);
+}
+
+/*
+ * __pci_bridge_secondary_bus_reset - assert Secondary Bus Reset on a bridge
+ * @dev: bridge device
+ * @action: what to do with the CXL regions reached through @dev
+ *
+ * See pci_bridge_secondary_bus_reset(). Pass CXL_SBR_UNBIND when the link is
+ * already down, which leaves the regions' memory online because offlining it
+ * needs a reachable device.
+ */
+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,
+				     enum cxl_sbr_region_action action)
+{
+	struct cxl_sbr_ctx ctx = {};
+	u16 dvsec = 0;
+	int rc;
+
+	if (!dev->block_cfg_access)
+		pci_warn_once(dev, "unlocked secondary bus reset via: %pS\n",
+			      __builtin_return_address(0));
+
+	if (is_cxl_dport(dev))
+		dvsec = cxl_port_dvsec(dev);
+	if (dvsec) {
+		rc = cxl_sbr_prepare(dev, dvsec, &ctx, action);
+		if (rc)
+			return rc;
+	}
+
+	pcibios_reset_secondary_bus(dev);
+
+	rc = pci_bridge_wait_for_secondary_bus(dev, "bus reset");
+
+	if (dvsec)
+		cxl_sbr_complete(dev, dvsec, &ctx);
+
+	return rc;
+}
+
 /**
  * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge.
  * @dev: Bridge device
  *
  * Use the bridge control register to assert reset on the secondary bus.
  * Devices on the secondary bus are left in power-on state.
+ *
+ * When @dev is a CXL Downstream Port, clear ACS Source Validation and Bus
+ * Master Enable across the reset, per the workaround in CXL r4.0 sec 8.1.5.1,
+ * so that Port Power Management Initialization completes at link-up. The
+ * bits stay cleared until the secondary bus is back, then are restored.
  */
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev)
 {
-	if (!dev->block_cfg_access)
-		pci_warn_once(dev, "unlocked secondary bus reset via: %pS\n",
-			      __builtin_return_address(0));
-	pcibios_reset_secondary_bus(dev);
-
-	return pci_bridge_wait_for_secondary_bus(dev, "bus reset");
+	return __pci_bridge_secondary_bus_reset(dev, CXL_SBR_OFFLINE_AND_UNBIND);
 }
 EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);
 
@@ -4904,12 +5119,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)
 	return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
 }
 
-static u16 cxl_port_dvsec(struct pci_dev *dev)
-{
-	return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
-					 PCI_DVSEC_CXL_PORT);
-}
-
 static bool cxl_sbr_masked(struct pci_dev *dev)
 {
 	u16 dvsec, reg;
@@ -4934,6 +5143,27 @@ static bool cxl_sbr_masked(struct pci_dev *dev)
 	return true;
 }
 
+/*
+ * cxl_sbr_allowed - whether an SBR of a CXL Downstream Port may go ahead
+ * @dev: Downstream Port to test
+ *
+ * Per CXL r4.0 sec 8.1.5.2 Table 8-32 the SBR bit in a CXL Port's Bridge
+ * Control register has no effect while the Port's Unmask SBR bit is clear, and
+ * sec 9.12.3 says System Firmware may leave it clear "to prevent CXL-unaware
+ * PCIe software from resetting the device and the link". A Port that already
+ * has it set needs no further permission; otherwise unmasking it takes the
+ * administrator's consent, given by writing 1 to the Port's cxl_unmask_sbr.
+ *
+ * Return: true if the reset paths may unmask and generate an SBR of @dev.
+ */
+bool cxl_sbr_allowed(struct pci_dev *dev)
+{
+	if (!cxl_port_dvsec(dev))
+		return false;
+
+	return dev->cxl_unmask_sbr || !cxl_sbr_masked(dev);
+}
+
 static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
 {
 	struct pci_dev *bridge = pci_upstream_bridge(dev);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4469e1a77f3c1..bea05acc58f0d 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -233,6 +233,22 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
 bool pci_reset_supported(struct pci_dev *dev);
 void pci_init_reset_methods(struct pci_dev *dev);
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
+
+/*
+ * What to do with the CXL regions reached through a Downstream Port before it
+ * is reset. Offlining their memory needs a reachable device, so a Port whose
+ * link is already down only unbinds and leaves the memory online.
+ */
+enum cxl_sbr_region_action {
+	CXL_SBR_OFFLINE_AND_UNBIND,
+	CXL_SBR_UNBIND,
+};
+
+int __pci_bridge_secondary_bus_reset(struct pci_dev *dev,
+				     enum cxl_sbr_region_action action);
+bool is_cxl_dport(struct pci_dev *dev);
+u16 cxl_port_dvsec(struct pci_dev *dev);
+bool cxl_sbr_allowed(struct pci_dev *dev);
 int pci_bus_error_reset(struct pci_dev *dev);
 int pci_try_reset_bridge(struct pci_dev *bridge);
 
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index 2b779bd1d861b..ad74086bf82c7 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -127,6 +127,44 @@ bool pci_dpc_recovered(struct pci_dev *pdev)
 }
 #endif /* CONFIG_HOTPLUG_PCI_PCIE */
 
+/**
+ * pci_dpc_containment_active - whether a Port above @pdev is contained by DPC
+ * @pdev: PCI device below the Port
+ *
+ * Per PCIe r7.0 sec 2.9.3 the Port's LTSSM stays in the Disabled state while
+ * DPC Trigger Status is set, and dpc_reset_link() clears that bit only after
+ * pcie_do_recovery() has broadcast error_detected. A ->error_detected()
+ * callback can therefore use this to tell a DPC containment from any other
+ * frozen-channel error, and to know that the link is about to be reset.
+ *
+ * The Port that triggered is on the path to @pdev, because the broadcast walks
+ * that Port's subordinate bus, so test every bridge above @pdev.
+ *
+ * Return: true if a Port on the path to @pdev has DPC Trigger Status set.
+ */
+bool pci_dpc_containment_active(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+
+	for (bridge = pci_upstream_bridge(pdev); bridge;
+	     bridge = pci_upstream_bridge(bridge)) {
+		u16 status;
+
+		if (!bridge->dpc_cap)
+			continue;
+
+		pci_read_config_word(bridge,
+				     bridge->dpc_cap + PCI_EXP_DPC_STATUS,
+				     &status);
+		if (!PCI_POSSIBLE_ERROR(status) &&
+		    (status & PCI_EXP_DPC_STATUS_TRIGGER))
+			return true;
+	}
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(pci_dpc_containment_active);
+
 static int dpc_wait_rp_inactive(struct pci_dev *pdev)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -149,6 +187,7 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
 	pci_ers_result_t ret;
 	u16 cap;
+	int rc;
 
 	set_bit(PCI_DPC_RECOVERING, &pdev->priv_flags);
 
@@ -174,7 +213,12 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 	pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
 			      PCI_EXP_DPC_STATUS_TRIGGER);
 
-	if (pci_bridge_wait_for_secondary_bus(pdev, "DPC")) {
+	if (is_cxl_dport(pdev) && cxl_port_dvsec(pdev))
+		rc = __pci_bridge_secondary_bus_reset(pdev, CXL_SBR_UNBIND);
+	else
+		rc = pci_bridge_wait_for_secondary_bus(pdev, "DPC");
+
+	if (rc) {
 		clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags);
 		ret = PCI_ERS_RESULT_DISCONNECT;
 	} else {
diff --git a/include/linux/aer.h b/include/linux/aer.h
index df0f5c382286f..7ac029f01c25c 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -66,6 +66,15 @@ static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; }
 static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { }
 #endif
 
+#if defined(CONFIG_PCIE_DPC)
+bool pci_dpc_containment_active(struct pci_dev *pdev);
+#else
+static inline bool pci_dpc_containment_active(struct pci_dev *pdev)
+{
+	return false;
+}
+#endif
+
 void pci_print_aer(struct pci_dev *dev, int aer_severity,
 		    struct aer_capability_regs *aer);
 int cper_severity_to_aer(int cper_severity);
diff --git a/include/linux/memory.h b/include/linux/memory.h
index 463dc02f6cff0..5dc0b96fb004a 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -149,6 +149,10 @@ static inline unsigned long memory_block_advised_max_size(void)
 {
 	return 0;
 }
+static inline int cxl_offline_memory(u64 start, u64 size)
+{
+	return 0;
+}
 #else /* CONFIG_MEMORY_HOTPLUG */
 extern int register_memory_notifier(struct notifier_block *nb);
 extern void unregister_memory_notifier(struct notifier_block *nb);
@@ -167,6 +171,7 @@ typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *);
 extern int walk_memory_blocks(unsigned long start, unsigned long size,
 			      void *arg, walk_memory_blocks_func_t func);
 extern int for_each_memory_block(void *arg, walk_memory_blocks_func_t func);
+int cxl_offline_memory(u64 start, u64 size);
 
 extern int memory_group_register_static(int nid, unsigned long max_pages);
 extern int memory_group_register_dynamic(int nid, unsigned long unit_pages);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 64b308b6e61c1..bc0f652eff804 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -485,6 +485,7 @@ struct pci_dev {
 	unsigned int	shpc_managed:1;		/* SHPC owned by shpchp */
 	unsigned int	is_thunderbolt:1;	/* Thunderbolt controller */
 	unsigned int	is_cxl:1;               /* Compute Express Link (CXL) */
+	unsigned int	cxl_unmask_sbr:1;	/* SBR unmask allowed by user */
 	/*
 	 * Devices marked being untrusted are the ones that can potentially
 	 * execute DMA attacks and similar. They are typically connected
@@ -1606,6 +1607,21 @@ int devm_request_pci_bus_resources(struct device *dev,
 /* Temporary until new and working PCI SBR API in place */
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
 
+/**
+ * struct pci_cxl_sbr_region_ops - CXL region callbacks for a bus reset
+ * @disable_regions: disable the regions below @dport, 0 or errno
+ * @unbind_regions: unbind the drivers of the regions below @dport, leaving
+ *		    their memory online, for a link already contained
+ * @enable_regions: re-enable the regions below @dport
+ */
+struct pci_cxl_sbr_region_ops {
+	int (*disable_regions)(struct pci_dev *dport);
+	void (*unbind_regions)(struct pci_dev *dport);
+	void (*enable_regions)(struct pci_dev *dport);
+};
+
+void pci_cxl_set_sbr_region_ops(const struct pci_cxl_sbr_region_ops *ops);
+
 #define __pci_bus_for_each_res0(bus, res, ...)				\
 	for (unsigned int __b = 0;					\
 	     (res = pci_bus_resource_n(bus, __b)) || __b < PCI_BRIDGE_RESOURCE_NUM; \
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index facaa324bd86a..0eaa34db93cef 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -1371,6 +1371,8 @@
 
 /* CXL r4.0, 8.1.5: Extensions DVSEC for Ports */
 #define PCI_DVSEC_CXL_PORT				3
+#define  PCI_DVSEC_CXL_PORT_EXT_STATUS			0x0a
+#define   PCI_DVSEC_CXL_PORT_EXT_STATUS_PM_INIT_COMP	0x00000001
 #define  PCI_DVSEC_CXL_PORT_CTL				0x0c
 #define   PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR		0x00000001
 
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 2be1df80fcc93..f18d41b299fec 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -63,7 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/hdm.o
 cxl_core-y += $(CXL_CORE_SRC)/pmu.o
 cxl_core-y += $(CXL_CORE_SRC)/cdat.o
 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
-cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o
+cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o $(CXL_CORE_SRC)/dport_sbr.o
 cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
 cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o

53/3 2026/08/25 02:54 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22395
							output: 18
							thoughts: 710
						

Thoughts:
**CXL Downstream Port SBR Feature Review**

Okay, so this patch is introducing SBR (Secondary Bus Reset) handling for CXL Downstream Ports – that's a good step forward. They're adding a sysfs attribute, `cxl_unmask_sbr`, which gives us control over unmasking SBR, which is always useful. The core of it seems to be in offlining memory blocks, unbinding regions, and restoring HDM decoders during the SBR process. Modifications to `pci_bridge_secondary_bus_reset` and `dpc_reset_link` to incorporate this new CXL logic make sense. They've also defined `cxl_sbr_region_ops` to manage region disable/unbind/enable and added `cxl_offline_memory`, which is self-explanatory. Saving and restoring HDM decoder state is handled by `cxl_port_save_hdm_state` and `cxl_port_recommit_decoders`, respectively. Seems like a reasonably comprehensive approach.

Now, let me dig into the memory allocation. Here's where I need to be careful. The code is using `kzalloc_obj` to allocate `hdm_state`, and the use of that function looks unusual. Is that a kernel function or some custom macro or a wrapper? The diff shows `hdm_state = kzalloc_obj(*hdm_state);`... Wait, is it supposed to be `kzalloc_obj(*hdm_state)`? I'm not familiar with `kzalloc_obj`, and I don't recall it being standard. I'm going to quickly grep through the kernel to confirm. I see `kzalloc_flex` is also used in another allocation, so they are probably memory allocation variants. Okay, so that should be zero-initialization, which is good to see.

Let me look at the `cxl_sbr_hdm_state` xarray. It looks like it is storing `hdm_state` which is itself an xarray.  Okay, so `kzalloc_obj` is likely zeroing out the contents of the xarray before putting it into the data structure.  Hold on, let me look at the code again to be sure: `hdm_state = kzalloc_obj(*hdm_state);`.  Where is this `kzalloc_obj`?  Ah, I'm going to have to grep for `kzalloc_obj` in the codebase.



54/3 2026/08/25 02:54 tool grepper
0m Args:
{
  "Expression": "kzalloc_obj"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 79132 lines.\nUse more precise expression if possible.\n\nDocumentation/core-api/list.rst=103=list:\n--\nDocumentation/core-api/list.rst-114-\nDocumentation/core-api/list.rst:115:          grock = kzalloc_obj(*grock);\nDocumentation/core-api/list.rst-116-          if (!grock)\n--\nDocumentation/core-api/list.rst-125-\nDocumentation/core-api/list.rst:126:          dimitri = kzalloc_obj(*dimitri);\nDocumentation/core-api/list.rst-127-          if (!dimitri)\n--\nDocumentation/driver-api/mailbox.rst=45=a message and a callback function to the API and return immediately).\n--\nDocumentation/driver-api/mailbox.rst-89-\nDocumentation/driver-api/mailbox.rst:90:\t\tdc_sync = kzalloc_obj(*dc_sync);\nDocumentation/driver-api/mailbox.rst:91:\t\tdc_async = kzalloc_obj(*dc_async);\nDocumentation/driver-api/mailbox.rst-92-\n--\nDocumentation/driver-api/media/v4l2-fh.rst=26=Example:\n--\nDocumentation/driver-api/media/v4l2-fh.rst-44-\nDocumentation/driver-api/media/v4l2-fh.rst:45:\t\tmy_fh = kzalloc_obj(*my_fh);\nDocumentation/driver-api/media/v4l2-fh.rst-46-\n--\nDocumentation/process/coding-style.rst=938=The kernel provides the following general purpose memory allocators:\nDocumentation/process/coding-style.rst:939:kmalloc(), kzalloc(), kmalloc_objs(), kzalloc_objs(), vmalloc(), and\nDocumentation/process/coding-style.rst-940-vzalloc().  Please refer to the API documentation for further information\n--\nDocumentation/process/coding-style.rst=964=The preferred form for allocating a zeroed array is the following:\n--\nDocumentation/process/coding-style.rst-967-\nDocumentation/process/coding-style.rst:968:\tp = kzalloc_objs(*p, n, ...);\nDocumentation/process/coding-style.rst-969-\n--\nDocumentation/process/deprecated.rst=398=become, respectively::\n--\nDocumentation/process/deprecated.rst-400-\tptr = kmalloc_obj(*ptr [, gfp] );\nDocumentation/process/deprecated.rst:401:\tptr = kzalloc_obj(*ptr [, gfp] );\nDocumentation/process/deprecated.rst-402-\tptr = kmalloc_objs(*ptr, count [, gfp] );\nDocumentation/process/deprecated.rst:403:\tptr = kzalloc_objs(*ptr, count [, gfp] );\nDocumentation/process/deprecated.rst-404-\tptr = kmalloc_flex(*ptr, flex_member, count [, gfp] );\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=208=to details explained in the following section.\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-268-              /* allocate a chip-specific data with zero filled */\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:269:              chip = kzalloc_obj(*chip);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-270-              if (chip == NULL)\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=623=After allocating a card instance via :c:func:`snd_card_new()`\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-630-  .....\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:631:  chip = kzalloc_obj(*chip);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-632-\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=698=destructor and PCI entries. Example code is shown first, below::\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-749-\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:750:              chip = kzalloc_obj(*chip);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-751-              if (chip == NULL) {\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=3823=chip data individually::\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-3835-          ....\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:3836:          chip = kzalloc_obj(*chip);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-3837-          ....\n--\nDocumentation/translations/zh_CN/video4linux/v4l2-framework.txt=794=int my_open(struct file *file)\n--\nDocumentation/translations/zh_CN/video4linux/v4l2-framework.txt-801-\nDocumentation/translations/zh_CN/video4linux/v4l2-framework.txt:802:\tmy_fh = kzalloc_obj(*my_fh);\nDocumentation/translations/zh_CN/video4linux/v4l2-framework.txt-803-\n--\narch/alpha/kernel/module.c=64=module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs,\n--\narch/alpha/kernel/module.c-95-\tnsyms = symtab-\u003esh_size / sizeof(Elf64_Sym);\narch/alpha/kernel/module.c:96:\tchains = kzalloc_objs(struct got_entry, nsyms);\narch/alpha/kernel/module.c-97-\tif (!chains) {\n--\narch/alpha/kernel/setup.c=390=register_cpus(void)\n--\narch/alpha/kernel/setup.c-394-\tfor_each_possible_cpu(i) {\narch/alpha/kernel/setup.c:395:\t\tstruct cpu *p = kzalloc_obj(*p);\narch/alpha/kernel/setup.c-396-\t\tif (!p)\n--\narch/arc/net/bpf_jit_core.c=1120=static int jit_prepare_final_mem_alloc(struct jit_context *ctx)\n--\narch/arc/net/bpf_jit_core.c-1131-\tif (ctx-\u003eneed_extra_pass) {\narch/arc/net/bpf_jit_core.c:1132:\t\tctx-\u003ejit_data = kzalloc_obj(*ctx-\u003ejit_data);\narch/arc/net/bpf_jit_core.c-1133-\t\tif (!ctx-\u003ejit_data)\n--\narch/arm/common/locomo.c=220=locomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)\n--\narch/arm/common/locomo.c-224-\narch/arm/common/locomo.c:225:\tdev = kzalloc_obj(struct locomo_dev);\narch/arm/common/locomo.c-226-\tif (!dev) {\n--\narch/arm/common/locomo.c=356=__locomo_probe(struct device *me, struct resource *mem, int irq)\n--\narch/arm/common/locomo.c-362-\narch/arm/common/locomo.c:363:\tlchip = kzalloc_obj(struct locomo);\narch/arm/common/locomo.c-364-\tif (!lchip)\n--\narch/arm/common/sa1111.c=733=sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,\n--\narch/arm/common/sa1111.c-739-\narch/arm/common/sa1111.c:740:\tdev = kzalloc_obj(struct sa1111_dev);\narch/arm/common/sa1111.c-741-\tif (!dev) {\n--\narch/arm/common/scoop.c=178=static int scoop_probe(struct platform_device *pdev)\n--\narch/arm/common/scoop.c-187-\narch/arm/common/scoop.c:188:\tdevptr = kzalloc_obj(struct scoop_dev);\narch/arm/common/scoop.c-189-\tif (!devptr)\n--\narch/arm/kernel/smp.c=108=static int secondary_biglittle_prepare(unsigned int cpu)\n--\narch/arm/kernel/smp.c-110-\tif (!cpu_vtable[cpu])\narch/arm/kernel/smp.c:111:\t\tcpu_vtable[cpu] = kzalloc_obj(*cpu_vtable[cpu]);\narch/arm/kernel/smp.c-112-\n--\narch/arm/kernel/vdso.c=169=static int __init vdso_init(void)\n--\narch/arm/kernel/vdso.c-181-\t/* Allocate the VDSO text pagelist */\narch/arm/kernel/vdso.c:182:\tvdso_text_pagelist = kzalloc_objs(struct page *, text_pages);\narch/arm/kernel/vdso.c-183-\tif (vdso_text_pagelist == NULL)\n--\narch/arm/mach-footbridge/dc21285.c=261=int __init dc21285_setup(int nr, struct pci_sys_data *sys)\n--\narch/arm/mach-footbridge/dc21285.c-264-\narch/arm/mach-footbridge/dc21285.c:265:\tres = kzalloc_objs(struct resource, 2);\narch/arm/mach-footbridge/dc21285.c-266-\tif (!res) {\n--\narch/arm/mach-footbridge/ebsa285.c=69=static int __init ebsa285_leds_init(void)\n--\narch/arm/mach-footbridge/ebsa285.c-86-\narch/arm/mach-footbridge/ebsa285.c:87:\t\tled = kzalloc_obj(*led);\narch/arm/mach-footbridge/ebsa285.c-88-\t\tif (!led)\n--\narch/arm/mach-footbridge/netwinder-hw.c=720=static int __init netwinder_leds_init(void)\n--\narch/arm/mach-footbridge/netwinder-hw.c-729-\narch/arm/mach-footbridge/netwinder-hw.c:730:\t\tled = kzalloc_obj(*led);\narch/arm/mach-footbridge/netwinder-hw.c-731-\t\tif (!led)\n--\narch/arm/mach-imx/mmdc.c=473=static int imx_mmdc_perf_init(struct platform_device *pdev, void __iomem *mmdc_base,\n--\narch/arm/mach-imx/mmdc.c-479-\narch/arm/mach-imx/mmdc.c:480:\tpmu_mmdc = kzalloc_obj(*pmu_mmdc);\narch/arm/mach-imx/mmdc.c-481-\tif (!pmu_mmdc) {\n--\narch/arm/mach-mvebu/board-v7.c=114=static void __init i2c_quirk(void)\n--\narch/arm/mach-mvebu/board-v7.c-129-\narch/arm/mach-mvebu/board-v7.c:130:\t\tnew_compat = kzalloc_obj(*new_compat);\narch/arm/mach-mvebu/board-v7.c-131-\n--\narch/arm/mach-mvebu/coherency.c=163=static void __init armada_375_380_coherency_init(struct device_node *np)\n--\narch/arm/mach-mvebu/coherency.c-187-\narch/arm/mach-mvebu/coherency.c:188:\t\tp = kzalloc_obj(*p);\narch/arm/mach-mvebu/coherency.c-189-\t\tp-\u003ename = kstrdup(\"arm,io-coherent\", GFP_KERNEL);\n--\narch/arm/mach-mvebu/mvebu-soc-id.c=148=static int __init mvebu_soc_device(void)\n--\narch/arm/mach-mvebu/mvebu-soc-id.c-156-\narch/arm/mach-mvebu/mvebu-soc-id.c:157:\tsoc_dev_attr = kzalloc_obj(*soc_dev_attr);\narch/arm/mach-mvebu/mvebu-soc-id.c-158-\tif (!soc_dev_attr)\n--\narch/arm/mach-mxs/mach-mxs.c=380=static void __init mxs_machine_init(void)\n--\narch/arm/mach-mxs/mach-mxs.c-389-\narch/arm/mach-mxs/mach-mxs.c:390:\tsoc_dev_attr = kzalloc_obj(*soc_dev_attr);\narch/arm/mach-mxs/mach-mxs.c-391-\tif (!soc_dev_attr)\n--\narch/arm/mach-omap1/dma.c=294=static int __init omap1_system_dma_init(void)\n--\narch/arm/mach-omap1/dma.c-321-\narch/arm/mach-omap1/dma.c:322:\td = kzalloc_obj(*d);\narch/arm/mach-omap1/dma.c-323-\tif (!d) {\n--\narch/arm/mach-omap1/mcbsp.c=292=static void omap_mcbsp_register_board_cfg(struct resource *res, int res_count,\n--\narch/arm/mach-omap1/mcbsp.c-296-\narch/arm/mach-omap1/mcbsp.c:297:\tomap_mcbsp_devices = kzalloc_objs(struct platform_device *, size);\narch/arm/mach-omap1/mcbsp.c-298-\tif (!omap_mcbsp_devices) {\n--\narch/arm/mach-omap1/timer.c=51=static int __init omap1_dm_timer_init(void)\n--\narch/arm/mach-omap1/timer.c-127-\narch/arm/mach-omap1/timer.c:128:\t\tpdata = kzalloc_obj(*pdata);\narch/arm/mach-omap1/timer.c-129-\t\tif (!pdata) {\n--\narch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c=230=void omap2xxx_clkt_vps_init(void)\n--\narch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c-239-\narch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c:240:\thw = kzalloc_obj(*hw);\narch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c-241-\tif (!hw)\n--\narch/arm/mach-omap2/id.c=786=void __init omap_soc_device_init(void)\n--\narch/arm/mach-omap2/id.c-790-\narch/arm/mach-omap2/id.c:791:\tsoc_dev_attr = kzalloc_obj(*soc_dev_attr);\narch/arm/mach-omap2/id.c-792-\tif (!soc_dev_attr)\n--\narch/arm/mach-omap2/omap_device.c=131=static int omap_device_build_from_dt(struct platform_device *pdev)\n--\narch/arm/mach-omap2/omap_device.c-158-\narch/arm/mach-omap2/omap_device.c:159:\thwmods = kzalloc_objs(struct omap_hwmod *, oh_cnt);\narch/arm/mach-omap2/omap_device.c-160-\tif (!hwmods) {\n--\narch/arm/mach-omap2/omap_hwmod.c=3381=static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,\n--\narch/arm/mach-omap2/omap_hwmod.c-3394-\narch/arm/mach-omap2/omap_hwmod.c:3395:\tsysc = kzalloc_obj(*sysc);\narch/arm/mach-omap2/omap_hwmod.c-3396-\tif (!sysc)\n--\narch/arm/mach-omap2/omap_hwmod.c-3424-\tif (list_empty(\u0026oh-\u003eslave_ports)) {\narch/arm/mach-omap2/omap_hwmod.c:3425:\t\toi = kzalloc_obj(*oi);\narch/arm/mach-omap2/omap_hwmod.c-3426-\t\tif (!oi)\n--\narch/arm/mach-omap2/omap_hwmod.c=3513=int omap_hwmod_init_module(struct device *dev,\n--\narch/arm/mach-omap2/omap_hwmod.c-3527-\tif (!oh) {\narch/arm/mach-omap2/omap_hwmod.c:3528:\t\toh = kzalloc_obj(*oh);\narch/arm/mach-omap2/omap_hwmod.c-3529-\t\tif (!oh)\n--\narch/arm/mach-omap2/omap_hwmod.c-3538-\narch/arm/mach-omap2/omap_hwmod.c:3539:\t\toh-\u003eclass = kzalloc_obj(*oh-\u003eclass);\narch/arm/mach-omap2/omap_hwmod.c-3540-\t\tif (!oh-\u003eclass) {\n--\narch/arm/mach-omap2/pm33xx-core.c=379=static int __init amx3_idle_init(struct device_node *cpu_node, int cpu)\n--\narch/arm/mach-omap2/pm33xx-core.c-412-\narch/arm/mach-omap2/pm33xx-core.c:413:\tidle_states = kzalloc_objs(*idle_states, state_count);\narch/arm/mach-omap2/pm33xx-core.c-414-\tif (!idle_states)\n--\narch/arm/mach-omap2/sr_device.c=30=static void __init sr_set_nvalues(struct omap_volt_data *volt_data,\n--\narch/arm/mach-omap2/sr_device.c-41-\narch/arm/mach-omap2/sr_device.c:42:\tnvalue_table = kzalloc_objs(*nvalue_table, count);\narch/arm/mach-omap2/sr_device.c-43-\tif (!nvalue_table)\n--\narch/arm/mach-orion5x/pci.c=139=static int __init pcie_setup(struct pci_sys_data *sys)\n--\narch/arm/mach-orion5x/pci.c-171-\t */\narch/arm/mach-orion5x/pci.c:172:\tres = kzalloc_obj(struct resource);\narch/arm/mach-orion5x/pci.c-173-\tif (!res)\n--\narch/arm/mach-orion5x/pci.c=466=static int __init pci_setup(struct pci_sys_data *sys)\n--\narch/arm/mach-orion5x/pci.c-492-\t */\narch/arm/mach-orion5x/pci.c:493:\tres = kzalloc_obj(struct resource);\narch/arm/mach-orion5x/pci.c-494-\tif (!res)\n--\narch/arm/mach-rpc/ecard.c=689=static struct expansion_card *__init ecard_alloc_card(int type, int slot)\n--\narch/arm/mach-rpc/ecard.c-694-\narch/arm/mach-rpc/ecard.c:695:\tec = kzalloc_obj(ecard_t);\narch/arm/mach-rpc/ecard.c-696-\tif (!ec) {\n--\narch/arm/mach-sa1100/clock.c=93=int __init sa11xx_clk_init(void)\n--\narch/arm/mach-sa1100/clock.c-109-\narch/arm/mach-sa1100/clock.c:110:\thw = kzalloc_obj(*hw);\narch/arm/mach-sa1100/clock.c-111-\tif (!hw)\n--\narch/arm/mach-sa1100/clock.c-131-\narch/arm/mach-sa1100/clock.c:132:\thw = kzalloc_obj(*hw);\narch/arm/mach-sa1100/clock.c-133-\tif (!hw)\n--\narch/arm/mach-sa1100/generic.c=317=int __init sa11x0_register_fixed_regulator(int n,\n--\narch/arm/mach-sa1100/generic.c-323-\narch/arm/mach-sa1100/generic.c:324:\tcfg-\u003einit_data = id = kzalloc_obj(*cfg-\u003einit_data);\narch/arm/mach-sa1100/generic.c-325-\tif (!cfg-\u003einit_data)\n--\narch/arm/mach-sa1100/neponset.c=225=static int neponset_probe(struct platform_device *dev)\n--\narch/arm/mach-sa1100/neponset.c-278-\narch/arm/mach-sa1100/neponset.c:279:\td = kzalloc_obj(*d);\narch/arm/mach-sa1100/neponset.c-280-\tif (!d) {\n--\narch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c=141=static int __init rcar_gen2_regulator_quirk(void)\n--\narch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c-166-\narch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c:167:\t\tquirk = kzalloc_obj(*quirk);\narch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c-168-\t\tif (!quirk) {\n--\narch/arm/mach-versatile/spc.c=393=static int ve_spc_populate_opps(uint32_t cluster)\n--\narch/arm/mach-versatile/spc.c-397-\narch/arm/mach-versatile/spc.c:398:\topps = kzalloc_objs(*opps, MAX_OPPS);\narch/arm/mach-versatile/spc.c-399-\tif (!opps)\n--\narch/arm/mach-versatile/spc.c=442=int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq)\n--\narch/arm/mach-versatile/spc.c-444-\tint ret;\narch/arm/mach-versatile/spc.c:445:\tinfo = kzalloc_obj(*info);\narch/arm/mach-versatile/spc.c-446-\tif (!info)\n--\narch/arm/mach-versatile/spc.c=523=static struct clk *ve_spc_clk_register(struct device *cpu_dev)\n--\narch/arm/mach-versatile/spc.c-527-\narch/arm/mach-versatile/spc.c:528:\tspc = kzalloc_obj(*spc);\narch/arm/mach-versatile/spc.c-529-\tif (!spc)\n--\narch/arm/mach-versatile/versatile.c=123=static void __init versatile_dt_pci_init(void)\n--\narch/arm/mach-versatile/versatile.c-144-\narch/arm/mach-versatile/versatile.c:145:\tnewprop = kzalloc_obj(*newprop);\narch/arm/mach-versatile/versatile.c-146-\tif (!newprop)\n--\narch/arm/mach-zynq/common.c=105=static void __init zynq_init_machine(void)\n--\narch/arm/mach-zynq/common.c-110-\narch/arm/mach-zynq/common.c:111:\tsoc_dev_attr = kzalloc_obj(*soc_dev_attr);\narch/arm/mach-zynq/common.c-112-\tif (!soc_dev_attr)\n--\narch/arm/mm/cache-l2x0-pmu.c=503=static __init int l2x0_pmu_init(void)\n--\narch/arm/mm/cache-l2x0-pmu.c-509-\narch/arm/mm/cache-l2x0-pmu.c:510:\tl2x0_pmu = kzalloc_obj(*l2x0_pmu);\narch/arm/mm/cache-l2x0-pmu.c-511-\tif (!l2x0_pmu) {\n--\narch/arm/mm/cache-uniphier.c=315=static int __init __uniphier_cache_init(struct device_node *np,\n--\narch/arm/mm/cache-uniphier.c-344-\narch/arm/mm/cache-uniphier.c:345:\tdata = kzalloc_obj(*data);\narch/arm/mm/cache-uniphier.c-346-\tif (!data)\n--\narch/arm/mm/dma-mapping.c=533=static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,\n--\narch/arm/mm/dma-mapping.c-560-\narch/arm/mm/dma-mapping.c:561:\tbuf = kzalloc_obj(*buf,\narch/arm/mm/dma-mapping.c-562-\t\t\t  gfp \u0026 ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM));\n--\narch/arm/mm/dma-mapping.c=1487=arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)\n--\narch/arm/mm/dma-mapping.c-1506-\narch/arm/mm/dma-mapping.c:1507:\tmapping = kzalloc_obj(struct dma_iommu_mapping);\narch/arm/mm/dma-mapping.c-1508-\tif (!mapping)\n--\narch/arm/xen/enlighten.c=316=int __init arch_xen_unpopulated_init(struct resource **res)\n--\narch/arm/xen/enlighten.c-343-\narch/arm/xen/enlighten.c:344:\tregs = kzalloc_objs(*regs, nr_reg);\narch/arm/xen/enlighten.c-345-\tif (!regs) {\n--\narch/arm/xen/enlighten.c-387-\narch/arm/xen/enlighten.c:388:\t\ttmp_res = kzalloc_obj(*tmp_res);\narch/arm/xen/enlighten.c-389-\t\tif (!tmp_res) {\n--\narch/arm/xen/p2m.c=150=bool __set_phys_to_machine_multi(unsigned long pfn,\n--\narch/arm/xen/p2m.c-178-\narch/arm/xen/p2m.c:179:\tp2m_entry = kzalloc_obj(*p2m_entry, GFP_NOWAIT);\narch/arm/xen/p2m.c-180-\tif (!p2m_entry)\n--\narch/arm64/kernel/vdso.c=68=static int __init __vdso_init(enum vdso_abi abi)\n--\narch/arm64/kernel/vdso.c-83-\narch/arm64/kernel/vdso.c:84:\tvdso_pagelist = kzalloc_objs(struct page *, vdso_info[abi].vdso_pages);\narch/arm64/kernel/vdso.c-85-\tif (vdso_pagelist == NULL)\n--\narch/arm64/kvm/mmu.c=480=static int share_pfn_hyp(u64 pfn)\n--\narch/arm64/kvm/mmu.c-492-\narch/arm64/kvm/mmu.c:493:\tthis = kzalloc_obj(*this);\narch/arm64/kvm/mmu.c-494-\tif (!this) {\n--\narch/arm64/kvm/mmu.c=981=int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type)\n--\narch/arm64/kvm/mmu.c-1007-\narch/arm64/kvm/mmu.c:1008:\tpgt = kzalloc_obj(*pgt, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/mmu.c-1009-\tif (!pgt)\n--\narch/arm64/kvm/mmu.c=1180=int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages)\n--\narch/arm64/kvm/mmu.c-1185-\tif (!mc-\u003emapping) {\narch/arm64/kvm/mmu.c:1186:\t\tmc-\u003emapping = kzalloc_obj(struct pkvm_mapping,\narch/arm64/kvm/mmu.c-1187-\t\t\t\t\t  GFP_KERNEL_ACCOUNT);\n--\narch/arm64/kvm/mmu.c=2510=int __init kvm_mmu_init(u32 hyp_va_bits)\n--\narch/arm64/kvm/mmu.c-2543-\narch/arm64/kvm/mmu.c:2544:\thyp_pgtable = kzalloc_obj(*hyp_pgtable);\narch/arm64/kvm/mmu.c-2545-\tif (!hyp_pgtable) {\n--\narch/arm64/kvm/nested.c=1328=int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1333-\tif (!vcpu-\u003earch.vncr_tlb) {\narch/arm64/kvm/nested.c:1334:\t\tstruct vncr_tlb *vt = kzalloc_obj(*vcpu-\u003earch.vncr_tlb,\narch/arm64/kvm/nested.c-1335-\t\t\t\t\t\t  GFP_KERNEL_ACCOUNT);\n--\narch/arm64/kvm/nested.c=1793=int kvm_init_nv_sysregs(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/nested.c-1802-\narch/arm64/kvm/nested.c:1803:\tkvm-\u003earch.sysreg_masks = kzalloc_obj(*(kvm-\u003earch.sysreg_masks),\narch/arm64/kvm/nested.c-1804-\t\t\t\t\t     GFP_KERNEL_ACCOUNT);\n--\narch/arm64/kvm/ptdump.c=116=static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu *mmu)\n--\narch/arm64/kvm/ptdump.c-121-\narch/arm64/kvm/ptdump.c:122:\tst = kzalloc_obj(struct kvm_ptdump_guest_state, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/ptdump.c-123-\tif (!st)\n--\narch/arm64/kvm/vgic/vgic-init.c=207=static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)\n--\narch/arm64/kvm/vgic/vgic-init.c-213-\tdist-\u003eactive_spis = (atomic_t)ATOMIC_INIT(0);\narch/arm64/kvm/vgic/vgic-init.c:214:\tdist-\u003espis = kzalloc_objs(struct vgic_irq, nr_spis, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-init.c-215-\tif (!dist-\u003espis)\n--\narch/arm64/kvm/vgic/vgic-init.c=316=static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type)\n--\narch/arm64/kvm/vgic/vgic-init.c-331-\narch/arm64/kvm/vgic/vgic-init.c:332:\tvgic_cpu-\u003eprivate_irqs = kzalloc_objs(struct vgic_irq,\narch/arm64/kvm/vgic/vgic-init.c-333-\t\t\t\t\t      num_private_irqs,\n--\narch/arm64/kvm/vgic/vgic-irqfd.c=142=int kvm_vgic_setup_default_irq_routing(struct kvm *kvm)\n--\narch/arm64/kvm/vgic/vgic-irqfd.c-148-\narch/arm64/kvm/vgic/vgic-irqfd.c:149:\tentries = kzalloc_objs(*entries, nr, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-irqfd.c-150-\tif (!entries)\n--\narch/arm64/kvm/vgic/vgic-its.c=76=static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,\n--\narch/arm64/kvm/vgic/vgic-its.c-87-\narch/arm64/kvm/vgic/vgic-its.c:88:\tirq = kzalloc_obj(struct vgic_irq, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-its.c-89-\tif (!irq)\n--\narch/arm64/kvm/vgic/vgic-its.c=962=static int vgic_its_alloc_collection(struct vgic_its *its,\n--\narch/arm64/kvm/vgic/vgic-its.c-967-\narch/arm64/kvm/vgic/vgic-its.c:968:\tcollection = kzalloc_obj(*collection, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-its.c-969-\tif (!collection)\n--\narch/arm64/kvm/vgic/vgic-its.c=1006=static struct its_ite *vgic_its_alloc_ite(struct its_device *device,\n--\narch/arm64/kvm/vgic/vgic-its.c-1011-\narch/arm64/kvm/vgic/vgic-its.c:1012:\tite = kzalloc_obj(*ite, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-its.c-1013-\tif (!ite)\n--\narch/arm64/kvm/vgic/vgic-its.c=1133=static struct its_device *vgic_its_alloc_device(struct vgic_its *its,\n--\narch/arm64/kvm/vgic/vgic-its.c-1138-\narch/arm64/kvm/vgic/vgic-its.c:1139:\tdevice = kzalloc_obj(*device, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-its.c-1140-\tif (!device)\n--\narch/arm64/kvm/vgic/vgic-its.c=1846=static int vgic_its_create(struct kvm_device *dev, u32 type)\n--\narch/arm64/kvm/vgic/vgic-its.c-1853-\narch/arm64/kvm/vgic/vgic-its.c:1854:\tits = kzalloc_obj(struct vgic_its, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-its.c-1855-\tif (!its)\n--\narch/arm64/kvm/vgic/vgic-mmio-v3.c=886=static int vgic_v3_alloc_redist_region(struct kvm *kvm, uint32_t index,\n--\narch/arm64/kvm/vgic/vgic-mmio-v3.c-931-\narch/arm64/kvm/vgic/vgic-mmio-v3.c:932:\trdreg = kzalloc_obj(*rdreg, GFP_KERNEL_ACCOUNT);\narch/arm64/kvm/vgic/vgic-mmio-v3.c-933-\tif (!rdreg)\n--\narch/arm64/kvm/vgic/vgic-v4.c=242=int vgic_v4_init(struct kvm *kvm)\n--\narch/arm64/kvm/vgic/vgic-v4.c-258-\narch/arm64/kvm/vgic/vgic-v4.c:259:\tdist-\u003eits_vm.vpes = kzalloc_objs(*dist-\u003eits_vm.vpes, nr_vcpus,\narch/arm64/kvm/vgic/vgic-v4.c-260-\t\t\t\t\t GFP_KERNEL_ACCOUNT);\n--\narch/arm64/net/bpf_jit_comp.c=2080=struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\n--\narch/arm64/net/bpf_jit_comp.c-2101-\tif (!jit_data) {\narch/arm64/net/bpf_jit_comp.c:2102:\t\tjit_data = kzalloc_obj(*jit_data);\narch/arm64/net/bpf_jit_comp.c-2103-\t\tif (!jit_data)\n--\narch/csky/kernel/vdso.c=17=static int __init vdso_init(void)\n--\narch/csky/kernel/vdso.c-22-\tvdso_pagelist =\narch/csky/kernel/vdso.c:23:\t\tkzalloc_objs(struct page *, vdso_pages);\narch/csky/kernel/vdso.c-24-\tif (unlikely(vdso_pagelist == NULL)) {\n--\narch/loongarch/kernel/setup.c=466=static int __init add_legacy_isa_io(struct fwnode_handle *fwnode,\n--\narch/loongarch/kernel/setup.c-472-\narch/loongarch/kernel/setup.c:473:\trange = kzalloc_obj(*range, GFP_ATOMIC);\narch/loongarch/kernel/setup.c-474-\tif (!range)\n--\narch/loongarch/kernel/vdso.c=45=static int __init init_vdso(void)\n--\narch/loongarch/kernel/vdso.c-55-\tvdso_info.code_mapping.pages =\narch/loongarch/kernel/vdso.c:56:\t\tkzalloc_objs(struct page *, vdso_info.size / PAGE_SIZE);\narch/loongarch/kernel/vdso.c-57-\n--\narch/loongarch/kvm/intc/eiointc.c=614=static int kvm_eiointc_create(struct kvm_device *dev, u32 type)\n--\narch/loongarch/kvm/intc/eiointc.c-624-\narch/loongarch/kvm/intc/eiointc.c:625:\ts = kzalloc_obj(struct loongarch_eiointc);\narch/loongarch/kvm/intc/eiointc.c-626-\tif (!s)\n--\narch/loongarch/kvm/intc/ipi.c=394=static int kvm_ipi_create(struct kvm_device *dev, u32 type)\n--\narch/loongarch/kvm/intc/ipi.c-411-\narch/loongarch/kvm/intc/ipi.c:412:\ts = kzalloc_obj(struct loongarch_ipi);\narch/loongarch/kvm/intc/ipi.c-413-\tif (!s)\n\n"
}

55/3 2026/08/25 02:54 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34575
							output: 196
							thoughts: 3289
						

Thoughts:
**Analysis of the CXL Secondary Bus Reset Patch for Uninitialized Memory Risks**

Okay, so I've gone through this patch carefully, focusing on potential uninitialized memory exposures. The key here is the `kzalloc` family of functions which guarantee zero-initialization on allocation. The patch introduces several new allocations. Specifically, the `hdm_state` (an xarray) and `cxl_hdm_state` structs are allocated with either `kzalloc_obj` or `kzalloc_flex`, which initializes the memory to zero, therefore, eliminating the risk of uninitialized data. These are used to store hardware register values that are read and restored during the SBR process. The structs such as `cxl_sbr_ctx` are zero-initialized at the point of declaration. Similarly, local variables like `regions` and `ctx` are explicitly initialized with `xa_init` or `={}` respectively.

The patch includes the `cxl_unmask_sbr` sysfs attribute, which deals with a single bitfield, so, the risk of data leakage is minimal. No new complex data structures are passed to user space. The patch seems to be correctly handling the hardware register reads and writes and associated memory management, preventing any uninitialized memory exposures.

Therefore, since the code diligently zero-initializes all allocated memory, initializes local variables correctly, and doesn't introduce any new user-space interfaces that return complex, potentially uninitialized data, I'm confident that KMSAN is not needed here. Standard memory error detectors should be sufficient.



56/3 2026/08/25 02:55 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces logic to handle Secondary Bus Reset (SBR) for CXL Downstream Ports, which involves saving and restoring hardware registers, offlining memory blocks, and unbinding drivers. All newly allocated data structures (`hdm_state`, `state`) are allocated using zero-initializing functions (`kzalloc_obj`, `kzalloc_flex`). Local variables (`ctx`, `regions`) are explicitly initialized. No new complex structures are exposed to user space. The only user-space interface added is a sysfs attribute (`cxl_unmask_sbr`) which reads/writes a single bitfield. There is no risk of uninitialized memory being used in control flow or leaked to user space. Standard KASAN and LOCKDEP are sufficient to detect any potential memory corruption, out-of-bounds accesses, or locking issues.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch introduces logic to handle Secondary Bus Reset (SBR) for CXL Downstream Ports, which involves saving and restoring hardware registers, offlining memory blocks, and unbinding drivers. All newly allocated data structures (`hdm_state`, `state`) are allocated using zero-initializing functions (`kzalloc_obj`, `kzalloc_flex`). Local variables (`ctx`, `regions`) are explicitly initialized. No new complex structures are exposed to user space. The only user-space interface added is a sysfs attribute (`cxl_unmask_sbr`) which reads/writes a single bitfield. There is no risk of uninitialized memory being used in control flow or leaked to user space. Standard KASAN and LOCKDEP are sufficient to detect any potential memory corruption, out-of-bounds accesses, or locking issues.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)