AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/21 01:11 flow patch-triage
5m Args:
null

Results:
{
  "EnableConfigs": [
    "PCI",
    "PCI_QUIRKS",
    "KEXEC_HANDOVER",
    "LIVEUPDATE",
    "PCI_LIVEUPDATE"
  ],
  "FocusSymbols": [
    "pci_save_acs_state",
    "pci_restore_acs_state",
    "pci_enable_acs",
    "pci_need_dev_specific_enable_acs",
    "pci_dev_specific_enable_acs",
    "pci_dev_specific_disable_acs_redir",
    "pci_configure_ari",
    "pci_allocate_cap_save_buffers",
    "pci_liveupdate_setup_device",
    "pci_liveupdate_cleanup_device",
    "pci_liveupdate_preserve_bus_numbers",
    "pci_liveupdate_refuse_bus_numbers",
    "pci_liveupdate_adopt_acs",
    "pci_liveupdate_adopt_ari"
  ],
  "KMSANReasoning": "The patch series introduces PCI Live Update support, allowing PCI devices and their bridge topologies to be preserved across kexec using Live Update Orchestrator (LUO) and Kexec Handover (KHO).\n\nAnalysis of memory initialization and sanitizers:\n1. Memory Allocation and Initialization:\n   - `struct pci_flb_outgoing` and `struct pci_flb_incoming` are allocated using `kzalloc_obj()`.\n   - `struct pci_ser` is allocated using `kho_alloc_preserve()`, which allocates and zeroes preserved memory, with all fields explicitly initialized (`version`, `nr_devices`, `devices`). The structure is packed and has no padding.\n   - `struct pci_dev_ser` is packed and allocated from KHO block sets (which allocate zeroed memory via `kho_alloc_preserve()`); all fields are explicitly initialized on preserve, and cleared with `memset()` on unpreserve.\n   - `struct pci_liveupdate` embedded in `struct pci_dev` is zero-initialized as `struct pci_dev` is allocated with `kzalloc_obj()`.\n   - The new ACS capability save buffer is allocated via `pci_add_ext_cap_save_buffer()`, which zeroes memory with `kzalloc()`.\n   - Local variables passed by pointer (e.g. `u16 val` in `pci_liveupdate_adopt_ari()`) are initialized by their callees (`pcie_capability_read_word()` explicitly sets `*val = 0` on entry).\n\n2. Userspace Leaks / Uninitialized Memory:\n   - There are no `copy_to_user()`, `put_user()`, netlink, or userspace-facing serialization paths modified or introduced in this patch.\n   - No branching or conditional logic depends on uninitialized fields or stack variables.\n\n3. Applicable Detectors:\n   - Any potential issues in this patch series are related to synchronization (`rwsem`), refcounting, pointer lifecycle management, or bus scanning logic, which fall under the purview of LOCKDEP, KASAN, and standard kernel debugging tools.\n\nTherefore, the patch introduces no uninitialized memory risks or info-leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series adds core PCI Live Update support and modifies core PCI subsystem logic. Beyond introducing drivers/pci/liveupdate.c, it modifies reachable PCI core paths in drivers/pci/pci.c, drivers/pci/quirks.c, and drivers/pci/probe.c: it adds ACS configuration saving and restoring (pci_save_acs_state, pci_restore_acs_state) called during pci_save_state/pci_restore_state (reachable via device reset through sysfs or VFIO), refactors ACS quirk lookup and dispatch (pci_need_dev_specific_enable_acs, pci_dev_specific_enable_acs), alters bridge scanning logic in pci_scan_bridge_extend, and adds liveupdate device setup/cleanup hooks with runtime warnings. Because reachable executable PCI subsystem logic and state management are altered, this warrants fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/09/21 01:11 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit d16d7de37068d14644f2734e19ff701b07419cd7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Sep 21 01:11:14 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst\nindex 5d720d2a415e0..23fb737ac969e 100644\n--- a/Documentation/PCI/index.rst\n+++ b/Documentation/PCI/index.rst\n@@ -20,3 +20,4 @@ PCI Bus Subsystem\n    controller/index\n    boot-interrupts\n    tph\n+   liveupdate\ndiff --git a/Documentation/PCI/liveupdate.rst b/Documentation/PCI/liveupdate.rst\nnew file mode 100644\nindex 0000000000000..96b1d7f5df3a0\n--- /dev/null\n+++ b/Documentation/PCI/liveupdate.rst\n@@ -0,0 +1,35 @@\n+.. SPDX-License-Identifier: GPL-2.0-or-later\n+\n+===========================\n+PCI Support for Live Update\n+===========================\n+\n+.. kernel-doc:: drivers/pci/liveupdate.c\n+   :doc: PCI Live Update\n+\n+Driver API\n+==========\n+\n+.. kernel-doc:: drivers/pci/liveupdate.c\n+   :export:\n+\n+Internal API\n+============\n+\n+.. kernel-doc:: drivers/pci/liveupdate.c\n+   :internal:\n+\n+Live Update ABI\n+===============\n+\n+.. kernel-doc:: include/linux/kho/abi/pci.h\n+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI\n+\n+.. kernel-doc:: include/linux/kho/abi/pci.h\n+   :internal:\n+\n+See Also\n+========\n+\n+ * :doc:`/core-api/liveupdate`\n+ * :doc:`/core-api/kho/index`\ndiff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt\nindex 68647ff4bdd24..7eabf6320abfa 100644\n--- a/Documentation/admin-guide/kernel-parameters.txt\n+++ b/Documentation/admin-guide/kernel-parameters.txt\n@@ -5170,7 +5170,12 @@ Kernel parameters\n \t\t\t\texplicitly which ones they are.\n \t\tassign-busses\t[X86] Always assign all PCI bus\n \t\t\t\tnumbers ourselves, overriding\n-\t\t\t\twhatever the firmware may have done.\n+\t\t\t\twhatever the firmware may have done. Ignored\n+\t\t\t\tif any device was preserved across a Live\n+\t\t\t\tUpdate, where the kernel must preserve the\n+\t\t\t\tPCI topology (including bus numbers) to\n+\t\t\t\tavoid interrupting ongoing memory transactions\n+\t\t\t\tof preserved devices.\n \t\tusepirqmask\t[X86] Honor the possible IRQ mask stored\n \t\t\t\tin the BIOS $PIR table. This is needed on\n \t\t\t\tsome systems with broken BIOSes, notably\ndiff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api/liveupdate.rst\nindex 5a292d0f37064..2bce2644eba22 100644\n--- a/Documentation/core-api/liveupdate.rst\n+++ b/Documentation/core-api/liveupdate.rst\n@@ -1,5 +1,7 @@\n .. SPDX-License-Identifier: GPL-2.0\n \n+.. _luo:\n+\n ========================\n Live Update Orchestrator\n ========================\n@@ -18,6 +20,8 @@ LUO Preserving File Descriptors\n .. kernel-doc:: kernel/liveupdate/luo_file.c\n    :doc: LUO File Descriptors\n \n+.. _flb:\n+\n LUO File Lifecycle Bound Global Data\n ====================================\n .. kernel-doc:: kernel/liveupdate/luo_flb.c\n@@ -70,3 +74,4 @@ See Also\n \n - :doc:`Live Update uAPI \u003c/userspace-api/liveupdate\u003e`\n - :doc:`/core-api/kho/index`\n+- :doc:`PCI \u003c/PCI/liveupdate\u003e`\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 3a19da74d00c9..5d1b58271989d 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -21046,6 +21046,21 @@ L:\tlinux-pci@vger.kernel.org\n S:\tSupported\n F:\tDocumentation/PCI/pci-error-recovery.rst\n \n+PCI LIVE UPDATE\n+M:\tDavid Matlack \u003cdmatlack@google.com\u003e\n+R:\tPasha Tatashin \u003cpasha.tatashin@soleen.com\u003e\n+R:\tMike Rapoport \u003crppt@kernel.org\u003e\n+R:\tPratyush Yadav \u003cpratyush@kernel.org\u003e\n+L:\tkexec@lists.infradead.org\n+L:\tlinux-pci@vger.kernel.org\n+S:\tMaintained\n+T:\tgit git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git\n+F:\tDocumentation/PCI/liveupdate.rst\n+F:\tdrivers/pci/liveupdate.c\n+F:\tdrivers/pci/liveupdate.h\n+F:\tinclude/linux/kho/abi/pci.h\n+F:\tinclude/linux/pci_liveupdate.h\n+\n PCI MSI DRIVER FOR ALTERA MSI IP\n L:\tlinux-pci@vger.kernel.org\n S:\tOrphan\ndiff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig\nindex 0c7408509ba24..16fbd4212e0f5 100644\n--- a/drivers/pci/Kconfig\n+++ b/drivers/pci/Kconfig\n@@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS\n \t  Reserves space in the kernel to maintain resource locking for\n \t  multiple GPUS.  The overhead for each GPU is very small.\n \n+config PCI_LIVEUPDATE\n+\tbool \"PCI Live Update Support\"\n+\tdepends on PCI \u0026\u0026 LIVEUPDATE \u0026\u0026 64BIT \u0026\u0026 !CARDBUS\n+\thelp\n+\t  Enable PCI core support for preserving PCI devices across Live\n+\t  Update. This, in combination with support in a device's driver,\n+\t  enables PCI devices to run and perform memory transactions\n+\t  uninterrupted during a kexec for Live Update.\n+\n+\t  This option should only be enabled by users who plan to use Live\n+\t  Update for kernel upgrades and require preserving PCI devices during\n+\t  those upgrades.\n+\n+\t  If unsure, say N.\n+\n source \"drivers/pci/hotplug/Kconfig\"\n source \"drivers/pci/controller/Kconfig\"\n source \"drivers/pci/endpoint/Kconfig\"\ndiff --git a/drivers/pci/Makefile b/drivers/pci/Makefile\nindex 41ebc3b9a5182..e8d003cb67576 100644\n--- a/drivers/pci/Makefile\n+++ b/drivers/pci/Makefile\n@@ -16,6 +16,7 @@ obj-$(CONFIG_PROC_FS)\t\t+= proc.o\n obj-$(CONFIG_SYSFS)\t\t+= pci-sysfs.o slot.o\n obj-$(CONFIG_ACPI)\t\t+= pci-acpi.o\n obj-$(CONFIG_GENERIC_PCI_IOMAP) += iomap.o\n+obj-$(CONFIG_PCI_LIVEUPDATE)\t+= liveupdate.o\n endif\n \n obj-$(CONFIG_OF)\t\t+= of.o\ndiff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c\nnew file mode 100644\nindex 0000000000000..51695782ed427\n--- /dev/null\n+++ b/drivers/pci/liveupdate.c\n@@ -0,0 +1,1015 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+/*\n+ * Copyright (c) 2026, Google LLC.\n+ * David Matlack \u003cdmatlack@google.com\u003e\n+ */\n+\n+/**\n+ * DOC: PCI Live Update\n+ *\n+ * The PCI subsystem participates in the Live Update process to enable drivers\n+ * to preserve their PCI devices across kexec.\n+ *\n+ * Preserving a device requires preserving two independent sets of state: the\n+ * driver's own state, which the driver preserves with no involvement from the\n+ * PCI core, and the PCI core's state about the device, which the next kernel\n+ * needs so that enumeration does not disturb a device that is still running.\n+ * This file implements the latter.\n+ *\n+ * :ref:`FLB \u003cflb\u003e` Data\n+ * =====================\n+ *\n+ * Userspace decides which devices are preserved, using :ref:`LUO \u003cluo\u003e` file\n+ * preservation: a driver exposes a file that represents a single PCI device,\n+ * and userspace preserves the device with\n+ * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preservation\n+ * to a file gives it proper lifecycle management, e.g. the preservation is\n+ * undone if userspace cancels it or goes away. How a driver exposes that file\n+ * is up to the driver and invisible to the PCI core (vfio-pci variant drivers,\n+ * the first intended use-case, use their per-device cdev).\n+ *\n+ * LUO only knows that a file was preserved; it does not know that the file\n+ * represents a PCI device. Drivers therefore register their\n+ * struct liveupdate_file_handler with the PCI core:\n+ *\n+ *  * ``pci_liveupdate_register_flb(driver_file_handler)``\n+ *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``\n+ *\n+ * LUO then refcounts the PCI core's FLB against the files preserved by that\n+ * handler, and that refcount drives the lifetime of struct pci_ser:\n+ * pci_flb_preserve() allocates and preserves it when the first file is\n+ * preserved, and pci_flb_unpreserve() frees it when the last file is\n+ * unpreserved. In the next kernel, pci_flb_retrieve() hands the PCI core the\n+ * struct pci_ser built by the previous kernel, whenever the PCI core asks for\n+ * it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI\n+ * core is done with it.\n+ *\n+ * State handed over by the previous kernel is trusted. The PCI core validates\n+ * it only far enough to detect an incompatible or corrupt hand over, and makes\n+ * no attempt to defend against deliberate modification, since a previous kernel\n+ * able to corrupt preserved state is able to corrupt arbitrary memory anyway.\n+ *\n+ * Call Flow\n+ * ---------\n+ *\n+ * ::\n+ *\n+ *   # Driver initialization\n+ *   pci_liveupdate_register_flb(fh)\n+ *\n+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)\n+ *   luo_preserve_file()\n+ *     luo_flb_file_preserve()\n+ *       luo_flb_file_preserve_one()      # first preserved file only\n+ *         pci_flb_preserve()             # alloc and preserve struct pci_ser\n+ *     fh-\u003eops-\u003epreserve()                # driver callback\n+ *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser\n+ *\n+ *   # Userspace: preservation cancelled or session torn down\n+ *   luo_file_unpreserve_files()\n+ *     luo_flb_file_unpreserve()\n+ *       liveupdate_flb_put_outgoing()    # last unpreserved file only\n+ *         pci_flb_unpreserve()           # free struct pci_ser\n+ *\n+ *   # ---------------- kexec ----------------\n+ *\n+ *   # New kernel: PCI enumeration\n+ *   pci_setup_device()\n+ *     pci_liveupdate_setup_device()\n+ *       liveupdate_flb_get_incoming()\n+ *         luo_flb_retrieve_one()         # first request only\n+ *           pci_flb_retrieve()           # previous kernel's struct pci_ser\n+ *\n+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)\n+ *   luo_file_finish_one()\n+ *     fh-\u003eops-\u003efinish()                  # driver callback\n+ *       pci_liveupdate_finish(dev)       # release this device's pci_dev_ser\n+ *     luo_flb_file_finish()\n+ *       liveupdate_flb_put_incoming()    # last incoming file only\n+ *         pci_flb_finish()               # free struct pci_ser\n+ *\n+ * Device Tracking\n+ * ===============\n+ *\n+ * Drivers must notify the PCI core when specific devices are preserved or\n+ * unpreserved with the following APIs:\n+ *\n+ *  * ``pci_liveupdate_preserve(pci_dev)``\n+ *  * ``pci_liveupdate_unpreserve(pci_dev)``\n+ *\n+ * This allows the PCI core to keep its FLB data (struct pci_ser) up to date\n+ * with the list of **outgoing** preserved devices for the next kernel.\n+ *\n+ * After kexec, whenever a device is enumerated, the PCI core will check if it\n+ * is an **incoming** preserved device (i.e. preserved by the previous kernel)\n+ * by checking the incoming FLB data (struct pci_ser).\n+ *\n+ * Drivers must notify the PCI core when an **incoming** device is done\n+ * participating in the incoming Live Update with the following API:\n+ *\n+ *  * ``pci_liveupdate_finish(pci_dev)``\n+ *\n+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and\n+ * ``pci_liveupdate_preserve()``, i.e., a PCI device can be **outgoing**\n+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)\n+ * at the same time.\n+ *\n+ * Restrictions\n+ * ============\n+ *\n+ * The PCI core enforces the following restrictions on which devices can be\n+ * preserved. These may be relaxed in the future:\n+ *\n+ *  * The device cannot be a Virtual Function (VF).\n+ *\n+ *  * The device cannot require device-specific quirks to enable Access\n+ *    Control Services (ACS).\n+ *\n+ * Driver Binding\n+ * ==============\n+ *\n+ * In the outgoing kernel, the driver must ensure that it does not release a\n+ * device between pci_liveupdate_preserve() and pci_liveupdate_unpreserve().\n+ *\n+ * In the incoming kernel, the driver must ensure that it does not release a\n+ * preserved device between probe() and pci_liveupdate_finish().\n+ *\n+ * It is the user's responsibility to ensure that incoming preserved devices are\n+ * bound to the correct driver. The PCI core does not protect against a device\n+ * getting preserved by driver A in the outgoing kernel and then getting bound\n+ * to driver B in the incoming kernel.\n+ *\n+ * PCI-to-PCI Bridges\n+ * ==================\n+ *\n+ * Any PCI-to-PCI bridges upstream of a preserved device are automatically\n+ * preserved when the device is preserved. The PCI core keeps track of the\n+ * number of downstream devices that are preserved under a bridge so that the\n+ * bridge is only unpreserved once all downstream devices are unpreserved.\n+ *\n+ * This enables the PCI core and any drivers bound to the bridge to participate\n+ * in the Live Update so that preserved endpoints can continue issuing memory\n+ * transactions during the Live Update.\n+ *\n+ * BDF Stability\n+ * =============\n+ *\n+ * The PCI core guarantees that preserved devices can be identified by the same\n+ * bus, device, and function numbers for as long as they are preserved\n+ * (including across kexec). To accomplish this, the PCI core keeps the\n+ * secondary and subordinate bus numbers that the previous kernel programmed\n+ * into bridges, if the previous kernel preserved any device. This is true even\n+ * on architectures that always assign new bus numbers during scanning. The\n+ * kernel assumes the previous kernel established a sane bus topology across\n+ * kexec.\n+ *\n+ * Bridges that do not have bus numbers are assigned new ones as usual, so\n+ * hot-adding a bridge keeps working, both during and after a Live Update. The\n+ * two-pass bridge scan ensures such bridges are only assigned bus numbers above\n+ * those already claimed by preserved bridges.\n+ *\n+ * If a preserved bridge comes up without a valid bus number configuration, e.g.\n+ * because it was reset during kexec, the PCI core refuses to assign it new bus\n+ * numbers and does not enumerate anything below it. Assigning new bus numbers\n+ * would silently change the BDF of every preserved device in its hierarchy. The\n+ * PCI core also stops assigning bus numbers to the other bridges on the same\n+ * bus, since the bus numbers of the failed bridge can no longer be read from\n+ * hardware and handing them to another bridge would let an unrelated device\n+ * inherit the BDF of a preserved device.\n+ *\n+ * Handling Preserved Devices\n+ * ==========================\n+ *\n+ * The PCI core treats preserved devices differently than non-preserved devices.\n+ * This section enumerates those differences.\n+ *\n+ *  * The PCI core adopts all ACS controls enabled on incoming preserved devices\n+ *    rather than assigning new ones. This ensures that TLPs are routed the same\n+ *    way after Live Update and ensures that IOMMU groups do not change. Note\n+ *    that a device will use its adopted ACS controls for the lifetime of its\n+ *    struct pci_dev (i.e. even after pci_liveupdate_finish()).\n+ *\n+ *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream\n+ *    preserved devices to ensure that all preserved devices on the bridge's\n+ *    secondary bus are addressable after the Live Update.\n+ *\n+ *  * The PCI core does not disable bus mastering on outgoing preserved devices\n+ *    during kexec. This allows preserved devices to issue memory transactions\n+ *    throughout the Live Update.\n+ */\n+\n+#define pr_fmt(fmt) \"PCI: liveupdate: \" fmt\n+\n+#include \u003clinux/io.h\u003e\n+#include \u003clinux/kexec_handover.h\u003e\n+#include \u003clinux/kho/abi/pci.h\u003e\n+#include \u003clinux/kho_block.h\u003e\n+#include \u003clinux/liveupdate.h\u003e\n+#include \u003clinux/mm.h\u003e\n+#include \u003clinux/mutex.h\u003e\n+#include \u003clinux/pci.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#include \"liveupdate.h\"\n+#include \"pci.h\"\n+\n+/**\n+ * struct pci_liveupdate_global - Global state for PCI Live Update support\n+ * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing\n+ *         FLBs, and the references to them in struct pci_dev.\n+ * @had_incoming: True if the previous kernel preserved at least one PCI device.\n+ *                Set when the incoming FLB is retrieved and never cleared, so\n+ *                it stays true after Live Update finishes.\n+ */\n+struct pci_liveupdate_global {\n+\tstruct rw_semaphore rwsem;\n+\tbool had_incoming;\n+};\n+\n+static struct pci_liveupdate_global pci_liveupdate = {\n+\t.rwsem = __RWSEM_INITIALIZER(pci_liveupdate.rwsem),\n+};\n+\n+/**\n+ * struct pci_flb_outgoing - Outgoing PCI FLB object\n+ * @ser: Pointer to the preserved struct pci_ser.\n+ * @block_set: The KHO block set holding the outgoing devices.\n+ *\n+ * This structure holds the runtime state for the outgoing PCI Live Update\n+ * state. It wraps the serialized pci_ser and the block_set used to manage\n+ * the serialized entries.\n+ */\n+struct pci_flb_outgoing {\n+\tstruct pci_ser *ser;\n+\tstruct kho_block_set block_set;\n+};\n+\n+/**\n+ * struct pci_flb_incoming - Incoming PCI FLB object\n+ * @ser: The incoming struct pci_ser from the previous kernel.\n+ * @xa: Xarray used to quickly lookup devices in @ser.\n+ * @block_set: The KHO block set holding the incoming devices.\n+ *\n+ * This structure holds the runtime state for the incoming PCI Live Update\n+ * state. It wraps the serialized pci_ser, the block_set used to restore\n+ * the serialized entries, and an xarray for fast lookups.\n+ */\n+struct pci_flb_incoming {\n+\tstruct pci_ser *ser;\n+\tstruct xarray xa;\n+\tstruct kho_block_set block_set;\n+};\n+\n+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)\n+{\n+\treturn (unsigned long)domain \u003c\u003c 16 | bdf;\n+}\n+\n+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)\n+{\n+\tstruct pci_flb_outgoing *outgoing __free(kfree) = NULL;\n+\tstruct pci_ser *ser;\n+\n+\toutgoing = kzalloc_obj(*outgoing);\n+\tif (!outgoing)\n+\t\treturn -ENOMEM;\n+\n+\tser = kho_alloc_preserve(sizeof(*ser));\n+\tif (IS_ERR(ser))\n+\t\treturn PTR_ERR(ser);\n+\n+\tser-\u003eversion = PCI_LUO_FLB_VERSION;\n+\tser-\u003enr_devices = 0;\n+\tser-\u003edevices = 0;\n+\n+\toutgoing-\u003eser = ser;\n+\tkho_block_set_init(\u0026outgoing-\u003eblock_set, sizeof(struct pci_dev_ser));\n+\n+\targs-\u003eobj = no_free_ptr(outgoing);\n+\targs-\u003edata = virt_to_phys(ser);\n+\tpr_debug(\"Preserved struct pci_ser (0x%llx)\\n\", args-\u003edata);\n+\treturn 0;\n+}\n+\n+static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)\n+{\n+\tstruct pci_flb_outgoing *outgoing = args-\u003eobj;\n+\n+\tpr_debug(\"Unpreserving struct pci_ser (0x%llx)\\n\", args-\u003edata);\n+\n+\tWARN_ON(outgoing-\u003eser-\u003enr_devices);\n+\tkho_block_set_destroy(\u0026outgoing-\u003eblock_set);\n+\tkho_unpreserve_free(outgoing-\u003eser);\n+\tkfree(outgoing);\n+}\n+\n+/*\n+ * Any failure here is fatal. The previous kernel handed over devices that are\n+ * still performing DMA, and this kernel cannot identify them without this\n+ * state. Continuing would let the PCI core reassign bus numbers and rebind\n+ * drivers underneath live devices, so fail loudly instead of unwinding.\n+ */\n+static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)\n+{\n+\tstruct pci_ser *ser = phys_to_virt(args-\u003edata);\n+\tstruct pci_flb_incoming *incoming;\n+\tstruct pci_dev_ser *dev_ser;\n+\tstruct kho_block_set_it it;\n+\tint ret;\n+\n+\tpr_debug(\"Retrieving struct pci_ser (0x%llx)\\n\", args-\u003edata);\n+\n+\tif (ser-\u003eversion != PCI_LUO_FLB_VERSION)\n+\t\tpanic(\"Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\\n\",\n+\t\t      ser-\u003eversion, PCI_LUO_FLB_VERSION);\n+\n+\tincoming = kzalloc_obj(*incoming);\n+\tif (!incoming)\n+\t\tpanic(\"Failed to allocate struct pci_flb_incoming\\n\");\n+\n+\tincoming-\u003eser = ser;\n+\txa_init(\u0026incoming-\u003exa);\n+\n+\tkho_block_set_init(\u0026incoming-\u003eblock_set, sizeof(struct pci_dev_ser));\n+\tret = kho_block_set_restore(\u0026incoming-\u003eblock_set, ser-\u003edevices);\n+\tif (ret)\n+\t\tpanic(\"Failed to restore devices KHO block set (%d)\\n\", ret);\n+\n+\tkho_block_set_it_init(\u0026it, \u0026incoming-\u003eblock_set);\n+\twhile ((dev_ser = kho_block_set_it_read_entry(\u0026it))) {\n+\t\tunsigned long key;\n+\n+\t\tif (!dev_ser-\u003erefcount)\n+\t\t\tcontinue;\n+\n+\t\tkey = pci_ser_xa_key(dev_ser-\u003edomain, dev_ser-\u003ebdf);\n+\t\tret = xa_insert(\u0026incoming-\u003exa, key, dev_ser, GFP_KERNEL);\n+\t\tif (ret)\n+\t\t\tpanic(\"Failed to insert PCI device %04x:%02x:%02x.%d into xarray (%d)\\n\",\n+\t\t\t      dev_ser-\u003edomain, PCI_BUS_NUM(dev_ser-\u003ebdf),\n+\t\t\t      PCI_SLOT(dev_ser-\u003ebdf), PCI_FUNC(dev_ser-\u003ebdf),\n+\t\t\t      ret);\n+\t}\n+\n+\t/*\n+\t * Remember that the previous kernel preserved devices for the lifetime\n+\t * of this kernel, even after Live Update finishes and the incoming FLB\n+\t * is freed. See pci_liveupdate_preserve_bus_numbers().\n+\t */\n+\tif (!xa_empty(\u0026incoming-\u003exa))\n+\t\tpci_liveupdate.had_incoming = true;\n+\n+\targs-\u003eobj = incoming;\n+\treturn 0;\n+}\n+\n+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)\n+{\n+\tstruct pci_dev_ser *dev_ser;\n+\tunsigned long index;\n+\tu32 nr_devices;\n+\n+\t/*\n+\t * nr_devices is only decremented by pci_liveupdate_finish_device().\n+\t * This runs once the last reference to the incoming FLB is dropped, so\n+\t * there are no finishers left in flight.\n+\t */\n+\tnr_devices = incoming-\u003eser-\u003enr_devices;\n+\tif (nr_devices == 0)\n+\t\treturn;\n+\n+\t/*\n+\t * Report the unfinished devices from the incoming FLB rather than by\n+\t * walking struct pci_dev, so that devices that never showed up after\n+\t * kexec, or that were destroyed before they finished, are identified\n+\t * as well.\n+\t */\n+\txa_for_each(\u0026incoming-\u003exa, index, dev_ser) {\n+\t\tif (!dev_ser-\u003erefcount)\n+\t\t\tcontinue;\n+\n+\t\tpr_emerg(\"%04x:%02x:%02x.%d was never finished!\\n\",\n+\t\t\t dev_ser-\u003edomain, PCI_BUS_NUM(dev_ser-\u003ebdf),\n+\t\t\t PCI_SLOT(dev_ser-\u003ebdf), PCI_FUNC(dev_ser-\u003ebdf));\n+\t}\n+\n+\t/*\n+\t * This should only happen if a driver violated the contract to call\n+\t * pci_liveupdate_finish() (something is extremely broken).\n+\t */\n+\tpanic(\"%u preserved device(s) were never finished!\\n\", nr_devices);\n+}\n+\n+static void pci_flb_finish(struct liveupdate_flb_op_args *args)\n+{\n+\tstruct pci_flb_incoming *incoming = args-\u003eobj;\n+\n+\tpr_debug(\"Finished struct pci_ser (0x%llx)\\n\", args-\u003edata);\n+\tpci_check_all_devices_finished(incoming);\n+\n+\txa_destroy(\u0026incoming-\u003exa);\n+\tkho_block_set_destroy(\u0026incoming-\u003eblock_set);\n+\tkho_restore_free(incoming-\u003eser);\n+\tkfree(incoming);\n+}\n+\n+static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {\n+\t.preserve = pci_flb_preserve,\n+\t.unpreserve = pci_flb_unpreserve,\n+\t.retrieve = pci_flb_retrieve,\n+\t.finish = pci_flb_finish,\n+\t.owner = THIS_MODULE,\n+};\n+\n+static struct liveupdate_flb pci_liveupdate_flb = {\n+\t.ops = \u0026pci_liveupdate_flb_ops,\n+\t.compatible = PCI_LUO_FLB_COMPATIBLE,\n+};\n+\n+static void pci_liveupdate_flb_put_outgoing(void)\n+{\n+\tliveupdate_flb_put_outgoing(\u0026pci_liveupdate_flb);\n+}\n+\n+static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)\n+{\n+\tstruct pci_flb_outgoing *outgoing = NULL;\n+\tint ret;\n+\n+\tret = liveupdate_flb_get_outgoing(\u0026pci_liveupdate_flb, (void **)\u0026outgoing);\n+\tif (ret)\n+\t\treturn ERR_PTR(ret);\n+\n+\tif (!outgoing)\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\treturn outgoing;\n+}\n+\n+static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)\n+{\n+\tstruct pci_dev_ser *dev_ser;\n+\tstruct kho_block_set_it it;\n+\tu64 count = 0;\n+\tint err;\n+\n+\tkho_block_set_it_init(\u0026it, \u0026outgoing-\u003eblock_set);\n+\n+\t/* Try to find an existing, previously unpreserved, entry. */\n+\twhile ((dev_ser = kho_block_set_it_read_entry(\u0026it))) {\n+\t\tif (!dev_ser-\u003erefcount)\n+\t\t\treturn dev_ser;\n+\n+\t\tcount++;\n+\t}\n+\n+\t/* Otherwise grow the block set and reserve a new entry. */\n+\terr = kho_block_set_grow(\u0026outgoing-\u003eblock_set, count + 1);\n+\tif (err)\n+\t\treturn ERR_PTR(err);\n+\n+\tif (!count)\n+\t\tkho_block_set_it_init(\u0026it, \u0026outgoing-\u003eblock_set);\n+\n+\t/* This should always succeed since kho_block_set_grow() succeeded. */\n+\tdev_ser = kho_block_set_it_reserve_entry(\u0026it);\n+\tif (WARN_ON_ONCE(!dev_ser))\n+\t\treturn ERR_PTR(-ENOSPC);\n+\n+\treturn dev_ser;\n+}\n+\n+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,\n+\t\t\t\t\t    struct pci_dev *dev)\n+{\n+\tstruct pci_dev_ser *dev_ser = dev-\u003eliveupdate.outgoing;\n+\n+\tif (dev-\u003eliveupdate.frozen) {\n+\t\tpci_warn(dev, \"Cannot unpreserve device after it is frozen!\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!dev_ser) {\n+\t\tpci_warn(dev, \"Cannot unpreserve device that is not preserved\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!dev_ser-\u003erefcount) {\n+\t\tpci_WARN(dev, 1, \"Preserved device has a 0 refcount!\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (--dev_ser-\u003erefcount)\n+\t\treturn 0;\n+\n+\tpci_info(dev, \"Device will no longer be preserved across next Live Update\\n\");\n+\toutgoing-\u003eser-\u003enr_devices--;\n+\tmemset(dev_ser, 0, sizeof(*dev_ser));\n+\tdev-\u003eliveupdate.outgoing = NULL;\n+\treturn 0;\n+}\n+\n+static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoing,\n+\t\t\t\t\t   struct pci_dev *dev,\n+\t\t\t\t\t   struct pci_dev *end)\n+{\n+\tfor_each_pci_dev_in_path(dev) {\n+\t\tif (dev == end)\n+\t\t\tbreak;\n+\n+\t\tif (pci_liveupdate_unpreserve_device(outgoing, dev))\n+\t\t\treturn;\n+\t}\n+}\n+\n+static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,\n+\t\t\t\t\t  struct pci_dev *dev)\n+{\n+\tif (dev-\u003eis_virtfn) {\n+\t\tpci_warn(dev, \"Cannot preserve Virtual Functions\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Do not preserve devices that rely on device-specific ACS equivalents\n+\t * (for now) since that would complicate keeping ACS constant across\n+\t * Live Update.\n+\t */\n+\tif (pci_need_dev_specific_enable_acs(dev)) {\n+\t\tpci_warn(dev, \"Refusing to preserve device that relies on ACS quirks\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Endpoint devices should not be preserved more than once.\n+\t * Bridges are preserved once for every downstream device that\n+\t * is preserved.\n+\t */\n+\tif (dev-\u003eliveupdate.outgoing \u0026\u0026 !dev-\u003esubordinate) {\n+\t\tpci_warn(dev, \"Device is already preserved\\n\");\n+\t\treturn -EBUSY;\n+\t}\n+\n+\tif (dev-\u003eliveupdate.outgoing \u0026\u0026 !dev-\u003eliveupdate.outgoing-\u003erefcount) {\n+\t\tpci_WARN(dev, 1, \"Preserved device with 0 refcount!\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (dev-\u003eliveupdate.frozen) {\n+\t\tpci_warn(dev, \"Cannot preserve device after it is frozen!\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!dev-\u003eliveupdate.outgoing) {\n+\t\tstruct pci_dev_ser *dev_ser;\n+\n+\t\tdev_ser = pci_flb_alloc_dev_ser(outgoing);\n+\t\tif (IS_ERR(dev_ser))\n+\t\t\treturn PTR_ERR(dev_ser);\n+\n+\t\tpci_info(dev, \"Device will be preserved across next Live Update\\n\");\n+\t\toutgoing-\u003eser-\u003enr_devices++;\n+\t\toutgoing-\u003eser-\u003edevices = kho_block_set_head_pa(\u0026outgoing-\u003eblock_set);\n+\n+\t\tdev_ser-\u003edomain = pci_domain_nr(dev-\u003ebus);\n+\t\tdev_ser-\u003ebdf = pci_dev_id(dev);\n+\t\tdev-\u003eliveupdate.outgoing = dev_ser;\n+\t}\n+\n+\tdev-\u003eliveupdate.outgoing-\u003erefcount++;\n+\treturn 0;\n+}\n+\n+/**\n+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update\n+ * @dev: The PCI device to preserve.\n+ *\n+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be\n+ * preserved across the next Live Update. Drivers are expected to call\n+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler\n+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.\n+ *\n+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of\n+ * @dev.\n+ *\n+ * Returns: 0 on success, \u003c0 on failure.\n+ */\n+int pci_liveupdate_preserve(struct pci_dev *dev)\n+{\n+\tstruct pci_flb_outgoing *outgoing = NULL;\n+\tstruct pci_dev *start = dev;\n+\tint ret = -ENODEV;\n+\n+\tguard(rwsem_write)(\u0026pci_liveupdate.rwsem);\n+\n+\toutgoing = pci_liveupdate_flb_get_outgoing();\n+\tif (IS_ERR(outgoing))\n+\t\treturn PTR_ERR(outgoing);\n+\n+\tfor_each_pci_dev_in_path(dev) {\n+\t\tret = pci_liveupdate_preserve_device(outgoing, dev);\n+\t\tif (ret) {\n+\t\t\tpci_liveupdate_unpreserve_path(outgoing, start, dev);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tpci_liveupdate_flb_put_outgoing();\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);\n+\n+/**\n+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device\n+ * @dev: The PCI device to unpreserve.\n+ *\n+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no\n+ * longer be preserved across the next Live Update. Drivers are expected to call\n+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler\n+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set\n+ * up.\n+ *\n+ * pci_liveupdate_unpreserve() automatically unpreserves all bridges upstream of\n+ * @dev.\n+ */\n+void pci_liveupdate_unpreserve(struct pci_dev *dev)\n+{\n+\tstruct pci_flb_outgoing *outgoing = NULL;\n+\n+\tguard(rwsem_write)(\u0026pci_liveupdate.rwsem);\n+\n+\toutgoing = pci_liveupdate_flb_get_outgoing();\n+\tif (IS_ERR(outgoing)) {\n+\t\tpci_warn(dev, \"Cannot unpreserve device without outgoing Live Update state\\n\");\n+\t\treturn;\n+\t}\n+\n+\tpci_liveupdate_unpreserve_path(outgoing, dev, /*end=*/NULL);\n+\tpci_liveupdate_flb_put_outgoing();\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);\n+\n+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)\n+{\n+\tstruct pci_flb_incoming *incoming = NULL;\n+\tint ret;\n+\n+\tret = liveupdate_flb_get_incoming(\u0026pci_liveupdate_flb, (void **)\u0026incoming);\n+\n+\t/* Live Update is not enabled. */\n+\tif (ret == -EOPNOTSUPP)\n+\t\treturn NULL;\n+\n+\t/* Live Update is enabled, but there is no incoming FLB data. */\n+\tif (ret == -ENODATA)\n+\t\treturn NULL;\n+\n+\t/*\n+\t * Live Update is enabled and there is incoming FLB data, but none of it\n+\t * matches pci_liveupdate_flb.compatible.\n+\t */\n+\tif (ret == -ENOENT)\n+\t\treturn NULL;\n+\n+\t/*\n+\t * There is incoming FLB data that matches pci_liveupdate_flb.compatible\n+\t * but retrieve failed (pci_flb_retrieve() returned an error or LUO\n+\t * failed to acquire a reference to pci_liveupdate_flb_ops.owner).\n+\t */\n+\tif (ret)\n+\t\tpanic(\"Failed to retrieve incoming FLB data (%d)\\n\", ret);\n+\n+\treturn incoming;\n+}\n+\n+static void pci_liveupdate_flb_put_incoming(void)\n+{\n+\tliveupdate_flb_put_incoming(\u0026pci_liveupdate_flb);\n+}\n+\n+/**\n+ * pci_liveupdate_preserve_bus_numbers() - Determine if the PCI core should\n+ *                                         preserve bus numbers when scanning\n+ *                                         bridges.\n+ *\n+ * This function is called by the PCI core when it is scanning a bridge. It\n+ * determines whether the PCI core should preserve the secondary and subordinate\n+ * bus numbers that the previous kernel programmed into that bridge, rather than\n+ * assigning new ones. This is necessary to keep RequesterIDs constant for\n+ * preserved devices issuing memory transactions.\n+ *\n+ * Bus numbers are preserved everywhere, and for the lifetime of the kernel, if\n+ * the previous kernel preserved any device. Bus numbers have to be preserved\n+ * above a preserved device anyway, since an upstream bridge cannot expand its\n+ * window. Applying the same policy everywhere matches the scope of\n+ * pcibios_assign_all_busses(), and gives an answer that cannot change part way\n+ * through the two passes of a bridge scan.\n+ *\n+ * The incoming FLB is retrieved while setting up the first device, which always\n+ * happens before any bridge is scanned, so this returns the same answer for the\n+ * entire enumeration.\n+ *\n+ * Note that this does not prevent the PCI core from assigning bus numbers to\n+ * bridges that do not have any, e.g. bridges that are hot-added after the\n+ * Live Update. See pci_liveupdate_refuse_bus_numbers() for the one case where\n+ * the PCI core must refuse to do so.\n+ *\n+ * Return: True if bus numbers should be preserved, false otherwise.\n+ */\n+bool pci_liveupdate_preserve_bus_numbers(void)\n+{\n+\treturn pci_liveupdate.had_incoming;\n+}\n+\n+/**\n+ * pci_liveupdate_refuse_bus_numbers() - Determine if the PCI core must refuse\n+ *                                       to assign bus numbers to the provided\n+ *                                       bridge.\n+ * @bus: The PCI bus the bus numbers would be assigned from.\n+ * @dev: The PCI bridge device the bus numbers would be assigned to.\n+ *\n+ * This function is called by the PCI core before it assigns bus numbers to a\n+ * bridge that does not have any.\n+ *\n+ * A bridge that was preserved by the previous kernel but came up without a\n+ * valid bus number configuration, e.g. because it was reset during kexec, is\n+ * left alone by the PCI core and therefore has no child bus once the first pass\n+ * of the bridge scan is done.\n+ *\n+ * The PCI core must not assign bus numbers from @bus while such a bridge is on\n+ * it, including to the failed bridge itself. Assigning new bus numbers to the\n+ * failed bridge would silently change the BDF of every preserved device in its\n+ * hierarchy. Its bus numbers cannot be read from hardware anymore either, so\n+ * they cannot be excluded from assignment, and handing them to another bridge\n+ * would let an unrelated device inherit the BDF of a preserved device.\n+ *\n+ * Return: True if @dev must not be assigned bus numbers, false otherwise.\n+ */\n+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)\n+{\n+\tstruct pci_dev *bridge;\n+\n+\tfor_each_pci_bridge(bridge, bus) {\n+\t\tif (!bridge-\u003eliveupdate.was_incoming || bridge-\u003esubordinate)\n+\t\t\tcontinue;\n+\n+\t\tpci_err(dev, \"Not assigning bus numbers, preserved bridge %s lost its bus number configuration\\n\",\n+\t\t\tpci_name(bridge));\n+\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+void pci_liveupdate_setup_device(struct pci_dev *dev)\n+{\n+\tstruct pci_flb_incoming *incoming;\n+\tstruct pci_dev_ser *dev_ser;\n+\tunsigned long key;\n+\n+\tguard(rwsem_write)(\u0026pci_liveupdate.rwsem);\n+\n+\tincoming = pci_liveupdate_flb_get_incoming();\n+\tif (!incoming)\n+\t\treturn;\n+\n+\tkey = pci_ser_xa_key(pci_domain_nr(dev-\u003ebus), pci_dev_id(dev));\n+\tdev_ser = xa_load(\u0026incoming-\u003exa, key);\n+\n+\t/*\n+\t * This device was not preserved across Live Update, or it was preserved\n+\t * but has already been probed and gone through pci_liveupdate_finish(),\n+\t * e.g. due to removing and re-adding the device. Either way, it's not\n+\t * treated as incoming-preserved.\n+\t */\n+\tif (!dev_ser || !dev_ser-\u003erefcount) {\n+\t\tpci_liveupdate_flb_put_incoming();\n+\t\treturn;\n+\t}\n+\n+\tpci_info(dev, \"Device was preserved by previous kernel across Live Update\\n\");\n+\tdev-\u003eliveupdate.incoming = dev_ser;\n+\tdev-\u003eliveupdate.was_incoming = true;\n+\n+\tpci_liveupdate_flb_put_incoming();\n+}\n+\n+void pci_liveupdate_cleanup_device(struct pci_dev *dev)\n+{\n+\t/*\n+\t * It should be safe to READ_ONCE() outside of the rwsem during cleanup\n+\t * since there should no longer be any references to @dev on the system.\n+\t *\n+\t * This should never happen in practice. Drivers should block removal\n+\t * while a device is preserved.\n+\t */\n+\tif (READ_ONCE(dev-\u003eliveupdate.outgoing))\n+\t\tpci_WARN(dev, 1, \"Destroying outgoing-preserved device!\\n\");\n+\n+\tif (READ_ONCE(dev-\u003eliveupdate.incoming))\n+\t\tpci_WARN(dev, 1, \"Destroying incoming-preserved device!\\n\");\n+}\n+\n+void pci_liveupdate_freeze(struct pci_dev *dev)\n+{\n+\tguard(rwsem_write)(\u0026pci_liveupdate.rwsem);\n+\tdev-\u003eliveupdate.frozen = true;\n+}\n+\n+static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)\n+{\n+\tif (!dev-\u003eliveupdate.incoming) {\n+\t\tpci_warn(dev, \"Cannot finish preserving an unpreserved device\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!dev-\u003eliveupdate.incoming-\u003erefcount) {\n+\t\tpci_WARN(dev, 1, \"Preserved device has a 0 refcount!\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Decrement the refcount so this device does not get treated as an\n+\t * incoming device again, e.g. in case pci_liveupdate_setup_device()\n+\t * gets called again because the device is hot-plugged.\n+\t */\n+\tif (--dev-\u003eliveupdate.incoming-\u003erefcount)\n+\t\treturn 0;\n+\n+\tpci_info(dev, \"Device is finished participating in Live Update\\n\");\n+\tdev-\u003eliveupdate.incoming = NULL;\n+\tser-\u003enr_devices--;\n+\treturn 0;\n+}\n+\n+/**\n+ * pci_liveupdate_finish() - Finish the preservation of a PCI device\n+ * @dev: The PCI device\n+ *\n+ * pci_liveupdate_finish() notifies the PCI core that a PCI device that was\n+ * preserved across the previous Live Update has finished participating in Live\n+ * Update. Drivers must call pci_liveupdate_finish() from their struct\n+ * liveupdate_file_handler finish() callback to ensure the incoming struct\n+ * pci_ser is allocated.\n+ *\n+ * pci_liveupdate_finish() automatically finishes all bridges upstream of @dev.\n+ */\n+void pci_liveupdate_finish(struct pci_dev *dev)\n+{\n+\tstruct pci_flb_incoming *incoming;\n+\n+\tguard(rwsem_write)(\u0026pci_liveupdate.rwsem);\n+\n+\tincoming = pci_liveupdate_flb_get_incoming();\n+\tif (!incoming) {\n+\t\tpci_warn(dev, \"Cannot finish preserving device without incoming FLB\\n\");\n+\t\treturn;\n+\t}\n+\n+\tfor_each_pci_dev_in_path(dev) {\n+\t\tif (pci_liveupdate_finish_device(incoming-\u003eser, dev))\n+\t\t\tbreak;\n+\t}\n+\n+\tpci_liveupdate_flb_put_incoming();\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_finish);\n+\n+/**\n+ * pci_liveupdate_adopt_acs() - Adopt ACS controls\n+ * @dev: The PCI device to adopt ACS controls for\n+ *\n+ * For devices preserved across a Live Update, leave the ACS controls\n+ * established by the previous kernel alone instead of programming new ones.\n+ * The adopted controls are recorded by the pci_save_state() call in\n+ * pci_bus_add_device(), so they are reapplied by pci_restore_state() if the\n+ * device is subsequently reset.\n+ *\n+ * Return: 0 on success, or -EINVAL if the device was not preserved, requires\n+ * device-specific quirks, or has nowhere to record the adopted controls.\n+ */\n+int pci_liveupdate_adopt_acs(struct pci_dev *dev)\n+{\n+\t/*\n+\t * Check if the device was preserved over a previous Live Update (even\n+\t * if it has already gone through pci_liveupdate_finish()). This ensures\n+\t * that the device continues to use the ACS controls established by the\n+\t * previous kernel.\n+\t */\n+\tif (!dev-\u003eliveupdate.was_incoming)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * The previous kernel should not have preserved any devices that\n+\t * require device-specific quirks to enable ACS, but if such a device is\n+\t * detected (e.g. new device-specific ACS quirk in the current kernel),\n+\t * log a big warning and fall back to the normal enable ACS path.\n+\t */\n+\tif (pci_need_dev_specific_enable_acs(dev)) {\n+\t\tpci_warn(dev, \"Device-specific quirk required to enable ACS!\\n\");\n+\t\tWARN_ON_ONCE(true);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Adopting the previous kernel's controls depends on them being\n+\t * captured in the ACS save buffer, so that they are reapplied if the\n+\t * device is later reset. Without that buffer, e.g. because it could\n+\t * not be allocated under memory pressure, the adopted controls would\n+\t * be silently lost by the first reset. Program ACS from scratch\n+\t * instead, which is a better outcome than leaving ACS disabled.\n+\t */\n+\tif (dev-\u003eacs_cap \u0026\u0026 !pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS)) {\n+\t\tpci_err(dev, \"No ACS save buffer, not adopting ACS controls\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * pci_liveupdate_adopt_ari() - Adopt ARI configuration\n+ * @dev: The PCI device to adopt ARI configuration for\n+ *\n+ * For devices preserved across a Live Update, read the ARI state from\n+ * hardware and adopt it. This ensures the device continues to use the ARI\n+ * configuration established by the previous kernel.\n+ *\n+ * Return: 0 on success, or -EINVAL if the device was not preserved.\n+ */\n+int pci_liveupdate_adopt_ari(struct pci_dev *dev)\n+{\n+\tu16 val;\n+\n+\tguard(rwsem_read)(\u0026pci_liveupdate.rwsem);\n+\n+\tif (!dev-\u003eliveupdate.incoming)\n+\t\treturn -EINVAL;\n+\n+\tpcie_capability_read_word(dev, PCI_EXP_DEVCTL2, \u0026val);\n+\n+\t/* Safe to modify dev-\u003eari_enabled bitfield during enumeration. */\n+\tdev-\u003eari_enabled = !!(val \u0026 PCI_EXP_DEVCTL2_ARI);\n+\treturn 0;\n+}\n+\n+bool pci_liveupdate_is_outgoing(struct pci_dev *dev)\n+{\n+\tguard(rwsem_read)(\u0026pci_liveupdate.rwsem);\n+\tpci_WARN_ONCE(dev, !dev-\u003eliveupdate.frozen, \"Preservation status is unstable!\\n\");\n+\treturn dev-\u003eliveupdate.outgoing;\n+}\n+\n+/**\n+ * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved\n+ * @dev: The PCI device to check\n+ *\n+ * Check if a device was preserved across Live Update by the previous kernel,\n+ * i.e. the device is incoming-preserved. Note that a device is only considered\n+ * incoming-preserved prior to pci_liveupdate_finish(). It is up to drivers to\n+ * synchronize usage of pci_liveupdate_is_incoming() with their own call to\n+ * pci_liveupdate_finish() to avoid acting on stale data.\n+ *\n+ * Returns: True if the device is incoming-preserved, false otherwise.\n+ */\n+bool pci_liveupdate_is_incoming(struct pci_dev *dev)\n+{\n+\tguard(rwsem_read)(\u0026pci_liveupdate.rwsem);\n+\treturn dev-\u003eliveupdate.incoming;\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);\n+\n+/**\n+ * pci_liveupdate_register_flb() - Register a file handler with the PCI core\n+ * @fh: The file handler to register.\n+ *\n+ * Drivers that support preserving PCI devices across Live Update must call\n+ * pci_liveupdate_register_flb() to register their\n+ * struct liveupdate_file_handler with the PCI core, typically at module init,\n+ * and always before any file managed by @fh can be preserved.\n+ *\n+ * Registering links the PCI core's FLB to @fh, so that LUO allocates the PCI\n+ * core's outgoing struct pci_ser (via pci_flb_preserve()) when the first file\n+ * managed by any registered handler is preserved, and frees it (via\n+ * pci_flb_unpreserve()) when the last such file is unpreserved.\n+ *\n+ * Return: 0 on success, \u003c0 on failure.\n+ */\n+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\n+{\n+\tpr_debug(\"Registering file handler \\\"%s\\\"\\n\", fh-\u003ecompatible);\n+\treturn liveupdate_register_flb(fh, \u0026pci_liveupdate_flb);\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);\n+\n+/**\n+ * pci_liveupdate_unregister_flb() - Unregister a file handler with the PCI core\n+ * @fh: The file handler to unregister.\n+ */\n+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\n+{\n+\tpr_debug(\"Unregistering file handler \\\"%s\\\"\\n\", fh-\u003ecompatible);\n+\tliveupdate_unregister_flb(fh, \u0026pci_liveupdate_flb);\n+}\n+EXPORT_SYMBOL_GPL(pci_liveupdate_unregister_flb);\ndiff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h\nnew file mode 100644\nindex 0000000000000..333ddd8f36c51\n--- /dev/null\n+++ b/drivers/pci/liveupdate.h\n@@ -0,0 +1,62 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * PCI Live Update support (core API)\n+ *\n+ * Copyright (c) 2026, Google LLC.\n+ * David Matlack \u003cdmatlack@google.com\u003e\n+ */\n+#ifndef DRIVERS_PCI_LIVEUPDATE_H\n+#define DRIVERS_PCI_LIVEUPDATE_H\n+\n+#include \u003clinux/pci.h\u003e\n+\n+#ifdef CONFIG_PCI_LIVEUPDATE\n+void pci_liveupdate_setup_device(struct pci_dev *dev);\n+void pci_liveupdate_cleanup_device(struct pci_dev *dev);\n+void pci_liveupdate_freeze(struct pci_dev *dev);\n+bool pci_liveupdate_preserve_bus_numbers(void);\n+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev);\n+int pci_liveupdate_adopt_acs(struct pci_dev *dev);\n+int pci_liveupdate_adopt_ari(struct pci_dev *dev);\n+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);\n+#else\n+static inline void pci_liveupdate_setup_device(struct pci_dev *dev)\n+{\n+}\n+\n+static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)\n+{\n+}\n+\n+static inline void pci_liveupdate_freeze(struct pci_dev *dev)\n+{\n+}\n+\n+static inline bool pci_liveupdate_preserve_bus_numbers(void)\n+{\n+\treturn false;\n+}\n+\n+static inline bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus,\n+\t\t\t\t\t\t     struct pci_dev *dev)\n+{\n+\treturn false;\n+}\n+\n+static inline int pci_liveupdate_adopt_acs(struct pci_dev *dev)\n+{\n+\treturn -EINVAL;\n+}\n+\n+static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)\n+{\n+\treturn -EINVAL;\n+}\n+\n+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)\n+{\n+\treturn false;\n+}\n+#endif\n+\n+#endif /* DRIVERS_PCI_LIVEUPDATE_H */\ndiff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c\nindex e16aa59dd7ac8..c1ac2ef025e23 100644\n--- a/drivers/pci/pci-driver.c\n+++ b/drivers/pci/pci-driver.c\n@@ -21,6 +21,7 @@\n #include \u003clinux/acpi.h\u003e\n #include \u003clinux/dma-map-ops.h\u003e\n #include \u003clinux/iommu.h\u003e\n+#include \"liveupdate.h\"\n #include \"pci.h\"\n #include \"pcie/portdrv.h\"\n \n@@ -559,6 +560,7 @@ static void pci_device_shutdown(struct device *dev)\n \tstruct pci_dev *pci_dev = to_pci_dev(dev);\n \tstruct pci_driver *drv = pci_dev-\u003edriver;\n \n+\tpci_liveupdate_freeze(pci_dev);\n \tpm_runtime_resume(dev);\n \n \tif (drv \u0026\u0026 drv-\u003eshutdown)\n@@ -567,11 +569,14 @@ static void pci_device_shutdown(struct device *dev)\n \t/*\n \t * If this is a kexec reboot, turn off Bus Master bit on the\n \t * device to tell it to not continue to do DMA. Don't touch\n-\t * devices in D3cold or unknown states.\n+\t * devices being preserved for Live Update or in D3cold or\n+\t * unknown states.\n+\t *\n \t * If it is not a kexec reboot, firmware will hit the PCI\n \t * devices with big hammer and stop their DMA any way.\n \t */\n-\tif (kexec_in_progress \u0026\u0026 (pci_dev-\u003ecurrent_state \u003c= PCI_D3hot))\n+\tif (kexec_in_progress \u0026\u0026 !pci_liveupdate_is_outgoing(pci_dev) \u0026\u0026\n+\t    pci_dev-\u003ecurrent_state \u003c= PCI_D3hot)\n \t\tpci_clear_master(pci_dev);\n }\n \ndiff --git a/drivers/pci/pci.c b/drivers/pci/pci.c\nindex b2879a6be5f80..0ae405b86b2f8 100644\n--- a/drivers/pci/pci.c\n+++ b/drivers/pci/pci.c\n@@ -35,6 +35,8 @@\n #include \u003clinux/aer.h\u003e\n #include \u003clinux/bitfield.h\u003e\n #include \u003clinux/suspend.h\u003e\n+\n+#include \"liveupdate.h\"\n #include \"pci.h\"\n \n DEFINE_MUTEX(pci_slot_mutex);\n@@ -1021,6 +1023,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)\n \t\tcaps-\u003ectrl |= (dev-\u003eacs_capabilities \u0026 PCI_ACS_TB);\n }\n \n+/**\n+ * pci_save_acs_state - save the ACS Control register\n+ * @dev: the PCI device\n+ *\n+ * Record the ACS controls currently programmed in hardware so that\n+ * pci_restore_acs_state() can reapply them after a reset.\n+ */\n+static void pci_save_acs_state(struct pci_dev *dev)\n+{\n+\tstruct pci_cap_saved_state *save_state;\n+\n+\tif (!dev-\u003eacs_cap)\n+\t\treturn;\n+\n+\tsave_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);\n+\tif (!save_state)\n+\t\treturn;\n+\n+\tpci_read_config_word(dev, dev-\u003eacs_cap + PCI_ACS_CTRL,\n+\t\t\t     (u16 *)\u0026save_state-\u003ecap.data[0]);\n+}\n+\n+/**\n+ * pci_restore_acs_state - restore the ACS Control register\n+ * @dev: the PCI device\n+ */\n+static void pci_restore_acs_state(struct pci_dev *dev)\n+{\n+\tstruct pci_cap_saved_state *save_state = NULL;\n+\n+\tif (dev-\u003eacs_cap \u0026\u0026 !pci_need_dev_specific_enable_acs(dev))\n+\t\tsave_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);\n+\n+\t/*\n+\t * Devices that rely on device-specific quirks to enable an ACS\n+\t * equivalent keep that configuration outside of the ACS Control\n+\t * register, so there is nothing useful to restore for them. Configure\n+\t * ACS from scratch instead, which also covers devices that have no\n+\t * saved ACS state at all.\n+\t */\n+\tif (!save_state) {\n+\t\tpci_enable_acs(dev);\n+\t\treturn;\n+\t}\n+\n+\tpci_write_config_word(dev, dev-\u003eacs_cap + PCI_ACS_CTRL,\n+\t\t\t      *(u16 *)\u0026save_state-\u003ecap.data[0]);\n+}\n+\n /**\n  * pci_enable_acs - enable ACS if hardware support it\n  * @dev: the PCI device\n@@ -1031,6 +1082,9 @@ void pci_enable_acs(struct pci_dev *dev)\n \tbool enable_acs = false;\n \tint pos;\n \n+\tif (!pci_liveupdate_adopt_acs(dev))\n+\t\treturn;\n+\n \t/* If an iommu is present we start with kernel default caps */\n \tif (pci_acs_enable) {\n \t\tif (pci_dev_specific_enable_acs(dev))\n@@ -1057,6 +1111,15 @@ void pci_enable_acs(struct pci_dev *dev)\n \t__pci_config_acs(dev, \u0026caps, config_acs_param, 0, 0);\n \n \tpci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);\n+\n+\t/*\n+\t * pci_enable_acs() runs when a driver binds to the device, i.e. after\n+\t * pci_bus_add_device() has already saved the device's state. Refresh\n+\t * the saved ACS Control register so that a subsequent reset restores\n+\t * the controls programmed here rather than the ones left behind by\n+\t * firmware.\n+\t */\n+\tpci_save_acs_state(dev);\n }\n \n /**\n@@ -1800,6 +1863,7 @@ int pci_save_state(struct pci_dev *dev)\n \tpci_save_aer_state(dev);\n \tpci_save_ptm_state(dev);\n \tpci_save_tph_state(dev);\n+\tpci_save_acs_state(dev);\n \treturn pci_save_vc_state(dev);\n }\n EXPORT_SYMBOL(pci_save_state);\n@@ -1877,7 +1941,7 @@ void pci_restore_state(struct pci_dev *dev)\n \tpci_restore_msi_state(dev);\n \n \t/* Restore ACS and IOV configuration state */\n-\tpci_enable_acs(dev);\n+\tpci_restore_acs_state(dev);\n \tpci_restore_iov_state(dev);\n \n \tdev-\u003estate_saved = false;\n@@ -3532,6 +3596,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)\n \tif (error)\n \t\tpci_err(dev, \"unable to allocate suspend buffer for LTR\\n\");\n \n+\terror = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,\n+\t\t\t\t\t    sizeof(u16));\n+\tif (error)\n+\t\tpci_err(dev, \"unable to allocate suspend buffer for ACS\\n\");\n+\n \tpci_allocate_vc_save_buffers(dev);\n }\n \n@@ -3556,7 +3625,7 @@ void pci_configure_ari(struct pci_dev *dev)\n \tu32 cap;\n \tstruct pci_dev *bridge;\n \n-\tif (pcie_ari_disabled || !pci_is_pcie(dev) || dev-\u003edevfn)\n+\tif (!pci_is_pcie(dev) || dev-\u003edevfn)\n \t\treturn;\n \n \tbridge = dev-\u003ebus-\u003eself;\n@@ -3567,6 +3636,12 @@ void pci_configure_ari(struct pci_dev *dev)\n \tif (!(cap \u0026 PCI_EXP_DEVCAP2_ARI))\n \t\treturn;\n \n+\tif (!pci_liveupdate_adopt_ari(bridge))\n+\t\treturn;\n+\n+\tif (pcie_ari_disabled)\n+\t\treturn;\n+\n \tif (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {\n \t\tpcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,\n \t\t\t\t\t PCI_EXP_DEVCTL2_ARI);\ndiff --git a/drivers/pci/pci.h b/drivers/pci/pci.h\nindex ba3c3fddddc23..037c1674f1648 100644\n--- a/drivers/pci/pci.h\n+++ b/drivers/pci/pci.h\n@@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev);\n void pci_enable_acs(struct pci_dev *dev);\n #ifdef CONFIG_PCI_QUIRKS\n int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);\n+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);\n int pci_dev_specific_enable_acs(struct pci_dev *dev);\n int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);\n void pci_disable_broken_acs_cap(struct pci_dev *pdev);\n@@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,\n {\n \treturn -ENOTTY;\n }\n+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)\n+{\n+\treturn false;\n+}\n static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)\n {\n \treturn -ENOTTY;\ndiff --git a/drivers/pci/probe.c b/drivers/pci/probe.c\nindex 27008e2ea5afc..debe0ad1ef68b 100644\n--- a/drivers/pci/probe.c\n+++ b/drivers/pci/probe.c\n@@ -24,6 +24,7 @@\n #include \u003clinux/pm_runtime.h\u003e\n #include \u003clinux/bitfield.h\u003e\n #include \u003ctrace/events/pci.h\u003e\n+#include \"liveupdate.h\"\n #include \"pci.h\"\n \n static struct resource busn_resource = {\n@@ -1396,6 +1397,8 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,\n \t\t\t\t  int max, unsigned int available_buses,\n \t\t\t\t  int pass)\n {\n+\tbool preserve_bus_numbers = !pcibios_assign_all_busses() ||\n+\t\t\t\t    pci_liveupdate_preserve_bus_numbers();\n \tstruct pci_bus *child;\n \tu32 buses;\n \tu16 bctl;\n@@ -1448,8 +1451,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,\n \t\tgoto out;\n \t}\n \n-\tif ((secondary || subordinate) \u0026\u0026\n-\t    !pcibios_assign_all_busses() \u0026\u0026 !broken) {\n+\tif ((secondary || subordinate) \u0026\u0026 preserve_bus_numbers \u0026\u0026 !broken) {\n \t\tunsigned int cmax, buses;\n \n \t\t/*\n@@ -1491,8 +1493,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,\n \t\t * do in the second pass.\n \t\t */\n \t\tif (!pass) {\n-\t\t\tif (pcibios_assign_all_busses() || broken)\n-\n+\t\t\tif (!preserve_bus_numbers || broken)\n \t\t\t\t/*\n \t\t\t\t * Temporarily disable forwarding of the\n \t\t\t\t * configuration cycles on all bridges in\n@@ -1506,6 +1507,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,\n \t\t\tgoto out;\n \t\t}\n \n+\t\tif (pci_liveupdate_refuse_bus_numbers(bus, dev))\n+\t\t\tgoto out;\n+\n \t\t/* Clear errors */\n \t\tpci_write_config_word(dev, PCI_STATUS, 0xffff);\n \n@@ -2064,6 +2068,8 @@ int pci_setup_device(struct pci_dev *dev)\n \tif (pci_early_dump)\n \t\tearly_dump_pci_device(dev);\n \n+\tpci_liveupdate_setup_device(dev);\n+\n \t/* Need to have dev-\u003eclass ready */\n \tdev-\u003ecfg_size = pci_cfg_space_size(dev);\n \n@@ -2187,6 +2193,7 @@ int pci_setup_device(struct pci_dev *dev)\n \tdefault:\t\t\t\t    /* unknown header */\n \t\tpci_err(dev, \"unknown header type %02x, ignoring device\\n\",\n \t\t\tdev-\u003ehdr_type);\n+\t\tpci_liveupdate_cleanup_device(dev);\n \t\tpci_release_of_node(dev);\n \t\treturn -EIO;\n \n@@ -2485,6 +2492,7 @@ static void pci_release_dev(struct device *dev)\n \n \tpci_dev = to_pci_dev(dev);\n \tpci_release_capabilities(pci_dev);\n+\tpci_liveupdate_cleanup_device(pci_dev);\n \tpci_release_of_node(pci_dev);\n \tpcibios_release_device(pci_dev);\n \tpci_bus_put(pci_dev-\u003ebus);\ndiff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c\nindex de9bbccda21fd..e500c202d2ec9 100644\n--- a/drivers/pci/quirks.c\n+++ b/drivers/pci/quirks.c\n@@ -5377,9 +5377,6 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)\n  */\n static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)\n {\n-\tif (!pci_quirk_intel_pch_acs_match(dev))\n-\t\treturn -ENOTTY;\n-\n \tif (pci_quirk_enable_intel_lpc_acs(dev)) {\n \t\tpci_warn(dev, \"Failed to enable Intel PCH ACS quirk\\n\");\n \t\treturn 0;\n@@ -5399,9 +5396,6 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)\n \tint pos;\n \tu32 cap, ctrl;\n \n-\tif (!pci_quirk_intel_spt_pch_acs_match(dev))\n-\t\treturn -ENOTTY;\n-\n \tpos = dev-\u003eacs_cap;\n \tif (!pos)\n \t\treturn -ENOTTY;\n@@ -5429,9 +5423,6 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)\n \tint pos;\n \tu32 cap, ctrl;\n \n-\tif (!pci_quirk_intel_spt_pch_acs_match(dev))\n-\t\treturn -ENOTTY;\n-\n \tpos = dev-\u003eacs_cap;\n \tif (!pos)\n \t\treturn -ENOTTY;\n@@ -5451,56 +5442,63 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)\n static const struct pci_dev_acs_ops {\n \tu16 vendor;\n \tu16 device;\n+\tbool (*match)(struct pci_dev *dev);\n \tint (*enable_acs)(struct pci_dev *dev);\n \tint (*disable_acs_redir)(struct pci_dev *dev);\n } pci_dev_acs_ops[] = {\n \t{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,\n+\t    .match = pci_quirk_intel_pch_acs_match,\n \t    .enable_acs = pci_quirk_enable_intel_pch_acs,\n \t},\n \t{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,\n+\t    .match = pci_quirk_intel_spt_pch_acs_match,\n \t    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,\n \t    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,\n \t},\n };\n \n-int pci_dev_specific_enable_acs(struct pci_dev *dev)\n+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)\n {\n \tconst struct pci_dev_acs_ops *p;\n-\tint i, ret;\n+\tint i;\n \n \tfor (i = 0; i \u003c ARRAY_SIZE(pci_dev_acs_ops); i++) {\n \t\tp = \u0026pci_dev_acs_ops[i];\n \t\tif ((p-\u003evendor == dev-\u003evendor ||\n \t\t     p-\u003evendor == (u16)PCI_ANY_ID) \u0026\u0026\n \t\t    (p-\u003edevice == dev-\u003edevice ||\n-\t\t     p-\u003edevice == (u16)PCI_ANY_ID) \u0026\u0026\n-\t\t    p-\u003eenable_acs) {\n-\t\t\tret = p-\u003eenable_acs(dev);\n-\t\t\tif (ret \u003e= 0)\n-\t\t\t\treturn ret;\n+\t\t     p-\u003edevice == (u16)PCI_ANY_ID)) {\n+\t\t\tif (!p-\u003ematch || p-\u003ematch(dev))\n+\t\t\t\treturn p;\n \t\t}\n \t}\n \n+\treturn NULL;\n+}\n+\n+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)\n+{\n+\tconst struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);\n+\n+\treturn p \u0026\u0026 p-\u003eenable_acs;\n+}\n+\n+int pci_dev_specific_enable_acs(struct pci_dev *dev)\n+{\n+\tconst struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);\n+\n+\tif (p \u0026\u0026 p-\u003eenable_acs)\n+\t\treturn p-\u003eenable_acs(dev);\n+\n \treturn -ENOTTY;\n }\n \n int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)\n {\n-\tconst struct pci_dev_acs_ops *p;\n-\tint i, ret;\n+\tconst struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);\n \n-\tfor (i = 0; i \u003c ARRAY_SIZE(pci_dev_acs_ops); i++) {\n-\t\tp = \u0026pci_dev_acs_ops[i];\n-\t\tif ((p-\u003evendor == dev-\u003evendor ||\n-\t\t     p-\u003evendor == (u16)PCI_ANY_ID) \u0026\u0026\n-\t\t    (p-\u003edevice == dev-\u003edevice ||\n-\t\t     p-\u003edevice == (u16)PCI_ANY_ID) \u0026\u0026\n-\t\t    p-\u003edisable_acs_redir) {\n-\t\t\tret = p-\u003edisable_acs_redir(dev);\n-\t\t\tif (ret \u003e= 0)\n-\t\t\t\treturn ret;\n-\t\t}\n-\t}\n+\tif (p \u0026\u0026 p-\u003edisable_acs_redir)\n+\t\treturn p-\u003edisable_acs_redir(dev);\n \n \treturn -ENOTTY;\n }\ndiff --git a/include/linux/kho/abi/pci.h b/include/linux/kho/abi/pci.h\nnew file mode 100644\nindex 0000000000000..9485ed73c3519\n--- /dev/null\n+++ b/include/linux/kho/abi/pci.h\n@@ -0,0 +1,66 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+\n+/*\n+ * Copyright (c) 2026, Google LLC.\n+ * David Matlack \u003cdmatlack@google.com\u003e\n+ */\n+\n+#ifndef _LINUX_KHO_ABI_PCI_H\n+#define _LINUX_KHO_ABI_PCI_H\n+\n+#include \u003clinux/bug.h\u003e\n+#include \u003clinux/compiler.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+/**\n+ * DOC: PCI File-Lifecycle Bound (FLB) Live Update ABI\n+ *\n+ * This header defines the ABI for preserving core PCI state across kexec using\n+ * Live Update File-Lifecycle Bound (FLB) data.\n+ *\n+ * This interface is a contract. Any modification to any of the serialization\n+ * structs defined here constitutes a breaking change. Such changes require\n+ * incrementing the version number in the PCI_LUO_FLB_VERSION number.\n+ */\n+\n+#define PCI_LUO_FLB_COMPATIBLE \"pci\"\n+#define PCI_LUO_FLB_VERSION 2\n+\n+/**\n+ * struct pci_dev_ser - Serialized state about a single PCI device.\n+ *\n+ * @domain: The device's PCI domain number (segment).\n+ * @bdf: The device's PCI bus, device, and function number.\n+ * @refcount: Reference count used by the PCI core to keep track of whether it\n+ *            is done using a device's struct pci_dev_ser. The value of the\n+ *            refcount is equal to the number of preserved devices at or below\n+ *            it in the PCI hierarchy when the struct pci_dev_ser is in use, and\n+ *            0 otherwise.\n+ */\n+struct pci_dev_ser {\n+\tu32 domain;\n+\tu16 bdf;\n+\tu16 refcount;\n+} __packed;\n+\n+/**\n+ * struct pci_ser - PCI Subsystem Live Update State\n+ *\n+ * This struct tracks state about all devices that are being preserved across\n+ * a Live Update for the next kernel. It contains only state owned by the PCI\n+ * core; the state a driver needs to resume its device is preserved separately\n+ * by that driver.\n+ *\n+ * @version: The version of the \"pci\" FLB struct. This field must never be\n+ *           deleted, moved, or resized, as the kernel depends on always being\n+ *           able to check the struct pci_ser version number.\n+ * @nr_devices: The number of devices that were preserved.\n+ * @devices: Physical address of the first KHO block containing pci_dev_ser.\n+ */\n+struct pci_ser {\n+\tu32 version;\n+\tu32 nr_devices;\n+\tu64 devices;\n+} __packed;\n+\n+#endif /* _LINUX_KHO_ABI_PCI_H */\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex d31a8d107b1ef..b35ac263c4517 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -43,6 +43,7 @@\n #include \u003cuapi/linux/pci.h\u003e\n \n #include \u003clinux/pci_ids.h\u003e\n+#include \u003clinux/pci_liveupdate.h\u003e\n \n #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY  | \\\n \t\t\t       PCI_STATUS_SIG_SYSTEM_ERROR | \\\n@@ -598,6 +599,9 @@ struct pci_dev {\n \tu8\t\ttph_mode;\t/* TPH mode */\n \tu8\t\ttph_req_type;\t/* TPH requester type */\n #endif\n+#ifdef CONFIG_PCI_LIVEUPDATE\n+\tstruct pci_liveupdate liveupdate;\n+#endif\n };\n \n static inline struct pci_dev *pci_physfn(struct pci_dev *dev)\n@@ -832,6 +836,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)\n \treturn dev-\u003ebus-\u003eself;\n }\n \n+#define for_each_pci_dev_in_path(dev) \\\n+\tfor (; dev; dev = pci_upstream_bridge(dev))\n+\n #ifdef CONFIG_PCI_MSI\n static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev)\n {\ndiff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h\nnew file mode 100644\nindex 0000000000000..bb79348769b72\n--- /dev/null\n+++ b/include/linux/pci_liveupdate.h\n@@ -0,0 +1,72 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * PCI Live Update support (Public/Driver API)\n+ *\n+ * Copyright (c) 2026, Google LLC.\n+ * David Matlack \u003cdmatlack@google.com\u003e\n+ */\n+#ifndef LINUX_PCI_LIVEUPDATE_H\n+#define LINUX_PCI_LIVEUPDATE_H\n+\n+#include \u003clinux/kho/abi/pci.h\u003e\n+#include \u003clinux/liveupdate.h\u003e\n+#include \u003clinux/spinlock_types.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+/**\n+ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev\n+ * @outgoing: State preserved for the next kernel.\n+ * @incoming: State preserved by the previous kernel.\n+ * @was_incoming: True if this struct pci_dev was incoming-preserved when it was\n+ *                set up, i.e. it was matched to state preserved by the previous\n+ *                kernel. Unlike @incoming, this is never cleared, so it stays\n+ *                true after the device finishes participating in Live Update.\n+ * @frozen: True if the outgoing preservation status of this device is frozen\n+ *          and thus cannot be changed.\n+ */\n+struct pci_liveupdate {\n+\tstruct pci_dev_ser *outgoing;\n+\tstruct pci_dev_ser *incoming;\n+\tbool was_incoming;\n+\tbool frozen;\n+};\n+\n+struct pci_dev;\n+\n+#ifdef CONFIG_PCI_LIVEUPDATE\n+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);\n+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);\n+int pci_liveupdate_preserve(struct pci_dev *dev);\n+void pci_liveupdate_unpreserve(struct pci_dev *dev);\n+void pci_liveupdate_finish(struct pci_dev *dev);\n+bool pci_liveupdate_is_incoming(struct pci_dev *dev);\n+#else\n+static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\n+{\n+}\n+\n+static inline int pci_liveupdate_preserve(struct pci_dev *dev)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static inline void pci_liveupdate_unpreserve(struct pci_dev *dev)\n+{\n+}\n+\n+static inline void pci_liveupdate_finish(struct pci_dev *dev)\n+{\n+}\n+\n+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)\n+{\n+\treturn false;\n+}\n+#endif\n+\n+#endif /* LINUX_PCI_LIVEUPDATE_H */\n"
}

2/1 2026/09/21 01:11 agent patch-evaluator
3m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "PCI",
    "PCI_QUIRKS",
    "KEXEC_HANDOVER",
    "LIVEUPDATE",
    "PCI_LIVEUPDATE"
  ],
  "FocusSymbols": [
    "pci_save_acs_state",
    "pci_restore_acs_state",
    "pci_enable_acs",
    "pci_need_dev_specific_enable_acs",
    "pci_dev_specific_enable_acs",
    "pci_dev_specific_disable_acs_redir",
    "pci_configure_ari",
    "pci_allocate_cap_save_buffers",
    "pci_liveupdate_setup_device",
    "pci_liveupdate_cleanup_device",
    "pci_liveupdate_preserve_bus_numbers",
    "pci_liveupdate_refuse_bus_numbers",
    "pci_liveupdate_adopt_acs",
    "pci_liveupdate_adopt_ari"
  ],
  "Reasoning": "The patch series adds core PCI Live Update support and modifies core PCI subsystem logic. Beyond introducing drivers/pci/liveupdate.c, it modifies reachable PCI core paths in drivers/pci/pci.c, drivers/pci/quirks.c, and drivers/pci/probe.c: it adds ACS configuration saving and restoring (pci_save_acs_state, pci_restore_acs_state) called during pci_save_state/pci_restore_state (reachable via device reset through sysfs or VFIO), refactors ACS quirk lookup and dispatch (pci_need_dev_specific_enable_acs, pci_dev_specific_enable_acs), alters bridge scanning logic in pci_scan_bridge_extend, and adds liveupdate device setup/cleanup hooks with runtime warnings. Because reachable executable PCI subsystem logic and state management are altered, this warrants fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit d16d7de37068d14644f2734e19ff701b07419cd7
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 21 01:11:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst
index 5d720d2a415e0..23fb737ac969e 100644
--- a/Documentation/PCI/index.rst
+++ b/Documentation/PCI/index.rst
@@ -20,3 +20,4 @@ PCI Bus Subsystem
    controller/index
    boot-interrupts
    tph
+   liveupdate
diff --git a/Documentation/PCI/liveupdate.rst b/Documentation/PCI/liveupdate.rst
new file mode 100644
index 0000000000000..96b1d7f5df3a0
--- /dev/null
+++ b/Documentation/PCI/liveupdate.rst
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+PCI Support for Live Update
+===========================
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :doc: PCI Live Update
+
+Driver API
+==========
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :export:
+
+Internal API
+============
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :internal:
+
+Live Update ABI
+===============
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :internal:
+
+See Also
+========
+
+ * :doc:`/core-api/liveupdate`
+ * :doc:`/core-api/kho/index`
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd24..7eabf6320abfa 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -5170,7 +5170,12 @@ Kernel parameters
 				explicitly which ones they are.
 		assign-busses	[X86] Always assign all PCI bus
 				numbers ourselves, overriding
-				whatever the firmware may have done.
+				whatever the firmware may have done. Ignored
+				if any device was preserved across a Live
+				Update, where the kernel must preserve the
+				PCI topology (including bus numbers) to
+				avoid interrupting ongoing memory transactions
+				of preserved devices.
 		usepirqmask	[X86] Honor the possible IRQ mask stored
 				in the BIOS $PIR table. This is needed on
 				some systems with broken BIOSes, notably
diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api/liveupdate.rst
index 5a292d0f37064..2bce2644eba22 100644
--- a/Documentation/core-api/liveupdate.rst
+++ b/Documentation/core-api/liveupdate.rst
@@ -1,5 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0
 
+.. _luo:
+
 ========================
 Live Update Orchestrator
 ========================
@@ -18,6 +20,8 @@ LUO Preserving File Descriptors
 .. kernel-doc:: kernel/liveupdate/luo_file.c
    :doc: LUO File Descriptors
 
+.. _flb:
+
 LUO File Lifecycle Bound Global Data
 ====================================
 .. kernel-doc:: kernel/liveupdate/luo_flb.c
@@ -70,3 +74,4 @@ See Also
 
 - :doc:`Live Update uAPI </userspace-api/liveupdate>`
 - :doc:`/core-api/kho/index`
+- :doc:`PCI </PCI/liveupdate>`
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c9..5d1b58271989d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21046,6 +21046,21 @@ L:	linux-pci@vger.kernel.org
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
+PCI LIVE UPDATE
+M:	David Matlack <dmatlack@google.com>
+R:	Pasha Tatashin <pasha.tatashin@soleen.com>
+R:	Mike Rapoport <rppt@kernel.org>
+R:	Pratyush Yadav <pratyush@kernel.org>
+L:	kexec@lists.infradead.org
+L:	linux-pci@vger.kernel.org
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F:	Documentation/PCI/liveupdate.rst
+F:	drivers/pci/liveupdate.c
+F:	drivers/pci/liveupdate.h
+F:	include/linux/kho/abi/pci.h
+F:	include/linux/pci_liveupdate.h
+
 PCI MSI DRIVER FOR ALTERA MSI IP
 L:	linux-pci@vger.kernel.org
 S:	Orphan
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 0c7408509ba24..16fbd4212e0f5 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS
 	  Reserves space in the kernel to maintain resource locking for
 	  multiple GPUS.  The overhead for each GPU is very small.
 
+config PCI_LIVEUPDATE
+	bool "PCI Live Update Support"
+	depends on PCI && LIVEUPDATE && 64BIT && !CARDBUS
+	help
+	  Enable PCI core support for preserving PCI devices across Live
+	  Update. This, in combination with support in a device's driver,
+	  enables PCI devices to run and perform memory transactions
+	  uninterrupted during a kexec for Live Update.
+
+	  This option should only be enabled by users who plan to use Live
+	  Update for kernel upgrades and require preserving PCI devices during
+	  those upgrades.
+
+	  If unsure, say N.
+
 source "drivers/pci/hotplug/Kconfig"
 source "drivers/pci/controller/Kconfig"
 source "drivers/pci/endpoint/Kconfig"
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a5182..e8d003cb67576 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PROC_FS)		+= proc.o
 obj-$(CONFIG_SYSFS)		+= pci-sysfs.o slot.o
 obj-$(CONFIG_ACPI)		+= pci-acpi.o
 obj-$(CONFIG_GENERIC_PCI_IOMAP) += iomap.o
+obj-$(CONFIG_PCI_LIVEUPDATE)	+= liveupdate.o
 endif
 
 obj-$(CONFIG_OF)		+= of.o
diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c
new file mode 100644
index 0000000000000..51695782ed427
--- /dev/null
+++ b/drivers/pci/liveupdate.c
@@ -0,0 +1,1015 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+
+/**
+ * DOC: PCI Live Update
+ *
+ * The PCI subsystem participates in the Live Update process to enable drivers
+ * to preserve their PCI devices across kexec.
+ *
+ * Preserving a device requires preserving two independent sets of state: the
+ * driver's own state, which the driver preserves with no involvement from the
+ * PCI core, and the PCI core's state about the device, which the next kernel
+ * needs so that enumeration does not disturb a device that is still running.
+ * This file implements the latter.
+ *
+ * :ref:`FLB <flb>` Data
+ * =====================
+ *
+ * Userspace decides which devices are preserved, using :ref:`LUO <luo>` file
+ * preservation: a driver exposes a file that represents a single PCI device,
+ * and userspace preserves the device with
+ * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preservation
+ * to a file gives it proper lifecycle management, e.g. the preservation is
+ * undone if userspace cancels it or goes away. How a driver exposes that file
+ * is up to the driver and invisible to the PCI core (vfio-pci variant drivers,
+ * the first intended use-case, use their per-device cdev).
+ *
+ * LUO only knows that a file was preserved; it does not know that the file
+ * represents a PCI device. Drivers therefore register their
+ * struct liveupdate_file_handler with the PCI core:
+ *
+ *  * ``pci_liveupdate_register_flb(driver_file_handler)``
+ *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``
+ *
+ * LUO then refcounts the PCI core's FLB against the files preserved by that
+ * handler, and that refcount drives the lifetime of struct pci_ser:
+ * pci_flb_preserve() allocates and preserves it when the first file is
+ * preserved, and pci_flb_unpreserve() frees it when the last file is
+ * unpreserved. In the next kernel, pci_flb_retrieve() hands the PCI core the
+ * struct pci_ser built by the previous kernel, whenever the PCI core asks for
+ * it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI
+ * core is done with it.
+ *
+ * State handed over by the previous kernel is trusted. The PCI core validates
+ * it only far enough to detect an incompatible or corrupt hand over, and makes
+ * no attempt to defend against deliberate modification, since a previous kernel
+ * able to corrupt preserved state is able to corrupt arbitrary memory anyway.
+ *
+ * Call Flow
+ * ---------
+ *
+ * ::
+ *
+ *   # Driver initialization
+ *   pci_liveupdate_register_flb(fh)
+ *
+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)
+ *   luo_preserve_file()
+ *     luo_flb_file_preserve()
+ *       luo_flb_file_preserve_one()      # first preserved file only
+ *         pci_flb_preserve()             # alloc and preserve struct pci_ser
+ *     fh->ops->preserve()                # driver callback
+ *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser
+ *
+ *   # Userspace: preservation cancelled or session torn down
+ *   luo_file_unpreserve_files()
+ *     luo_flb_file_unpreserve()
+ *       liveupdate_flb_put_outgoing()    # last unpreserved file only
+ *         pci_flb_unpreserve()           # free struct pci_ser
+ *
+ *   # ---------------- kexec ----------------
+ *
+ *   # New kernel: PCI enumeration
+ *   pci_setup_device()
+ *     pci_liveupdate_setup_device()
+ *       liveupdate_flb_get_incoming()
+ *         luo_flb_retrieve_one()         # first request only
+ *           pci_flb_retrieve()           # previous kernel's struct pci_ser
+ *
+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)
+ *   luo_file_finish_one()
+ *     fh->ops->finish()                  # driver callback
+ *       pci_liveupdate_finish(dev)       # release this device's pci_dev_ser
+ *     luo_flb_file_finish()
+ *       liveupdate_flb_put_incoming()    # last incoming file only
+ *         pci_flb_finish()               # free struct pci_ser
+ *
+ * Device Tracking
+ * ===============
+ *
+ * Drivers must notify the PCI core when specific devices are preserved or
+ * unpreserved with the following APIs:
+ *
+ *  * ``pci_liveupdate_preserve(pci_dev)``
+ *  * ``pci_liveupdate_unpreserve(pci_dev)``
+ *
+ * This allows the PCI core to keep its FLB data (struct pci_ser) up to date
+ * with the list of **outgoing** preserved devices for the next kernel.
+ *
+ * After kexec, whenever a device is enumerated, the PCI core will check if it
+ * is an **incoming** preserved device (i.e. preserved by the previous kernel)
+ * by checking the incoming FLB data (struct pci_ser).
+ *
+ * Drivers must notify the PCI core when an **incoming** device is done
+ * participating in the incoming Live Update with the following API:
+ *
+ *  * ``pci_liveupdate_finish(pci_dev)``
+ *
+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and
+ * ``pci_liveupdate_preserve()``, i.e., a PCI device can be **outgoing**
+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)
+ * at the same time.
+ *
+ * Restrictions
+ * ============
+ *
+ * The PCI core enforces the following restrictions on which devices can be
+ * preserved. These may be relaxed in the future:
+ *
+ *  * The device cannot be a Virtual Function (VF).
+ *
+ *  * The device cannot require device-specific quirks to enable Access
+ *    Control Services (ACS).
+ *
+ * Driver Binding
+ * ==============
+ *
+ * In the outgoing kernel, the driver must ensure that it does not release a
+ * device between pci_liveupdate_preserve() and pci_liveupdate_unpreserve().
+ *
+ * In the incoming kernel, the driver must ensure that it does not release a
+ * preserved device between probe() and pci_liveupdate_finish().
+ *
+ * It is the user's responsibility to ensure that incoming preserved devices are
+ * bound to the correct driver. The PCI core does not protect against a device
+ * getting preserved by driver A in the outgoing kernel and then getting bound
+ * to driver B in the incoming kernel.
+ *
+ * PCI-to-PCI Bridges
+ * ==================
+ *
+ * Any PCI-to-PCI bridges upstream of a preserved device are automatically
+ * preserved when the device is preserved. The PCI core keeps track of the
+ * number of downstream devices that are preserved under a bridge so that the
+ * bridge is only unpreserved once all downstream devices are unpreserved.
+ *
+ * This enables the PCI core and any drivers bound to the bridge to participate
+ * in the Live Update so that preserved endpoints can continue issuing memory
+ * transactions during the Live Update.
+ *
+ * BDF Stability
+ * =============
+ *
+ * The PCI core guarantees that preserved devices can be identified by the same
+ * bus, device, and function numbers for as long as they are preserved
+ * (including across kexec). To accomplish this, the PCI core keeps the
+ * secondary and subordinate bus numbers that the previous kernel programmed
+ * into bridges, if the previous kernel preserved any device. This is true even
+ * on architectures that always assign new bus numbers during scanning. The
+ * kernel assumes the previous kernel established a sane bus topology across
+ * kexec.
+ *
+ * Bridges that do not have bus numbers are assigned new ones as usual, so
+ * hot-adding a bridge keeps working, both during and after a Live Update. The
+ * two-pass bridge scan ensures such bridges are only assigned bus numbers above
+ * those already claimed by preserved bridges.
+ *
+ * If a preserved bridge comes up without a valid bus number configuration, e.g.
+ * because it was reset during kexec, the PCI core refuses to assign it new bus
+ * numbers and does not enumerate anything below it. Assigning new bus numbers
+ * would silently change the BDF of every preserved device in its hierarchy. The
+ * PCI core also stops assigning bus numbers to the other bridges on the same
+ * bus, since the bus numbers of the failed bridge can no longer be read from
+ * hardware and handing them to another bridge would let an unrelated device
+ * inherit the BDF of a preserved device.
+ *
+ * Handling Preserved Devices
+ * ==========================
+ *
+ * The PCI core treats preserved devices differently than non-preserved devices.
+ * This section enumerates those differences.
+ *
+ *  * The PCI core adopts all ACS controls enabled on incoming preserved devices
+ *    rather than assigning new ones. This ensures that TLPs are routed the same
+ *    way after Live Update and ensures that IOMMU groups do not change. Note
+ *    that a device will use its adopted ACS controls for the lifetime of its
+ *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
+ *
+ *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
+ *    preserved devices to ensure that all preserved devices on the bridge's
+ *    secondary bus are addressable after the Live Update.
+ *
+ *  * The PCI core does not disable bus mastering on outgoing preserved devices
+ *    during kexec. This allows preserved devices to issue memory transactions
+ *    throughout the Live Update.
+ */
+
+#define pr_fmt(fmt) "PCI: liveupdate: " fmt
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/pci.h>
+#include <linux/kho_block.h>
+#include <linux/liveupdate.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "liveupdate.h"
+#include "pci.h"
+
+/**
+ * struct pci_liveupdate_global - Global state for PCI Live Update support
+ * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing
+ *         FLBs, and the references to them in struct pci_dev.
+ * @had_incoming: True if the previous kernel preserved at least one PCI device.
+ *                Set when the incoming FLB is retrieved and never cleared, so
+ *                it stays true after Live Update finishes.
+ */
+struct pci_liveupdate_global {
+	struct rw_semaphore rwsem;
+	bool had_incoming;
+};
+
+static struct pci_liveupdate_global pci_liveupdate = {
+	.rwsem = __RWSEM_INITIALIZER(pci_liveupdate.rwsem),
+};
+
+/**
+ * struct pci_flb_outgoing - Outgoing PCI FLB object
+ * @ser: Pointer to the preserved struct pci_ser.
+ * @block_set: The KHO block set holding the outgoing devices.
+ *
+ * This structure holds the runtime state for the outgoing PCI Live Update
+ * state. It wraps the serialized pci_ser and the block_set used to manage
+ * the serialized entries.
+ */
+struct pci_flb_outgoing {
+	struct pci_ser *ser;
+	struct kho_block_set block_set;
+};
+
+/**
+ * struct pci_flb_incoming - Incoming PCI FLB object
+ * @ser: The incoming struct pci_ser from the previous kernel.
+ * @xa: Xarray used to quickly lookup devices in @ser.
+ * @block_set: The KHO block set holding the incoming devices.
+ *
+ * This structure holds the runtime state for the incoming PCI Live Update
+ * state. It wraps the serialized pci_ser, the block_set used to restore
+ * the serialized entries, and an xarray for fast lookups.
+ */
+struct pci_flb_incoming {
+	struct pci_ser *ser;
+	struct xarray xa;
+	struct kho_block_set block_set;
+};
+
+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)
+{
+	return (unsigned long)domain << 16 | bdf;
+}
+
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing __free(kfree) = NULL;
+	struct pci_ser *ser;
+
+	outgoing = kzalloc_obj(*outgoing);
+	if (!outgoing)
+		return -ENOMEM;
+
+	ser = kho_alloc_preserve(sizeof(*ser));
+	if (IS_ERR(ser))
+		return PTR_ERR(ser);
+
+	ser->version = PCI_LUO_FLB_VERSION;
+	ser->nr_devices = 0;
+	ser->devices = 0;
+
+	outgoing->ser = ser;
+	kho_block_set_init(&outgoing->block_set, sizeof(struct pci_dev_ser));
+
+	args->obj = no_free_ptr(outgoing);
+	args->data = virt_to_phys(ser);
+	pr_debug("Preserved struct pci_ser (0x%llx)\n", args->data);
+	return 0;
+}
+
+static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing = args->obj;
+
+	pr_debug("Unpreserving struct pci_ser (0x%llx)\n", args->data);
+
+	WARN_ON(outgoing->ser->nr_devices);
+	kho_block_set_destroy(&outgoing->block_set);
+	kho_unpreserve_free(outgoing->ser);
+	kfree(outgoing);
+}
+
+/*
+ * Any failure here is fatal. The previous kernel handed over devices that are
+ * still performing DMA, and this kernel cannot identify them without this
+ * state. Continuing would let the PCI core reassign bus numbers and rebind
+ * drivers underneath live devices, so fail loudly instead of unwinding.
+ */
+static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	int ret;
+
+	pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
+
+	if (ser->version != PCI_LUO_FLB_VERSION)
+		panic("Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\n",
+		      ser->version, PCI_LUO_FLB_VERSION);
+
+	incoming = kzalloc_obj(*incoming);
+	if (!incoming)
+		panic("Failed to allocate struct pci_flb_incoming\n");
+
+	incoming->ser = ser;
+	xa_init(&incoming->xa);
+
+	kho_block_set_init(&incoming->block_set, sizeof(struct pci_dev_ser));
+	ret = kho_block_set_restore(&incoming->block_set, ser->devices);
+	if (ret)
+		panic("Failed to restore devices KHO block set (%d)\n", ret);
+
+	kho_block_set_it_init(&it, &incoming->block_set);
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		unsigned long key;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			panic("Failed to insert PCI device %04x:%02x:%02x.%d into xarray (%d)\n",
+			      dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf),
+			      PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf),
+			      ret);
+	}
+
+	/*
+	 * Remember that the previous kernel preserved devices for the lifetime
+	 * of this kernel, even after Live Update finishes and the incoming FLB
+	 * is freed. See pci_liveupdate_preserve_bus_numbers().
+	 */
+	if (!xa_empty(&incoming->xa))
+		pci_liveupdate.had_incoming = true;
+
+	args->obj = incoming;
+	return 0;
+}
+
+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)
+{
+	struct pci_dev_ser *dev_ser;
+	unsigned long index;
+	u32 nr_devices;
+
+	/*
+	 * nr_devices is only decremented by pci_liveupdate_finish_device().
+	 * This runs once the last reference to the incoming FLB is dropped, so
+	 * there are no finishers left in flight.
+	 */
+	nr_devices = incoming->ser->nr_devices;
+	if (nr_devices == 0)
+		return;
+
+	/*
+	 * Report the unfinished devices from the incoming FLB rather than by
+	 * walking struct pci_dev, so that devices that never showed up after
+	 * kexec, or that were destroyed before they finished, are identified
+	 * as well.
+	 */
+	xa_for_each(&incoming->xa, index, dev_ser) {
+		if (!dev_ser->refcount)
+			continue;
+
+		pr_emerg("%04x:%02x:%02x.%d was never finished!\n",
+			 dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf),
+			 PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf));
+	}
+
+	/*
+	 * This should only happen if a driver violated the contract to call
+	 * pci_liveupdate_finish() (something is extremely broken).
+	 */
+	panic("%u preserved device(s) were never finished!\n", nr_devices);
+}
+
+static void pci_flb_finish(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_incoming *incoming = args->obj;
+
+	pr_debug("Finished struct pci_ser (0x%llx)\n", args->data);
+	pci_check_all_devices_finished(incoming);
+
+	xa_destroy(&incoming->xa);
+	kho_block_set_destroy(&incoming->block_set);
+	kho_restore_free(incoming->ser);
+	kfree(incoming);
+}
+
+static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {
+	.preserve = pci_flb_preserve,
+	.unpreserve = pci_flb_unpreserve,
+	.retrieve = pci_flb_retrieve,
+	.finish = pci_flb_finish,
+	.owner = THIS_MODULE,
+};
+
+static struct liveupdate_flb pci_liveupdate_flb = {
+	.ops = &pci_liveupdate_flb_ops,
+	.compatible = PCI_LUO_FLB_COMPATIBLE,
+};
+
+static void pci_liveupdate_flb_put_outgoing(void)
+{
+	liveupdate_flb_put_outgoing(&pci_liveupdate_flb);
+}
+
+static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&outgoing);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!outgoing)
+		return ERR_PTR(-ENOENT);
+
+	return outgoing;
+}
+
+static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)
+{
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	u64 count = 0;
+	int err;
+
+	kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* Try to find an existing, previously unpreserved, entry. */
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		if (!dev_ser->refcount)
+			return dev_ser;
+
+		count++;
+	}
+
+	/* Otherwise grow the block set and reserve a new entry. */
+	err = kho_block_set_grow(&outgoing->block_set, count + 1);
+	if (err)
+		return ERR_PTR(err);
+
+	if (!count)
+		kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* This should always succeed since kho_block_set_grow() succeeded. */
+	dev_ser = kho_block_set_it_reserve_entry(&it);
+	if (WARN_ON_ONCE(!dev_ser))
+		return ERR_PTR(-ENOSPC);
+
+	return dev_ser;
+}
+
+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					    struct pci_dev *dev)
+{
+	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
+
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot unpreserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
+	if (!dev_ser) {
+		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
+		return -EINVAL;
+	}
+
+	if (!dev_ser->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	if (--dev_ser->refcount)
+		return 0;
+
+	pci_info(dev, "Device will no longer be preserved across next Live Update\n");
+	outgoing->ser->nr_devices--;
+	memset(dev_ser, 0, sizeof(*dev_ser));
+	dev->liveupdate.outgoing = NULL;
+	return 0;
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoing,
+					   struct pci_dev *dev,
+					   struct pci_dev *end)
+{
+	for_each_pci_dev_in_path(dev) {
+		if (dev == end)
+			break;
+
+		if (pci_liveupdate_unpreserve_device(outgoing, dev))
+			return;
+	}
+}
+
+static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
+					  struct pci_dev *dev)
+{
+	if (dev->is_virtfn) {
+		pci_warn(dev, "Cannot preserve Virtual Functions\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Do not preserve devices that rely on device-specific ACS equivalents
+	 * (for now) since that would complicate keeping ACS constant across
+	 * Live Update.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Endpoint devices should not be preserved more than once.
+	 * Bridges are preserved once for every downstream device that
+	 * is preserved.
+	 */
+	if (dev->liveupdate.outgoing && !dev->subordinate) {
+		pci_warn(dev, "Device is already preserved\n");
+		return -EBUSY;
+	}
+
+	if (dev->liveupdate.outgoing && !dev->liveupdate.outgoing->refcount) {
+		pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->liveupdate.outgoing) {
+		struct pci_dev_ser *dev_ser;
+
+		dev_ser = pci_flb_alloc_dev_ser(outgoing);
+		if (IS_ERR(dev_ser))
+			return PTR_ERR(dev_ser);
+
+		pci_info(dev, "Device will be preserved across next Live Update\n");
+		outgoing->ser->nr_devices++;
+		outgoing->ser->devices = kho_block_set_head_pa(&outgoing->block_set);
+
+		dev_ser->domain = pci_domain_nr(dev->bus);
+		dev_ser->bdf = pci_dev_id(dev);
+		dev->liveupdate.outgoing = dev_ser;
+	}
+
+	dev->liveupdate.outgoing->refcount++;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
+ * @dev: The PCI device to preserve.
+ *
+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be
+ * preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler
+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.
+ *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
+ * Returns: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	struct pci_dev *start = dev;
+	int ret = -ENODEV;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing))
+		return PTR_ERR(outgoing);
+
+	for_each_pci_dev_in_path(dev) {
+		ret = pci_liveupdate_preserve_device(outgoing, dev);
+		if (ret) {
+			pci_liveupdate_unpreserve_path(outgoing, start, dev);
+			break;
+		}
+	}
+
+	pci_liveupdate_flb_put_outgoing();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
+
+/**
+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device
+ * @dev: The PCI device to unpreserve.
+ *
+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no
+ * longer be preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set
+ * up.
+ *
+ * pci_liveupdate_unpreserve() automatically unpreserves all bridges upstream of
+ * @dev.
+ */
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_path(outgoing, dev, /*end=*/NULL);
+	pci_liveupdate_flb_put_outgoing();
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
+
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+	struct pci_flb_incoming *incoming = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
+
+	/* Live Update is not enabled. */
+	if (ret == -EOPNOTSUPP)
+		return NULL;
+
+	/* Live Update is enabled, but there is no incoming FLB data. */
+	if (ret == -ENODATA)
+		return NULL;
+
+	/*
+	 * Live Update is enabled and there is incoming FLB data, but none of it
+	 * matches pci_liveupdate_flb.compatible.
+	 */
+	if (ret == -ENOENT)
+		return NULL;
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but retrieve failed (pci_flb_retrieve() returned an error or LUO
+	 * failed to acquire a reference to pci_liveupdate_flb_ops.owner).
+	 */
+	if (ret)
+		panic("Failed to retrieve incoming FLB data (%d)\n", ret);
+
+	return incoming;
+}
+
+static void pci_liveupdate_flb_put_incoming(void)
+{
+	liveupdate_flb_put_incoming(&pci_liveupdate_flb);
+}
+
+/**
+ * pci_liveupdate_preserve_bus_numbers() - Determine if the PCI core should
+ *                                         preserve bus numbers when scanning
+ *                                         bridges.
+ *
+ * This function is called by the PCI core when it is scanning a bridge. It
+ * determines whether the PCI core should preserve the secondary and subordinate
+ * bus numbers that the previous kernel programmed into that bridge, rather than
+ * assigning new ones. This is necessary to keep RequesterIDs constant for
+ * preserved devices issuing memory transactions.
+ *
+ * Bus numbers are preserved everywhere, and for the lifetime of the kernel, if
+ * the previous kernel preserved any device. Bus numbers have to be preserved
+ * above a preserved device anyway, since an upstream bridge cannot expand its
+ * window. Applying the same policy everywhere matches the scope of
+ * pcibios_assign_all_busses(), and gives an answer that cannot change part way
+ * through the two passes of a bridge scan.
+ *
+ * The incoming FLB is retrieved while setting up the first device, which always
+ * happens before any bridge is scanned, so this returns the same answer for the
+ * entire enumeration.
+ *
+ * Note that this does not prevent the PCI core from assigning bus numbers to
+ * bridges that do not have any, e.g. bridges that are hot-added after the
+ * Live Update. See pci_liveupdate_refuse_bus_numbers() for the one case where
+ * the PCI core must refuse to do so.
+ *
+ * Return: True if bus numbers should be preserved, false otherwise.
+ */
+bool pci_liveupdate_preserve_bus_numbers(void)
+{
+	return pci_liveupdate.had_incoming;
+}
+
+/**
+ * pci_liveupdate_refuse_bus_numbers() - Determine if the PCI core must refuse
+ *                                       to assign bus numbers to the provided
+ *                                       bridge.
+ * @bus: The PCI bus the bus numbers would be assigned from.
+ * @dev: The PCI bridge device the bus numbers would be assigned to.
+ *
+ * This function is called by the PCI core before it assigns bus numbers to a
+ * bridge that does not have any.
+ *
+ * A bridge that was preserved by the previous kernel but came up without a
+ * valid bus number configuration, e.g. because it was reset during kexec, is
+ * left alone by the PCI core and therefore has no child bus once the first pass
+ * of the bridge scan is done.
+ *
+ * The PCI core must not assign bus numbers from @bus while such a bridge is on
+ * it, including to the failed bridge itself. Assigning new bus numbers to the
+ * failed bridge would silently change the BDF of every preserved device in its
+ * hierarchy. Its bus numbers cannot be read from hardware anymore either, so
+ * they cannot be excluded from assignment, and handing them to another bridge
+ * would let an unrelated device inherit the BDF of a preserved device.
+ *
+ * Return: True if @dev must not be assigned bus numbers, false otherwise.
+ */
+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *bridge;
+
+	for_each_pci_bridge(bridge, bus) {
+		if (!bridge->liveupdate.was_incoming || bridge->subordinate)
+			continue;
+
+		pci_err(dev, "Not assigning bus numbers, preserved bridge %s lost its bus number configuration\n",
+			pci_name(bridge));
+		return true;
+	}
+
+	return false;
+}
+
+void pci_liveupdate_setup_device(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	unsigned long key;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming)
+		return;
+
+	key = pci_ser_xa_key(pci_domain_nr(dev->bus), pci_dev_id(dev));
+	dev_ser = xa_load(&incoming->xa, key);
+
+	/*
+	 * This device was not preserved across Live Update, or it was preserved
+	 * but has already been probed and gone through pci_liveupdate_finish(),
+	 * e.g. due to removing and re-adding the device. Either way, it's not
+	 * treated as incoming-preserved.
+	 */
+	if (!dev_ser || !dev_ser->refcount) {
+		pci_liveupdate_flb_put_incoming();
+		return;
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
+	dev->liveupdate.was_incoming = true;
+
+	pci_liveupdate_flb_put_incoming();
+}
+
+void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+	/*
+	 * It should be safe to READ_ONCE() outside of the rwsem during cleanup
+	 * since there should no longer be any references to @dev on the system.
+	 *
+	 * This should never happen in practice. Drivers should block removal
+	 * while a device is preserved.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing))
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+
+	if (READ_ONCE(dev->liveupdate.incoming))
+		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
+}
+
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = true;
+}
+
+static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	if (!dev->liveupdate.incoming) {
+		pci_warn(dev, "Cannot finish preserving an unpreserved device\n");
+		return -EINVAL;
+	}
+
+	if (!dev->liveupdate.incoming->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Decrement the refcount so this device does not get treated as an
+	 * incoming device again, e.g. in case pci_liveupdate_setup_device()
+	 * gets called again because the device is hot-plugged.
+	 */
+	if (--dev->liveupdate.incoming->refcount)
+		return 0;
+
+	pci_info(dev, "Device is finished participating in Live Update\n");
+	dev->liveupdate.incoming = NULL;
+	ser->nr_devices--;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_finish() - Finish the preservation of a PCI device
+ * @dev: The PCI device
+ *
+ * pci_liveupdate_finish() notifies the PCI core that a PCI device that was
+ * preserved across the previous Live Update has finished participating in Live
+ * Update. Drivers must call pci_liveupdate_finish() from their struct
+ * liveupdate_file_handler finish() callback to ensure the incoming struct
+ * pci_ser is allocated.
+ *
+ * pci_liveupdate_finish() automatically finishes all bridges upstream of @dev.
+ */
+void pci_liveupdate_finish(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming) {
+		pci_warn(dev, "Cannot finish preserving device without incoming FLB\n");
+		return;
+	}
+
+	for_each_pci_dev_in_path(dev) {
+		if (pci_liveupdate_finish_device(incoming->ser, dev))
+			break;
+	}
+
+	pci_liveupdate_flb_put_incoming();
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
+
+/**
+ * pci_liveupdate_adopt_acs() - Adopt ACS controls
+ * @dev: The PCI device to adopt ACS controls for
+ *
+ * For devices preserved across a Live Update, leave the ACS controls
+ * established by the previous kernel alone instead of programming new ones.
+ * The adopted controls are recorded by the pci_save_state() call in
+ * pci_bus_add_device(), so they are reapplied by pci_restore_state() if the
+ * device is subsequently reset.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved, requires
+ * device-specific quirks, or has nowhere to record the adopted controls.
+ */
+int pci_liveupdate_adopt_acs(struct pci_dev *dev)
+{
+	/*
+	 * Check if the device was preserved over a previous Live Update (even
+	 * if it has already gone through pci_liveupdate_finish()). This ensures
+	 * that the device continues to use the ACS controls established by the
+	 * previous kernel.
+	 */
+	if (!dev->liveupdate.was_incoming)
+		return -EINVAL;
+
+	/*
+	 * The previous kernel should not have preserved any devices that
+	 * require device-specific quirks to enable ACS, but if such a device is
+	 * detected (e.g. new device-specific ACS quirk in the current kernel),
+	 * log a big warning and fall back to the normal enable ACS path.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}
+
+	/*
+	 * Adopting the previous kernel's controls depends on them being
+	 * captured in the ACS save buffer, so that they are reapplied if the
+	 * device is later reset. Without that buffer, e.g. because it could
+	 * not be allocated under memory pressure, the adopted controls would
+	 * be silently lost by the first reset. Program ACS from scratch
+	 * instead, which is a better outcome than leaving ACS disabled.
+	 */
+	if (dev->acs_cap && !pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS)) {
+		pci_err(dev, "No ACS save buffer, not adopting ACS controls\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * pci_liveupdate_adopt_ari() - Adopt ARI configuration
+ * @dev: The PCI device to adopt ARI configuration for
+ *
+ * For devices preserved across a Live Update, read the ARI state from
+ * hardware and adopt it. This ensures the device continues to use the ARI
+ * configuration established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved.
+ */
+int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+
+	/* Safe to modify dev->ari_enabled bitfield during enumeration. */
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	pci_WARN_ONCE(dev, !dev->liveupdate.frozen, "Preservation status is unstable!\n");
+	return dev->liveupdate.outgoing;
+}
+
+/**
+ * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
+ * @dev: The PCI device to check
+ *
+ * Check if a device was preserved across Live Update by the previous kernel,
+ * i.e. the device is incoming-preserved. Note that a device is only considered
+ * incoming-preserved prior to pci_liveupdate_finish(). It is up to drivers to
+ * synchronize usage of pci_liveupdate_is_incoming() with their own call to
+ * pci_liveupdate_finish() to avoid acting on stale data.
+ *
+ * Returns: True if the device is incoming-preserved, false otherwise.
+ */
+bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	return dev->liveupdate.incoming;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);
+
+/**
+ * pci_liveupdate_register_flb() - Register a file handler with the PCI core
+ * @fh: The file handler to register.
+ *
+ * Drivers that support preserving PCI devices across Live Update must call
+ * pci_liveupdate_register_flb() to register their
+ * struct liveupdate_file_handler with the PCI core, typically at module init,
+ * and always before any file managed by @fh can be preserved.
+ *
+ * Registering links the PCI core's FLB to @fh, so that LUO allocates the PCI
+ * core's outgoing struct pci_ser (via pci_flb_preserve()) when the first file
+ * managed by any registered handler is preserved, and frees it (via
+ * pci_flb_unpreserve()) when the last such file is unpreserved.
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Registering file handler \"%s\"\n", fh->compatible);
+	return liveupdate_register_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);
+
+/**
+ * pci_liveupdate_unregister_flb() - Unregister a file handler with the PCI core
+ * @fh: The file handler to unregister.
+ */
+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Unregistering file handler \"%s\"\n", fh->compatible);
+	liveupdate_unregister_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unregister_flb);
diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h
new file mode 100644
index 0000000000000..333ddd8f36c51
--- /dev/null
+++ b/drivers/pci/liveupdate.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCI Live Update support (core API)
+ *
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+#ifndef DRIVERS_PCI_LIVEUPDATE_H
+#define DRIVERS_PCI_LIVEUPDATE_H
+
+#include <linux/pci.h>
+
+#ifdef CONFIG_PCI_LIVEUPDATE
+void pci_liveupdate_setup_device(struct pci_dev *dev);
+void pci_liveupdate_cleanup_device(struct pci_dev *dev);
+void pci_liveupdate_freeze(struct pci_dev *dev);
+bool pci_liveupdate_preserve_bus_numbers(void);
+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev);
+int pci_liveupdate_adopt_acs(struct pci_dev *dev);
+int pci_liveupdate_adopt_ari(struct pci_dev *dev);
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);
+#else
+static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+}
+
+static inline bool pci_liveupdate_preserve_bus_numbers(void)
+{
+	return false;
+}
+
+static inline bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus,
+						     struct pci_dev *dev)
+{
+	return false;
+}
+
+static inline int pci_liveupdate_adopt_acs(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
+
+static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
+
+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	return false;
+}
+#endif
+
+#endif /* DRIVERS_PCI_LIVEUPDATE_H */
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e16aa59dd7ac8..c1ac2ef025e23 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -21,6 +21,7 @@
 #include <linux/acpi.h>
 #include <linux/dma-map-ops.h>
 #include <linux/iommu.h>
+#include "liveupdate.h"
 #include "pci.h"
 #include "pcie/portdrv.h"
 
@@ -559,6 +560,7 @@ static void pci_device_shutdown(struct device *dev)
 	struct pci_dev *pci_dev = to_pci_dev(dev);
 	struct pci_driver *drv = pci_dev->driver;
 
+	pci_liveupdate_freeze(pci_dev);
 	pm_runtime_resume(dev);
 
 	if (drv && drv->shutdown)
@@ -567,11 +569,14 @@ static void pci_device_shutdown(struct device *dev)
 	/*
 	 * If this is a kexec reboot, turn off Bus Master bit on the
 	 * device to tell it to not continue to do DMA. Don't touch
-	 * devices in D3cold or unknown states.
+	 * devices being preserved for Live Update or in D3cold or
+	 * unknown states.
+	 *
 	 * If it is not a kexec reboot, firmware will hit the PCI
 	 * devices with big hammer and stop their DMA any way.
 	 */
-	if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
+	if (kexec_in_progress && !pci_liveupdate_is_outgoing(pci_dev) &&
+	    pci_dev->current_state <= PCI_D3hot)
 		pci_clear_master(pci_dev);
 }
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5f80..0ae405b86b2f8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -35,6 +35,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);
@@ -1021,6 +1023,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
 		caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
 }
 
+/**
+ * pci_save_acs_state - save the ACS Control register
+ * @dev: the PCI device
+ *
+ * Record the ACS controls currently programmed in hardware so that
+ * pci_restore_acs_state() can reapply them after a reset.
+ */
+static void pci_save_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state;
+
+	if (!dev->acs_cap)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+	if (!save_state)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			     (u16 *)&save_state->cap.data[0]);
+}
+
+/**
+ * pci_restore_acs_state - restore the ACS Control register
+ * @dev: the PCI device
+ */
+static void pci_restore_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state = NULL;
+
+	if (dev->acs_cap && !pci_need_dev_specific_enable_acs(dev))
+		save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+
+	/*
+	 * Devices that rely on device-specific quirks to enable an ACS
+	 * equivalent keep that configuration outside of the ACS Control
+	 * register, so there is nothing useful to restore for them. Configure
+	 * ACS from scratch instead, which also covers devices that have no
+	 * saved ACS state at all.
+	 */
+	if (!save_state) {
+		pci_enable_acs(dev);
+		return;
+	}
+
+	pci_write_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			      *(u16 *)&save_state->cap.data[0]);
+}
+
 /**
  * pci_enable_acs - enable ACS if hardware support it
  * @dev: the PCI device
@@ -1031,6 +1082,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_adopt_acs(dev))
+		return;
+
 	/* If an iommu is present we start with kernel default caps */
 	if (pci_acs_enable) {
 		if (pci_dev_specific_enable_acs(dev))
@@ -1057,6 +1111,15 @@ void pci_enable_acs(struct pci_dev *dev)
 	__pci_config_acs(dev, &caps, config_acs_param, 0, 0);
 
 	pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
+
+	/*
+	 * pci_enable_acs() runs when a driver binds to the device, i.e. after
+	 * pci_bus_add_device() has already saved the device's state. Refresh
+	 * the saved ACS Control register so that a subsequent reset restores
+	 * the controls programmed here rather than the ones left behind by
+	 * firmware.
+	 */
+	pci_save_acs_state(dev);
 }
 
 /**
@@ -1800,6 +1863,7 @@ int pci_save_state(struct pci_dev *dev)
 	pci_save_aer_state(dev);
 	pci_save_ptm_state(dev);
 	pci_save_tph_state(dev);
+	pci_save_acs_state(dev);
 	return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1877,7 +1941,7 @@ void pci_restore_state(struct pci_dev *dev)
 	pci_restore_msi_state(dev);
 
 	/* Restore ACS and IOV configuration state */
-	pci_enable_acs(dev);
+	pci_restore_acs_state(dev);
 	pci_restore_iov_state(dev);
 
 	dev->state_saved = false;
@@ -3532,6 +3596,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
 	if (error)
 		pci_err(dev, "unable to allocate suspend buffer for LTR\n");
 
+	error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,
+					    sizeof(u16));
+	if (error)
+		pci_err(dev, "unable to allocate suspend buffer for ACS\n");
+
 	pci_allocate_vc_save_buffers(dev);
 }
 
@@ -3556,7 +3625,7 @@ void pci_configure_ari(struct pci_dev *dev)
 	u32 cap;
 	struct pci_dev *bridge;
 
-	if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
+	if (!pci_is_pcie(dev) || dev->devfn)
 		return;
 
 	bridge = dev->bus->self;
@@ -3567,6 +3636,12 @@ void pci_configure_ari(struct pci_dev *dev)
 	if (!(cap & PCI_EXP_DEVCAP2_ARI))
 		return;
 
+	if (!pci_liveupdate_adopt_ari(bridge))
+		return;
+
+	if (pcie_ari_disabled)
+		return;
+
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
 		pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
 					 PCI_EXP_DEVCTL2_ARI);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc23..037c1674f1648 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 void pci_disable_broken_acs_cap(struct pci_dev *pdev);
@@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
 	return -ENOTTY;
 }
+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	return false;
+}
 static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	return -ENOTTY;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5afc..debe0ad1ef68b 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -24,6 +24,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/bitfield.h>
 #include <trace/events/pci.h>
+#include "liveupdate.h"
 #include "pci.h"
 
 static struct resource busn_resource = {
@@ -1396,6 +1397,8 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 				  int max, unsigned int available_buses,
 				  int pass)
 {
+	bool preserve_bus_numbers = !pcibios_assign_all_busses() ||
+				    pci_liveupdate_preserve_bus_numbers();
 	struct pci_bus *child;
 	u32 buses;
 	u16 bctl;
@@ -1448,8 +1451,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		goto out;
 	}
 
-	if ((secondary || subordinate) &&
-	    !pcibios_assign_all_busses() && !broken) {
+	if ((secondary || subordinate) && preserve_bus_numbers && !broken) {
 		unsigned int cmax, buses;
 
 		/*
@@ -1491,8 +1493,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		 * do in the second pass.
 		 */
 		if (!pass) {
-			if (pcibios_assign_all_busses() || broken)
-
+			if (!preserve_bus_numbers || broken)
 				/*
 				 * Temporarily disable forwarding of the
 				 * configuration cycles on all bridges in
@@ -1506,6 +1507,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (pci_liveupdate_refuse_bus_numbers(bus, dev))
+			goto out;
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -2064,6 +2068,8 @@ int pci_setup_device(struct pci_dev *dev)
 	if (pci_early_dump)
 		early_dump_pci_device(dev);
 
+	pci_liveupdate_setup_device(dev);
+
 	/* Need to have dev->class ready */
 	dev->cfg_size = pci_cfg_space_size(dev);
 
@@ -2187,6 +2193,7 @@ int pci_setup_device(struct pci_dev *dev)
 	default:				    /* unknown header */
 		pci_err(dev, "unknown header type %02x, ignoring device\n",
 			dev->hdr_type);
+		pci_liveupdate_cleanup_device(dev);
 		pci_release_of_node(dev);
 		return -EIO;
 
@@ -2485,6 +2492,7 @@ static void pci_release_dev(struct device *dev)
 
 	pci_dev = to_pci_dev(dev);
 	pci_release_capabilities(pci_dev);
+	pci_liveupdate_cleanup_device(pci_dev);
 	pci_release_of_node(pci_dev);
 	pcibios_release_device(pci_dev);
 	pci_bus_put(pci_dev->bus);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21fd..e500c202d2ec9 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -5377,9 +5377,6 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
  */
 static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
 {
-	if (!pci_quirk_intel_pch_acs_match(dev))
-		return -ENOTTY;
-
 	if (pci_quirk_enable_intel_lpc_acs(dev)) {
 		pci_warn(dev, "Failed to enable Intel PCH ACS quirk\n");
 		return 0;
@@ -5399,9 +5396,6 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5429,9 +5423,6 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5451,56 +5442,63 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 static const struct pci_dev_acs_ops {
 	u16 vendor;
 	u16 device;
+	bool (*match)(struct pci_dev *dev);
 	int (*enable_acs)(struct pci_dev *dev);
 	int (*disable_acs_redir)(struct pci_dev *dev);
 } pci_dev_acs_ops[] = {
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];
 		if ((p->vendor == dev->vendor ||
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
+		     p->device == (u16)PCI_ANY_ID)) {
+			if (!p->match || p->match(dev))
+				return p;
 		}
 	}
 
+	return NULL;
+}
+
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	return p && p->enable_acs;
+}
+
+int pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	if (p && p->enable_acs)
+		return p->enable_acs(dev);
+
 	return -ENOTTY;
 }
 
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
 {
-	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
 
-	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
-		p = &pci_dev_acs_ops[i];
-		if ((p->vendor == dev->vendor ||
-		     p->vendor == (u16)PCI_ANY_ID) &&
-		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->disable_acs_redir) {
-			ret = p->disable_acs_redir(dev);
-			if (ret >= 0)
-				return ret;
-		}
-	}
+	if (p && p->disable_acs_redir)
+		return p->disable_acs_redir(dev);
 
 	return -ENOTTY;
 }
diff --git a/include/linux/kho/abi/pci.h b/include/linux/kho/abi/pci.h
new file mode 100644
index 0000000000000..9485ed73c3519
--- /dev/null
+++ b/include/linux/kho/abi/pci.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+
+#ifndef _LINUX_KHO_ABI_PCI_H
+#define _LINUX_KHO_ABI_PCI_H
+
+#include <linux/bug.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/**
+ * DOC: PCI File-Lifecycle Bound (FLB) Live Update ABI
+ *
+ * This header defines the ABI for preserving core PCI state across kexec using
+ * Live Update File-Lifecycle Bound (FLB) data.
+ *
+ * This interface is a contract. Any modification to any of the serialization
+ * structs defined here constitutes a breaking change. Such changes require
+ * incrementing the version number in the PCI_LUO_FLB_VERSION number.
+ */
+
+#define PCI_LUO_FLB_COMPATIBLE "pci"
+#define PCI_LUO_FLB_VERSION 2
+
+/**
+ * struct pci_dev_ser - Serialized state about a single PCI device.
+ *
+ * @domain: The device's PCI domain number (segment).
+ * @bdf: The device's PCI bus, device, and function number.
+ * @refcount: Reference count used by the PCI core to keep track of whether it
+ *            is done using a device's struct pci_dev_ser. The value of the
+ *            refcount is equal to the number of preserved devices at or below
+ *            it in the PCI hierarchy when the struct pci_dev_ser is in use, and
+ *            0 otherwise.
+ */
+struct pci_dev_ser {
+	u32 domain;
+	u16 bdf;
+	u16 refcount;
+} __packed;
+
+/**
+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel. It contains only state owned by the PCI
+ * core; the state a driver needs to resume its device is preserved separately
+ * by that driver.
+ *
+ * @version: The version of the "pci" FLB struct. This field must never be
+ *           deleted, moved, or resized, as the kernel depends on always being
+ *           able to check the struct pci_ser version number.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Physical address of the first KHO block containing pci_dev_ser.
+ */
+struct pci_ser {
+	u32 version;
+	u32 nr_devices;
+	u64 devices;
+} __packed;
+
+#endif /* _LINUX_KHO_ABI_PCI_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d31a8d107b1ef..b35ac263c4517 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -43,6 +43,7 @@
 #include <uapi/linux/pci.h>
 
 #include <linux/pci_ids.h>
+#include <linux/pci_liveupdate.h>
 
 #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY  | \
 			       PCI_STATUS_SIG_SYSTEM_ERROR | \
@@ -598,6 +599,9 @@ struct pci_dev {
 	u8		tph_mode;	/* TPH mode */
 	u8		tph_req_type;	/* TPH requester type */
 #endif
+#ifdef CONFIG_PCI_LIVEUPDATE
+	struct pci_liveupdate liveupdate;
+#endif
 };
 
 static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
@@ -832,6 +836,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)
 	return dev->bus->self;
 }
 
+#define for_each_pci_dev_in_path(dev) \
+	for (; dev; dev = pci_upstream_bridge(dev))
+
 #ifdef CONFIG_PCI_MSI
 static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev)
 {
diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h
new file mode 100644
index 0000000000000..bb79348769b72
--- /dev/null
+++ b/include/linux/pci_liveupdate.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCI Live Update support (Public/Driver API)
+ *
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+#ifndef LINUX_PCI_LIVEUPDATE_H
+#define LINUX_PCI_LIVEUPDATE_H
+
+#include <linux/kho/abi/pci.h>
+#include <linux/liveupdate.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+/**
+ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
+ * @outgoing: State preserved for the next kernel.
+ * @incoming: State preserved by the previous kernel.
+ * @was_incoming: True if this struct pci_dev was incoming-preserved when it was
+ *                set up, i.e. it was matched to state preserved by the previous
+ *                kernel. Unlike @incoming, this is never cleared, so it stays
+ *                true after the device finishes participating in Live Update.
+ * @frozen: True if the outgoing preservation status of this device is frozen
+ *          and thus cannot be changed.
+ */
+struct pci_liveupdate {
+	struct pci_dev_ser *outgoing;
+	struct pci_dev_ser *incoming;
+	bool was_incoming;
+	bool frozen;
+};
+
+struct pci_dev;
+
+#ifdef CONFIG_PCI_LIVEUPDATE
+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);
+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);
+int pci_liveupdate_preserve(struct pci_dev *dev);
+void pci_liveupdate_unpreserve(struct pci_dev *dev);
+void pci_liveupdate_finish(struct pci_dev *dev);
+bool pci_liveupdate_is_incoming(struct pci_dev *dev);
+#else
+static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+}
+
+static inline int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_finish(struct pci_dev *dev)
+{
+}
+
+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+	return false;
+}
+#endif
+
+#endif /* LINUX_PCI_LIVEUPDATE_H */

3/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24227
							output: 17
							thoughts: 1015
						

Thoughts:
**PCI Live Update Patch Analysis**

Alright, let's dive into this patch series. My initial thought is to assess its fuzzing potential, identify necessary configurations, and pinpoint interesting symbols.

First, I need to understand what this patch is doing. Okay, it's adding PCI support for Live Update (`CONFIG_PCI_LIVEUPDATE`). That's immediately interesting, especially considering the potential for subtle interactions.

Looking at the file changes, we have modifications across the core PCI files. It's touching `Kconfig`, `Makefile`, and introduces new files `liveupdate.c`, and `liveupdate.h`. Beyond that it's modifying several key PCI drivers. It's got changes in `pci-driver.c`, `pci.c`, `probe.c` and `quirks.c`. It's also making changes to header files, and the documentation.

Hold on a second... This is where it gets interesting. I'm seeing that *some* changes are guarded by `CONFIG_PCI_LIVEUPDATE`, but not *all* of them. That immediately raises a red flag. Specifically, in `drivers/pci/pci.c`: `pci_save_acs_state` and `pci_restore_acs_state` are added unconditionally! Also, the allocation of ACS capability save buffers in `pci_allocate_cap_save_buffers` is unconditional too!

Now, the `pci_enable_acs` function is modified, and `pci_restore_state` *now* calls `pci_restore_acs_state` instead of `pci_enable_acs`. I'm starting to get a clearer picture of how ACS state management is being handled, and it seems like this patch is altering the base ACS state handling, which is crucial.

Furthermore, in `drivers/pci/quirks.c`, the `pci_dev_acs_ops` structure has been refactored, which might have implications for how ACS quirks are handled. And now in `drivers/pci/probe.c`: `pci_setup_device` has `pci_liveupdate_setup_device(dev)` and `pci_release_dev` has `pci_liveupdate_cleanup_device(pci_dev)`. Also it touches how we are handling PCI bus numbers in `pci_scan_bridge_extend`.

Now, I need to dig into the relationship between these modifications and LiveUpdate itself, and specifically what Live Update even *is* in the context of the kernel. I'll need to check the LUO, KHO, and liveupdate context of the code. Time to fire up `grepper` and search for "LIVEUPDATE" in the `Kconfig` and code!



4/2 2026/09/21 01:11 tool grepper
0m Args:
{
  "Expression": "config LIVEUPDATE"
}

Results:
{
  "Output": "kernel/liveupdate/Kconfig=38=config KEXEC_HANDOVER_ENABLE_DEFAULT\n--\nkernel/liveupdate/Kconfig-51-\nkernel/liveupdate/Kconfig:52:config LIVEUPDATE\nkernel/liveupdate/Kconfig-53-\tbool \"Live Update Orchestrator\"\n--\nkernel/liveupdate/Kconfig-72-\nkernel/liveupdate/Kconfig:73:config LIVEUPDATE_MEMFD\nkernel/liveupdate/Kconfig-74-\tbool \"Live update support for memfd\"\n--\nlib/Kconfig.debug=2872=config CONTEXT_ANALYSIS_TEST\n--\nlib/Kconfig.debug-2885-\nlib/Kconfig.debug:2886:config LIVEUPDATE_TEST\nlib/Kconfig.debug-2887-\tbool \"Live Update Kernel Test\"\n"
}

5/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26065
							output: 34
							thoughts: 13
						

Thoughts:
Here's a summary of my thought process, as requested:

**Navigating the Kernel's Live Update Configuration**

Okay, so I need to get a handle on the live update functionality within the kernel. My initial thought is to zero in on the configuration file. Given my experience, the standard place to start looking for this type of thing would be under `kernel/`. Therefore, I'm going to head directly to `kernel/liveupdate/Kconfig`. That configuration file is the bedrock for enabling, disabling, and customizing the live update features. I need to understand what options are exposed there, what dependencies they have, and how they interact with the rest of the kernel's configuration. Hopefully, the Kconfig file will give me the high-level picture before I have to dive deeper into the code. Time to dig in and see what I can find.



6/2 2026/09/21 01:11 tool read-file
0m Args:
{
  "File": "kernel/liveupdate/Kconfig",
  "FirstLine": 40,
  "LineCount": 40
}

Results:
{
  "Contents": "  40:\t\tdepends on KEXEC_HANDOVER\n  41:\t\thelp\n  42:\t\t  Enable Kexec Handover by default. This avoids the need to\n  43:\t\t  explicitly pass 'kho=on' on the kernel command line.\n  44:\t\n  45:\t\t  This is useful for systems where KHO is a prerequisite for other\n  46:\t\t  features, such as Live Update, ensuring the mechanism is always\n  47:\t\t  active.\n  48:\t\n  49:\t\t  The default behavior can still be overridden at boot time by\n  50:\t\t  passing 'kho=off'.\n  51:\t\n  52:\tconfig LIVEUPDATE\n  53:\t\tbool \"Live Update Orchestrator\"\n  54:\t\tdepends on KEXEC_HANDOVER\n  55:\t\thelp\n  56:\t\t  Enable the Live Update Orchestrator. Live Update is a mechanism,\n  57:\t\t  typically based on kexec, that allows the kernel to be updated\n  58:\t\t  while keeping selected devices operational across the transition.\n  59:\t\t  These devices are intended to be reclaimed by the new kernel and\n  60:\t\t  re-attached to their original workload without requiring a device\n  61:\t\t  reset.\n  62:\t\n  63:\t\t  Ability to handover a device from current to the next kernel depends\n  64:\t\t  on specific support within device drivers and related kernel\n  65:\t\t  subsystems.\n  66:\t\n  67:\t\t  This feature primarily targets virtual machine hosts to quickly update\n  68:\t\t  the kernel hypervisor with minimal disruption to the running virtual\n  69:\t\t  machines.\n  70:\t\n  71:\t\t  If unsure, say N.\n  72:\t\n  73:\tconfig LIVEUPDATE_MEMFD\n  74:\t\tbool \"Live update support for memfd\"\n  75:\t\tdepends on LIVEUPDATE\n  76:\t\tdepends on MEMFD_CREATE\n  77:\t\tdepends on SHMEM\n  78:\t\tdefault LIVEUPDATE\n  79:\t\thelp\n"
}

7/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26810
							output: 24
							thoughts: 51
						

Thoughts:
Here's my thought process, as if I were analyzing this code:

**Diving into the Live Update Hooks**

Okay, so I need to understand how this live update functionality is working within the PCI subsystem. My first step is to trace the calls. I'll start with `pci_liveupdate_preserve` and the `pci_liveupdate_register_flb` function calls. Grabbing the call graph for these will give me a good overview.

Since `pci_liveupdate_register_flb` seems like a registration function, I'll `grep` for it across the entire codebase. This will identify all the places where this function is called, which should reveal the components that are registering their callback functions for the live update mechanism. That should give me a good high-level map of the interaction. I expect to find device drivers, possibly some core PCI code, and maybe some platform-specific initialization routines involved in this registration process. Knowing who's registering will tell me a lot about who's participating.



8/2 2026/09/21 01:11 tool grepper
0m Args:
{
  "Expression": "pci_liveupdate_register_flb"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-35- *\ndrivers/pci/liveupdate.c:36: *  * ``pci_liveupdate_register_flb(driver_file_handler)``\ndrivers/pci/liveupdate.c-37- *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``\n--\ndrivers/pci/liveupdate.c-58- *   # Driver initialization\ndrivers/pci/liveupdate.c:59: *   pci_liveupdate_register_flb(fh)\ndrivers/pci/liveupdate.c-60- *\n--\ndrivers/pci/liveupdate.c=981=EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);\n--\ndrivers/pci/liveupdate.c-983-/**\ndrivers/pci/liveupdate.c:984: * pci_liveupdate_register_flb() - Register a file handler with the PCI core\ndrivers/pci/liveupdate.c-985- * @fh: The file handler to register.\n--\ndrivers/pci/liveupdate.c-987- * Drivers that support preserving PCI devices across Live Update must call\ndrivers/pci/liveupdate.c:988: * pci_liveupdate_register_flb() to register their\ndrivers/pci/liveupdate.c-989- * struct liveupdate_file_handler with the PCI core, typically at module init,\n--\ndrivers/pci/liveupdate.c-998- */\ndrivers/pci/liveupdate.c:999:int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\ndrivers/pci/liveupdate.c-1000-{\n--\ndrivers/pci/liveupdate.c-1003-}\ndrivers/pci/liveupdate.c:1004:EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);\ndrivers/pci/liveupdate.c-1005-\n--\ninclude/linux/pci_liveupdate.h=34=struct pci_dev;\n--\ninclude/linux/pci_liveupdate.h-36-#ifdef CONFIG_PCI_LIVEUPDATE\ninclude/linux/pci_liveupdate.h:37:int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);\ninclude/linux/pci_liveupdate.h-38-void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);\n--\ninclude/linux/pci_liveupdate.h=42=bool pci_liveupdate_is_incoming(struct pci_dev *dev);\ninclude/linux/pci_liveupdate.h-43-#else\ninclude/linux/pci_liveupdate.h:44:static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\ninclude/linux/pci_liveupdate.h-45-{\n"
}

9/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27847
							output: 21
							thoughts: 50
						

Thoughts:
**Checking for Live Update Usage**

Okay, hold on a second. Before I proceed, I need to make sure I understand the landscape of `pci_liveupdate`. My intuition tells me that I should see if any drivers are currently calling `pci_liveupdate_register_flb` or, more generally, `pci_liveupdate_preserve`. I suspect the answer is *no*, since this code is under active development, but let's not make assumptions. Time to verify. I'll need to do a quick scan of the codebase to confirm if any existing drivers are leveraging these functions. A simple `grep` for `pci_liveupdate_preserve` should give me a quick overview of any current usages. That's the next logical step.



10/2 2026/09/21 01:11 tool grepper
0m Args:
{
  "Expression": "pci_liveupdate_preserve"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-66- *     fh-\u003eops-\u003epreserve()                # driver callback\ndrivers/pci/liveupdate.c:67: *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser\ndrivers/pci/liveupdate.c-68- *\n--\ndrivers/pci/liveupdate.c-97- *\ndrivers/pci/liveupdate.c:98: *  * ``pci_liveupdate_preserve(pci_dev)``\ndrivers/pci/liveupdate.c-99- *  * ``pci_liveupdate_unpreserve(pci_dev)``\n--\ndrivers/pci/liveupdate.c-113- * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and\ndrivers/pci/liveupdate.c:114: * ``pci_liveupdate_preserve()``, i.e., a PCI device can be **outgoing**\ndrivers/pci/liveupdate.c-115- * (preserved for next kernel) and **incoming** (preserved by previous kernel)\n--\ndrivers/pci/liveupdate.c-132- * In the outgoing kernel, the driver must ensure that it does not release a\ndrivers/pci/liveupdate.c:133: * device between pci_liveupdate_preserve() and pci_liveupdate_unpreserve().\ndrivers/pci/liveupdate.c-134- *\n--\ndrivers/pci/liveupdate.c=313=static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)\n--\ndrivers/pci/liveupdate.c-357-\t * of this kernel, even after Live Update finishes and the incoming FLB\ndrivers/pci/liveupdate.c:358:\t * is freed. See pci_liveupdate_preserve_bus_numbers().\ndrivers/pci/liveupdate.c-359-\t */\n--\ndrivers/pci/liveupdate.c=513=static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoing,\n--\ndrivers/pci/liveupdate.c-525-\ndrivers/pci/liveupdate.c:526:static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,\ndrivers/pci/liveupdate.c-527-\t\t\t\t\t  struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-584-/**\ndrivers/pci/liveupdate.c:585: * pci_liveupdate_preserve() - Preserve a PCI device across Live Update\ndrivers/pci/liveupdate.c-586- * @dev: The PCI device to preserve.\ndrivers/pci/liveupdate.c-587- *\ndrivers/pci/liveupdate.c:588: * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be\ndrivers/pci/liveupdate.c-589- * preserved across the next Live Update. Drivers are expected to call\ndrivers/pci/liveupdate.c:590: * pci_liveupdate_preserve() from their struct liveupdate_file_handler\ndrivers/pci/liveupdate.c-591- * preserve() callback to ensure the outgoing struct pci_ser is already set up.\ndrivers/pci/liveupdate.c-592- *\ndrivers/pci/liveupdate.c:593: * pci_liveupdate_preserve() automatically preserves all bridges upstream of\ndrivers/pci/liveupdate.c-594- * @dev.\n--\ndrivers/pci/liveupdate.c-597- */\ndrivers/pci/liveupdate.c:598:int pci_liveupdate_preserve(struct pci_dev *dev)\ndrivers/pci/liveupdate.c-599-{\n--\ndrivers/pci/liveupdate.c-610-\tfor_each_pci_dev_in_path(dev) {\ndrivers/pci/liveupdate.c:611:\t\tret = pci_liveupdate_preserve_device(outgoing, dev);\ndrivers/pci/liveupdate.c-612-\t\tif (ret) {\n--\ndrivers/pci/liveupdate.c-620-}\ndrivers/pci/liveupdate.c:621:EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);\ndrivers/pci/liveupdate.c-622-\n--\ndrivers/pci/liveupdate.c=686=static void pci_liveupdate_flb_put_incoming(void)\n--\ndrivers/pci/liveupdate.c-691-/**\ndrivers/pci/liveupdate.c:692: * pci_liveupdate_preserve_bus_numbers() - Determine if the PCI core should\ndrivers/pci/liveupdate.c-693- *                                         preserve bus numbers when scanning\n--\ndrivers/pci/liveupdate.c-719- */\ndrivers/pci/liveupdate.c:720:bool pci_liveupdate_preserve_bus_numbers(void)\ndrivers/pci/liveupdate.c-721-{\n--\ndrivers/pci/liveupdate.h=16=void pci_liveupdate_freeze(struct pci_dev *dev);\ndrivers/pci/liveupdate.h:17:bool pci_liveupdate_preserve_bus_numbers(void);\ndrivers/pci/liveupdate.h-18-bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev);\n--\ndrivers/pci/liveupdate.h=31=static inline void pci_liveupdate_freeze(struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.h-34-\ndrivers/pci/liveupdate.h:35:static inline bool pci_liveupdate_preserve_bus_numbers(void)\ndrivers/pci/liveupdate.h-36-{\n--\ndrivers/pci/probe.c=1396=static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,\n--\ndrivers/pci/probe.c-1400-\tbool preserve_bus_numbers = !pcibios_assign_all_busses() ||\ndrivers/pci/probe.c:1401:\t\t\t\t    pci_liveupdate_preserve_bus_numbers();\ndrivers/pci/probe.c-1402-\tstruct pci_bus *child;\n--\ninclude/linux/pci_liveupdate.h=38=void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);\ninclude/linux/pci_liveupdate.h:39:int pci_liveupdate_preserve(struct pci_dev *dev);\ninclude/linux/pci_liveupdate.h-40-void pci_liveupdate_unpreserve(struct pci_dev *dev);\n--\ninclude/linux/pci_liveupdate.h=49=static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\n--\ninclude/linux/pci_liveupdate.h-52-\ninclude/linux/pci_liveupdate.h:53:static inline int pci_liveupdate_preserve(struct pci_dev *dev)\ninclude/linux/pci_liveupdate.h-54-{\n"
}

11/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29916
							output: 32
							thoughts: 245
						

Thoughts:
**Analyzing the PCI Live Update Implementation**

Okay, let's break this down. I see `liveupdate.c` defines `pci_liveupdate_flb` as a static `liveupdate_flb` structure. Right off the bat, I want to confirm that it's being properly registered. My gut reaction is to see if it’s registered with `liveupdate_register_flb` but I need to be sure. I'm looking for the registration call and it appears it's done via `pci_liveupdate_register_flb`. Perfect.

Next, I need to know where `pci_liveupdate_register_flb` is actually *called*. The critical piece of information here will be figuring out *who* needs this update. I need to quickly identify if VFIO is involved, or if any other driver is registering a file handler. Let me quickly use `git log` to see if there have been any recent commits, or if that fails, a simple `grep` should be able to get me what I need.



12/2 2026/09/21 01:11 tool grepper
0m Args:
{
  "Expression": "pci_liveupdate_register_flb",
  "PathPrefix": "drivers/"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-35- *\ndrivers/pci/liveupdate.c:36: *  * ``pci_liveupdate_register_flb(driver_file_handler)``\ndrivers/pci/liveupdate.c-37- *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``\n--\ndrivers/pci/liveupdate.c-58- *   # Driver initialization\ndrivers/pci/liveupdate.c:59: *   pci_liveupdate_register_flb(fh)\ndrivers/pci/liveupdate.c-60- *\n--\ndrivers/pci/liveupdate.c=981=EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);\n--\ndrivers/pci/liveupdate.c-983-/**\ndrivers/pci/liveupdate.c:984: * pci_liveupdate_register_flb() - Register a file handler with the PCI core\ndrivers/pci/liveupdate.c-985- * @fh: The file handler to register.\n--\ndrivers/pci/liveupdate.c-987- * Drivers that support preserving PCI devices across Live Update must call\ndrivers/pci/liveupdate.c:988: * pci_liveupdate_register_flb() to register their\ndrivers/pci/liveupdate.c-989- * struct liveupdate_file_handler with the PCI core, typically at module init,\n--\ndrivers/pci/liveupdate.c-998- */\ndrivers/pci/liveupdate.c:999:int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\ndrivers/pci/liveupdate.c-1000-{\n--\ndrivers/pci/liveupdate.c-1003-}\ndrivers/pci/liveupdate.c:1004:EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);\ndrivers/pci/liveupdate.c-1005-\n"
}

13/2 2026/09/21 01:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30928
							output: 31
							thoughts: 886
						

Thoughts:
**Analyzing PCI State Restoration After Modifications**

Okay, so nobody's calling `pci_liveupdate_register_flb` yet. But hold on, what about these PCI core changes in `drivers/pci/pci.c`, `drivers/pci/probe.c`, `drivers/pci/quirks.c`, and `drivers/pci/pci-driver.c`?  I need to figure out if any of this modified code is actually *reachable*. I have to check the execution paths during a normal boot, PCI device scanning, the probing of a new device, device reset, suspend/resume operations and sysfs operations.

Wait, let me think. Can PCI devices be scanned, probed or reset in standard virtualized environments like QEMU? Yes, of course! Standard QEMU boots with PCI devices, including virtio-pci devices, e1000, and others. That gives me a good test environment.

Okay, back to the modified code. What changes are actually *reachable* in `drivers/pci/pci.c`, `drivers/pci/quirks.c`, and `drivers/pci/probe.c`? I need to analyze these changes in detail. Let's start with `drivers/pci/pci.c`.

Specifically, I'm looking at two new functions: `pci_save_acs_state(struct pci_dev *dev)` and `pci_restore_acs_state(struct pci_dev *dev)`.

`pci_save_acs_state` saves ACS (Access Control Services) state. It's called from `pci_enable_acs()` and `pci_save_state(dev)`.

`pci_restore_acs_state` restores the ACS state. It's called from `pci_restore_state(dev)`. Crucially, in this modified code, it replaces an earlier call to `pci_enable_acs(dev)`.

The crucial question is: where is `pci_restore_state(dev)` actually called? I need to figure out the call paths.

Ah, looks like `pci_restore_state` gets called when a PCI device is reset via the sysfs "reset" attribute, or through an ioctl call such as `VFIO_DEVICE_RESET`, and also during power management (resume).

Now, the really critical question: can syzkaller trigger `pci_restore_state`?  That would be a great way to exercise this code.  I'll start checking the callers of `pci_restore_state` to understand its use in the system.



14/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "pci_restore_state",
  "PathPrefix": "drivers/pci/"
}

Results:
{
  "Output": "drivers/pci/hotplug/pciehp_core.c=288=static int pciehp_resume_noirq(struct pcie_device *dev)\n--\ndrivers/pci/hotplug/pciehp_core.c-291-\ndrivers/pci/hotplug/pciehp_core.c:292:\t/* pci_restore_state() just wrote to the Slot Control register */\ndrivers/pci/hotplug/pciehp_core.c-293-\tctrl-\u003ecmd_started = jiffies;\n--\ndrivers/pci/hotplug/pciehp_core.c=336=static int pciehp_runtime_resume(struct pcie_device *dev)\n--\ndrivers/pci/hotplug/pciehp_core.c-339-\ndrivers/pci/hotplug/pciehp_core.c:340:\t/* pci_restore_state() just wrote to the Slot Control register */\ndrivers/pci/hotplug/pciehp_core.c-341-\tctrl-\u003ecmd_started = jiffies;\n--\ndrivers/pci/liveupdate.c=877=EXPORT_SYMBOL_GPL(pci_liveupdate_finish);\n--\ndrivers/pci/liveupdate.c-885- * The adopted controls are recorded by the pci_save_state() call in\ndrivers/pci/liveupdate.c:886: * pci_bus_add_device(), so they are reapplied by pci_restore_state() if the\ndrivers/pci/liveupdate.c-887- * device is subsequently reset.\n--\ndrivers/pci/pci-driver.c=591=static int pci_restore_standard_config(struct pci_dev *pci_dev)\n--\ndrivers/pci/pci-driver.c-600-\ndrivers/pci/pci-driver.c:601:\tpci_restore_state(pci_dev);\ndrivers/pci/pci-driver.c-602-\tpci_pme_restore(pci_dev);\n--\ndrivers/pci/pci-driver.c=617=static void pci_pm_default_resume_early(struct pci_dev *pci_dev)\n--\ndrivers/pci/pci-driver.c-619-\tpci_pm_power_up_and_verify_state(pci_dev);\ndrivers/pci/pci-driver.c:620:\tpci_restore_state(pci_dev);\ndrivers/pci/pci-driver.c-621-\tpci_pme_restore(pci_dev);\n--\ndrivers/pci/pci-driver.c=1154=static int pci_pm_thaw_noirq(struct device *dev)\n--\ndrivers/pci/pci-driver.c-1162-\t *\ndrivers/pci/pci-driver.c:1163:\t * In addition, pci_restore_state() restores MSI-X state in MMIO\ndrivers/pci/pci-driver.c-1164-\t * space, which requires the device to be in D0, so return it to D0\n--\ndrivers/pci/pci-driver.c-1168-\tpci_pm_power_up_and_verify_state(pci_dev);\ndrivers/pci/pci-driver.c:1169:\tpci_restore_state(pci_dev);\ndrivers/pci/pci-driver.c-1170-\n--\ndrivers/pci/pci.c=1385=int pci_power_up(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-1472- *\ndrivers/pci/pci.c:1473: * If pci_restore_state() is going to be called right after a power state change\ndrivers/pci/pci.c-1474- * to D0, it is more efficient to use pci_power_up() directly instead of this\n--\ndrivers/pci/pci.c=1897=static void pci_restore_config_space(struct pci_dev *pdev)\n--\ndrivers/pci/pci.c-1919-/**\ndrivers/pci/pci.c:1920: * pci_restore_state - Restore the saved state of a PCI device\ndrivers/pci/pci.c-1921- * @dev: PCI device that we're dealing with\ndrivers/pci/pci.c-1922- */\ndrivers/pci/pci.c:1923:void pci_restore_state(struct pci_dev *dev)\ndrivers/pci/pci.c-1924-{\n--\ndrivers/pci/pci.c-1948-}\ndrivers/pci/pci.c:1949:EXPORT_SYMBOL(pci_restore_state);\ndrivers/pci/pci.c-1950-\n--\ndrivers/pci/pci.c=4953=void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-4962-\t\telse\ndrivers/pci/pci.c:4963:\t\t\tpci_restore_state(dev);\ndrivers/pci/pci.c-4964-\n--\ndrivers/pci/pci.c=5200=static void pci_dev_restore(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5204-\ndrivers/pci/pci.c:5205:\tpci_restore_state(dev);\ndrivers/pci/pci.c-5206-\n--\ndrivers/pci/pcie/portdrv.c=758=static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)\n--\ndrivers/pci/pcie/portdrv.c-762-\ndrivers/pci/pcie/portdrv.c:763:\tpci_restore_state(dev);\ndrivers/pci/pcie/portdrv.c-764-\treturn PCI_ERS_RESULT_RECOVERED;\n--\ndrivers/pci/quirks.c=4005=static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)\n--\ndrivers/pci/quirks.c-4043-\t * So we briefly re-enable MSI-X interrupts for the duration of the\ndrivers/pci/quirks.c:4044:\t * FLR.  The pci_restore_state() below will restore the original\ndrivers/pci/quirks.c-4045-\t * MSI-X state.\n--\ndrivers/pci/quirks.c-4060-\t */\ndrivers/pci/quirks.c:4061:\tpci_restore_state(dev);\ndrivers/pci/quirks.c-4062-\tpci_write_config_word(dev, PCI_COMMAND, old_command);\n"
}

15/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 34226
							output: 31
							thoughts: 37
						

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

**Tracing the Restore of a PCI Device**

Alright, let's dive into this `pci_dev_restore` function within the PCI driver's `pci.c` file. My immediate focus is, naturally, to understand the function's context and its role in the PCI subsystem. Where does this crucial restore function actually get invoked? I need to trace its call sites to get a grasp on when and why a PCI device's state needs to be re-established. Understanding the surrounding logic is key to comprehending its purpose and potential impact. Time to start digging for those call paths.



16/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "pci_dev_restore",
  "PathPrefix": "drivers/pci/"
}

Results:
{
  "Output": "drivers/pci/pci.c=5163=static void pci_dev_save_and_disable(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5199-\ndrivers/pci/pci.c:5200:static void pci_dev_restore(struct pci_dev *dev)\ndrivers/pci/pci.c-5201-{\n--\ndrivers/pci/pci.c=5336=int pci_reset_function(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5356-\ndrivers/pci/pci.c:5357:\tpci_dev_restore(dev);\ndrivers/pci/pci.c-5358-\tpci_dev_unlock(dev);\n--\ndrivers/pci/pci.c=5386=int pci_reset_function_locked(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5398-\ndrivers/pci/pci.c:5399:\tpci_dev_restore(dev);\ndrivers/pci/pci.c-5400-\n--\ndrivers/pci/pci.c=5411=int pci_try_reset_function(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5422-\trc = __pci_reset_function_locked(dev);\ndrivers/pci/pci.c:5423:\tpci_dev_restore(dev);\ndrivers/pci/pci.c-5424-\tpci_dev_unlock(dev);\n--\ndrivers/pci/pci.c=5597=static void pci_bus_restore_locked(struct pci_bus *bus)\n--\ndrivers/pci/pci.c-5601-\tlist_for_each_entry(dev, \u0026bus-\u003edevices, bus_list) {\ndrivers/pci/pci.c:5602:\t\tpci_dev_restore(dev);\ndrivers/pci/pci.c-5603-\t\tif (dev-\u003esubordinate) {\n--\ndrivers/pci/pci.c=5632=static void pci_slot_restore_locked(struct pci_slot *slot)\n--\ndrivers/pci/pci.c-5638-\t\t\tcontinue;\ndrivers/pci/pci.c:5639:\t\tpci_dev_restore(dev);\ndrivers/pci/pci.c-5640-\t\tif (dev-\u003esubordinate) {\n--\ndrivers/pci/pci.c=5741=static int pci_try_reset_bus(struct pci_bus *bus)\n--\ndrivers/pci/pci.c-5765- * @bridge: bridge that connects to the bus to reset\ndrivers/pci/pci.c:5766: * @restore: when true use a reset method that invokes pci_dev_restore() post\ndrivers/pci/pci.c-5767- *           reset for affected devices\n"
}

17/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 35238
							output: 31
							thoughts: 57
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the PCI Reset Mechanism**

Okay, so I see a user's interaction – a write to `/sys/bus/pci/devices/.../reset` – triggers a call to `pci_reset_function`. That's the entry point! Time to dissect this reset functionality. Let's head straight to the source: `drivers/pci/pci-sysfs.c`. I need to understand exactly what `pci_reset_function` is doing when a user attempts a PCI device reset. I'll need to look for how this function retrieves device information, what checks it performs, and most importantly, what steps it's taking to actually *reset* the targeted PCI device. Is it a function pointer, maybe? Is it specific to a controller or more generic? Let's get reading and figure out this flow.



18/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "pci_reset_function",
  "PathPrefix": "drivers/pci/"
}

Results:
{
  "Output": "drivers/pci/pci-sysfs.c=1556=static ssize_t reset_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/pci/pci-sysfs.c-1569-\tpm_runtime_get_sync(dev);\ndrivers/pci/pci-sysfs.c:1570:\tresult = pci_reset_function(pdev);\ndrivers/pci/pci-sysfs.c-1571-\tpm_runtime_put(dev);\n--\ndrivers/pci/pci.c=5219=const struct pci_reset_fn_method pci_reset_fn_methods[] = {\n--\ndrivers/pci/pci.c-5230-/**\ndrivers/pci/pci.c:5231: * __pci_reset_function_locked - reset a PCI device function while holding\ndrivers/pci/pci.c-5232- * the @dev mutex lock.\n--\ndrivers/pci/pci.c-5251- */\ndrivers/pci/pci.c:5252:int __pci_reset_function_locked(struct pci_dev *dev)\ndrivers/pci/pci.c-5253-{\n--\ndrivers/pci/pci.c-5285-}\ndrivers/pci/pci.c:5286:EXPORT_SYMBOL_GPL(__pci_reset_function_locked);\ndrivers/pci/pci.c-5287-\n--\ndrivers/pci/pci.c=5300=void pci_init_reset_methods(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5320-/**\ndrivers/pci/pci.c:5321: * pci_reset_function - quiesce and reset a PCI device function\ndrivers/pci/pci.c-5322- * @dev: PCI device to reset\n--\ndrivers/pci/pci.c-5329- * clears all the state associated with the device.  This function differs\ndrivers/pci/pci.c:5330: * from __pci_reset_function_locked() in that it saves and restores device state\ndrivers/pci/pci.c-5331- * over the reset and takes the PCI device lock.\n--\ndrivers/pci/pci.c-5335- */\ndrivers/pci/pci.c:5336:int pci_reset_function(struct pci_dev *dev)\ndrivers/pci/pci.c-5337-{\n--\ndrivers/pci/pci.c-5354-\ndrivers/pci/pci.c:5355:\trc = __pci_reset_function_locked(dev);\ndrivers/pci/pci.c-5356-\n--\ndrivers/pci/pci.c-5364-}\ndrivers/pci/pci.c:5365:EXPORT_SYMBOL_GPL(pci_reset_function);\ndrivers/pci/pci.c-5366-\ndrivers/pci/pci.c-5367-/**\ndrivers/pci/pci.c:5368: * pci_reset_function_locked - quiesce and reset a PCI device function\ndrivers/pci/pci.c-5369- * @dev: PCI device to reset\n--\ndrivers/pci/pci.c-5376- * clears all the state associated with the device.  This function differs\ndrivers/pci/pci.c:5377: * from __pci_reset_function_locked() in that it saves and restores device state\ndrivers/pci/pci.c:5378: * over the reset.  It also differs from pci_reset_function() in that it\ndrivers/pci/pci.c-5379- * requires the PCI device lock to be held.\n--\ndrivers/pci/pci.c-5385- */\ndrivers/pci/pci.c:5386:int pci_reset_function_locked(struct pci_dev *dev)\ndrivers/pci/pci.c-5387-{\n--\ndrivers/pci/pci.c-5396-\ndrivers/pci/pci.c:5397:\trc = __pci_reset_function_locked(dev);\ndrivers/pci/pci.c-5398-\n--\ndrivers/pci/pci.c-5402-}\ndrivers/pci/pci.c:5403:EXPORT_SYMBOL_GPL(pci_reset_function_locked);\ndrivers/pci/pci.c-5404-\n--\ndrivers/pci/pci.c=5411=int pci_try_reset_function(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-5421-\tpci_dev_save_and_disable(dev);\ndrivers/pci/pci.c:5422:\trc = __pci_reset_function_locked(dev);\ndrivers/pci/pci.c-5423-\tpci_dev_restore(dev);\n--\ndrivers/pci/quirks.c=3814=static void quirk_no_pm_reset(struct pci_dev *dev)\n--\ndrivers/pci/quirks.c-3828- * monitor sync.  Advertising this support makes other layers, like VFIO,\ndrivers/pci/quirks.c:3829: * assume pci_reset_function() is viable for this device.  Mark it as\ndrivers/pci/quirks.c-3830- * unavailable to skip it when testing reset methods.\n--\ndrivers/pci/quirks.c=4005=static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)\n--\ndrivers/pci/quirks.c-4027-\t * Level Reset completes.  (BUS_MASTER is disabled in\ndrivers/pci/quirks.c:4028:\t * pci_reset_function()).\ndrivers/pci/quirks.c-4029-\t */\n"
}

19/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37097
							output: 18
							thoughts: 671
						

Thoughts:
**My PCI Liveupdate Thoughts**

Okay, so I'm looking at how PCI device resets and hotplug events are handled, and it's all pointing towards `pci_liveupdate`. The first thing that jumps out is the direct link: writing '1' to `/sys/bus/pci/devices/.../reset` triggers a PCI device reset, which ultimately calls `pci_dev_save_and_disable` and `pci_dev_restore`. Within those, I see the calls to `pci_save_state` and `pci_restore_state`, which include `pci_save_acs_state` and `pci_restore_acs_state`, and VFIO also calls the `pci_reset_function`. This is the basic reset path.

But the boot and hotplug side gets really interesting. `pci_setup_device(dev)` gets called on *every* PCI device during boot, which then calls `pci_liveupdate_setup_device(dev)`. There's also `pci_liveupdate_cleanup_device` in other related functions. Plus, bridge scanning has `pci_liveupdate_preserve_bus_numbers` and `pci_liveupdate_refuse_bus_numbers`, `pci_configure_ari` has `pci_liveupdate_adopt_ari`, and `pci_enable_acs` includes `pci_liveupdate_adopt_acs` *and* calls `pci_save_acs_state`! Then there's `pci_allocate_cap_save_buffers`, `pci_save_state`, and `pci_restore_state` which also all do ACS state saving and restoring, as well as `pci_need_dev_specific_enable_acs`, `pci_dev_specific_enable_acs`, and `pci_dev_specific_disable_acs_redir`. It's all tied together.

So, the whole `pci_liveupdate` framework appears throughout this process, and clearly, it's managing more than just *live* updates, but is a fundamental aspect of the state of the device and its ACS properties. The system is definitely saving and restoring ACS state.

This leads me to wonder about the implications of the Liveupdate Orchestrator (LUO) and, specifically, the `CONFIG_PCI_LIVEUPDATE` configuration. I need to see if syzkaller can somehow manipulate liveupdate sessions, maybe through an `ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)` or something similar to affect the PCI state. I should dig deeper into the codebase and search for anything related to `LIVEUPDATE_SESSION`.



20/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "LIVEUPDATE_SESSION"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-25- * and userspace preserves the device with\ndrivers/pci/liveupdate.c:26: * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preservation\ndrivers/pci/liveupdate.c-27- * to a file gives it proper lifecycle management, e.g. the preservation is\n--\ndrivers/pci/liveupdate.c-60- *\ndrivers/pci/liveupdate.c:61: *   # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)\ndrivers/pci/liveupdate.c-62- *   luo_preserve_file()\n--\ndrivers/pci/liveupdate.c-83- *\ndrivers/pci/liveupdate.c:84: *   # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)\ndrivers/pci/liveupdate.c-85- *   luo_file_finish_one()\n--\ninclude/linux/kho/abi/luo.h=131=struct luo_session_ser {\ninclude/linux/kho/abi/luo.h:132:\tchar name[LIVEUPDATE_SESSION_NAME_LENGTH];\ninclude/linux/kho/abi/luo.h-133-\tstruct luo_file_set_ser file_set_ser;\n--\ninclude/uapi/linux/liveupdate.h-46-/* The maximum length of session name including null termination */\ninclude/uapi/linux/liveupdate.h:47:#define LIVEUPDATE_SESSION_NAME_LENGTH 64\ninclude/uapi/linux/liveupdate.h-48-\n--\ninclude/uapi/linux/liveupdate.h=57=enum {\n--\ninclude/uapi/linux/liveupdate.h-69- * @name:\tInput; A null-terminated string for the session name, max\ninclude/uapi/linux/liveupdate.h:70: *\t\tlength %LIVEUPDATE_SESSION_NAME_LENGTH including termination\ninclude/uapi/linux/liveupdate.h-71- *\t\tcharacter.\n--\ninclude/uapi/linux/liveupdate.h=78=struct liveupdate_ioctl_create_session {\n--\ninclude/uapi/linux/liveupdate.h-80-\t__s32\t\tfd;\ninclude/uapi/linux/liveupdate.h:81:\t__u8\t\tname[LIVEUPDATE_SESSION_NAME_LENGTH];\ninclude/uapi/linux/liveupdate.h-82-};\n--\ninclude/uapi/linux/liveupdate.h-103- * operations, such as restoring individual file descriptors with\ninclude/uapi/linux/liveupdate.h:104: * LIVEUPDATE_SESSION_RETRIEVE_FD.\ninclude/uapi/linux/liveupdate.h-105- *\n--\ninclude/uapi/linux/liveupdate.h=113=struct liveupdate_ioctl_retrieve_session {\n--\ninclude/uapi/linux/liveupdate.h-115-\t__s32\t\tfd;\ninclude/uapi/linux/liveupdate.h:116:\t__u8\t\tname[LIVEUPDATE_SESSION_NAME_LENGTH];\ninclude/uapi/linux/liveupdate.h-117-};\n--\ninclude/uapi/linux/liveupdate.h-124-/**\ninclude/uapi/linux/liveupdate.h:125: * struct liveupdate_session_preserve_fd - ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)\ninclude/uapi/linux/liveupdate.h-126- * @size:  Input; sizeof(struct liveupdate_session_preserve_fd)\n--\ninclude/uapi/linux/liveupdate.h-141- * This token confirms the FD is targeted for preservation and is required for\ninclude/uapi/linux/liveupdate.h:142: * the subsequent %LIVEUPDATE_SESSION_RETRIEVE_FD call after the live update.\ninclude/uapi/linux/liveupdate.h-143- *\n--\ninclude/uapi/linux/liveupdate.h=148=struct liveupdate_session_preserve_fd {\n--\ninclude/uapi/linux/liveupdate.h-153-\ninclude/uapi/linux/liveupdate.h:154:#define LIVEUPDATE_SESSION_PRESERVE_FD\t\t\t\t\t\\\ninclude/uapi/linux/liveupdate.h-155-\t_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_PRESERVE_FD)\n--\ninclude/uapi/linux/liveupdate.h-157-/**\ninclude/uapi/linux/liveupdate.h:158: * struct liveupdate_session_retrieve_fd - ioctl(LIVEUPDATE_SESSION_RETRIEVE_FD)\ninclude/uapi/linux/liveupdate.h-159- * @size:  Input; sizeof(struct liveupdate_session_retrieve_fd)\n--\ninclude/uapi/linux/liveupdate.h=178=struct liveupdate_session_retrieve_fd {\n--\ninclude/uapi/linux/liveupdate.h-183-\ninclude/uapi/linux/liveupdate.h:184:#define LIVEUPDATE_SESSION_RETRIEVE_FD\t\t\t\t\t\\\ninclude/uapi/linux/liveupdate.h-185-\t_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD)\n--\ninclude/uapi/linux/liveupdate.h-187-/**\ninclude/uapi/linux/liveupdate.h:188: * struct liveupdate_session_finish - ioctl(LIVEUPDATE_SESSION_FINISH)\ninclude/uapi/linux/liveupdate.h-189- * @size:     Input; sizeof(struct liveupdate_session_finish)\n--\ninclude/uapi/linux/liveupdate.h-196- * This ioctl must be called once all required file descriptors for the session\ninclude/uapi/linux/liveupdate.h:197: * have been successfully retrieved (using %LIVEUPDATE_SESSION_RETRIEVE_FD) and\ninclude/uapi/linux/liveupdate.h-198- * are fully restored from the userspace and kernel perspective.\n--\ninclude/uapi/linux/liveupdate.h=211=struct liveupdate_session_finish {\n--\ninclude/uapi/linux/liveupdate.h-215-\ninclude/uapi/linux/liveupdate.h:216:#define LIVEUPDATE_SESSION_FINISH\t\t\t\t\t\\\ninclude/uapi/linux/liveupdate.h-217-\t_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH)\n--\ninclude/uapi/linux/liveupdate.h-219-/**\ninclude/uapi/linux/liveupdate.h:220: * struct liveupdate_session_get_name - ioctl(LIVEUPDATE_SESSION_GET_NAME)\ninclude/uapi/linux/liveupdate.h-221- * @size:  Input; sizeof(struct liveupdate_session_get_name)\n--\ninclude/uapi/linux/liveupdate.h=230=struct liveupdate_session_get_name {\n--\ninclude/uapi/linux/liveupdate.h-232-\t__u32\t\treserved;\ninclude/uapi/linux/liveupdate.h:233:\t__u8\t\tname[LIVEUPDATE_SESSION_NAME_LENGTH];\ninclude/uapi/linux/liveupdate.h-234-};\ninclude/uapi/linux/liveupdate.h-235-\ninclude/uapi/linux/liveupdate.h:236:#define LIVEUPDATE_SESSION_GET_NAME\t\t\t\t\t\\\ninclude/uapi/linux/liveupdate.h-237-\t_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_GET_NAME)\n--\nkernel/liveupdate/luo_internal.h=70=struct luo_session {\nkernel/liveupdate/luo_internal.h:71:\tchar name[LIVEUPDATE_SESSION_NAME_LENGTH];\nkernel/liveupdate/luo_internal.h-72-\tstruct list_head list;\n--\nkernel/liveupdate/luo_session.c=376=static const struct luo_ioctl_op luo_session_ioctl_ops[] = {\nkernel/liveupdate/luo_session.c:377:\tIOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish,\nkernel/liveupdate/luo_session.c-378-\t\t struct liveupdate_session_finish, reserved, LUO_IOCTL_INCOMING),\nkernel/liveupdate/luo_session.c:379:\tIOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd,\nkernel/liveupdate/luo_session.c-380-\t\t struct liveupdate_session_preserve_fd, token, LUO_IOCTL_OUTGOING),\nkernel/liveupdate/luo_session.c:381:\tIOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd,\nkernel/liveupdate/luo_session.c-382-\t\t struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING),\nkernel/liveupdate/luo_session.c:383:\tIOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name,\nkernel/liveupdate/luo_session.c-384-\t\t struct liveupdate_session_get_name, name, LUO_IOCTL_ALL),\n--\nkernel/liveupdate/luo_session.c=466=int luo_session_create(const char *name, struct file **filep)\nkernel/liveupdate/luo_session.c-467-{\nkernel/liveupdate/luo_session.c:468:\tsize_t len = strnlen(name, LIVEUPDATE_SESSION_NAME_LENGTH);\nkernel/liveupdate/luo_session.c-469-\tstruct luo_session *session;\n--\nkernel/liveupdate/luo_session.c-471-\nkernel/liveupdate/luo_session.c:472:\tif (len == 0 || len \u003e LIVEUPDATE_SESSION_NAME_LENGTH - 1)\nkernel/liveupdate/luo_session.c-473-\t\treturn -EINVAL;\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=56=int luo_create_session(int luo_fd, const char *name)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-59-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:60:\tsnprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, \"%.*s\",\ntools/testing/selftests/liveupdate/lib/lu_utils.c:61:\t\t LIVEUPDATE_SESSION_NAME_LENGTH - 1, name);\ntools/testing/selftests/liveupdate/lib/lu_utils.c-62-\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=69=int luo_retrieve_session(int luo_fd, const char *name)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-72-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:73:\tsnprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, \"%.*s\",\ntools/testing/selftests/liveupdate/lib/lu_utils.c:74:\t\t LIVEUPDATE_SESSION_NAME_LENGTH - 1, name);\ntools/testing/selftests/liveupdate/lib/lu_utils.c-75-\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=82=int luo_session_preserve_fd(int session_fd, int fd, __u64 token)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-89-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:90:\tif (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, \u0026arg))\ntools/testing/selftests/liveupdate/lib/lu_utils.c-91-\t\treturn -errno;\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=96=int luo_session_retrieve_fd(int session_fd, __u64 token)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-102-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:103:\tif (ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_FD, \u0026arg))\ntools/testing/selftests/liveupdate/lib/lu_utils.c-104-\t\treturn -errno;\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=110=int luo_get_session_name(int session_fd, char *name, size_t name_len)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-115-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:116:\tif (ioctl(session_fd, LIVEUPDATE_SESSION_GET_NAME, \u0026args))\ntools/testing/selftests/liveupdate/lib/lu_utils.c-117-\t\treturn -errno;\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=191=int luo_session_finish(int session_fd)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-194-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:195:\tif (ioctl(session_fd, LIVEUPDATE_SESSION_FINISH, \u0026arg) \u003c 0)\ntools/testing/selftests/liveupdate/lib/lu_utils.c-196-\t\treturn -errno;\n--\ntools/testing/selftests/liveupdate/liveupdate.c=410=TEST_F(liveupdate_device, get_session_name)\ntools/testing/selftests/liveupdate/liveupdate.c-411-{\ntools/testing/selftests/liveupdate/liveupdate.c:412:\tchar name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {};\ntools/testing/selftests/liveupdate/liveupdate.c-413-\tconst char *session_name = \"get-name-test-session\";\n--\ntools/testing/selftests/liveupdate/liveupdate.c-432- *\ntools/testing/selftests/liveupdate/liveupdate.c:433: * Verifies that a session name using the full LIVEUPDATE_SESSION_NAME_LENGTH\ntools/testing/selftests/liveupdate/liveupdate.c-434- * (minus the null terminator) can be correctly retrieved.\n--\ntools/testing/selftests/liveupdate/liveupdate.c=436=TEST_F(liveupdate_device, get_session_name_max_length)\ntools/testing/selftests/liveupdate/liveupdate.c-437-{\ntools/testing/selftests/liveupdate/liveupdate.c:438:\tchar name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {};\ntools/testing/selftests/liveupdate/liveupdate.c:439:\tchar long_name[LIVEUPDATE_SESSION_NAME_LENGTH];\ntools/testing/selftests/liveupdate/liveupdate.c-440-\tint session_fd;\n--\ntools/testing/selftests/liveupdate/luo_stress_sessions.c=21=static void run_stage_1(int luo_fd)\n--\ntools/testing/selftests/liveupdate/luo_stress_sessions.c-37-\tfor (i = 0; i \u003c NUM_SESSIONS; i++) {\ntools/testing/selftests/liveupdate/luo_stress_sessions.c:38:\t\tchar name[LIVEUPDATE_SESSION_NAME_LENGTH];\ntools/testing/selftests/liveupdate/luo_stress_sessions.c-39-\t\tint s_fd;\n--\ntools/testing/selftests/liveupdate/luo_stress_sessions.c=57=static void run_stage_2(int luo_fd, int state_session_fd)\n--\ntools/testing/selftests/liveupdate/luo_stress_sessions.c-72-\tfor (i = 0; i \u003c NUM_SESSIONS; i++) {\ntools/testing/selftests/liveupdate/luo_stress_sessions.c:73:\t\tchar name[LIVEUPDATE_SESSION_NAME_LENGTH];\ntools/testing/selftests/liveupdate/luo_stress_sessions.c-74-\t\tint s_fd;\n"
}

21/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42568
							output: 18
							thoughts: 63
						

Thoughts:
Here's my take:

**Investigating the `/dev/liveupdate` Interface**

Okay, so `liveupdate` is definitely exposed to user space, probably through a character or misc device like `/dev/liveupdate`. That's the entry point. Now, I need to understand how it's being *used*. Time to dig around. My focus is going to be on the `tools/testing/selftests/liveupdate` directory, specifically looking for how `luo_fd` – that file descriptor, I'm assuming, points back to that device node – is being opened within those selftests. Finding that open call is the key to understanding the expected interaction and the intended API. I need to see *how* these self-tests are actually using this thing.



22/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "dev/liveupdate"
}

Results:
{
  "Output": "Documentation/userspace-api/liveupdate.rst=31=and push file descriptors with a name into the store via\n--\nDocumentation/userspace-api/liveupdate.rst-33-\nDocumentation/userspace-api/liveupdate.rst:34:Services may also create their own LUO sessions (via ``/dev/liveupdate``) and\nDocumentation/userspace-api/liveupdate.rst-35-push the resulting session fds into their file descriptor store like any other\n--\ninclude/uapi/linux/liveupdate.h-3-/*\ninclude/uapi/linux/liveupdate.h:4: * Userspace interface for /dev/liveupdate\ninclude/uapi/linux/liveupdate.h-5- * Live Update Orchestrator\n--\ninclude/uapi/linux/liveupdate.h-48-\ninclude/uapi/linux/liveupdate.h:49:/* The /dev/liveupdate ioctl commands */\ninclude/uapi/linux/liveupdate.h-50-enum {\n--\ninclude/uapi/linux/liveupdate.h=57=enum {\n--\ninclude/uapi/linux/liveupdate.h-73- * Creates a new live update session for managing preserved resources.\ninclude/uapi/linux/liveupdate.h:74: * This ioctl can only be called on the main /dev/liveupdate device.\ninclude/uapi/linux/liveupdate.h-75- *\n--\ninclude/uapi/linux/liveupdate.h=78=struct liveupdate_ioctl_create_session {\n--\ninclude/uapi/linux/liveupdate.h-109- *\ninclude/uapi/linux/liveupdate.h:110: * This ioctl can only be called on the main /dev/liveupdate device when the\ninclude/uapi/linux/liveupdate.h-111- * system is in the LIVEUPDATE_STATE_UPDATED state.\n--\nkernel/liveupdate/luo_core.c=154=static int __init luo_state_setup(void)\n--\nkernel/liveupdate/luo_core.c-190- * late initcall because it initializes the outgoing tree that is needed only\nkernel/liveupdate/luo_core.c:191: * once userspace starts using /dev/liveupdate.\nkernel/liveupdate/luo_core.c-192- */\n--\nkernel/liveupdate/luo_core.c=244=bool liveupdate_enabled(void)\n--\nkernel/liveupdate/luo_core.c-252- * The IOCTL user-space control interface for the LUO subsystem.\nkernel/liveupdate/luo_core.c:253: * It registers a character device, typically found at ``/dev/liveupdate``,\nkernel/liveupdate/luo_core.c-254- * which allows a userspace agent to manage the LUO state machine and its\n--\nkernel/liveupdate/luo_core.c-258- * to this device is exclusive: only one process is permitted to have\nkernel/liveupdate/luo_core.c:259: * ``/dev/liveupdate`` open at any given time. Subsequent open attempts will\nkernel/liveupdate/luo_core.c-260- * fail with -EBUSY until the first process closes its file descriptor.\n--\nkernel/liveupdate/luo_file.c-56- * 3. Deserialize: After kexec, luo_file_deserialize() runs when session gets\nkernel/liveupdate/luo_file.c:57: *    deserialized (which is when /dev/liveupdate is first opened). It reads the\nkernel/liveupdate/luo_file.c-58- *    serialized data from the KHO memory region and reconstructs the in-memory\n--\nkernel/liveupdate/luo_session.c-23- * - Userspace Interface: Session management is driven from userspace via\nkernel/liveupdate/luo_session.c:24: *   ioctls on /dev/liveupdate.\nkernel/liveupdate/luo_session.c-25- *\n--\ntools/testing/selftests/liveupdate/lib/include/libliveupdate.h-17-\ntools/testing/selftests/liveupdate/lib/include/libliveupdate.h:18:#define LUO_DEVICE \"/dev/liveupdate\"\ntools/testing/selftests/liveupdate/lib/include/libliveupdate.h-19-\n--\ntools/testing/selftests/liveupdate/liveupdate.c-10- * This test suite verifies the functionality and behavior of the\ntools/testing/selftests/liveupdate/liveupdate.c:11: * /dev/liveupdate character device and its session management capabilities.\ntools/testing/selftests/liveupdate/liveupdate.c-12- *\n--\ntools/testing/selftests/liveupdate/liveupdate.c-32-\ntools/testing/selftests/liveupdate/liveupdate.c:33:#define LIVEUPDATE_DEV \"/dev/liveupdate\"\ntools/testing/selftests/liveupdate/liveupdate.c-34-\n--\ntools/testing/selftests/liveupdate/liveupdate.c=46=FIXTURE_TEARDOWN(liveupdate_device)\n--\ntools/testing/selftests/liveupdate/liveupdate.c-56- *\ntools/testing/selftests/liveupdate/liveupdate.c:57: * Verifies that the /dev/liveupdate device can be opened and subsequently\ntools/testing/selftests/liveupdate/liveupdate.c-58- * closed without errors. Skips if the device does not exist.\n--\ntools/testing/selftests/liveupdate/liveupdate.c=60=TEST_F(liveupdate_device, basic_open_close)\n--\ntools/testing/selftests/liveupdate/liveupdate.c-74- *\ntools/testing/selftests/liveupdate/liveupdate.c:75: * Verifies that the /dev/liveupdate device can only be opened by one process\ntools/testing/selftests/liveupdate/liveupdate.c-76- * at a time. It checks that a second attempt to open the device fails with\n"
}

23/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44322
							output: 20
							thoughts: 171
						

Thoughts:
**Diving into PCI Live Update and LUO**

Okay, let's dissect the interaction between `pci_liveupdate_register_flb` and Live Update Operations (LUO). I'm seeing this function defined in `drivers/pci/liveupdate.c`. It's pretty straightforward: it registers a file handler (`fh`) using `liveupdate_register_flb`, passing along the `pci_liveupdate_flb` structure. Simple enough. But... hold on a second. I need to know *who* is actually using this. Does any driver even call `pci_liveupdate_register_flb` in this kernel tree? I need to check the definition of `liveupdate_file_handler` across the source tree to be sure. This is where the real work begins.



24/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "liveupdate_file_handler"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-33- * represents a PCI device. Drivers therefore register their\ndrivers/pci/liveupdate.c:34: * struct liveupdate_file_handler with the PCI core:\ndrivers/pci/liveupdate.c-35- *\n--\ndrivers/pci/liveupdate.c=526=static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,\n--\ndrivers/pci/liveupdate.c-589- * preserved across the next Live Update. Drivers are expected to call\ndrivers/pci/liveupdate.c:590: * pci_liveupdate_preserve() from their struct liveupdate_file_handler\ndrivers/pci/liveupdate.c-591- * preserve() callback to ensure the outgoing struct pci_ser is already set up.\n--\ndrivers/pci/liveupdate.c=621=EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);\n--\ndrivers/pci/liveupdate.c-628- * longer be preserved across the next Live Update. Drivers are expected to call\ndrivers/pci/liveupdate.c:629: * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler\ndrivers/pci/liveupdate.c-630- * unpreserve() callback to ensure the outgoing struct pci_ser is already set\n--\ndrivers/pci/liveupdate.c=820=static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-852- * Update. Drivers must call pci_liveupdate_finish() from their struct\ndrivers/pci/liveupdate.c:853: * liveupdate_file_handler finish() callback to ensure the incoming struct\ndrivers/pci/liveupdate.c-854- * pci_ser is allocated.\n--\ndrivers/pci/liveupdate.c=981=EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);\n--\ndrivers/pci/liveupdate.c-988- * pci_liveupdate_register_flb() to register their\ndrivers/pci/liveupdate.c:989: * struct liveupdate_file_handler with the PCI core, typically at module init,\ndrivers/pci/liveupdate.c-990- * and always before any file managed by @fh can be preserved.\n--\ndrivers/pci/liveupdate.c-998- */\ndrivers/pci/liveupdate.c:999:int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\ndrivers/pci/liveupdate.c-1000-{\n--\ndrivers/pci/liveupdate.c=1004=EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);\n--\ndrivers/pci/liveupdate.c-1009- */\ndrivers/pci/liveupdate.c:1010:void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\ndrivers/pci/liveupdate.c-1011-{\n--\ninclude/linux/liveupdate.h-19-\ninclude/linux/liveupdate.h:20:struct liveupdate_file_handler;\ninclude/linux/liveupdate.h-21-struct liveupdate_flb;\n--\ninclude/linux/liveupdate.h=46=struct liveupdate_file_op_args {\ninclude/linux/liveupdate.h:47:\tstruct liveupdate_file_handler *handler;\ninclude/linux/liveupdate.h-48-\tint retrieve_status;\n--\ninclude/linux/liveupdate.h=74=struct liveupdate_file_ops {\ninclude/linux/liveupdate.h:75:\tbool (*can_preserve)(struct liveupdate_file_handler *handler,\ninclude/linux/liveupdate.h-76-\t\t\t     struct file *file);\n--\ninclude/linux/liveupdate.h-88-/**\ninclude/linux/liveupdate.h:89: * struct liveupdate_file_handler - Represents a handler for a live-updatable file type.\ninclude/linux/liveupdate.h-90- * @ops:                Callback functions\n--\ninclude/linux/liveupdate.h-100- */\ninclude/linux/liveupdate.h:101:struct liveupdate_file_handler {\ninclude/linux/liveupdate.h-102-\tconst struct liveupdate_file_ops *ops;\n--\ninclude/linux/liveupdate.h=234=int liveupdate_reboot(void);\ninclude/linux/liveupdate.h-235-\ninclude/linux/liveupdate.h:236:int liveupdate_register_file_handler(struct liveupdate_file_handler *fh);\ninclude/linux/liveupdate.h:237:void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh);\ninclude/linux/liveupdate.h-238-\ninclude/linux/liveupdate.h:239:int liveupdate_register_flb(struct liveupdate_file_handler *fh,\ninclude/linux/liveupdate.h-240-\t\t\t    struct liveupdate_flb *flb);\ninclude/linux/liveupdate.h:241:void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,\ninclude/linux/liveupdate.h-242-\t\t\t       struct liveupdate_flb *flb);\n--\ninclude/linux/liveupdate.h=257=static inline int liveupdate_reboot(void)\n--\ninclude/linux/liveupdate.h-261-\ninclude/linux/liveupdate.h:262:static inline int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)\ninclude/linux/liveupdate.h-263-{\n--\ninclude/linux/liveupdate.h-266-\ninclude/linux/liveupdate.h:267:static inline void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)\ninclude/linux/liveupdate.h-268-{\n--\ninclude/linux/liveupdate.h-270-\ninclude/linux/liveupdate.h:271:static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh,\ninclude/linux/liveupdate.h-272-\t\t\t\t\t  struct liveupdate_flb *flb)\n--\ninclude/linux/liveupdate.h-276-\ninclude/linux/liveupdate.h:277:static inline void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,\ninclude/linux/liveupdate.h-278-\t\t\t\t\t     struct liveupdate_flb *flb)\n--\ninclude/linux/pci_liveupdate.h=34=struct pci_dev;\n--\ninclude/linux/pci_liveupdate.h-36-#ifdef CONFIG_PCI_LIVEUPDATE\ninclude/linux/pci_liveupdate.h:37:int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);\ninclude/linux/pci_liveupdate.h:38:void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);\ninclude/linux/pci_liveupdate.h-39-int pci_liveupdate_preserve(struct pci_dev *dev);\n--\ninclude/linux/pci_liveupdate.h=42=bool pci_liveupdate_is_incoming(struct pci_dev *dev);\ninclude/linux/pci_liveupdate.h-43-#else\ninclude/linux/pci_liveupdate.h:44:static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\ninclude/linux/pci_liveupdate.h-45-{\n--\ninclude/linux/pci_liveupdate.h-48-\ninclude/linux/pci_liveupdate.h:49:static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\ninclude/linux/pci_liveupdate.h-50-{\n--\nkernel/liveupdate/luo_file.c-20- * Kernel modules responsible for a specific file type (e.g., memfd, vfio)\nkernel/liveupdate/luo_file.c:21: * register a \u0026struct liveupdate_file_handler. This handler provides a set of\nkernel/liveupdate/luo_file.c-22- * callbacks that LUO invokes at different stages of the update process, most\n--\nkernel/liveupdate/luo_file.c=119=static DEFINE_XARRAY(luo_preserved_files);\n--\nkernel/liveupdate/luo_file.c-122- * struct luo_file - Represents a single preserved file instance.\nkernel/liveupdate/luo_file.c:123: * @fh:            Pointer to the \u0026struct liveupdate_file_handler that manages\nkernel/liveupdate/luo_file.c-124- *                 this type of file.\n--\nkernel/liveupdate/luo_file.c=161=struct luo_file {\nkernel/liveupdate/luo_file.c:162:\tstruct liveupdate_file_handler *fh;\nkernel/liveupdate/luo_file.c-163-\tstruct file *file;\n--\nkernel/liveupdate/luo_file.c-171-\nkernel/liveupdate/luo_file.c:172:static unsigned long luo_get_id(struct liveupdate_file_handler *fh,\nkernel/liveupdate/luo_file.c-173-\t\t\t\tstruct file *file)\n--\nkernel/liveupdate/luo_file.c=230=int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)\n--\nkernel/liveupdate/luo_file.c-232-\tstruct liveupdate_file_op_args args = {0};\nkernel/liveupdate/luo_file.c:233:\tstruct liveupdate_file_handler *fh;\nkernel/liveupdate/luo_file.c-234-\tstruct luo_file *luo_file;\n--\nkernel/liveupdate/luo_file.c=719=static int luo_file_deserialize_one(struct luo_file_set *file_set,\n--\nkernel/liveupdate/luo_file.c-721-{\nkernel/liveupdate/luo_file.c:722:\tstruct liveupdate_file_handler *fh;\nkernel/liveupdate/luo_file.c-723-\tbool handler_found = false;\n--\nkernel/liveupdate/luo_file.c=855=void luo_file_set_destroy(struct luo_file_set *file_set)\n--\nkernel/liveupdate/luo_file.c-863- * liveupdate_register_file_handler - Register a file handler with LUO.\nkernel/liveupdate/luo_file.c:864: * @fh: Pointer to a caller-allocated \u0026struct liveupdate_file_handler.\nkernel/liveupdate/luo_file.c-865- * The caller must initialize this structure, including a unique\n--\nkernel/liveupdate/luo_file.c-873- */\nkernel/liveupdate/luo_file.c:874:int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_file.c-875-{\nkernel/liveupdate/luo_file.c:876:\tstruct liveupdate_file_handler *fh_iter;\nkernel/liveupdate/luo_file.c-877-\tint err;\n--\nkernel/liveupdate/luo_file.c-919- */\nkernel/liveupdate/luo_file.c:920:void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_file.c-921-{\n--\nkernel/liveupdate/luo_flb.c=211=void liveupdate_flb_put_incoming(struct liveupdate_flb *flb)\n--\nkernel/liveupdate/luo_flb.c-252- */\nkernel/liveupdate/luo_flb.c:253:int luo_flb_file_preserve(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_flb.c-254-{\n--\nkernel/liveupdate/luo_flb.c-288- */\nkernel/liveupdate/luo_flb.c:289:void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_flb.c-290-{\n--\nkernel/liveupdate/luo_flb.c-309- */\nkernel/liveupdate/luo_flb.c:310:void luo_flb_file_finish(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_flb.c-311-{\n--\nkernel/liveupdate/luo_flb.c-319-\nkernel/liveupdate/luo_flb.c:320:static void luo_flb_unregister_one(struct liveupdate_file_handler *fh,\nkernel/liveupdate/luo_flb.c-321-\t\t\t\t   struct liveupdate_flb *flb)\n--\nkernel/liveupdate/luo_flb.c-362- */\nkernel/liveupdate/luo_flb.c:363:void luo_flb_unregister_all(struct liveupdate_file_handler *fh)\nkernel/liveupdate/luo_flb.c-364-{\n--\nkernel/liveupdate/luo_flb.c-397- */\nkernel/liveupdate/luo_flb.c:398:int liveupdate_register_flb(struct liveupdate_file_handler *fh,\nkernel/liveupdate/luo_flb.c-399-\t\t\t    struct liveupdate_flb *flb)\n--\nkernel/liveupdate/luo_flb.c-477- */\nkernel/liveupdate/luo_flb.c:478:void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,\nkernel/liveupdate/luo_flb.c-479-\t\t\t       struct liveupdate_flb *flb)\n--\nkernel/liveupdate/luo_internal.h=99=void luo_file_set_destroy(struct luo_file_set *file_set);\nkernel/liveupdate/luo_internal.h-100-\nkernel/liveupdate/luo_internal.h:101:int luo_flb_file_preserve(struct liveupdate_file_handler *fh);\nkernel/liveupdate/luo_internal.h:102:void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);\nkernel/liveupdate/luo_internal.h:103:void luo_flb_file_finish(struct liveupdate_file_handler *fh);\nkernel/liveupdate/luo_internal.h:104:void luo_flb_unregister_all(struct liveupdate_file_handler *fh);\nkernel/liveupdate/luo_internal.h-105-int __init luo_flb_setup_outgoing(u64 *flbs_pa);\n--\nkernel/liveupdate/luo_internal.h=107=void luo_flb_serialize(void);\n--\nkernel/liveupdate/luo_internal.h-109-#ifdef CONFIG_LIVEUPDATE_TEST\nkernel/liveupdate/luo_internal.h:110:void liveupdate_test_register(struct liveupdate_file_handler *fh);\nkernel/liveupdate/luo_internal.h-111-#else\nkernel/liveupdate/luo_internal.h:112:static inline void liveupdate_test_register(struct liveupdate_file_handler *fh) { }\nkernel/liveupdate/luo_internal.h-113-#endif\n--\nlib/tests/liveupdate.c=87=static void liveupdate_test_init(void)\n--\nlib/tests/liveupdate.c-114-\nlib/tests/liveupdate.c:115:void liveupdate_test_register(struct liveupdate_file_handler *fh)\nlib/tests/liveupdate.c-116-{\n--\nmm/memfd_luo.c=518=static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)\n--\nmm/memfd_luo.c-579-\nmm/memfd_luo.c:580:static bool memfd_luo_can_preserve(struct liveupdate_file_handler *handler,\nmm/memfd_luo.c-581-\t\t\t\t   struct file *file)\n--\nmm/memfd_luo.c=593=static const struct liveupdate_file_ops memfd_luo_file_ops = {\n--\nmm/memfd_luo.c-603-\nmm/memfd_luo.c:604:static struct liveupdate_file_handler memfd_luo_handler = {\nmm/memfd_luo.c-605-\t.ops = \u0026memfd_luo_file_ops,\n"
}

25/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49029
							output: 22
							thoughts: 3180
						

Thoughts:
**PCI Live Update: Reachability and Fuzzing Considerations**

Alright, let's break this down. This commit introduces PCI Live Update support, specifically the `drivers/pci/liveupdate.c` file and its associated functions (`pci_liveupdate_register_flb`, `pci_liveupdate_preserve`, etc.). The initial intent seems to be for drivers like vfio-pci variants, using their per-device cdevs. However, as it stands, no *existing* drivers are actually registering file handlers or calling `pci_liveupdate_preserve`. That's the key: are any of these functions *currently* being used? Or could they be called with the new changes?

First, I checked `pci_setup_device(dev)` which calls `pci_liveupdate_setup_device(dev)`. This function does get executed on every PCI device during boot, as does `pci_liveupdate_flb_get_incoming()`, which does call the `liveupdate_flb_get_incoming` which is our entry point! This makes a call to the function we are interested in, `pci_liveupdate_setup_device`.

Then I looked at the call to `pci_scan_bridge_extend` and `pci_device_shutdown` where `pci_liveupdate_freeze` is called. Also, there's `pci_release_dev` which calls `pci_liveupdate_cleanup_device`. All of these functions are reached during boot, shutdown, and the lifecycle of PCI devices.

Next I went through ACS state saving and restoring, which also calls these functions. Also, there are the `pci_dev_acs_ops` functions, as well as `pci_configure_ari`, which calls both `pci_liveupdate_adopt_acs` and `pci_liveupdate_adopt_ari`. I needed to check these functions.

Crucially, I dug into the `pci_liveupdate_adopt_acs` and `pci_liveupdate_adopt_ari` functions and their return values when `CONFIG_PCI_LIVEUPDATE` is disabled or not actively in use. Both can return `-EINVAL` which evaluates to 0.

Now, the important question: is this code *reachable* in a typical fuzzing environment (e.g., QEMU with syzkaller)? And it is. Syzkaller *can* interact with PCI devices through `/sys/bus/pci/devices/...` (rescan, remove, reset, config), and PCI reset, rescan, and save/restore state are all possible interactions. Even if the live update feature isn't actively enabled, the *hooks* are present. The *code* in the `pci_liveupdate` path is called. However, `pci_liveupdate_preserve` is not called, neither is `pci_liveupdate_register_flb` called, nor `pci_flb_preserve`, all due to the fact that the initial file handler is never registered.

Therefore, because this code *is* reachable – even if it *seems* like no driver is using it fully (yet) – it *should* be fuzzed. The logic modifications, refactoring, and potential new assertions warrant the effort. We should run it!



26/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "pci_liveupdate_flb"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c=404=static void pci_flb_finish(struct liveupdate_flb_op_args *args)\n--\ndrivers/pci/liveupdate.c-416-\ndrivers/pci/liveupdate.c:417:static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {\ndrivers/pci/liveupdate.c-418-\t.preserve = pci_flb_preserve,\n--\ndrivers/pci/liveupdate.c-424-\ndrivers/pci/liveupdate.c:425:static struct liveupdate_flb pci_liveupdate_flb = {\ndrivers/pci/liveupdate.c:426:\t.ops = \u0026pci_liveupdate_flb_ops,\ndrivers/pci/liveupdate.c-427-\t.compatible = PCI_LUO_FLB_COMPATIBLE,\n--\ndrivers/pci/liveupdate.c-429-\ndrivers/pci/liveupdate.c:430:static void pci_liveupdate_flb_put_outgoing(void)\ndrivers/pci/liveupdate.c-431-{\ndrivers/pci/liveupdate.c:432:\tliveupdate_flb_put_outgoing(\u0026pci_liveupdate_flb);\ndrivers/pci/liveupdate.c-433-}\ndrivers/pci/liveupdate.c-434-\ndrivers/pci/liveupdate.c:435:static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)\ndrivers/pci/liveupdate.c-436-{\n--\ndrivers/pci/liveupdate.c-439-\ndrivers/pci/liveupdate.c:440:\tret = liveupdate_flb_get_outgoing(\u0026pci_liveupdate_flb, (void **)\u0026outgoing);\ndrivers/pci/liveupdate.c-441-\tif (ret)\n--\ndrivers/pci/liveupdate.c=598=int pci_liveupdate_preserve(struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-605-\ndrivers/pci/liveupdate.c:606:\toutgoing = pci_liveupdate_flb_get_outgoing();\ndrivers/pci/liveupdate.c-607-\tif (IS_ERR(outgoing))\n--\ndrivers/pci/liveupdate.c-617-\ndrivers/pci/liveupdate.c:618:\tpci_liveupdate_flb_put_outgoing();\ndrivers/pci/liveupdate.c-619-\treturn ret;\n--\ndrivers/pci/liveupdate.c=636=void pci_liveupdate_unpreserve(struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-641-\ndrivers/pci/liveupdate.c:642:\toutgoing = pci_liveupdate_flb_get_outgoing();\ndrivers/pci/liveupdate.c-643-\tif (IS_ERR(outgoing)) {\n--\ndrivers/pci/liveupdate.c-648-\tpci_liveupdate_unpreserve_path(outgoing, dev, /*end=*/NULL);\ndrivers/pci/liveupdate.c:649:\tpci_liveupdate_flb_put_outgoing();\ndrivers/pci/liveupdate.c-650-}\ndrivers/pci/liveupdate.c=651=EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);\ndrivers/pci/liveupdate.c-652-\ndrivers/pci/liveupdate.c:653:static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)\ndrivers/pci/liveupdate.c-654-{\n--\ndrivers/pci/liveupdate.c-657-\ndrivers/pci/liveupdate.c:658:\tret = liveupdate_flb_get_incoming(\u0026pci_liveupdate_flb, (void **)\u0026incoming);\ndrivers/pci/liveupdate.c-659-\n--\ndrivers/pci/liveupdate.c-669-\t * Live Update is enabled and there is incoming FLB data, but none of it\ndrivers/pci/liveupdate.c:670:\t * matches pci_liveupdate_flb.compatible.\ndrivers/pci/liveupdate.c-671-\t */\n--\ndrivers/pci/liveupdate.c-675-\t/*\ndrivers/pci/liveupdate.c:676:\t * There is incoming FLB data that matches pci_liveupdate_flb.compatible\ndrivers/pci/liveupdate.c-677-\t * but retrieve failed (pci_flb_retrieve() returned an error or LUO\ndrivers/pci/liveupdate.c:678:\t * failed to acquire a reference to pci_liveupdate_flb_ops.owner).\ndrivers/pci/liveupdate.c-679-\t */\n--\ndrivers/pci/liveupdate.c-685-\ndrivers/pci/liveupdate.c:686:static void pci_liveupdate_flb_put_incoming(void)\ndrivers/pci/liveupdate.c-687-{\ndrivers/pci/liveupdate.c:688:\tliveupdate_flb_put_incoming(\u0026pci_liveupdate_flb);\ndrivers/pci/liveupdate.c-689-}\n--\ndrivers/pci/liveupdate.c=765=void pci_liveupdate_setup_device(struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-772-\ndrivers/pci/liveupdate.c:773:\tincoming = pci_liveupdate_flb_get_incoming();\ndrivers/pci/liveupdate.c-774-\tif (!incoming)\n--\ndrivers/pci/liveupdate.c-786-\tif (!dev_ser || !dev_ser-\u003erefcount) {\ndrivers/pci/liveupdate.c:787:\t\tpci_liveupdate_flb_put_incoming();\ndrivers/pci/liveupdate.c-788-\t\treturn;\n--\ndrivers/pci/liveupdate.c-794-\ndrivers/pci/liveupdate.c:795:\tpci_liveupdate_flb_put_incoming();\ndrivers/pci/liveupdate.c-796-}\n--\ndrivers/pci/liveupdate.c=858=void pci_liveupdate_finish(struct pci_dev *dev)\n--\ndrivers/pci/liveupdate.c-863-\ndrivers/pci/liveupdate.c:864:\tincoming = pci_liveupdate_flb_get_incoming();\ndrivers/pci/liveupdate.c-865-\tif (!incoming) {\n--\ndrivers/pci/liveupdate.c-874-\ndrivers/pci/liveupdate.c:875:\tpci_liveupdate_flb_put_incoming();\ndrivers/pci/liveupdate.c-876-}\n--\ndrivers/pci/liveupdate.c=999=int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)\n--\ndrivers/pci/liveupdate.c-1001-\tpr_debug(\"Registering file handler \\\"%s\\\"\\n\", fh-\u003ecompatible);\ndrivers/pci/liveupdate.c:1002:\treturn liveupdate_register_flb(fh, \u0026pci_liveupdate_flb);\ndrivers/pci/liveupdate.c-1003-}\n--\ndrivers/pci/liveupdate.c=1010=void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)\n--\ndrivers/pci/liveupdate.c-1012-\tpr_debug(\"Unregistering file handler \\\"%s\\\"\\n\", fh-\u003ecompatible);\ndrivers/pci/liveupdate.c:1013:\tliveupdate_unregister_flb(fh, \u0026pci_liveupdate_flb);\ndrivers/pci/liveupdate.c-1014-}\n"
}

27/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54996
							output: 35
							thoughts: 310
						

Thoughts:
**My Initial Assessment of the PCI Live Update Implementation**

Okay, so here's what's immediately striking me about this `pci_liveupdate` structure.  `pci_liveupdate_register_flb` is the ONLY function registering `pci_liveupdate_flb` with the Live Update Object, using `liveupdate_register_flb`. That’s a pretty tight coupling. Furthermore, `pci_liveupdate_preserve()` appears to be the lone function responsible for actually adding devices into `pci_liveupdate_flb`. Now, the really odd part... `pci_liveupdate_preserve()` has ZERO call sites within the entire codebase. This is... unexpected, to put it mildly. Where are these devices being preserved?

Wait a minute, what about those modifications to the PCI core files - specifically `drivers/pci/pci.c`, `drivers/pci/quirks.c`, and `drivers/pci/probe.c`?  Those changes *must* be relevant. Let's delve into what's actually been modified.

First, `drivers/pci/pci.c`: In the `pci_allocate_cap_save_buffers(struct pci_dev *dev)` function, there's this addition:

```c
 	error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,
 					    sizeof(u16));
 	if (error)
 		pci_err(dev, "unable to allocate suspend buffer for ACS\n");
```

So, this looks like it's setting up an ACS extended capability save buffer. Alright, now the obvious question: *where* is `pci_allocate_cap_save_buffers` actually being called? I need to grep for that function name to get a handle on the context. Let's find out!



28/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "pci_allocate_cap_save_buffers",
  "PathPrefix": "drivers/pci/"
}

Results:
{
  "Output": "drivers/pci/pci.c=3572=int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)\n--\ndrivers/pci/pci.c-3577-/**\ndrivers/pci/pci.c:3578: * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities\ndrivers/pci/pci.c-3579- * @dev: the PCI device\ndrivers/pci/pci.c-3580- */\ndrivers/pci/pci.c:3581:void pci_allocate_cap_save_buffers(struct pci_dev *dev)\ndrivers/pci/pci.c-3582-{\n--\ndrivers/pci/pci.h=247=struct pci_cap_saved_state {\n--\ndrivers/pci/pci.h-251-\ndrivers/pci/pci.h:252:void pci_allocate_cap_save_buffers(struct pci_dev *dev);\ndrivers/pci/pci.h-253-void pci_free_cap_save_buffers(struct pci_dev *dev);\n--\ndrivers/pci/probe.c=2658=static void pci_init_capabilities(struct pci_dev *dev)\n--\ndrivers/pci/probe.c-2664-\t/* Buffers for saving PCIe and PCI-X capabilities */\ndrivers/pci/probe.c:2665:\tpci_allocate_cap_save_buffers(dev);\ndrivers/pci/probe.c-2666-\n"
}

29/2 2026/09/21 01:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56184
							output: 20
							thoughts: 1339
						

Thoughts:
**PCI Live Update and ACS State Management: A Deep Dive**

Okay, so here's what's running through my head after looking at this code. It seems like a new mechanism is being implemented within the PCI subsystem, and it's quite involved.

First, `pci_allocate_cap_save_buffers` is being called during the initialization of *every* PCIe device. This immediately sets off alarms in my expert brain – global actions like this need careful consideration. The function calls `pci_add_ext_cap_save_buffer` to save the ACS capability.

Next, I see that the state save/restore process revolves around the `pci_save_acs_state` and `pci_restore_acs_state` functions. The save function is straightforward: it reads the ACS control register and saves its contents. The restore function is more interesting. It now checks the return of a new function `pci_need_dev_specific_enable_acs` before trying to restore the ACS state. If this function returns true, the code then skips restoration of the ACS state. Instead it calls `pci_enable_acs` from scratch. The call flow of the save code is now modified to call `pci_save_acs_state` at the end of the `pci_enable_acs` function.

Now, this `pci_need_dev_specific_enable_acs` function seems key. Digging into `drivers/pci/quirks.c`, I see a new structure `pci_dev_acs_ops` and a corresponding lookup function, `pci_dev_acs_ops_get`. This structure allows for device-specific ACS enablement/disablement logic, likely to handle devices with quirky or non-standard ACS implementations. This means there's now a mechanism to create device specific functions that can override default ACS control.

This makes it clear: the `pci_need_dev_specific_enable_acs` function is checking if a device has a known quirk that requires a custom ACS enablement procedure. If so, the standard ACS state restore is skipped and a device specific function is called.

The use of `pci_liveupdate_adopt_acs` in the `pci_enable_acs` suggests this is related to some form of live update functionality. And then the `pci_liveupdate_adopt_acs` calls `pci_save_acs_state`. This means that ACS state is being saved even *before* it is enabled, possibly to restore the initial state?

Further inspection reveals that `pci_liveupdate_setup_device` and `pci_liveupdate_cleanup_device` are also involved, suggesting that live update is being used to manage device setup and cleanup, and these functions are likely involved in preparing the device for live update or cleaning up after it. Additionally, I see that the code is taking the live update status into account in the bus scan. So this likely has something to do with preserving bus numbers.

My primary questions now are: what specific devices are being targeted by these quirks? What's the exact flow and purpose of this live update mechanism? Is it really a live update or something else? And, most importantly, is this `CONFIG_PCI_LIVEUPDATE` enabled by default?

A quick check shows that `CONFIG_PCI_LIVEUPDATE` is not enabled by default. Now, I should double-check the configuration of the target systems to ensure I get to the bottom of the implications of this feature.



30/2 2026/09/21 01:12 tool grepper
0m Args:
{
  "Expression": "CONFIG_PCI_LIVEUPDATE"
}

Results:
{
  "Output": "drivers/pci/Makefile=18=obj-$(CONFIG_GENERIC_PCI_IOMAP) += iomap.o\ndrivers/pci/Makefile:19:obj-$(CONFIG_PCI_LIVEUPDATE)\t+= liveupdate.o\ndrivers/pci/Makefile-20-endif\n--\ndrivers/pci/liveupdate.h-12-\ndrivers/pci/liveupdate.h:13:#ifdef CONFIG_PCI_LIVEUPDATE\ndrivers/pci/liveupdate.h-14-void pci_liveupdate_setup_device(struct pci_dev *dev);\n--\ninclude/linux/pci.h=357=struct pci_dev {\n--\ninclude/linux/pci.h-601-#endif\ninclude/linux/pci.h:602:#ifdef CONFIG_PCI_LIVEUPDATE\ninclude/linux/pci.h-603-\tstruct pci_liveupdate liveupdate;\n--\ninclude/linux/pci_liveupdate.h=34=struct pci_dev;\ninclude/linux/pci_liveupdate.h-35-\ninclude/linux/pci_liveupdate.h:36:#ifdef CONFIG_PCI_LIVEUPDATE\ninclude/linux/pci_liveupdate.h-37-int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);\n"
}

31/2 2026/09/21 01:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 58626
							output: 33
							thoughts: 84
						

Thoughts:
**Examining the Kernel Configuration for Live Updates**

Okay, let's dive into this `CONFIG_PCI_LIVEUPDATE` situation. I need to understand its dependencies. First, the configuration system says it relies on `PCI`, `LIVEUPDATE`, `64BIT`, and the negation of `CARDBUS`. Makes sense, considering the live update concept and the need for a modern PCI setup. Now, what's behind `LIVEUPDATE` itself? Ah, it depends on `KEXEC_HANDOVER`. That's the key. I need to understand how the kernel's handover mechanism comes into play. Time to check the `kernel/liveupdate/Kconfig` file. I'll need to explore that further to gain full understanding.



32/2 2026/09/21 01:13 tool read-file
0m Args:
{
  "File": "kernel/liveupdate/Kconfig",
  "FirstLine": 1,
  "LineCount": 40
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\t\n   3:\tmenu \"Live Update and Kexec HandOver\"\n   4:\t\n   5:\tconfig KEXEC_HANDOVER\n   6:\t\tbool \"kexec handover\"\n   7:\t\tdepends on ARCH_SUPPORTS_KEXEC_HANDOVER \u0026\u0026 ARCH_SUPPORTS_KEXEC_FILE\n   8:\t\tselect MEMBLOCK_KHO_SCRATCH\n   9:\t\tselect KEXEC_FILE\n  10:\t\tselect LIBFDT\n  11:\t\tselect CMA\n  12:\t\thelp\n  13:\t\t  Allow kexec to hand over state across kernels by generating and\n  14:\t\t  passing additional metadata to the target kernel. This is useful\n  15:\t\t  to keep data or state alive across the kexec. For this to work,\n  16:\t\t  both source and target kernels need to have this option enabled.\n  17:\t\n  18:\tconfig KEXEC_HANDOVER_DEBUG\n  19:\t\tbool \"Enable Kexec Handover debug checks\"\n  20:\t\tdepends on KEXEC_HANDOVER\n  21:\t\thelp\n  22:\t\t  This option enables extra sanity checks for the Kexec Handover\n  23:\t\t  subsystem. Since, KHO performance is crucial in live update\n  24:\t\t  scenarios and the extra code might be adding overhead it is\n  25:\t\t  only optionally enabled.\n  26:\t\n  27:\tconfig KEXEC_HANDOVER_DEBUGFS\n  28:\t\tbool \"kexec handover debugfs interface\"\n  29:\t\tdefault KEXEC_HANDOVER\n  30:\t\tdepends on KEXEC_HANDOVER\n  31:\t\tselect DEBUG_FS\n  32:\t\thelp\n  33:\t\t  Allow to control kexec handover device tree via debugfs\n  34:\t\t  interface, i.e. finalize the state or aborting the finalization.\n  35:\t\t  Also, enables inspecting the KHO fdt trees with the debugfs binary\n  36:\t\t  blobs.\n  37:\t\n  38:\tconfig KEXEC_HANDOVER_ENABLE_DEFAULT\n  39:\t\tbool \"Enable kexec handover by default\"\n  40:\t\tdepends on KEXEC_HANDOVER\n"
}

33/2 2026/09/21 01:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 59459
							output: 34
							thoughts: 1725
						

Thoughts:
**Analyzing PCI Live Update and ACS State Management**

Okay, let's break this down systematically. My initial question: does enabling `CONFIG_PCI_LIVEUPDATE` even make `WorthFuzzing` true? First, I need to verify reachability in standard QEMU/GCE environments; that's critical. I'll need to check *both* cases: with `CONFIG_PCI_LIVEUPDATE=y` and also with it disabled.

With live update enabled, specifically, what code *actually* executes?  It seems `pci_setup_device()` calls `pci_liveupdate_setup_device(dev)`. Inside that function, the important function call is `pci_liveupdate_flb_get_incoming()`. What does this do?  If it returns something, it indicates an incoming live update, otherwise, in the normal boot path, it returns `NULL`. The code clearly shows that if a normal boot occurs (no live update data), the return values are `-ENODATA`, `-ENOENT`, or `-EOPNOTSUPP`, and the function returns `NULL`.

Now the question is, can syzkaller *trigger* `pci_liveupdate_preserve()`?  But wait. There are NO callers of `pci_liveupdate_preserve()`! Not in the entire kernel!  I searched, and there is only a declaration, but zero callers. The same is true for `pci_liveupdate_unpreserve()`, `pci_liveupdate_finish()`, `pci_liveupdate_register_flb()`, and `pci_liveupdate_is_incoming()`... Nothing!

WAIT!  This is important. Can any of the live update functions in `drivers/pci/liveupdate.c` *ever* execute if `pci_liveupdate_register_flb` is *never* called?  And I think that's the key.  LUO *only* calls `flb->ops->preserve()` if the FLB was registered with a file handler via `liveupdate_register_flb(fh, flb)`, and that a session has the `LIVEUPDATE_SESSION_PRESERVE_FD` called on a file matching that file handler.  Since NO driver calls `pci_liveupdate_register_flb()`, `pci_liveupdate_flb` is never registered with any file handler! This means that LUO will *never* invoke any of those functions, such as `pci_liveupdate_flb_ops.preserve`!  And since, there are no other callers, the liveupdate functions will never be called.

BUT WAIT! What about the *rest* of the patch series related to ACS state? I should focus on the following functions: `pci_save_acs_state(dev)`, `pci_restore_acs_state(dev)`, `pci_allocate_cap_save_buffers(dev)`, `pci_enable_acs(dev)` from `drivers/pci/pci.c`, and, from `drivers/pci/quirks.c`, `pci_dev_acs_ops_get(dev)`, `pci_need_dev_specific_enable_acs(dev)`, `pci_dev_specific_enable_acs(dev)` and `pci_dev_specific_disable_acs_redir(dev)`.

Are *those* reachable? Let's start with `pci_allocate_cap_save_buffers(dev)`. The code indicates that it calls `pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS, sizeof(u16))`. It gets called within `pci_init_capabilities()` in `drivers/pci/probe.c`. This is called for *every* PCIe device during initialization/probing!  So, if a device has an ACS capability, the system allocates the buffer! During `pci_save_state(dev)`, `pci_save_acs_state(dev)` is called!  And when is *that* called? When a device is added: `pci_bus_add_device(dev)` which then calls `pci_save_state(dev)!  I need to double-check this though by grepping for `pci_save_state` in `drivers/pci/bus.c`.



34/2 2026/09/21 01:13 tool grepper
0m Args:
{
  "Expression": "pci_save_state",
  "PathPrefix": "drivers/pci/bus.c"
}

Results:
{
  "Output": "drivers/pci/bus.c=345=void pci_bus_add_device(struct pci_dev *dev)\n--\ndrivers/pci/bus.c-360-\t/* Save config space for error recoverability */\ndrivers/pci/bus.c:361:\tpci_save_state(dev);\ndrivers/pci/bus.c-362-\n"
}

35/2 2026/09/21 01:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 62276
							output: 24
							thoughts: 5047
						

Thoughts:
**PCI Core Patch Analysis for Fuzzing Suitability**

Okay, so I've got this patch that dives deep into the PCI subsystem. My initial instinct, after seeing the code modifications, is that this *needs* to be fuzzed. Let's break down why.

First, I see changes directly impacting the PCI save/restore state process. `pci_save_state`, `pci_restore_state`, `pci_save_acs_state`, and `pci_restore_acs_state` are all getting touched. The code is refactored to now utilize saved ACS state more explicitly through `pci_write_config_word`, and  `pci_allocate_cap_save_buffers` are modified. This kind of state management is a prime target for fuzzing because subtle changes here can trigger all sorts of data corruption or device misconfiguration scenarios.

I know Syzkaller can manipulate PCI devices through sysfs (`/sys/bus/pci/rescan`, `/sys/bus/pci/devices/.../reset`, `/sys/bus/pci/devices/.../remove`) and potentially VFIO. I can get to `pci_restore_state` and other functions via `echo 1 > /sys/bus/pci/devices/.../reset`, or `VFIO_DEVICE_RESET` ioctls or power management! That means these code paths are *definitely* reachable in the standard fuzzing environment.

Now, checking against the negative criteria, the patch doesn't *only* modify documentation, user-space tools, or unreachable hardware. It modifies the PCI core, which is essential to virtualized PCI devices. This means it's not excluded by the "ONLY IF all modified code falls strictly..." clause.

Specifically, the patch modifies `drivers/pci/pci.c`, `drivers/pci/quirks.c`, `drivers/pci/probe.c`, and `drivers/pci/liveupdate.c`. It's refactoring ACS quirk handling (e.g. `pci_dev_acs_ops_get`, `pci_need_dev_specific_enable_acs`, `pci_dev_specific_enable_acs`). The bridge scanning logic in `pci_scan_bridge_extend` is modified, which can be triggered on `rescan`. It's also making changes to PCI Live Update core support. These are core PCI functions.

Remember that I can't take claims of "no functional change" or "refactoring" at face value. Any reorganization of code in core subsystems can lead to new bugs. Also, it's introducing more explicit save/restore mechanisms for the ACS state. This is absolutely something I need to check.

The patch also introduces new logic within `pci_liveupdate_cleanup_device` and `pci_liveupdate_setup_device` and touches  `pci_configure_ari`, meaning that the new live update code and bridge scanning logic could have subtle interactions that I need to check.

Given the potential for fuzzing via sysfs, VFIO, and the refactoring and state management in core PCI code, this patch meets the criteria for fuzzing. The changes are reachable and touch executable core PCI code. I am confident to report `WorthFuzzing=true`.



36/2 2026/09/21 01:13 tool grepper
0m Args:
{
  "Expression": "ARCH_SUPPORTS_KEXEC_HANDOVER"
}

Results:
{
  "Output": "arch/arm64/Kconfig=1708=config ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG\n--\narch/arm64/Kconfig-1710-\narch/arm64/Kconfig:1711:config ARCH_SUPPORTS_KEXEC_HANDOVER\narch/arm64/Kconfig-1712-\tdef_bool y\n--\narch/x86/Kconfig=1978=config ARCH_SUPPORTS_KEXEC_JUMP\n--\narch/x86/Kconfig-1980-\narch/x86/Kconfig:1981:config ARCH_SUPPORTS_KEXEC_HANDOVER\narch/x86/Kconfig-1982-\tdef_bool X86_64\n--\nkernel/liveupdate/Kconfig=5=config KEXEC_HANDOVER\nkernel/liveupdate/Kconfig-6-\tbool \"kexec handover\"\nkernel/liveupdate/Kconfig:7:\tdepends on ARCH_SUPPORTS_KEXEC_HANDOVER \u0026\u0026 ARCH_SUPPORTS_KEXEC_FILE\nkernel/liveupdate/Kconfig-8-\tselect MEMBLOCK_KHO_SCRATCH\n"
}

37/2 2026/09/21 01:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 68348
							output: 20
							thoughts: 3427
						

Thoughts:
**PCI Live Update and ACS State Management: A Fuzzing Perspective**

Okay, so `ARCH_SUPPORTS_KEXEC_HANDOVER` is definitely defined as `def_bool X86_64` on amd64, and `ARCH_SUPPORTS_KEXEC_FILE` is also enabled. Good, that sets the stage for `KEXEC_HANDOVER` and, by extension, `LIVEUPDATE`. Let's confirm that `PCI_QUIRKS` is standard on x86, which it is. I'll need to check the Kconfig files. The `#ifdef CONFIG_PCI_QUIRKS` guard in `pci.h` points me in the right direction for the ACS quirk logic.

The Liveupdate Orchestrator (LUO) and `/dev/liveupdate` are interesting components here. Checking `kernel/liveupdate/Kconfig` reveals that `LIVEUPDATE` depends on `KEXEC_HANDOVER`, and `PCI_LIVEUPDATE` depends on `PCI`, `LIVEUPDATE`, `64BIT` and `!CARDBUS`.

Now, the crucial question: can `liveupdate.c` itself be fuzzed? Can a user-space program trigger any code in `drivers/pci/liveupdate.c`? Let's break down the call chains:

1.  `pci_liveupdate_setup_device(dev)` is called by `pci_setup_device(dev)` during PCI device enumeration. It uses a write lock `rwsem_write` to protect access to  `pci_liveupdate.rwsem`. The function also calls `pci_liveupdate_flb_get_incoming()`, returning immediately if no incoming FLB exists.
2.  `pci_liveupdate_cleanup_device(dev)` is called during PCI device cleanup. It checks for outgoing or incoming flags and prints a `pci_WARN` if set. The function is called by `pci_setup_device` and `pci_release_dev`
3.  `pci_liveupdate_freeze(dev)`: Called by `pci_device_shutdown(dev)`
4.  `pci_liveupdate_preserve_bus_numbers()` is called by `pci_scan_bridge_extend()`.
5.  `pci_liveupdate_refuse_bus_numbers(bus, dev)` is also called by `pci_scan_bridge_extend()`.
6.  `pci_liveupdate_adopt_acs(dev)`:  Called by `pci_enable_acs(dev)`.
7.  `pci_liveupdate_adopt_ari(bridge)`:  Called by `pci_configure_ari(dev)`.
8.  `pci_liveupdate_is_outgoing(dev)`:  Called by `pci_device_shutdown(dev)`.
9.  `pci_liveupdate_preserve(dev)` and `pci_liveupdate_unpreserve(dev)`, `pci_liveupdate_finish(dev)`, `pci_liveupdate_is_incoming(dev)`, `pci_liveupdate_register_flb(fh)`, and `pci_liveupdate_unregister_flb(fh)`, and `pci_liveupdate_flb_ops` are never called.

Wait a second. What about the significant changes in `drivers/pci/pci.c`, `drivers/pci/quirks.c`, and `drivers/pci/probe.c`? Those functions are pivotal: `pci_save_acs_state`, `pci_restore_acs_state`, `pci_enable_acs`, `pci_restore_state`, `pci_allocate_cap_save_buffers`, `pci_configure_ari`, `pci_need_dev_specific_enable_acs`, `pci_dev_specific_enable_acs`, `pci_dev_specific_disable_acs_redir`, `pci_scan_bridge_extend`, `pci_setup_device`, `pci_release_dev`, `pci_liveupdate_setup_device`, `pci_liveupdate_cleanup_device`, `pci_liveupdate_preserve_bus_numbers`, `pci_liveupdate_refuse_bus_numbers`, `pci_liveupdate_adopt_acs`, `pci_liveupdate_adopt_ari`. *Are* those functions reachable?

Absolutely. Those functions are *directly* involved in standard PCI enumeration, bus scanning, bridge configuration, ACS setup and quirks, and device state save/restore *during reset*, which userspace triggers through `/sys/bus/pci


38/2 2026/09/21 01:14 tool grepper
0m Args:
{
  "Expression": "config PCI_QUIRKS"
}

Results:
{
  "Output": "drivers/pci/Kconfig=59=config PCI_MSI_ARCH_FALLBACKS\n--\ndrivers/pci/Kconfig-61-\ndrivers/pci/Kconfig:62:config PCI_QUIRKS\ndrivers/pci/Kconfig-63-\tdefault y\n"
}

39/2 2026/09/21 01:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 72902
							output: 34
							thoughts: 21
						

Thoughts:
Here's my summary, as though I'm thinking it:

**PCI Quirks Investigation**

Alright, time to get to the bottom of this. The problem feels like it might be related to some hardware-specific quirks in the PCI domain. So, my first instinct is to check out the `PCI_QUIRKS` option. That's usually where the kernel handles the more unusual, non-standard PCI devices or behaviors. I'll need to head over to `drivers/pci/Kconfig` to see what options are available and if anything seems relevant. Gotta start with the basics, right? Digging into Kconfig will help me understand what's already being handled and perhaps point me in the right direction to diagnose whatever is happening.



40/2 2026/09/21 01:14 tool read-file
0m Args:
{
  "File": "drivers/pci/Kconfig",
  "FirstLine": 58,
  "LineCount": 20
}

Results:
{
  "Contents": "  58:\t\n  59:\tconfig PCI_MSI_ARCH_FALLBACKS\n  60:\t\tbool\n  61:\t\n  62:\tconfig PCI_QUIRKS\n  63:\t\tdefault y\n  64:\t\tbool \"Enable PCI quirk workarounds\" if EXPERT\n  65:\t\thelp\n  66:\t\t  This enables workarounds for various PCI chipset bugs/quirks.\n  67:\t\t  Disable this only if your target machine is unaffected by PCI\n  68:\t\t  quirks.\n  69:\t\n  70:\tconfig PCI_DEBUG\n  71:\t\tbool \"PCI Debugging\"\n  72:\t\tdepends on DEBUG_KERNEL\n  73:\t\thelp\n  74:\t\t  Say Y here if you want the PCI core to produce a bunch of debug\n  75:\t\t  messages to the system log.  Select this if you are having a\n  76:\t\t  problem with PCI support and want to see more of what is going on.\n  77:\t\n"
}

41/2 2026/09/21 01:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 73373
							output: 428
							thoughts: 2137
						

Thoughts:
**PCI Live Update Support: Focused Symbol Selection and Reasoning**

Okay, so I'm diving into this PCI live update patch, and first things first, even though `PCI_QUIRKS` is normally enabled, explicitly mentioning it is a good practice because `pci_need_dev_specific_enable_acs` is right there inside `drivers/pci/quirks.c`, under that config option. Now, let's get into the *FocusSymbols* guidelines. I need to avoid listing ubiquitous hot-path functions, you know, stuff like allocators, deallocators, page fault handlers, and core synchronization primitives. Those would just clutter things up. The goal here is to target the specific feature logic and API entry points related to this live update functionality.

I've been looking at the modified and added feature-specific functions and I've come up with a list.

*   In `drivers/pci/pci.c`, I'm keying on: `pci_save_acs_state`, `pci_restore_acs_state`, `pci_enable_acs`, `pci_restore_state`, `pci_allocate_cap_save_buffers`, and `pci_configure_ari`.
*   Then, in `drivers/pci/quirks.c`, `pci_need_dev_specific_enable_acs`, `pci_dev_specific_enable_acs`, and `pci_dev_specific_disable_acs_redir`.
*   Over in `drivers/pci/probe.c`, I've got `pci_scan_bridge_extend`.
*   Finally, `drivers/pci/liveupdate.c` has a suite of functions that all sound pretty important to the live update feature: `pci_liveupdate_setup_device`, `pci_liveupdate_cleanup_device`, `pci_liveupdate_preserve_bus_numbers`, `pci_liveupdate_refuse_bus_numbers`, `pci_liveupdate_adopt_acs`, `pci_liveupdate_adopt_ari`, `pci_liveupdate_preserve`, and `pci_liveupdate_unpreserve`.

Now, before I commit to those, I need to make sure none of them are just the usual suspects – ubiquitous code that gets called all over the place. Nope, they're all PCI-specific, so we're good there. To refine this further, I'll filter down to the most direct, specific, non-static/addressable functions:

*   `pci_save_acs_state`
*   `pci_restore_acs_state`
*   `pci_enable_acs`
*   `pci_need_dev_specific_enable_acs`
*   `pci_dev_specific_enable_acs`
*   `pci_dev_specific_disable_acs_redir`
*   `pci_configure_ari`
*   `pci_allocate_cap_save_buffers`
*   `pci_liveupdate_setup_device`
*   `pci_liveupdate_cleanup_device`
*   `pci_liveupdate_preserve_bus_numbers`
*   `pci_liveupdate_refuse_bus_numbers`
*   `pci_liveupdate_adopt_acs`
*   `pci_liveupdate_adopt_ari`
*   `pci_liveupdate_preserve`

Alright, let me double-check these to make sure I haven't made any typos. I'm verifying the function definitions are in the patch and everything lines up in the files. I've confirmed all of them exist at the expected line numbers within the patch. I'll need to figure out the EnableConfigs to be able to build the code so I can begin to fuzz. I see the following as potentially important.

*   `PCI`
*   `PCI_QUIRKS`
*   `KEXEC_HANDOVER`
*   `LIVEUPDATE`
*   `PCI_LIVEUPDATE`

I checked `drivers/pci/Kconfig` to make sure there aren't any other configuration dependencies. Okay, it looks like `CONFIG_PCI_LIVEUPDATE=y` compiles `liveupdate.c`. Now, for the reasoning:

The patch introduces core PCI support for live update (`CONFIG_PCI_LIVEUPDATE`) and modifies PCI state management, bridge scanning, ACS handling, and quirk dispatch logic. These changes touch reachable PCI subsystem logic in `drivers/pci/pci.c`, `drivers/pci/quirks.c`, and `drivers/pci/probe.c`. Specifically, it


42/2 2026/09/21 01:14 tool set-results
0m Args:
{
  "EnableConfigs": [
    "PCI",
    "PCI_QUIRKS",
    "KEXEC_HANDOVER",
    "LIVEUPDATE",
    "PCI_LIVEUPDATE"
  ],
  "FocusSymbols": [
    "pci_save_acs_state",
    "pci_restore_acs_state",
    "pci_enable_acs",
    "pci_need_dev_specific_enable_acs",
    "pci_dev_specific_enable_acs",
    "pci_dev_specific_disable_acs_redir",
    "pci_configure_ari",
    "pci_allocate_cap_save_buffers",
    "pci_liveupdate_setup_device",
    "pci_liveupdate_cleanup_device",
    "pci_liveupdate_preserve_bus_numbers",
    "pci_liveupdate_refuse_bus_numbers",
    "pci_liveupdate_adopt_acs",
    "pci_liveupdate_adopt_ari"
  ],
  "Reasoning": "The patch series adds core PCI Live Update support and modifies core PCI subsystem logic. Beyond introducing drivers/pci/liveupdate.c, it modifies reachable PCI core paths in drivers/pci/pci.c, drivers/pci/quirks.c, and drivers/pci/probe.c: it adds ACS configuration saving and restoring (pci_save_acs_state, pci_restore_acs_state) called during pci_save_state/pci_restore_state (reachable via device reset through sysfs or VFIO), refactors ACS quirk lookup and dispatch (pci_need_dev_specific_enable_acs, pci_dev_specific_enable_acs), alters bridge scanning logic in pci_scan_bridge_extend, and adds liveupdate device setup/cleanup hooks with runtime warnings. Because reachable executable PCI subsystem logic and state management are altered, this warrants fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "PCI",
    "PCI_QUIRKS",
    "KEXEC_HANDOVER",
    "LIVEUPDATE",
    "PCI_LIVEUPDATE"
  ],
  "FocusSymbols": [
    "pci_save_acs_state",
    "pci_restore_acs_state",
    "pci_enable_acs",
    "pci_need_dev_specific_enable_acs",
    "pci_dev_specific_enable_acs",
    "pci_dev_specific_disable_acs_redir",
    "pci_configure_ari",
    "pci_allocate_cap_save_buffers",
    "pci_liveupdate_setup_device",
    "pci_liveupdate_cleanup_device",
    "pci_liveupdate_preserve_bus_numbers",
    "pci_liveupdate_refuse_bus_numbers",
    "pci_liveupdate_adopt_acs",
    "pci_liveupdate_adopt_ari"
  ],
  "Reasoning": "The patch series adds core PCI Live Update support and modifies core PCI subsystem logic. Beyond introducing drivers/pci/liveupdate.c, it modifies reachable PCI core paths in drivers/pci/pci.c, drivers/pci/quirks.c, and drivers/pci/probe.c: it adds ACS configuration saving and restoring (pci_save_acs_state, pci_restore_acs_state) called during pci_save_state/pci_restore_state (reachable via device reset through sysfs or VFIO), refactors ACS quirk lookup and dispatch (pci_need_dev_specific_enable_acs, pci_dev_specific_enable_acs), alters bridge scanning logic in pci_scan_bridge_extend, and adds liveupdate device setup/cleanup hooks with runtime warnings. Because reachable executable PCI subsystem logic and state management are altered, this warrants fuzzing.",
  "WorthFuzzing": true
}

43/1 2026/09/21 01:14 action If
2m Args:
{
  "WorthFuzzing": true
}

Results:
null

44/2 2026/09/21 01:14 agent kmsan-evaluator
2m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series introduces PCI Live Update support, allowing PCI devices and their bridge topologies to be preserved across kexec using Live Update Orchestrator (LUO) and Kexec Handover (KHO).\n\nAnalysis of memory initialization and sanitizers:\n1. Memory Allocation and Initialization:\n   - `struct pci_flb_outgoing` and `struct pci_flb_incoming` are allocated using `kzalloc_obj()`.\n   - `struct pci_ser` is allocated using `kho_alloc_preserve()`, which allocates and zeroes preserved memory, with all fields explicitly initialized (`version`, `nr_devices`, `devices`). The structure is packed and has no padding.\n   - `struct pci_dev_ser` is packed and allocated from KHO block sets (which allocate zeroed memory via `kho_alloc_preserve()`); all fields are explicitly initialized on preserve, and cleared with `memset()` on unpreserve.\n   - `struct pci_liveupdate` embedded in `struct pci_dev` is zero-initialized as `struct pci_dev` is allocated with `kzalloc_obj()`.\n   - The new ACS capability save buffer is allocated via `pci_add_ext_cap_save_buffer()`, which zeroes memory with `kzalloc()`.\n   - Local variables passed by pointer (e.g. `u16 val` in `pci_liveupdate_adopt_ari()`) are initialized by their callees (`pcie_capability_read_word()` explicitly sets `*val = 0` on entry).\n\n2. Userspace Leaks / Uninitialized Memory:\n   - There are no `copy_to_user()`, `put_user()`, netlink, or userspace-facing serialization paths modified or introduced in this patch.\n   - No branching or conditional logic depends on uninitialized fields or stack variables.\n\n3. Applicable Detectors:\n   - Any potential issues in this patch series are related to synchronization (`rwsem`), refcounting, pointer lifecycle management, or bus scanning logic, which fall under the purview of LOCKDEP, KASAN, and standard kernel debugging tools.\n\nTherefore, the patch introduces no uninitialized memory risks or info-leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit d16d7de37068d14644f2734e19ff701b07419cd7
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 21 01:11:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst
index 5d720d2a415e0..23fb737ac969e 100644
--- a/Documentation/PCI/index.rst
+++ b/Documentation/PCI/index.rst
@@ -20,3 +20,4 @@ PCI Bus Subsystem
    controller/index
    boot-interrupts
    tph
+   liveupdate
diff --git a/Documentation/PCI/liveupdate.rst b/Documentation/PCI/liveupdate.rst
new file mode 100644
index 0000000000000..96b1d7f5df3a0
--- /dev/null
+++ b/Documentation/PCI/liveupdate.rst
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+PCI Support for Live Update
+===========================
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :doc: PCI Live Update
+
+Driver API
+==========
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :export:
+
+Internal API
+============
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :internal:
+
+Live Update ABI
+===============
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :internal:
+
+See Also
+========
+
+ * :doc:`/core-api/liveupdate`
+ * :doc:`/core-api/kho/index`
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd24..7eabf6320abfa 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -5170,7 +5170,12 @@ Kernel parameters
 				explicitly which ones they are.
 		assign-busses	[X86] Always assign all PCI bus
 				numbers ourselves, overriding
-				whatever the firmware may have done.
+				whatever the firmware may have done. Ignored
+				if any device was preserved across a Live
+				Update, where the kernel must preserve the
+				PCI topology (including bus numbers) to
+				avoid interrupting ongoing memory transactions
+				of preserved devices.
 		usepirqmask	[X86] Honor the possible IRQ mask stored
 				in the BIOS $PIR table. This is needed on
 				some systems with broken BIOSes, notably
diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api/liveupdate.rst
index 5a292d0f37064..2bce2644eba22 100644
--- a/Documentation/core-api/liveupdate.rst
+++ b/Documentation/core-api/liveupdate.rst
@@ -1,5 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0
 
+.. _luo:
+
 ========================
 Live Update Orchestrator
 ========================
@@ -18,6 +20,8 @@ LUO Preserving File Descriptors
 .. kernel-doc:: kernel/liveupdate/luo_file.c
    :doc: LUO File Descriptors
 
+.. _flb:
+
 LUO File Lifecycle Bound Global Data
 ====================================
 .. kernel-doc:: kernel/liveupdate/luo_flb.c
@@ -70,3 +74,4 @@ See Also
 
 - :doc:`Live Update uAPI </userspace-api/liveupdate>`
 - :doc:`/core-api/kho/index`
+- :doc:`PCI </PCI/liveupdate>`
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c9..5d1b58271989d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21046,6 +21046,21 @@ L:	linux-pci@vger.kernel.org
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
+PCI LIVE UPDATE
+M:	David Matlack <dmatlack@google.com>
+R:	Pasha Tatashin <pasha.tatashin@soleen.com>
+R:	Mike Rapoport <rppt@kernel.org>
+R:	Pratyush Yadav <pratyush@kernel.org>
+L:	kexec@lists.infradead.org
+L:	linux-pci@vger.kernel.org
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git
+F:	Documentation/PCI/liveupdate.rst
+F:	drivers/pci/liveupdate.c
+F:	drivers/pci/liveupdate.h
+F:	include/linux/kho/abi/pci.h
+F:	include/linux/pci_liveupdate.h
+
 PCI MSI DRIVER FOR ALTERA MSI IP
 L:	linux-pci@vger.kernel.org
 S:	Orphan
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 0c7408509ba24..16fbd4212e0f5 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS
 	  Reserves space in the kernel to maintain resource locking for
 	  multiple GPUS.  The overhead for each GPU is very small.
 
+config PCI_LIVEUPDATE
+	bool "PCI Live Update Support"
+	depends on PCI && LIVEUPDATE && 64BIT && !CARDBUS
+	help
+	  Enable PCI core support for preserving PCI devices across Live
+	  Update. This, in combination with support in a device's driver,
+	  enables PCI devices to run and perform memory transactions
+	  uninterrupted during a kexec for Live Update.
+
+	  This option should only be enabled by users who plan to use Live
+	  Update for kernel upgrades and require preserving PCI devices during
+	  those upgrades.
+
+	  If unsure, say N.
+
 source "drivers/pci/hotplug/Kconfig"
 source "drivers/pci/controller/Kconfig"
 source "drivers/pci/endpoint/Kconfig"
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a5182..e8d003cb67576 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PROC_FS)		+= proc.o
 obj-$(CONFIG_SYSFS)		+= pci-sysfs.o slot.o
 obj-$(CONFIG_ACPI)		+= pci-acpi.o
 obj-$(CONFIG_GENERIC_PCI_IOMAP) += iomap.o
+obj-$(CONFIG_PCI_LIVEUPDATE)	+= liveupdate.o
 endif
 
 obj-$(CONFIG_OF)		+= of.o
diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c
new file mode 100644
index 0000000000000..51695782ed427
--- /dev/null
+++ b/drivers/pci/liveupdate.c
@@ -0,0 +1,1015 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+
+/**
+ * DOC: PCI Live Update
+ *
+ * The PCI subsystem participates in the Live Update process to enable drivers
+ * to preserve their PCI devices across kexec.
+ *
+ * Preserving a device requires preserving two independent sets of state: the
+ * driver's own state, which the driver preserves with no involvement from the
+ * PCI core, and the PCI core's state about the device, which the next kernel
+ * needs so that enumeration does not disturb a device that is still running.
+ * This file implements the latter.
+ *
+ * :ref:`FLB <flb>` Data
+ * =====================
+ *
+ * Userspace decides which devices are preserved, using :ref:`LUO <luo>` file
+ * preservation: a driver exposes a file that represents a single PCI device,
+ * and userspace preserves the device with
+ * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preservation
+ * to a file gives it proper lifecycle management, e.g. the preservation is
+ * undone if userspace cancels it or goes away. How a driver exposes that file
+ * is up to the driver and invisible to the PCI core (vfio-pci variant drivers,
+ * the first intended use-case, use their per-device cdev).
+ *
+ * LUO only knows that a file was preserved; it does not know that the file
+ * represents a PCI device. Drivers therefore register their
+ * struct liveupdate_file_handler with the PCI core:
+ *
+ *  * ``pci_liveupdate_register_flb(driver_file_handler)``
+ *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``
+ *
+ * LUO then refcounts the PCI core's FLB against the files preserved by that
+ * handler, and that refcount drives the lifetime of struct pci_ser:
+ * pci_flb_preserve() allocates and preserves it when the first file is
+ * preserved, and pci_flb_unpreserve() frees it when the last file is
+ * unpreserved. In the next kernel, pci_flb_retrieve() hands the PCI core the
+ * struct pci_ser built by the previous kernel, whenever the PCI core asks for
+ * it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI
+ * core is done with it.
+ *
+ * State handed over by the previous kernel is trusted. The PCI core validates
+ * it only far enough to detect an incompatible or corrupt hand over, and makes
+ * no attempt to defend against deliberate modification, since a previous kernel
+ * able to corrupt preserved state is able to corrupt arbitrary memory anyway.
+ *
+ * Call Flow
+ * ---------
+ *
+ * ::
+ *
+ *   # Driver initialization
+ *   pci_liveupdate_register_flb(fh)
+ *
+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)
+ *   luo_preserve_file()
+ *     luo_flb_file_preserve()
+ *       luo_flb_file_preserve_one()      # first preserved file only
+ *         pci_flb_preserve()             # alloc and preserve struct pci_ser
+ *     fh->ops->preserve()                # driver callback
+ *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser
+ *
+ *   # Userspace: preservation cancelled or session torn down
+ *   luo_file_unpreserve_files()
+ *     luo_flb_file_unpreserve()
+ *       liveupdate_flb_put_outgoing()    # last unpreserved file only
+ *         pci_flb_unpreserve()           # free struct pci_ser
+ *
+ *   # ---------------- kexec ----------------
+ *
+ *   # New kernel: PCI enumeration
+ *   pci_setup_device()
+ *     pci_liveupdate_setup_device()
+ *       liveupdate_flb_get_incoming()
+ *         luo_flb_retrieve_one()         # first request only
+ *           pci_flb_retrieve()           # previous kernel's struct pci_ser
+ *
+ *   # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)
+ *   luo_file_finish_one()
+ *     fh->ops->finish()                  # driver callback
+ *       pci_liveupdate_finish(dev)       # release this device's pci_dev_ser
+ *     luo_flb_file_finish()
+ *       liveupdate_flb_put_incoming()    # last incoming file only
+ *         pci_flb_finish()               # free struct pci_ser
+ *
+ * Device Tracking
+ * ===============
+ *
+ * Drivers must notify the PCI core when specific devices are preserved or
+ * unpreserved with the following APIs:
+ *
+ *  * ``pci_liveupdate_preserve(pci_dev)``
+ *  * ``pci_liveupdate_unpreserve(pci_dev)``
+ *
+ * This allows the PCI core to keep its FLB data (struct pci_ser) up to date
+ * with the list of **outgoing** preserved devices for the next kernel.
+ *
+ * After kexec, whenever a device is enumerated, the PCI core will check if it
+ * is an **incoming** preserved device (i.e. preserved by the previous kernel)
+ * by checking the incoming FLB data (struct pci_ser).
+ *
+ * Drivers must notify the PCI core when an **incoming** device is done
+ * participating in the incoming Live Update with the following API:
+ *
+ *  * ``pci_liveupdate_finish(pci_dev)``
+ *
+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and
+ * ``pci_liveupdate_preserve()``, i.e., a PCI device can be **outgoing**
+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)
+ * at the same time.
+ *
+ * Restrictions
+ * ============
+ *
+ * The PCI core enforces the following restrictions on which devices can be
+ * preserved. These may be relaxed in the future:
+ *
+ *  * The device cannot be a Virtual Function (VF).
+ *
+ *  * The device cannot require device-specific quirks to enable Access
+ *    Control Services (ACS).
+ *
+ * Driver Binding
+ * ==============
+ *
+ * In the outgoing kernel, the driver must ensure that it does not release a
+ * device between pci_liveupdate_preserve() and pci_liveupdate_unpreserve().
+ *
+ * In the incoming kernel, the driver must ensure that it does not release a
+ * preserved device between probe() and pci_liveupdate_finish().
+ *
+ * It is the user's responsibility to ensure that incoming preserved devices are
+ * bound to the correct driver. The PCI core does not protect against a device
+ * getting preserved by driver A in the outgoing kernel and then getting bound
+ * to driver B in the incoming kernel.
+ *
+ * PCI-to-PCI Bridges
+ * ==================
+ *
+ * Any PCI-to-PCI bridges upstream of a preserved device are automatically
+ * preserved when the device is preserved. The PCI core keeps track of the
+ * number of downstream devices that are preserved under a bridge so that the
+ * bridge is only unpreserved once all downstream devices are unpreserved.
+ *
+ * This enables the PCI core and any drivers bound to the bridge to participate
+ * in the Live Update so that preserved endpoints can continue issuing memory
+ * transactions during the Live Update.
+ *
+ * BDF Stability
+ * =============
+ *
+ * The PCI core guarantees that preserved devices can be identified by the same
+ * bus, device, and function numbers for as long as they are preserved
+ * (including across kexec). To accomplish this, the PCI core keeps the
+ * secondary and subordinate bus numbers that the previous kernel programmed
+ * into bridges, if the previous kernel preserved any device. This is true even
+ * on architectures that always assign new bus numbers during scanning. The
+ * kernel assumes the previous kernel established a sane bus topology across
+ * kexec.
+ *
+ * Bridges that do not have bus numbers are assigned new ones as usual, so
+ * hot-adding a bridge keeps working, both during and after a Live Update. The
+ * two-pass bridge scan ensures such bridges are only assigned bus numbers above
+ * those already claimed by preserved bridges.
+ *
+ * If a preserved bridge comes up without a valid bus number configuration, e.g.
+ * because it was reset during kexec, the PCI core refuses to assign it new bus
+ * numbers and does not enumerate anything below it. Assigning new bus numbers
+ * would silently change the BDF of every preserved device in its hierarchy. The
+ * PCI core also stops assigning bus numbers to the other bridges on the same
+ * bus, since the bus numbers of the failed bridge can no longer be read from
+ * hardware and handing them to another bridge would let an unrelated device
+ * inherit the BDF of a preserved device.
+ *
+ * Handling Preserved Devices
+ * ==========================
+ *
+ * The PCI core treats preserved devices differently than non-preserved devices.
+ * This section enumerates those differences.
+ *
+ *  * The PCI core adopts all ACS controls enabled on incoming preserved devices
+ *    rather than assigning new ones. This ensures that TLPs are routed the same
+ *    way after Live Update and ensures that IOMMU groups do not change. Note
+ *    that a device will use its adopted ACS controls for the lifetime of its
+ *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
+ *
+ *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
+ *    preserved devices to ensure that all preserved devices on the bridge's
+ *    secondary bus are addressable after the Live Update.
+ *
+ *  * The PCI core does not disable bus mastering on outgoing preserved devices
+ *    during kexec. This allows preserved devices to issue memory transactions
+ *    throughout the Live Update.
+ */
+
+#define pr_fmt(fmt) "PCI: liveupdate: " fmt
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/pci.h>
+#include <linux/kho_block.h>
+#include <linux/liveupdate.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "liveupdate.h"
+#include "pci.h"
+
+/**
+ * struct pci_liveupdate_global - Global state for PCI Live Update support
+ * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing
+ *         FLBs, and the references to them in struct pci_dev.
+ * @had_incoming: True if the previous kernel preserved at least one PCI device.
+ *                Set when the incoming FLB is retrieved and never cleared, so
+ *                it stays true after Live Update finishes.
+ */
+struct pci_liveupdate_global {
+	struct rw_semaphore rwsem;
+	bool had_incoming;
+};
+
+static struct pci_liveupdate_global pci_liveupdate = {
+	.rwsem = __RWSEM_INITIALIZER(pci_liveupdate.rwsem),
+};
+
+/**
+ * struct pci_flb_outgoing - Outgoing PCI FLB object
+ * @ser: Pointer to the preserved struct pci_ser.
+ * @block_set: The KHO block set holding the outgoing devices.
+ *
+ * This structure holds the runtime state for the outgoing PCI Live Update
+ * state. It wraps the serialized pci_ser and the block_set used to manage
+ * the serialized entries.
+ */
+struct pci_flb_outgoing {
+	struct pci_ser *ser;
+	struct kho_block_set block_set;
+};
+
+/**
+ * struct pci_flb_incoming - Incoming PCI FLB object
+ * @ser: The incoming struct pci_ser from the previous kernel.
+ * @xa: Xarray used to quickly lookup devices in @ser.
+ * @block_set: The KHO block set holding the incoming devices.
+ *
+ * This structure holds the runtime state for the incoming PCI Live Update
+ * state. It wraps the serialized pci_ser, the block_set used to restore
+ * the serialized entries, and an xarray for fast lookups.
+ */
+struct pci_flb_incoming {
+	struct pci_ser *ser;
+	struct xarray xa;
+	struct kho_block_set block_set;
+};
+
+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)
+{
+	return (unsigned long)domain << 16 | bdf;
+}
+
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing __free(kfree) = NULL;
+	struct pci_ser *ser;
+
+	outgoing = kzalloc_obj(*outgoing);
+	if (!outgoing)
+		return -ENOMEM;
+
+	ser = kho_alloc_preserve(sizeof(*ser));
+	if (IS_ERR(ser))
+		return PTR_ERR(ser);
+
+	ser->version = PCI_LUO_FLB_VERSION;
+	ser->nr_devices = 0;
+	ser->devices = 0;
+
+	outgoing->ser = ser;
+	kho_block_set_init(&outgoing->block_set, sizeof(struct pci_dev_ser));
+
+	args->obj = no_free_ptr(outgoing);
+	args->data = virt_to_phys(ser);
+	pr_debug("Preserved struct pci_ser (0x%llx)\n", args->data);
+	return 0;
+}
+
+static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing = args->obj;
+
+	pr_debug("Unpreserving struct pci_ser (0x%llx)\n", args->data);
+
+	WARN_ON(outgoing->ser->nr_devices);
+	kho_block_set_destroy(&outgoing->block_set);
+	kho_unpreserve_free(outgoing->ser);
+	kfree(outgoing);
+}
+
+/*
+ * Any failure here is fatal. The previous kernel handed over devices that are
+ * still performing DMA, and this kernel cannot identify them without this
+ * state. Continuing would let the PCI core reassign bus numbers and rebind
+ * drivers underneath live devices, so fail loudly instead of unwinding.
+ */
+static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	int ret;
+
+	pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
+
+	if (ser->version != PCI_LUO_FLB_VERSION)
+		panic("Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\n",
+		      ser->version, PCI_LUO_FLB_VERSION);
+
+	incoming = kzalloc_obj(*incoming);
+	if (!incoming)
+		panic("Failed to allocate struct pci_flb_incoming\n");
+
+	incoming->ser = ser;
+	xa_init(&incoming->xa);
+
+	kho_block_set_init(&incoming->block_set, sizeof(struct pci_dev_ser));
+	ret = kho_block_set_restore(&incoming->block_set, ser->devices);
+	if (ret)
+		panic("Failed to restore devices KHO block set (%d)\n", ret);
+
+	kho_block_set_it_init(&it, &incoming->block_set);
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		unsigned long key;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			panic("Failed to insert PCI device %04x:%02x:%02x.%d into xarray (%d)\n",
+			      dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf),
+			      PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf),
+			      ret);
+	}
+
+	/*
+	 * Remember that the previous kernel preserved devices for the lifetime
+	 * of this kernel, even after Live Update finishes and the incoming FLB
+	 * is freed. See pci_liveupdate_preserve_bus_numbers().
+	 */
+	if (!xa_empty(&incoming->xa))
+		pci_liveupdate.had_incoming = true;
+
+	args->obj = incoming;
+	return 0;
+}
+
+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)
+{
+	struct pci_dev_ser *dev_ser;
+	unsigned long index;
+	u32 nr_devices;
+
+	/*
+	 * nr_devices is only decremented by pci_liveupdate_finish_device().
+	 * This runs once the last reference to the incoming FLB is dropped, so
+	 * there are no finishers left in flight.
+	 */
+	nr_devices = incoming->ser->nr_devices;
+	if (nr_devices == 0)
+		return;
+
+	/*
+	 * Report the unfinished devices from the incoming FLB rather than by
+	 * walking struct pci_dev, so that devices that never showed up after
+	 * kexec, or that were destroyed before they finished, are identified
+	 * as well.
+	 */
+	xa_for_each(&incoming->xa, index, dev_ser) {
+		if (!dev_ser->refcount)
+			continue;
+
+		pr_emerg("%04x:%02x:%02x.%d was never finished!\n",
+			 dev_ser->domain, PCI_BUS_NUM(dev_ser->bdf),
+			 PCI_SLOT(dev_ser->bdf), PCI_FUNC(dev_ser->bdf));
+	}
+
+	/*
+	 * This should only happen if a driver violated the contract to call
+	 * pci_liveupdate_finish() (something is extremely broken).
+	 */
+	panic("%u preserved device(s) were never finished!\n", nr_devices);
+}
+
+static void pci_flb_finish(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_incoming *incoming = args->obj;
+
+	pr_debug("Finished struct pci_ser (0x%llx)\n", args->data);
+	pci_check_all_devices_finished(incoming);
+
+	xa_destroy(&incoming->xa);
+	kho_block_set_destroy(&incoming->block_set);
+	kho_restore_free(incoming->ser);
+	kfree(incoming);
+}
+
+static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {
+	.preserve = pci_flb_preserve,
+	.unpreserve = pci_flb_unpreserve,
+	.retrieve = pci_flb_retrieve,
+	.finish = pci_flb_finish,
+	.owner = THIS_MODULE,
+};
+
+static struct liveupdate_flb pci_liveupdate_flb = {
+	.ops = &pci_liveupdate_flb_ops,
+	.compatible = PCI_LUO_FLB_COMPATIBLE,
+};
+
+static void pci_liveupdate_flb_put_outgoing(void)
+{
+	liveupdate_flb_put_outgoing(&pci_liveupdate_flb);
+}
+
+static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&outgoing);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!outgoing)
+		return ERR_PTR(-ENOENT);
+
+	return outgoing;
+}
+
+static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)
+{
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	u64 count = 0;
+	int err;
+
+	kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* Try to find an existing, previously unpreserved, entry. */
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		if (!dev_ser->refcount)
+			return dev_ser;
+
+		count++;
+	}
+
+	/* Otherwise grow the block set and reserve a new entry. */
+	err = kho_block_set_grow(&outgoing->block_set, count + 1);
+	if (err)
+		return ERR_PTR(err);
+
+	if (!count)
+		kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* This should always succeed since kho_block_set_grow() succeeded. */
+	dev_ser = kho_block_set_it_reserve_entry(&it);
+	if (WARN_ON_ONCE(!dev_ser))
+		return ERR_PTR(-ENOSPC);
+
+	return dev_ser;
+}
+
+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					    struct pci_dev *dev)
+{
+	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
+
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot unpreserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
+	if (!dev_ser) {
+		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
+		return -EINVAL;
+	}
+
+	if (!dev_ser->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	if (--dev_ser->refcount)
+		return 0;
+
+	pci_info(dev, "Device will no longer be preserved across next Live Update\n");
+	outgoing->ser->nr_devices--;
+	memset(dev_ser, 0, sizeof(*dev_ser));
+	dev->liveupdate.outgoing = NULL;
+	return 0;
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoing,
+					   struct pci_dev *dev,
+					   struct pci_dev *end)
+{
+	for_each_pci_dev_in_path(dev) {
+		if (dev == end)
+			break;
+
+		if (pci_liveupdate_unpreserve_device(outgoing, dev))
+			return;
+	}
+}
+
+static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
+					  struct pci_dev *dev)
+{
+	if (dev->is_virtfn) {
+		pci_warn(dev, "Cannot preserve Virtual Functions\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Do not preserve devices that rely on device-specific ACS equivalents
+	 * (for now) since that would complicate keeping ACS constant across
+	 * Live Update.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Endpoint devices should not be preserved more than once.
+	 * Bridges are preserved once for every downstream device that
+	 * is preserved.
+	 */
+	if (dev->liveupdate.outgoing && !dev->subordinate) {
+		pci_warn(dev, "Device is already preserved\n");
+		return -EBUSY;
+	}
+
+	if (dev->liveupdate.outgoing && !dev->liveupdate.outgoing->refcount) {
+		pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->liveupdate.outgoing) {
+		struct pci_dev_ser *dev_ser;
+
+		dev_ser = pci_flb_alloc_dev_ser(outgoing);
+		if (IS_ERR(dev_ser))
+			return PTR_ERR(dev_ser);
+
+		pci_info(dev, "Device will be preserved across next Live Update\n");
+		outgoing->ser->nr_devices++;
+		outgoing->ser->devices = kho_block_set_head_pa(&outgoing->block_set);
+
+		dev_ser->domain = pci_domain_nr(dev->bus);
+		dev_ser->bdf = pci_dev_id(dev);
+		dev->liveupdate.outgoing = dev_ser;
+	}
+
+	dev->liveupdate.outgoing->refcount++;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
+ * @dev: The PCI device to preserve.
+ *
+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be
+ * preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler
+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.
+ *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
+ * Returns: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	struct pci_dev *start = dev;
+	int ret = -ENODEV;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing))
+		return PTR_ERR(outgoing);
+
+	for_each_pci_dev_in_path(dev) {
+		ret = pci_liveupdate_preserve_device(outgoing, dev);
+		if (ret) {
+			pci_liveupdate_unpreserve_path(outgoing, start, dev);
+			break;
+		}
+	}
+
+	pci_liveupdate_flb_put_outgoing();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
+
+/**
+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device
+ * @dev: The PCI device to unpreserve.
+ *
+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no
+ * longer be preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set
+ * up.
+ *
+ * pci_liveupdate_unpreserve() automatically unpreserves all bridges upstream of
+ * @dev.
+ */
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_path(outgoing, dev, /*end=*/NULL);
+	pci_liveupdate_flb_put_outgoing();
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
+
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+	struct pci_flb_incoming *incoming = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
+
+	/* Live Update is not enabled. */
+	if (ret == -EOPNOTSUPP)
+		return NULL;
+
+	/* Live Update is enabled, but there is no incoming FLB data. */
+	if (ret == -ENODATA)
+		return NULL;
+
+	/*
+	 * Live Update is enabled and there is incoming FLB data, but none of it
+	 * matches pci_liveupdate_flb.compatible.
+	 */
+	if (ret == -ENOENT)
+		return NULL;
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but retrieve failed (pci_flb_retrieve() returned an error or LUO
+	 * failed to acquire a reference to pci_liveupdate_flb_ops.owner).
+	 */
+	if (ret)
+		panic("Failed to retrieve incoming FLB data (%d)\n", ret);
+
+	return incoming;
+}
+
+static void pci_liveupdate_flb_put_incoming(void)
+{
+	liveupdate_flb_put_incoming(&pci_liveupdate_flb);
+}
+
+/**
+ * pci_liveupdate_preserve_bus_numbers() - Determine if the PCI core should
+ *                                         preserve bus numbers when scanning
+ *                                         bridges.
+ *
+ * This function is called by the PCI core when it is scanning a bridge. It
+ * determines whether the PCI core should preserve the secondary and subordinate
+ * bus numbers that the previous kernel programmed into that bridge, rather than
+ * assigning new ones. This is necessary to keep RequesterIDs constant for
+ * preserved devices issuing memory transactions.
+ *
+ * Bus numbers are preserved everywhere, and for the lifetime of the kernel, if
+ * the previous kernel preserved any device. Bus numbers have to be preserved
+ * above a preserved device anyway, since an upstream bridge cannot expand its
+ * window. Applying the same policy everywhere matches the scope of
+ * pcibios_assign_all_busses(), and gives an answer that cannot change part way
+ * through the two passes of a bridge scan.
+ *
+ * The incoming FLB is retrieved while setting up the first device, which always
+ * happens before any bridge is scanned, so this returns the same answer for the
+ * entire enumeration.
+ *
+ * Note that this does not prevent the PCI core from assigning bus numbers to
+ * bridges that do not have any, e.g. bridges that are hot-added after the
+ * Live Update. See pci_liveupdate_refuse_bus_numbers() for the one case where
+ * the PCI core must refuse to do so.
+ *
+ * Return: True if bus numbers should be preserved, false otherwise.
+ */
+bool pci_liveupdate_preserve_bus_numbers(void)
+{
+	return pci_liveupdate.had_incoming;
+}
+
+/**
+ * pci_liveupdate_refuse_bus_numbers() - Determine if the PCI core must refuse
+ *                                       to assign bus numbers to the provided
+ *                                       bridge.
+ * @bus: The PCI bus the bus numbers would be assigned from.
+ * @dev: The PCI bridge device the bus numbers would be assigned to.
+ *
+ * This function is called by the PCI core before it assigns bus numbers to a
+ * bridge that does not have any.
+ *
+ * A bridge that was preserved by the previous kernel but came up without a
+ * valid bus number configuration, e.g. because it was reset during kexec, is
+ * left alone by the PCI core and therefore has no child bus once the first pass
+ * of the bridge scan is done.
+ *
+ * The PCI core must not assign bus numbers from @bus while such a bridge is on
+ * it, including to the failed bridge itself. Assigning new bus numbers to the
+ * failed bridge would silently change the BDF of every preserved device in its
+ * hierarchy. Its bus numbers cannot be read from hardware anymore either, so
+ * they cannot be excluded from assignment, and handing them to another bridge
+ * would let an unrelated device inherit the BDF of a preserved device.
+ *
+ * Return: True if @dev must not be assigned bus numbers, false otherwise.
+ */
+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *bridge;
+
+	for_each_pci_bridge(bridge, bus) {
+		if (!bridge->liveupdate.was_incoming || bridge->subordinate)
+			continue;
+
+		pci_err(dev, "Not assigning bus numbers, preserved bridge %s lost its bus number configuration\n",
+			pci_name(bridge));
+		return true;
+	}
+
+	return false;
+}
+
+void pci_liveupdate_setup_device(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	unsigned long key;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming)
+		return;
+
+	key = pci_ser_xa_key(pci_domain_nr(dev->bus), pci_dev_id(dev));
+	dev_ser = xa_load(&incoming->xa, key);
+
+	/*
+	 * This device was not preserved across Live Update, or it was preserved
+	 * but has already been probed and gone through pci_liveupdate_finish(),
+	 * e.g. due to removing and re-adding the device. Either way, it's not
+	 * treated as incoming-preserved.
+	 */
+	if (!dev_ser || !dev_ser->refcount) {
+		pci_liveupdate_flb_put_incoming();
+		return;
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
+	dev->liveupdate.was_incoming = true;
+
+	pci_liveupdate_flb_put_incoming();
+}
+
+void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+	/*
+	 * It should be safe to READ_ONCE() outside of the rwsem during cleanup
+	 * since there should no longer be any references to @dev on the system.
+	 *
+	 * This should never happen in practice. Drivers should block removal
+	 * while a device is preserved.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing))
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+
+	if (READ_ONCE(dev->liveupdate.incoming))
+		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
+}
+
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = true;
+}
+
+static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	if (!dev->liveupdate.incoming) {
+		pci_warn(dev, "Cannot finish preserving an unpreserved device\n");
+		return -EINVAL;
+	}
+
+	if (!dev->liveupdate.incoming->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Decrement the refcount so this device does not get treated as an
+	 * incoming device again, e.g. in case pci_liveupdate_setup_device()
+	 * gets called again because the device is hot-plugged.
+	 */
+	if (--dev->liveupdate.incoming->refcount)
+		return 0;
+
+	pci_info(dev, "Device is finished participating in Live Update\n");
+	dev->liveupdate.incoming = NULL;
+	ser->nr_devices--;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_finish() - Finish the preservation of a PCI device
+ * @dev: The PCI device
+ *
+ * pci_liveupdate_finish() notifies the PCI core that a PCI device that was
+ * preserved across the previous Live Update has finished participating in Live
+ * Update. Drivers must call pci_liveupdate_finish() from their struct
+ * liveupdate_file_handler finish() callback to ensure the incoming struct
+ * pci_ser is allocated.
+ *
+ * pci_liveupdate_finish() automatically finishes all bridges upstream of @dev.
+ */
+void pci_liveupdate_finish(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming) {
+		pci_warn(dev, "Cannot finish preserving device without incoming FLB\n");
+		return;
+	}
+
+	for_each_pci_dev_in_path(dev) {
+		if (pci_liveupdate_finish_device(incoming->ser, dev))
+			break;
+	}
+
+	pci_liveupdate_flb_put_incoming();
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
+
+/**
+ * pci_liveupdate_adopt_acs() - Adopt ACS controls
+ * @dev: The PCI device to adopt ACS controls for
+ *
+ * For devices preserved across a Live Update, leave the ACS controls
+ * established by the previous kernel alone instead of programming new ones.
+ * The adopted controls are recorded by the pci_save_state() call in
+ * pci_bus_add_device(), so they are reapplied by pci_restore_state() if the
+ * device is subsequently reset.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved, requires
+ * device-specific quirks, or has nowhere to record the adopted controls.
+ */
+int pci_liveupdate_adopt_acs(struct pci_dev *dev)
+{
+	/*
+	 * Check if the device was preserved over a previous Live Update (even
+	 * if it has already gone through pci_liveupdate_finish()). This ensures
+	 * that the device continues to use the ACS controls established by the
+	 * previous kernel.
+	 */
+	if (!dev->liveupdate.was_incoming)
+		return -EINVAL;
+
+	/*
+	 * The previous kernel should not have preserved any devices that
+	 * require device-specific quirks to enable ACS, but if such a device is
+	 * detected (e.g. new device-specific ACS quirk in the current kernel),
+	 * log a big warning and fall back to the normal enable ACS path.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}
+
+	/*
+	 * Adopting the previous kernel's controls depends on them being
+	 * captured in the ACS save buffer, so that they are reapplied if the
+	 * device is later reset. Without that buffer, e.g. because it could
+	 * not be allocated under memory pressure, the adopted controls would
+	 * be silently lost by the first reset. Program ACS from scratch
+	 * instead, which is a better outcome than leaving ACS disabled.
+	 */
+	if (dev->acs_cap && !pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS)) {
+		pci_err(dev, "No ACS save buffer, not adopting ACS controls\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * pci_liveupdate_adopt_ari() - Adopt ARI configuration
+ * @dev: The PCI device to adopt ARI configuration for
+ *
+ * For devices preserved across a Live Update, read the ARI state from
+ * hardware and adopt it. This ensures the device continues to use the ARI
+ * configuration established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved.
+ */
+int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+
+	/* Safe to modify dev->ari_enabled bitfield during enumeration. */
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	pci_WARN_ONCE(dev, !dev->liveupdate.frozen, "Preservation status is unstable!\n");
+	return dev->liveupdate.outgoing;
+}
+
+/**
+ * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
+ * @dev: The PCI device to check
+ *
+ * Check if a device was preserved across Live Update by the previous kernel,
+ * i.e. the device is incoming-preserved. Note that a device is only considered
+ * incoming-preserved prior to pci_liveupdate_finish(). It is up to drivers to
+ * synchronize usage of pci_liveupdate_is_incoming() with their own call to
+ * pci_liveupdate_finish() to avoid acting on stale data.
+ *
+ * Returns: True if the device is incoming-preserved, false otherwise.
+ */
+bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	return dev->liveupdate.incoming;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);
+
+/**
+ * pci_liveupdate_register_flb() - Register a file handler with the PCI core
+ * @fh: The file handler to register.
+ *
+ * Drivers that support preserving PCI devices across Live Update must call
+ * pci_liveupdate_register_flb() to register their
+ * struct liveupdate_file_handler with the PCI core, typically at module init,
+ * and always before any file managed by @fh can be preserved.
+ *
+ * Registering links the PCI core's FLB to @fh, so that LUO allocates the PCI
+ * core's outgoing struct pci_ser (via pci_flb_preserve()) when the first file
+ * managed by any registered handler is preserved, and frees it (via
+ * pci_flb_unpreserve()) when the last such file is unpreserved.
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Registering file handler \"%s\"\n", fh->compatible);
+	return liveupdate_register_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);
+
+/**
+ * pci_liveupdate_unregister_flb() - Unregister a file handler with the PCI core
+ * @fh: The file handler to unregister.
+ */
+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Unregistering file handler \"%s\"\n", fh->compatible);
+	liveupdate_unregister_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unregister_flb);
diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h
new file mode 100644
index 0000000000000..333ddd8f36c51
--- /dev/null
+++ b/drivers/pci/liveupdate.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCI Live Update support (core API)
+ *
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+#ifndef DRIVERS_PCI_LIVEUPDATE_H
+#define DRIVERS_PCI_LIVEUPDATE_H
+
+#include <linux/pci.h>
+
+#ifdef CONFIG_PCI_LIVEUPDATE
+void pci_liveupdate_setup_device(struct pci_dev *dev);
+void pci_liveupdate_cleanup_device(struct pci_dev *dev);
+void pci_liveupdate_freeze(struct pci_dev *dev);
+bool pci_liveupdate_preserve_bus_numbers(void);
+bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev);
+int pci_liveupdate_adopt_acs(struct pci_dev *dev);
+int pci_liveupdate_adopt_ari(struct pci_dev *dev);
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);
+#else
+static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+}
+
+static inline bool pci_liveupdate_preserve_bus_numbers(void)
+{
+	return false;
+}
+
+static inline bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus,
+						     struct pci_dev *dev)
+{
+	return false;
+}
+
+static inline int pci_liveupdate_adopt_acs(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
+
+static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
+
+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	return false;
+}
+#endif
+
+#endif /* DRIVERS_PCI_LIVEUPDATE_H */
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e16aa59dd7ac8..c1ac2ef025e23 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -21,6 +21,7 @@
 #include <linux/acpi.h>
 #include <linux/dma-map-ops.h>
 #include <linux/iommu.h>
+#include "liveupdate.h"
 #include "pci.h"
 #include "pcie/portdrv.h"
 
@@ -559,6 +560,7 @@ static void pci_device_shutdown(struct device *dev)
 	struct pci_dev *pci_dev = to_pci_dev(dev);
 	struct pci_driver *drv = pci_dev->driver;
 
+	pci_liveupdate_freeze(pci_dev);
 	pm_runtime_resume(dev);
 
 	if (drv && drv->shutdown)
@@ -567,11 +569,14 @@ static void pci_device_shutdown(struct device *dev)
 	/*
 	 * If this is a kexec reboot, turn off Bus Master bit on the
 	 * device to tell it to not continue to do DMA. Don't touch
-	 * devices in D3cold or unknown states.
+	 * devices being preserved for Live Update or in D3cold or
+	 * unknown states.
+	 *
 	 * If it is not a kexec reboot, firmware will hit the PCI
 	 * devices with big hammer and stop their DMA any way.
 	 */
-	if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
+	if (kexec_in_progress && !pci_liveupdate_is_outgoing(pci_dev) &&
+	    pci_dev->current_state <= PCI_D3hot)
 		pci_clear_master(pci_dev);
 }
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5f80..0ae405b86b2f8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -35,6 +35,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);
@@ -1021,6 +1023,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
 		caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
 }
 
+/**
+ * pci_save_acs_state - save the ACS Control register
+ * @dev: the PCI device
+ *
+ * Record the ACS controls currently programmed in hardware so that
+ * pci_restore_acs_state() can reapply them after a reset.
+ */
+static void pci_save_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state;
+
+	if (!dev->acs_cap)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+	if (!save_state)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			     (u16 *)&save_state->cap.data[0]);
+}
+
+/**
+ * pci_restore_acs_state - restore the ACS Control register
+ * @dev: the PCI device
+ */
+static void pci_restore_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state = NULL;
+
+	if (dev->acs_cap && !pci_need_dev_specific_enable_acs(dev))
+		save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+
+	/*
+	 * Devices that rely on device-specific quirks to enable an ACS
+	 * equivalent keep that configuration outside of the ACS Control
+	 * register, so there is nothing useful to restore for them. Configure
+	 * ACS from scratch instead, which also covers devices that have no
+	 * saved ACS state at all.
+	 */
+	if (!save_state) {
+		pci_enable_acs(dev);
+		return;
+	}
+
+	pci_write_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			      *(u16 *)&save_state->cap.data[0]);
+}
+
 /**
  * pci_enable_acs - enable ACS if hardware support it
  * @dev: the PCI device
@@ -1031,6 +1082,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_adopt_acs(dev))
+		return;
+
 	/* If an iommu is present we start with kernel default caps */
 	if (pci_acs_enable) {
 		if (pci_dev_specific_enable_acs(dev))
@@ -1057,6 +1111,15 @@ void pci_enable_acs(struct pci_dev *dev)
 	__pci_config_acs(dev, &caps, config_acs_param, 0, 0);
 
 	pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
+
+	/*
+	 * pci_enable_acs() runs when a driver binds to the device, i.e. after
+	 * pci_bus_add_device() has already saved the device's state. Refresh
+	 * the saved ACS Control register so that a subsequent reset restores
+	 * the controls programmed here rather than the ones left behind by
+	 * firmware.
+	 */
+	pci_save_acs_state(dev);
 }
 
 /**
@@ -1800,6 +1863,7 @@ int pci_save_state(struct pci_dev *dev)
 	pci_save_aer_state(dev);
 	pci_save_ptm_state(dev);
 	pci_save_tph_state(dev);
+	pci_save_acs_state(dev);
 	return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1877,7 +1941,7 @@ void pci_restore_state(struct pci_dev *dev)
 	pci_restore_msi_state(dev);
 
 	/* Restore ACS and IOV configuration state */
-	pci_enable_acs(dev);
+	pci_restore_acs_state(dev);
 	pci_restore_iov_state(dev);
 
 	dev->state_saved = false;
@@ -3532,6 +3596,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
 	if (error)
 		pci_err(dev, "unable to allocate suspend buffer for LTR\n");
 
+	error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,
+					    sizeof(u16));
+	if (error)
+		pci_err(dev, "unable to allocate suspend buffer for ACS\n");
+
 	pci_allocate_vc_save_buffers(dev);
 }
 
@@ -3556,7 +3625,7 @@ void pci_configure_ari(struct pci_dev *dev)
 	u32 cap;
 	struct pci_dev *bridge;
 
-	if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
+	if (!pci_is_pcie(dev) || dev->devfn)
 		return;
 
 	bridge = dev->bus->self;
@@ -3567,6 +3636,12 @@ void pci_configure_ari(struct pci_dev *dev)
 	if (!(cap & PCI_EXP_DEVCAP2_ARI))
 		return;
 
+	if (!pci_liveupdate_adopt_ari(bridge))
+		return;
+
+	if (pcie_ari_disabled)
+		return;
+
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
 		pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
 					 PCI_EXP_DEVCTL2_ARI);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc23..037c1674f1648 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 void pci_disable_broken_acs_cap(struct pci_dev *pdev);
@@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
 	return -ENOTTY;
 }
+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	return false;
+}
 static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	return -ENOTTY;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5afc..debe0ad1ef68b 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -24,6 +24,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/bitfield.h>
 #include <trace/events/pci.h>
+#include "liveupdate.h"
 #include "pci.h"
 
 static struct resource busn_resource = {
@@ -1396,6 +1397,8 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 				  int max, unsigned int available_buses,
 				  int pass)
 {
+	bool preserve_bus_numbers = !pcibios_assign_all_busses() ||
+				    pci_liveupdate_preserve_bus_numbers();
 	struct pci_bus *child;
 	u32 buses;
 	u16 bctl;
@@ -1448,8 +1451,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		goto out;
 	}
 
-	if ((secondary || subordinate) &&
-	    !pcibios_assign_all_busses() && !broken) {
+	if ((secondary || subordinate) && preserve_bus_numbers && !broken) {
 		unsigned int cmax, buses;
 
 		/*
@@ -1491,8 +1493,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		 * do in the second pass.
 		 */
 		if (!pass) {
-			if (pcibios_assign_all_busses() || broken)
-
+			if (!preserve_bus_numbers || broken)
 				/*
 				 * Temporarily disable forwarding of the
 				 * configuration cycles on all bridges in
@@ -1506,6 +1507,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (pci_liveupdate_refuse_bus_numbers(bus, dev))
+			goto out;
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -2064,6 +2068,8 @@ int pci_setup_device(struct pci_dev *dev)
 	if (pci_early_dump)
 		early_dump_pci_device(dev);
 
+	pci_liveupdate_setup_device(dev);
+
 	/* Need to have dev->class ready */
 	dev->cfg_size = pci_cfg_space_size(dev);
 
@@ -2187,6 +2193,7 @@ int pci_setup_device(struct pci_dev *dev)
 	default:				    /* unknown header */
 		pci_err(dev, "unknown header type %02x, ignoring device\n",
 			dev->hdr_type);
+		pci_liveupdate_cleanup_device(dev);
 		pci_release_of_node(dev);
 		return -EIO;
 
@@ -2485,6 +2492,7 @@ static void pci_release_dev(struct device *dev)
 
 	pci_dev = to_pci_dev(dev);
 	pci_release_capabilities(pci_dev);
+	pci_liveupdate_cleanup_device(pci_dev);
 	pci_release_of_node(pci_dev);
 	pcibios_release_device(pci_dev);
 	pci_bus_put(pci_dev->bus);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21fd..e500c202d2ec9 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -5377,9 +5377,6 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
  */
 static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
 {
-	if (!pci_quirk_intel_pch_acs_match(dev))
-		return -ENOTTY;
-
 	if (pci_quirk_enable_intel_lpc_acs(dev)) {
 		pci_warn(dev, "Failed to enable Intel PCH ACS quirk\n");
 		return 0;
@@ -5399,9 +5396,6 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5429,9 +5423,6 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5451,56 +5442,63 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 static const struct pci_dev_acs_ops {
 	u16 vendor;
 	u16 device;
+	bool (*match)(struct pci_dev *dev);
 	int (*enable_acs)(struct pci_dev *dev);
 	int (*disable_acs_redir)(struct pci_dev *dev);
 } pci_dev_acs_ops[] = {
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];
 		if ((p->vendor == dev->vendor ||
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
+		     p->device == (u16)PCI_ANY_ID)) {
+			if (!p->match || p->match(dev))
+				return p;
 		}
 	}
 
+	return NULL;
+}
+
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	return p && p->enable_acs;
+}
+
+int pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	if (p && p->enable_acs)
+		return p->enable_acs(dev);
+
 	return -ENOTTY;
 }
 
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
 {
-	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
 
-	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
-		p = &pci_dev_acs_ops[i];
-		if ((p->vendor == dev->vendor ||
-		     p->vendor == (u16)PCI_ANY_ID) &&
-		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->disable_acs_redir) {
-			ret = p->disable_acs_redir(dev);
-			if (ret >= 0)
-				return ret;
-		}
-	}
+	if (p && p->disable_acs_redir)
+		return p->disable_acs_redir(dev);
 
 	return -ENOTTY;
 }
diff --git a/include/linux/kho/abi/pci.h b/include/linux/kho/abi/pci.h
new file mode 100644
index 0000000000000..9485ed73c3519
--- /dev/null
+++ b/include/linux/kho/abi/pci.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+
+#ifndef _LINUX_KHO_ABI_PCI_H
+#define _LINUX_KHO_ABI_PCI_H
+
+#include <linux/bug.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/**
+ * DOC: PCI File-Lifecycle Bound (FLB) Live Update ABI
+ *
+ * This header defines the ABI for preserving core PCI state across kexec using
+ * Live Update File-Lifecycle Bound (FLB) data.
+ *
+ * This interface is a contract. Any modification to any of the serialization
+ * structs defined here constitutes a breaking change. Such changes require
+ * incrementing the version number in the PCI_LUO_FLB_VERSION number.
+ */
+
+#define PCI_LUO_FLB_COMPATIBLE "pci"
+#define PCI_LUO_FLB_VERSION 2
+
+/**
+ * struct pci_dev_ser - Serialized state about a single PCI device.
+ *
+ * @domain: The device's PCI domain number (segment).
+ * @bdf: The device's PCI bus, device, and function number.
+ * @refcount: Reference count used by the PCI core to keep track of whether it
+ *            is done using a device's struct pci_dev_ser. The value of the
+ *            refcount is equal to the number of preserved devices at or below
+ *            it in the PCI hierarchy when the struct pci_dev_ser is in use, and
+ *            0 otherwise.
+ */
+struct pci_dev_ser {
+	u32 domain;
+	u16 bdf;
+	u16 refcount;
+} __packed;
+
+/**
+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel. It contains only state owned by the PCI
+ * core; the state a driver needs to resume its device is preserved separately
+ * by that driver.
+ *
+ * @version: The version of the "pci" FLB struct. This field must never be
+ *           deleted, moved, or resized, as the kernel depends on always being
+ *           able to check the struct pci_ser version number.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Physical address of the first KHO block containing pci_dev_ser.
+ */
+struct pci_ser {
+	u32 version;
+	u32 nr_devices;
+	u64 devices;
+} __packed;
+
+#endif /* _LINUX_KHO_ABI_PCI_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d31a8d107b1ef..b35ac263c4517 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -43,6 +43,7 @@
 #include <uapi/linux/pci.h>
 
 #include <linux/pci_ids.h>
+#include <linux/pci_liveupdate.h>
 
 #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY  | \
 			       PCI_STATUS_SIG_SYSTEM_ERROR | \
@@ -598,6 +599,9 @@ struct pci_dev {
 	u8		tph_mode;	/* TPH mode */
 	u8		tph_req_type;	/* TPH requester type */
 #endif
+#ifdef CONFIG_PCI_LIVEUPDATE
+	struct pci_liveupdate liveupdate;
+#endif
 };
 
 static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
@@ -832,6 +836,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)
 	return dev->bus->self;
 }
 
+#define for_each_pci_dev_in_path(dev) \
+	for (; dev; dev = pci_upstream_bridge(dev))
+
 #ifdef CONFIG_PCI_MSI
 static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev)
 {
diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h
new file mode 100644
index 0000000000000..bb79348769b72
--- /dev/null
+++ b/include/linux/pci_liveupdate.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCI Live Update support (Public/Driver API)
+ *
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <dmatlack@google.com>
+ */
+#ifndef LINUX_PCI_LIVEUPDATE_H
+#define LINUX_PCI_LIVEUPDATE_H
+
+#include <linux/kho/abi/pci.h>
+#include <linux/liveupdate.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+/**
+ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
+ * @outgoing: State preserved for the next kernel.
+ * @incoming: State preserved by the previous kernel.
+ * @was_incoming: True if this struct pci_dev was incoming-preserved when it was
+ *                set up, i.e. it was matched to state preserved by the previous
+ *                kernel. Unlike @incoming, this is never cleared, so it stays
+ *                true after the device finishes participating in Live Update.
+ * @frozen: True if the outgoing preservation status of this device is frozen
+ *          and thus cannot be changed.
+ */
+struct pci_liveupdate {
+	struct pci_dev_ser *outgoing;
+	struct pci_dev_ser *incoming;
+	bool was_incoming;
+	bool frozen;
+};
+
+struct pci_dev;
+
+#ifdef CONFIG_PCI_LIVEUPDATE
+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh);
+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh);
+int pci_liveupdate_preserve(struct pci_dev *dev);
+void pci_liveupdate_unpreserve(struct pci_dev *dev);
+void pci_liveupdate_finish(struct pci_dev *dev);
+bool pci_liveupdate_is_incoming(struct pci_dev *dev);
+#else
+static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+}
+
+static inline int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+}
+
+static inline void pci_liveupdate_finish(struct pci_dev *dev)
+{
+}
+
+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+	return false;
+}
+#endif
+
+#endif /* LINUX_PCI_LIVEUPDATE_H */

45/3 2026/09/21 01:14 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23439
							output: 38
							thoughts: 627
						

Thoughts:
**KMSAN Fuzzing Session Evaluation**

Okay, so the task at hand is to determine if this patch series warrants a dedicated KMSAN fuzzing run. We need to be judicious about this. KASAN already covers a lot of ground – OOB, UAF, double frees, invalid frees, LOCKDEP, and various UB sanitizers. KMSAN's specialty is detecting reads of *uninitialized* memory, both on the stack, heap, and page allocations, and also kernel-to-user memory leaks. The critical rule is: if KASAN, LOCKDEP, or any standard bug detector can catch it, we *don't* need KMSAN. We only invoke KMSAN if the code changes specifically introduce or expose uninitialized memory risks that *only* KMSAN would detect.

Let's dive into the patch itself. It's adding PCI Live Update support (`CONFIG_PCI_LIVEUPDATE`). That means new documentation, kernel configuration options, Kconfig, Makefiles, the `liveupdate.c` and header, and modifications to other `drivers/pci` files, and even an include file.

Now comes the crucial part. I need to scrutinize `drivers/pci/liveupdate.c` and the related code changes. I'm looking for very specific things, primarily related to uninitialized memory. I'm focusing on these aspects:
1.  **Kernel structures passed to userspace:** Any instance of `copy_to_user`, `put_user`, netlink, ioctl, socket, BPF buffers, where fields or padding might not be fully initialized. This is a very common source of info-leaks.
2.  **Conditional logic and branching:** Any code paths that rely on potentially uninitialized variables or struct fields, where the logic could lead to unintended consequences.
3.  **Complex data structure allocation and initialization:** Situations where complex structures are allocated, but some of their fields are left uninitialized, and those uninitialized fields could then be read later.
4.  **Bounds checks and lengths:** Is there code that might be accessing uninitialized bytes around existing buffers due to poorly constructed bound checks or length calculations?

Okay, time to check for any userspace interaction. I'll search for `copy_to_user` or other indicators of userspace interactions in `drivers/pci/liveupdate.c`. If there aren't any, I'm already in a better position. I'll need to go through the diff carefully.



46/3 2026/09/21 01:14 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "copy_to_user|put_user",
  "PathPrefix": "drivers/pci/liveupdate.c"
}

Results:
{
  "error": "no matches"
}

47/3 2026/09/21 01:14 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24646
							output: 19
							thoughts: 365
						

Thoughts:
**My Initial Thoughts on Live Update's Inner Workings**

Okay, so I'm diving into this `liveupdate.c` code and immediately notice the absence of any `copy_to_user` or `put_user` functions, which is odd. My first thought is, how exactly *does* Live Update work in this context? I need to get a better understanding of the underlying mechanism – specifically, this KHO (Kexec Handover) process. I'm going to start digging around for clues, focusing on `kho_alloc_preserve` or, failing that, how the `struct pci_ser` is allocated and initialized.

Ah, here's `pci_flb_preserve`, which seems relevant. I'm looking at the allocation of `struct pci_ser`:

```c
static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
{
	struct pci_flb_outgoing *outgoing __free(kfree) = NULL;
	struct pci_ser *ser;

	outgoing = kzalloc_obj(*outgoing);
	if (!outgoing)
		return -ENOMEM;

	ser = kho_alloc_preserve(sizeof(*ser));
	if (IS_ERR(ser))
		return PTR_ERR(ser);

	ser->version = PCI_LUO_FLB_VERSION;
	ser->nr_devices = 0;
	ser->devices = 0;

	outgoing->ser = ser;
	kho_block_set_init(&outgoing->block_set, sizeof(struct pci_dev_ser));

	args->obj = no_free_ptr(outgoing);
	args->data = virt_to_phys(ser);
	pr_debug("Preserved struct pci_ser (0x%llx)\n", args->data);
	return 0;
}
```

Okay, so `kho_alloc_preserve` is being used here. This is key. But what exactly does it *do*? Does it zero the memory? It's essential to understand its behavior within the KHO context. Let's see if grepping will reveal more about `kho_alloc_preserve`.



48/3 2026/09/21 01:14 tool grepper
0m Args:
{
  "Expression": "kho_alloc_preserve"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c=269=static int pci_flb_preserve(struct liveupdate_flb_op_args *args)\n--\ndrivers/pci/liveupdate.c-277-\ndrivers/pci/liveupdate.c:278:\tser = kho_alloc_preserve(sizeof(*ser));\ndrivers/pci/liveupdate.c-279-\tif (IS_ERR(ser))\n--\ninclude/linux/kexec_handover.h=25=void kho_unpreserve_vmalloc(struct kho_vmalloc *preservation);\ninclude/linux/kexec_handover.h:26:void *kho_alloc_preserve(size_t size);\ninclude/linux/kexec_handover.h-27-void kho_unpreserve_free(void *mem);\n--\ninclude/linux/kexec_handover.h=82=static inline void kho_unpreserve_vmalloc(struct kho_vmalloc *preservation) { }\ninclude/linux/kexec_handover.h-83-\ninclude/linux/kexec_handover.h:84:static inline void *kho_alloc_preserve(size_t size)\ninclude/linux/kexec_handover.h-85-{\n--\nkernel/liveupdate/kexec_handover.c=1529=EXPORT_SYMBOL_GPL(kho_restore_vmalloc);\n--\nkernel/liveupdate/kexec_handover.c-1531-/**\nkernel/liveupdate/kexec_handover.c:1532: * kho_alloc_preserve - Allocate, zero, and preserve memory.\nkernel/liveupdate/kexec_handover.c-1533- * @size: The number of bytes to allocate.\n--\nkernel/liveupdate/kexec_handover.c-1544- */\nkernel/liveupdate/kexec_handover.c:1545:void *kho_alloc_preserve(size_t size)\nkernel/liveupdate/kexec_handover.c-1546-{\n--\nkernel/liveupdate/kexec_handover.c-1568-}\nkernel/liveupdate/kexec_handover.c:1569:EXPORT_SYMBOL_GPL(kho_alloc_preserve);\nkernel/liveupdate/kexec_handover.c-1570-\n--\nkernel/liveupdate/kexec_handover.c-1572- * kho_unpreserve_free - Unpreserve and free memory.\nkernel/liveupdate/kexec_handover.c:1573: * @mem:  Pointer to the memory allocated by kho_alloc_preserve().\nkernel/liveupdate/kexec_handover.c-1574- *\n--\nkernel/liveupdate/kexec_handover.c-1576- * pages back to the system. This function should be called to clean up\nkernel/liveupdate/kexec_handover.c:1577: * memory allocated with kho_alloc_preserve().\nkernel/liveupdate/kexec_handover.c-1578- */\n--\nkernel/liveupdate/kexec_handover.c=1590=EXPORT_SYMBOL_GPL(kho_unpreserve_free);\n--\nkernel/liveupdate/kexec_handover.c-1598- * to take ownership of and free a memory region that was preserved by the\nkernel/liveupdate/kexec_handover.c:1599: * old kernel using kho_alloc_preserve().\nkernel/liveupdate/kexec_handover.c-1600- *\n--\nkernel/liveupdate/kexec_handover.c=1779=static __init int kho_out_kexec_metadata(void)\n--\nkernel/liveupdate/kexec_handover.c-1783-\nkernel/liveupdate/kexec_handover.c:1784:\tmetadata = kho_alloc_preserve(sizeof(*metadata));\nkernel/liveupdate/kexec_handover.c-1785-\tif (IS_ERR(metadata))\n--\nkernel/liveupdate/kexec_handover.c=1818=static __init int kho_init(void)\n--\nkernel/liveupdate/kexec_handover.c-1830-\nkernel/liveupdate/kexec_handover.c:1831:\tkho_out.fdt = kho_alloc_preserve(PAGE_SIZE);\nkernel/liveupdate/kexec_handover.c-1832-\tif (IS_ERR(kho_out.fdt)) {\n--\nkernel/liveupdate/kho_block.c=73=static struct kho_block_header_ser *kho_block_alloc_ser(struct kho_block_set *bs)\n--\nkernel/liveupdate/kho_block.c-75-\tWARN_ON_ONCE(bs-\u003eincoming);\nkernel/liveupdate/kho_block.c:76:\treturn kho_alloc_preserve(KHO_BLOCK_SIZE);\nkernel/liveupdate/kho_block.c-77-}\n--\nkernel/liveupdate/luo_core.c=154=static int __init luo_state_setup(void)\n--\nkernel/liveupdate/luo_core.c-158-\nkernel/liveupdate/luo_core.c:159:\tluo_ser = kho_alloc_preserve(sizeof(*luo_ser));\nkernel/liveupdate/luo_core.c-160-\tif (IS_ERR(luo_ser)) {\n--\nkernel/liveupdate/luo_flb.c=560=int __init luo_flb_setup_outgoing(u64 *flbs_pa)\n--\nkernel/liveupdate/luo_flb.c-563-\nkernel/liveupdate/luo_flb.c:564:\theader_ser = kho_alloc_preserve(LUO_FLB_PGCNT \u003c\u003c PAGE_SHIFT);\nkernel/liveupdate/luo_flb.c-565-\tif (IS_ERR(header_ser))\n--\nmm/memfd_luo.c=258=static int memfd_luo_preserve(struct liveupdate_file_op_args *args)\n--\nmm/memfd_luo.c-269-\t/* Allocate the main serialization structure in preserved memory */\nmm/memfd_luo.c:270:\tser = kho_alloc_preserve(sizeof(*ser));\nmm/memfd_luo.c-271-\tif (IS_ERR(ser)) {\n"
}

49/3 2026/09/21 01:14 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27126
							output: 25
							thoughts: 452
						

Thoughts:
**Analyzing Memory Allocation in PCI FLB Serialization**

Okay, so I'm looking at how memory is managed for PCI FLB serialization. Specifically, I see `kho_alloc_preserve` is being used, which allocates, zeros, and preserves memory. That's good, a clean slate. Also, the `ser` structure, which I assume is some kind of serialization context, is initialized right off the bat with version and device count information. No surprises there.

Next, I need to understand `pci_dev_ser`. It’s a packed structure, with a size of 8 bytes (4 + 2 + 2), holding domain, BDF, and a reference count. I'm trying to trace where these `pci_dev_ser` structures are actually allocated. Looking at `pci_flb_alloc_dev_ser`, I can see a fairly interesting allocation strategy. It attempts to reuse existing, unreferenced entries in a `kho_block_set` before growing the set.  This implies some kind of memory pool management, which is a common performance optimization.  If a suitable unreferenced entry isn't found, it grows the `block_set` and reserves a new entry. The code looks pretty robust with the `WARN_ON_ONCE` in case something goes wrong with the reservation after a successful grow.

To confirm how the `kho_block_set` itself is initialized, I'll need to look at `kho_block_alloc_ser` and dive into the internals of `kho_block.c`. I expect to find the actual memory initialization logic there.



50/3 2026/09/21 01:14 tool grepper
0m Args:
{
  "Expression": "kho_block_set_it_reserve_entry"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c=450=static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)\n--\ndrivers/pci/liveupdate.c-475-\t/* This should always succeed since kho_block_set_grow() succeeded. */\ndrivers/pci/liveupdate.c:476:\tdev_ser = kho_block_set_it_reserve_entry(\u0026it);\ndrivers/pci/liveupdate.c-477-\tif (WARN_ON_ONCE(!dev_ser))\n--\ninclude/linux/kho_block.h=101=void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs);\ninclude/linux/kho_block.h:102:void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it);\ninclude/linux/kho_block.h-103-void *kho_block_set_it_read_entry(struct kho_block_set_it *it);\n--\nkernel/liveupdate/kho_block.c=331=void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs)\n--\nkernel/liveupdate/kho_block.c-338-/**\nkernel/liveupdate/kho_block.c:339: * kho_block_set_it_reserve_entry - Reserve and return the next available slot for writing.\nkernel/liveupdate/kho_block.c-340- * @it: The block iterator.\n--\nkernel/liveupdate/kho_block.c-348- */\nkernel/liveupdate/kho_block.c:349:void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it)\nkernel/liveupdate/kho_block.c-350-{\n--\nkernel/liveupdate/luo_file.c=455=int luo_file_freeze(struct luo_file_set *file_set,\n--\nkernel/liveupdate/luo_file.c-467-\tlist_for_each_entry(luo_file, \u0026file_set-\u003efiles_list, list) {\nkernel/liveupdate/luo_file.c:468:\t\tstruct luo_file_ser *file_ser = kho_block_set_it_reserve_entry(\u0026it);\nkernel/liveupdate/luo_file.c-469-\n--\nkernel/liveupdate/luo_session.c=637=int luo_session_serialize(void)\n--\nkernel/liveupdate/luo_session.c-650-\tlist_for_each_entry(session, \u0026sh-\u003elist, list) {\nkernel/liveupdate/luo_session.c:651:\t\tstruct luo_session_ser *ser = kho_block_set_it_reserve_entry(\u0026it);\nkernel/liveupdate/luo_session.c-652-\n"
}

51/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28708
							output: 26
							thoughts: 1318
						

Thoughts:
**Analyzing PCI Live Update and Memory Safety**

Okay, let's break this down. My initial concern was potentially uninitialized memory. I noted that `kho_block_alloc_ser` uses `kho_alloc_preserve(KHO_BLOCK_SIZE)`, which *should* allocate zeroed memory. That's a good start. I then confirmed that the critical structure, `dev_ser`, is explicitly initialized with fields like `domain`, `bdf`, and `refcount` when allocated within `pci_liveupdate_preserve_device`. That looks pretty robust. And, importantly, when the device is *un*preserved, `dev_ser` is explicitly zeroed with `memset`. So, from the initial look at the core allocation and deallocation path, I don't see any immediate red flags. Good!

Now, I need to broaden my scope. It's crucial to check the context around these changes. I've got a diff list: `drivers/pci/liveupdate.c`, `drivers/pci/liveupdate.h`, `drivers/pci/pci-driver.c`, `drivers/pci/pci.c`, `drivers/pci/pci.h`, `drivers/pci/probe.c`, `drivers/pci/quirks.c`, `include/linux/kho/abi/pci.h`, `include/linux/pci.h`, and `include/linux/pci_liveupdate.h`. I'll go through each of these files methodically.

First, `drivers/pci/pci-driver.c` and its `pci_device_shutdown` function. No apparent memory issues here. It's mostly related to shutdown procedures, runtime management, and Kexec operations, but nothing seems to be allocating or manipulating memory that could introduce a vulnerability. Fine, moving on.

Next, `drivers/pci/pci.c`, specifically focusing on `pci_save_acs_state` and `pci_restore_acs_state`. Wait, there's a structure called `save_state` used there that is not obviously initialized. Where is `save_state` allocated? Digging deeper... it is allocated by `pci_allocate_cap_save_buffers`. Looks like there's an `error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS, sizeof(u16));` call in there. Now I need to check what `pci_add_ext_cap_save_buffer` does! That's the next critical piece of information.



52/3 2026/09/21 01:15 tool grepper
0m Args:
{
  "Expression": "pci_add_ext_cap_save_buffer"
}

Results:
{
  "Output": "drivers/pci/pci.c=3567=int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size)\n--\ndrivers/pci/pci.c-3571-\ndrivers/pci/pci.c:3572:int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)\ndrivers/pci/pci.c-3573-{\n--\ndrivers/pci/pci.c=3581=void pci_allocate_cap_save_buffers(struct pci_dev *dev)\n--\ndrivers/pci/pci.c-3593-\ndrivers/pci/pci.c:3594:\terror = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR,\ndrivers/pci/pci.c-3595-\t\t\t\t\t    2 * sizeof(u16));\n--\ndrivers/pci/pci.c-3598-\ndrivers/pci/pci.c:3599:\terror = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,\ndrivers/pci/pci.c-3600-\t\t\t\t\t    sizeof(u16));\n--\ndrivers/pci/pci.h=254=int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size);\ndrivers/pci/pci.h:255:int pci_add_ext_cap_save_buffer(struct pci_dev *dev,\ndrivers/pci/pci.h-256-\t\t\t\tu16 cap, unsigned int size);\n--\ndrivers/pci/pcie/aer.c=399=void pci_aer_init(struct pci_dev *dev)\n--\ndrivers/pci/pcie/aer.c-424-\tn = pcie_cap_has_rtctl(dev) ? 5 : 4;\ndrivers/pci/pcie/aer.c:425:\tpci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n);\ndrivers/pci/pcie/aer.c-426-\n--\ndrivers/pci/pcie/aspm.c=70=void pci_configure_aspm_l1ss(struct pci_dev *pdev)\n--\ndrivers/pci/pcie/aspm.c-75-\ndrivers/pci/pcie/aspm.c:76:\trc = pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_L1SS,\ndrivers/pci/pcie/aspm.c-77-\t\t\t\t\t 2 * sizeof(u32));\n--\ndrivers/pci/pcie/dpc.c=473=static int dpc_probe(struct pcie_device *dev)\n--\ndrivers/pci/pcie/dpc.c-501-\ndrivers/pci/pcie/dpc.c:502:\tpci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16));\ndrivers/pci/pcie/dpc.c-503-\treturn status;\n--\ndrivers/pci/pcie/ptm.c=41=void pci_ptm_init(struct pci_dev *dev)\n--\ndrivers/pci/pcie/ptm.c-55-\tatomic_set(\u0026dev-\u003eptm_enable_cnt, 0);\ndrivers/pci/pcie/ptm.c:56:\tpci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u32));\ndrivers/pci/pcie/ptm.c-57-\n--\ndrivers/pci/tph.c=509=void pci_tph_init(struct pci_dev *pdev)\n--\ndrivers/pci/tph.c-519-\tsave_size = sizeof(u32) + num_entries * sizeof(u16);\ndrivers/pci/tph.c:520:\tpci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);\ndrivers/pci/tph.c-521-}\n--\ndrivers/pci/vc.c=414=void pci_allocate_vc_save_buffers(struct pci_dev *dev)\n--\ndrivers/pci/vc.c-424-\t\tlen = pci_vc_do_save_buffer(dev, pos, NULL, false);\ndrivers/pci/vc.c:425:\t\tif (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len))\ndrivers/pci/vc.c-426-\t\t\tpci_err(dev, \"unable to preallocate %s save buffer\\n\",\n"
}

53/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31909
							output: 38
							thoughts: 24
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into `pci_add_ext_cap_save_buffer`**

Okay, so I'm pulling up the `pci_add_ext_cap_save_buffer` implementation. Let's see how they're handling this. I'm especially interested in how they are managing the buffer for saving extended capabilities during device enumeration or possibly driver loading/reloading. I'll be looking for things like:

*   **Buffer allocation:** How are they allocating the save buffer? Is it statically sized, dynamically allocated, or using some kind of pool? What are the memory management implications here? Any potential for fragmentation or out-of-memory errors?
*   **Capacity management:** How do they determine the required size of the buffer? Are they querying the PCI configuration space directly, or are they relying on some form of pre-computed value or heuristic? Is there proper error checking for the size of the cap that is going to be saved?
*   **Data integrity:** I need to understand how the capabilities are *actually* copied into the buffer. Any potential for off-by-one errors or buffer overflows? Are there any data alignment considerations? Is there any check for corruption/invalid data?
*   **Concurrency/Synchronization:** Is there any locking involved when accessing or modifying this buffer? I'll need to watch out for potential race conditions if multiple threads or processes might be interacting with the PCI device simultaneously.
*   **Error Handling:** What's the error handling like? How are they reporting failures if the buffer allocation fails, the PCI read fails, or if there's a problem with writing the data? Is it graceful or catastrophic?
*   **Use-Case:** What's the intended use of this? Is this only for PCI enumeration, or for driver resume, or for something else?

I'm mentally preparing for a deep dive. I'll start with the entry point, follow the control flow, and try to understand the rationale behind each decision. I'm expecting to see some good use of PCI library functions, but I'll be looking for any potential for improvements or areas where it could be problematic down the line. Let's see what's in here.



54/3 2026/09/21 01:15 tool read-file
0m Args:
{
  "File": "drivers/pci/pci.c",
  "FirstLine": 3565,
  "LineCount": 20
}

Results:
{
  "Contents": "3565:\t}\n3566:\t\n3567:\tint pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size)\n3568:\t{\n3569:\t\treturn _pci_add_cap_save_buffer(dev, cap, false, size);\n3570:\t}\n3571:\t\n3572:\tint pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)\n3573:\t{\n3574:\t\treturn _pci_add_cap_save_buffer(dev, cap, true, size);\n3575:\t}\n3576:\t\n3577:\t/**\n3578:\t * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities\n3579:\t * @dev: the PCI device\n3580:\t */\n3581:\tvoid pci_allocate_cap_save_buffers(struct pci_dev *dev)\n3582:\t{\n3583:\t\tint error;\n3584:\t\n"
}

55/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32762
							output: 25
							thoughts: 17
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:

**Diving into `_pci_add_cap_save_buffer`**

Alright, let's take a look at this `_pci_add_cap_save_buffer` function. My initial thought is it's likely involved in a core PCI initialization or management sequence. The naming convention suggests it handles saving or storing some sort of capability information. Given the context, this probably relates to those critical PCI configuration space capabilities – things like power management, MSI, PCIe extended capabilities, and so forth. The "buffer" part likely indicates we're dealing with a temporary storage space before, during, or after some action.

My immediate questions are: What triggers this save? Is it during device enumeration, driver probing, or potentially even hotplug events? What specifically gets saved? Is it the *entire* capability structure, or just selective fields of interest? And importantly, *why* are we saving it? Is it for restoration later (e.g., during resume from suspend), for comparison purposes, or for auditing/debugging? Context is key.

I'm mentally running through the typical PCI initialization flows... is this function being called within the standard PCI driver model? Is it part of a platform-specific or architecture-specific layer? And what’s the expected data structure format for the "buffer" itself? Is it a well-defined struct, a raw byte array, or something more abstracted? I need to understand the data representation to interpret the intent. My experience tells me that these types of "save" functions are critical in maintaining the integrity of the PCI configuration during device transitions, so it’s likely protecting against configuration corruption or unexpected changes.

Finally, I'm thinking about potential issues. Are there any race conditions with concurrent access to the configuration space? What about memory allocation errors or buffer overflows? And what are the implications of a failed save? Does the system gracefully handle that or lead to instability? I need to analyze this function carefully to ensure it's robust and prevents any unexpected behavior. I'll need to scrutinize the locking mechanisms and error handling.



56/3 2026/09/21 01:15 tool grepper
0m Args:
{
  "Expression": "_pci_add_cap_save_buffer"
}

Results:
{
  "Output": "drivers/pci/pci.c=3527=static void pci_add_saved_cap(struct pci_dev *pci_dev,\n--\ndrivers/pci/pci.c-3533-/**\ndrivers/pci/pci.c:3534: * _pci_add_cap_save_buffer - allocate buffer for saving given\ndrivers/pci/pci.c-3535- *\t\t\t      capability registers\n--\ndrivers/pci/pci.c-3540- */\ndrivers/pci/pci.c:3541:static int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap,\ndrivers/pci/pci.c-3542-\t\t\t\t    bool extended, unsigned int size)\n--\ndrivers/pci/pci.c=3567=int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size)\ndrivers/pci/pci.c-3568-{\ndrivers/pci/pci.c:3569:\treturn _pci_add_cap_save_buffer(dev, cap, false, size);\ndrivers/pci/pci.c-3570-}\n--\ndrivers/pci/pci.c=3572=int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)\ndrivers/pci/pci.c-3573-{\ndrivers/pci/pci.c:3574:\treturn _pci_add_cap_save_buffer(dev, cap, true, size);\ndrivers/pci/pci.c-3575-}\n"
}

57/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 33711
							output: 38
							thoughts: 27
						

Thoughts:
Okay, here's my interpretation of what's going through my mind, pretending I'm an expert reading those lines of code:

**Diving into the PCI Core: Lines 3540-3560**

Alright, time to get my hands dirty. Let's pull up `drivers/pci/pci.c` and zero in on lines 3540 through 3560. I need to understand this specific section because, judging by the surrounding code, it’s likely handling some critical PCI device initialization, configuration, or perhaps resource allocation. I'm going to scan these lines quickly, keeping an eye out for any calls to potentially problematic functions, any complex bitwise operations, or anything that seems out of the ordinary. My spidey-sense is up; I know PCI internals can get very tricky, very fast, so I'm looking for clues about the overall context within the larger driver. I'll be keeping track of possible edge cases or race conditions. This section will likely touch a lot of different internal data structures and probably interacts with the PCI host bridge, so I'll be mapping the logic, and will likely be making a mental map of important internal pointers and variables, as I get there. This looks important.



58/3 2026/09/21 01:15 tool read-file
0m Args:
{
  "File": "drivers/pci/pci.c",
  "FirstLine": 3540,
  "LineCount": 25
}

Results:
{
  "Contents": "3540:\t */\n3541:\tstatic int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap,\n3542:\t\t\t\t\t    bool extended, unsigned int size)\n3543:\t{\n3544:\t\tint pos;\n3545:\t\tstruct pci_cap_saved_state *save_state;\n3546:\t\n3547:\t\tif (extended)\n3548:\t\t\tpos = pci_find_ext_capability(dev, cap);\n3549:\t\telse\n3550:\t\t\tpos = pci_find_capability(dev, cap);\n3551:\t\n3552:\t\tif (!pos)\n3553:\t\t\treturn 0;\n3554:\t\n3555:\t\tsave_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL);\n3556:\t\tif (!save_state)\n3557:\t\t\treturn -ENOMEM;\n3558:\t\n3559:\t\tsave_state-\u003ecap.cap_nr = cap;\n3560:\t\tsave_state-\u003ecap.cap_extended = extended;\n3561:\t\tsave_state-\u003ecap.size = size;\n3562:\t\tpci_add_saved_cap(dev, save_state);\n3563:\t\n3564:\t\treturn 0;\n"
}

59/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 34411
							output: 22
							thoughts: 331
						

Thoughts:
**My Concerns Regarding ACS State Saving and ARI Adoption**

Okay, so I'm looking at this ACS state saving mechanism, and it allocates `save_state` using `kzalloc`. That means the entire buffer, including the `cap.data` field within `save_state`, is initialized to zero. That's good, a sensible starting point. Now, when `pci_save_acs_state` reads the ACS control word into `save_state->cap.data[0]`, I'm pausing. Why?

Because I'm also thinking about this `pci_liveupdate_adopt_ari` function. It seems to be reading the ARI (Alternative Routing ID Interpretation) enable bit from `PCI_EXP_DEVCTL2`. The code grabs this value and stores it into `dev->ari_enabled`. That looks like a problem area.

Specifically, I need to check whether `pcie_capability_read_word` initializes the variable `val` before its value is used to set `dev->ari_enabled`. This potentially introduces a race condition because the ACS state data is not guaranteed to be initialized before a live update attempt. I must check the implementation of `pcie_capability_read_word` to be sure. It could lead to a read-before-write vulnerability. This is something I'm going to have to dive deeper into.



60/3 2026/09/21 01:15 tool grepper
0m Args:
{
  "Expression": "pcie_capability_read_word"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 871 lines.\nUse more precise expression if possible.\n\ndrivers/block/mtip32xx/mtip32xx.c=3647=static void mtip_disable_link_opts(struct driver_data *dd, struct pci_dev *pdev)\n--\ndrivers/block/mtip32xx/mtip32xx.c-3651-\tif (pci_is_pcie(pdev)) {\ndrivers/block/mtip32xx/mtip32xx.c:3652:\t\tpcie_capability_read_word(pdev, PCI_EXP_DEVCTL, \u0026pcie_dev_ctrl);\ndrivers/block/mtip32xx/mtip32xx.c-3653-\t\tif (pcie_dev_ctrl \u0026 PCI_EXP_DEVCTL_NOSNOOP_EN ||\n--\ndrivers/cxl/core/pci.c=757=int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c)\n--\ndrivers/cxl/core/pci.c-767-\ndrivers/cxl/core/pci.c:768:\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026lnksta);\ndrivers/cxl/core/pci.c-769-\twidth = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);\n--\ndrivers/cxl/cxlpci.h=59=static inline bool cxl_pci_flit_256(struct pci_dev *pdev)\n--\ndrivers/cxl/cxlpci.h-62-\ndrivers/cxl/cxlpci.h:63:\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, \u0026lnksta2);\ndrivers/cxl/cxlpci.h-64-\treturn lnksta2 \u0026 PCI_EXP_LNKSTA2_FLIT;\n--\ndrivers/cxl/port.c=72=static int cxl_ras_unmask(struct cxl_port *port)\n--\ndrivers/cxl/port.c-92-\ndrivers/cxl/port.c:93:\trc = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, \u0026cap);\ndrivers/cxl/port.c-94-\tif (rc)\n--\ndrivers/dma/ioat/init.c=1076=static int ioat3_dma_probe(struct ioatdma_device *ioat_dma, int dca)\n--\ndrivers/dma/ioat/init.c-1182-\t/* disable relaxed ordering */\ndrivers/dma/ioat/init.c:1183:\terr = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, \u0026val16);\ndrivers/dma/ioat/init.c-1184-\tif (err) {\n--\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c=617=static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev,\n--\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c-657-\tif (us_pdev) {\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c:658:\t\tpcie_capability_read_word(us_pdev, PCI_EXP_DEVSTA,\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c-659-\t\t\t\t\t  \u0026pcie_regs-\u003edevice_status);\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c:660:\t\tpcie_capability_read_word(us_pdev, PCI_EXP_LNKSTA,\ndrivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c-661-\t\t\t\t\t  \u0026pcie_regs-\u003elink_status);\n--\ndrivers/gpu/drm/amd/amdgpu/cik.c=1531=static void cik_pcie_gen3_enable(struct amdgpu_device *adev)\n--\ndrivers/gpu/drm/amd/amdgpu/cik.c-1602-\t\t\t\t/* check status */\ndrivers/gpu/drm/amd/amdgpu/cik.c:1603:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/cik.c-1604-\t\t\t\t\t\t\t  PCI_EXP_DEVSTA,\n--\ndrivers/gpu/drm/amd/amdgpu/cik.c-1608-\ndrivers/gpu/drm/amd/amdgpu/cik.c:1609:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL,\ndrivers/gpu/drm/amd/amdgpu/cik.c-1610-\t\t\t\t\t\t\t  \u0026bridge_cfg);\ndrivers/gpu/drm/amd/amdgpu/cik.c:1611:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/cik.c-1612-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL,\n--\ndrivers/gpu/drm/amd/amdgpu/cik.c-1614-\ndrivers/gpu/drm/amd/amdgpu/cik.c:1615:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL2,\ndrivers/gpu/drm/amd/amdgpu/cik.c-1616-\t\t\t\t\t\t\t  \u0026bridge_cfg2);\ndrivers/gpu/drm/amd/amdgpu/cik.c:1617:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/cik.c-1618-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL2,\n--\ndrivers/gpu/drm/amd/amdgpu/si.c=2225=static void si_pcie_gen3_enable(struct amdgpu_device *adev)\n--\ndrivers/gpu/drm/amd/amdgpu/si.c-2288-\t\t\tfor (i = 0; i \u003c 10; i++) {\ndrivers/gpu/drm/amd/amdgpu/si.c:2289:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/si.c-2290-\t\t\t\t\t\t\t  PCI_EXP_DEVSTA,\n--\ndrivers/gpu/drm/amd/amdgpu/si.c-2294-\ndrivers/gpu/drm/amd/amdgpu/si.c:2295:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL,\ndrivers/gpu/drm/amd/amdgpu/si.c-2296-\t\t\t\t\t\t\t  \u0026bridge_cfg);\ndrivers/gpu/drm/amd/amdgpu/si.c:2297:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/si.c-2298-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL,\n--\ndrivers/gpu/drm/amd/amdgpu/si.c-2300-\ndrivers/gpu/drm/amd/amdgpu/si.c:2301:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL2,\ndrivers/gpu/drm/amd/amdgpu/si.c-2302-\t\t\t\t\t\t\t  \u0026bridge_cfg2);\ndrivers/gpu/drm/amd/amdgpu/si.c:2303:\t\t\t\tpcie_capability_read_word(adev-\u003epdev,\ndrivers/gpu/drm/amd/amdgpu/si.c-2304-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL2,\n--\ndrivers/gpu/drm/radeon/cik.c=9500=static void cik_pcie_gen3_enable(struct radeon_device *rdev)\n--\ndrivers/gpu/drm/radeon/cik.c-9573-\t\t\t\t/* check status */\ndrivers/gpu/drm/radeon/cik.c:9574:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/cik.c-9575-\t\t\t\t\t\t\t  PCI_EXP_DEVSTA,\n--\ndrivers/gpu/drm/radeon/cik.c-9579-\ndrivers/gpu/drm/radeon/cik.c:9580:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL,\ndrivers/gpu/drm/radeon/cik.c-9581-\t\t\t\t\t\t\t  \u0026bridge_cfg);\ndrivers/gpu/drm/radeon/cik.c:9582:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/cik.c-9583-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL,\n--\ndrivers/gpu/drm/radeon/cik.c-9585-\ndrivers/gpu/drm/radeon/cik.c:9586:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL2,\ndrivers/gpu/drm/radeon/cik.c-9587-\t\t\t\t\t\t\t  \u0026bridge_cfg2);\ndrivers/gpu/drm/radeon/cik.c:9588:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/cik.c-9589-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL2,\n--\ndrivers/gpu/drm/radeon/si.c=7064=static void si_pcie_gen3_enable(struct radeon_device *rdev)\n--\ndrivers/gpu/drm/radeon/si.c-7137-\t\t\t\t/* check status */\ndrivers/gpu/drm/radeon/si.c:7138:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/si.c-7139-\t\t\t\t\t\t\t  PCI_EXP_DEVSTA,\n--\ndrivers/gpu/drm/radeon/si.c-7143-\ndrivers/gpu/drm/radeon/si.c:7144:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL,\ndrivers/gpu/drm/radeon/si.c-7145-\t\t\t\t\t\t\t  \u0026bridge_cfg);\ndrivers/gpu/drm/radeon/si.c:7146:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/si.c-7147-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL,\n--\ndrivers/gpu/drm/radeon/si.c-7149-\ndrivers/gpu/drm/radeon/si.c:7150:\t\t\t\tpcie_capability_read_word(root, PCI_EXP_LNKCTL2,\ndrivers/gpu/drm/radeon/si.c-7151-\t\t\t\t\t\t\t  \u0026bridge_cfg2);\ndrivers/gpu/drm/radeon/si.c:7152:\t\t\t\tpcie_capability_read_word(rdev-\u003epdev,\ndrivers/gpu/drm/radeon/si.c-7153-\t\t\t\t\t\t\t  PCI_EXP_LNKCTL2,\n--\ndrivers/infiniband/hw/bng_re/bng_sp.c=11=static bool bng_re_is_atomic_cap(struct bng_re_rcfw *rcfw)\n--\ndrivers/infiniband/hw/bng_re/bng_sp.c-14-\ndrivers/infiniband/hw/bng_re/bng_sp.c:15:\tpcie_capability_read_word(rcfw-\u003epdev, PCI_EXP_DEVCTL2, \u0026pcie_ctl2);\ndrivers/infiniband/hw/bng_re/bng_sp.c-16-\treturn (pcie_ctl2 \u0026 PCI_EXP_DEVCTL2_ATOMIC_REQ);\n--\ndrivers/infiniband/hw/bnxt_re/qplib_res.c=992=int bnxt_qplib_determine_atomics(struct pci_dev *dev)\n--\ndrivers/infiniband/hw/bnxt_re/qplib_res.c-1004-\t\treturn -EOPNOTSUPP;\ndrivers/infiniband/hw/bnxt_re/qplib_res.c:1005:\tpcie_capability_read_word(dev, PCI_EXP_DEVCTL2, \u0026ctl2);\ndrivers/infiniband/hw/bnxt_re/qplib_res.c-1006-\treturn !(ctl2 \u0026 PCI_EXP_DEVCTL2_ATOMIC_REQ);\n--\ndrivers/infiniband/hw/bnxt_re/qplib_sp.c=58=static bool bnxt_qplib_is_atomic_cap(struct bnxt_qplib_rcfw *rcfw)\n--\ndrivers/infiniband/hw/bnxt_re/qplib_sp.c-64-\ndrivers/infiniband/hw/bnxt_re/qplib_sp.c:65:\tpcie_capability_read_word(rcfw-\u003epdev, PCI_EXP_DEVCTL2, \u0026pcie_ctl2);\ndrivers/infiniband/hw/bnxt_re/qplib_sp.c-66-\treturn (pcie_ctl2 \u0026 PCI_EXP_DEVCTL2_ATOMIC_REQ);\n--\ndrivers/infiniband/hw/hfi1/pcie.c=217=static void update_lbus_info(struct hfi1_devdata *dd)\n--\ndrivers/infiniband/hw/hfi1/pcie.c-221-\ndrivers/infiniband/hw/hfi1/pcie.c:222:\tret = pcie_capability_read_word(dd-\u003epcidev, PCI_EXP_LNKSTA, \u0026linkstat);\ndrivers/infiniband/hw/hfi1/pcie.c-223-\tif (ret) {\n--\ndrivers/infiniband/hw/hfi1/pcie.c=347=int save_pci_variables(struct hfi1_devdata *dd)\n--\ndrivers/infiniband/hw/hfi1/pcie.c-368-\ndrivers/infiniband/hw/hfi1/pcie.c:369:\tret = pcie_capability_read_word(dd-\u003epcidev, PCI_EXP_DEVCTL,\ndrivers/infiniband/hw/hfi1/pcie.c-370-\t\t\t\t\t\u0026dd-\u003epcie_devctl);\n--\ndrivers/infiniband/hw/hfi1/pcie.c-373-\ndrivers/infiniband/hw/hfi1/pcie.c:374:\tret = pcie_capability_read_word(dd-\u003epcidev, PCI_EXP_LNKCTL,\ndrivers/infiniband/hw/hfi1/pcie.c-375-\t\t\t\t\t\u0026dd-\u003epcie_lnkctl);\n--\ndrivers/infiniband/hw/hfi1/pcie.c-378-\ndrivers/infiniband/hw/hfi1/pcie.c:379:\tret = pcie_capability_read_word(dd-\u003epcidev, PCI_EXP_DEVCTL2,\ndrivers/infiniband/hw/hfi1/pcie.c-380-\t\t\t\t\t\u0026dd-\u003epcie_devctl2);\n--\ndrivers/infiniband/hw/hfi1/pcie.c=414=void tune_pcie_caps(struct hfi1_devdata *dd)\n--\ndrivers/infiniband/hw/hfi1/pcie.c-424-\t */\ndrivers/infiniband/hw/hfi1/pcie.c:425:\tret = pcie_capability_read_word(dd-\u003epcidev, PCI_EXP_DEVCTL, \u0026ectl);\ndrivers/infiniband/hw/hfi1/pcie.c-426-\tif ((!ret) \u0026\u0026 !(ectl \u0026 PCI_EXP_DEVCTL_EXT_TAG)) {\n--\ndrivers/infiniband/hw/hfi1/pcie.c=937=int do_pcie_gen3_transition(struct hfi1_devdata *dd)\n--\ndrivers/infiniband/hw/hfi1/pcie.c-1200-\tdd_dev_info(dd, \"%s: setting parent target link speed\\n\", __func__);\ndrivers/infiniband/hw/hfi1/pcie.c:1201:\tret = pcie_capability_read_word(parent, PCI_EXP_LNKCTL2, \u0026lnkctl2);\ndrivers/infiniband/hw/hfi1/pcie.c-1202-\tif (ret) {\n--\ndrivers/media/pci/cobalt/cobalt-driver.c=185=void cobalt_pcie_status_show(struct cobalt *cobalt)\n--\ndrivers/media/pci/cobalt/cobalt-driver.c-196-\tpcie_capability_read_dword(pci_dev, PCI_EXP_DEVCAP, \u0026capa);\ndrivers/media/pci/cobalt/cobalt-driver.c:197:\tpcie_capability_read_word(pci_dev, PCI_EXP_DEVCTL, \u0026ctrl);\ndrivers/media/pci/cobalt/cobalt-driver.c:198:\tpcie_capability_read_word(pci_dev, PCI_EXP_DEVSTA, \u0026stat);\ndrivers/media/pci/cobalt/cobalt-driver.c-199-\tcobalt_info(\"PCIe device capability 0x%08x: Max payload %d\\n\",\n--\ndrivers/media/pci/cobalt/cobalt-driver.c-208-\tpcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, \u0026capa);\ndrivers/media/pci/cobalt/cobalt-driver.c:209:\tpcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, \u0026ctrl);\ndrivers/media/pci/cobalt/cobalt-driver.c:210:\tpcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, \u0026stat);\ndrivers/media/pci/cobalt/cobalt-driver.c-211-\tcobalt_info(\"PCIe link capability 0x%08x: %s per lane and %u lanes\\n\",\n--\ndrivers/media/pci/cobalt/cobalt-driver.c-226-\tpcie_capability_read_dword(pci_dev, PCI_EXP_SLTCAP, \u0026capa);\ndrivers/media/pci/cobalt/cobalt-driver.c:227:\tpcie_capability_read_word(pci_dev, PCI_EXP_SLTCTL, \u0026ctrl);\ndrivers/media/pci/cobalt/cobalt-driver.c:228:\tpcie_capability_read_word(pci_dev, PCI_EXP_SLTSTA, \u0026stat);\ndrivers/media/pci/cobalt/cobalt-driver.c-229-\tcobalt_info(\"PCIe slot capability 0x%08x\\n\", capa);\n--\ndrivers/media/pci/cobalt/cobalt-driver.c=234=static unsigned pcie_link_get_lanes(struct cobalt *cobalt)\n--\ndrivers/media/pci/cobalt/cobalt-driver.c-240-\t\treturn 0;\ndrivers/media/pci/cobalt/cobalt-driver.c:241:\tpcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, \u0026link);\ndrivers/media/pci/cobalt/cobalt-driver.c-242-\treturn FIELD_GET(PCI_EXP_LNKSTA_NLW, link);\n--\ndrivers/misc/bcm-vk/bcm_vk_dev.c=1593=static void bcm_vk_shutdown(struct pci_dev *pdev)\n--\ndrivers/misc/bcm-vk/bcm_vk_dev.c-1615-\t\t */\ndrivers/misc/bcm-vk/bcm_vk_dev.c:1616:\t\terr = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026lnksta);\ndrivers/misc/bcm-vk/bcm_vk_dev.c-1617-\t\tif (!err \u0026\u0026\n--\ndrivers/misc/cardreader/rts5227.c=106=static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)\n--\ndrivers/misc/cardreader/rts5227.c-123-\t/* Configure LTR */\ndrivers/misc/cardreader/rts5227.c:124:\tpcie_capability_read_word(pcr-\u003epci, PCI_EXP_DEVCTL2, \u0026cap);\ndrivers/misc/cardreader/rts5227.c-125-\tif (cap \u0026 PCI_EXP_DEVCTL2_LTR_EN)\n--\ndrivers/misc/cardreader/rtsx_pcr.c=1317=static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)\n--\ndrivers/misc/cardreader/rtsx_pcr.c-1394-\tif (pcr-\u003easpm_mode == ASPM_MODE_CFG) {\ndrivers/misc/cardreader/rtsx_pcr.c:1395:\t\tpcie_capability_read_word(pcr-\u003epci, PCI_EXP_LNKCTL, \u0026cfg_val);\ndrivers/misc/cardreader/rtsx_pcr.c-1396-\t\tif (cfg_val \u0026 PCI_EXP_LNKCTL_ASPM_L1)\n--\ndrivers/misc/cardreader/rtsx_pcr.c-1432-\ndrivers/misc/cardreader/rtsx_pcr.c:1433:\t\tpcie_capability_read_word(pcr-\u003epci, PCI_EXP_DEVCTL2, \u0026cfg_val);\ndrivers/misc/cardreader/rtsx_pcr.c-1434-\t\tif (cfg_val \u0026 PCI_EXP_DEVCTL2_LTR_EN) {\n--\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c=1438=u8 bnx2x_is_pcie_pending(struct pci_dev *dev)\n--\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c-1441-\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c:1442:\tpcie_capability_read_word(dev, PCI_EXP_DEVSTA, \u0026status);\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c-1443-\treturn status \u0026 PCI_EXP_DEVSTA_TRPND;\n--\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c=6920=static void bnx2x_init_pxp(struct bnx2x *bp)\n--\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c-6924-\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c:6925:\tpcie_capability_read_word(bp-\u003epdev, PCI_EXP_DEVCTL, \u0026devctl);\ndrivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c-6926-\tDP(NETIF_MSG_HW, \"read 0x%x from devctl\\n\", devctl);\n--\ndrivers/net/ethernet/broadcom/tg3.c=16251=static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/broadcom/tg3.c-16515-\ndrivers/net/ethernet/broadcom/tg3.c:16516:\t\tpcie_capability_read_word(tp-\u003epdev, PCI_EXP_LNKCTL, \u0026lnkctl);\ndrivers/net/ethernet/broadcom/tg3.c-16517-\t\tif (lnkctl \u0026 PCI_EXP_LNKCTL_CLKREQ_EN) {\n--\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c=3243=static void config_pcie(struct adapter *adap)\n--\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3261-\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c:3262:\tpcie_capability_read_word(adap-\u003epdev, PCI_EXP_DEVCTL, \u0026val);\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3263-\tpldsize = (val \u0026 PCI_EXP_DEVCTL_PAYLOAD) \u003e\u003e 5;\n--\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3272-\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c:3273:\tpcie_capability_read_word(adap-\u003epdev, PCI_EXP_LNKCTL, \u0026val);\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3274-\n--\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c=3391=static void get_pci_mode(struct adapter *adapter, struct pci_params *p)\n--\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3399-\t\tp-\u003evariant = PCI_VARIANT_PCIE;\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c:3400:\t\tpcie_capability_read_word(adapter-\u003epdev, PCI_EXP_LNKSTA, \u0026val);\ndrivers/net/ethernet/chelsio/cxgb3/t3_hw.c-3401-\t\tp-\u003ewidth = (val \u003e\u003e 4) \u0026 0x3f;\n--\ndrivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c=6235=static int cxgb4_iov_configure(struct pci_dev *pdev, int num_vfs)\n--\ndrivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c-6298-\t\tpbridge = pdev-\u003ebus-\u003eself;\ndrivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c:6299:\t\tpcie_capability_read_word(pbridge, PCI_EXP_FLAGS, \u0026flags);\ndrivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c-6300-\t\tpcie_capability_read_dword(pbridge, PCI_EXP_DEVCAP2, \u0026devcap2);\n--\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c=7265=int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,\n--\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c-7325-\t\t\t */\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c:7326:\t\t\tpcie_capability_read_word(adap-\u003epdev, PCI_EXP_DEVCTL,\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c-7327-\t\t\t\t\t\t  \u0026devctl);\n--\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c=8871=static void get_pci_mode(struct adapter *adapter, struct pci_params *p)\n--\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c-8875-\tif (pci_is_pcie(adapter-\u003epdev)) {\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c:8876:\t\tpcie_capability_read_word(adapter-\u003epdev, PCI_EXP_LNKSTA, \u0026val);\ndrivers/net/ethernet/chelsio/cxgb4/t4_hw.c-8877-\t\tp-\u003espeed = val \u0026 PCI_EXP_LNKSTA_CLS;\n--\ndrivers/net/ethernet/intel/e1000e/mac.c=16=s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw)\n--\ndrivers/net/ethernet/intel/e1000e/mac.c-25-\t} else {\ndrivers/net/ethernet/intel/e1000e/mac.c:26:\t\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026pcie_link_status);\ndrivers/net/ethernet/intel/e1000e/mac.c-27-\t\tbus-\u003ewidth = (enum e1000_bus_width)FIELD_GET(PCI_EXP_LNKSTA_NLW,\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c=6639=static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c-6776-\ndrivers/net/ethernet/intel/e1000e/netdev.c:6777:\t\tpcie_capability_read_word(us_dev, PCI_EXP_DEVCTL, \u0026devctl);\ndrivers/net/ethernet/intel/e1000e/netdev.c-6778-\t\tpcie_capability_write_word(us_dev, PCI_EXP_DEVCTL,\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c=6798=static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state, int locked)\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c-6815-\ndrivers/net/ethernet/intel/e1000e/netdev.c:6816:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026pdev_aspmc);\ndrivers/net/ethernet/intel/e1000e/netdev.c-6817-\tpdev_aspmc \u0026= PCI_EXP_LNKCTL_ASPMC;\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c-6819-\tif (parent) {\ndrivers/net/ethernet/intel/e1000e/netdev.c:6820:\t\tpcie_capability_read_word(parent, PCI_EXP_LNKCTL,\ndrivers/net/ethernet/intel/e1000e/netdev.c-6821-\t\t\t\t\t  \u0026parent_aspmc);\n--\ndrivers/net/ethernet/intel/e1000e/netdev.c-6845-\t */\ndrivers/net/ethernet/intel/e1000e/netdev.c:6846:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026pdev_aspmc);\ndrivers/net/ethernet/intel/e1000e/netdev.c-6847-\tpdev_aspmc \u0026= PCI_EXP_LNKCTL_ASPMC;\n--\ndrivers/net/ethernet/intel/i40e/i40e_main.c=15528=static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/intel/i40e/i40e_main.c-16017-\t\t */\ndrivers/net/ethernet/intel/i40e/i40e_main.c:16018:\t\tpcie_capability_read_word(pf-\u003epdev, PCI_EXP_LNKSTA,\ndrivers/net/ethernet/intel/i40e/i40e_main.c-16019-\t\t\t\t\t  \u0026link_status);\n--\ndrivers/net/ethernet/intel/igb/igb_main.c=9345=s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)\n--\ndrivers/net/ethernet/intel/igb/igb_main.c-9348-\ndrivers/net/ethernet/intel/igb/igb_main.c:9349:\tif (pcie_capability_read_word(adapter-\u003epdev, reg, value))\ndrivers/net/ethernet/intel/igb/igb_main.c-9350-\t\treturn -E1000_ERR_CONFIG;\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c=196=static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c-212-\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c:213:\tpcie_capability_read_word(parent_dev, reg, value);\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c-214-\tif (*value == IXGBE_FAILED_READ_CFG_WORD \u0026\u0026\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c=6314=static unsigned long ixgbe_get_completion_timeout(struct ixgbe_adapter *adapter)\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c-6317-\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c:6318:\tpcie_capability_read_word(adapter-\u003epdev, PCI_EXP_DEVCTL2, \u0026devctl2);\ndrivers/net/ethernet/intel/ixgbe/ixgbe_main.c-6319-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/fw_reset.c=372=static int mlx5_check_hotplug_interrupt(struct mlx5_core_dev *dev,\n--\ndrivers/net/ethernet/mellanox/mlx5/core/fw_reset.c-377-\ndrivers/net/ethernet/mellanox/mlx5/core/fw_reset.c:378:\terr = pcie_capability_read_word(bridge, PCI_EXP_SLTCTL, \u0026reg16);\ndrivers/net/ethernet/mellanox/mlx5/core/fw_reset.c-379-\tif (err)\n--\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c=1080=static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)\n--\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c-1084-\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c:1085:\tpcie_capability_read_word(pdev, PCI_EXP_DEVCTL, \u0026ctl);\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c-1086-\n--\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c=3215=static void myri10ge_select_firmware(struct myri10ge_priv *mgp)\n--\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c-3222-\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c:3223:\t\tpcie_capability_read_word(mgp-\u003epdev, PCI_EXP_LNKSTA, \u0026lnk);\ndrivers/net/ethernet/myricom/myri10ge/myri10ge.c-3224-\t\tlink_width = (lnk \u003e\u003e 4) \u0026 0x3f;\n--\ndrivers/net/wireless/ath/ath10k/pci.c=2817=static int ath10k_pci_hif_power_up(struct ath10k *ar,\n--\ndrivers/net/wireless/ath/ath10k/pci.c-2824-\ndrivers/net/wireless/ath/ath10k/pci.c:2825:\tpcie_capability_read_word(ar_pci-\u003epdev, PCI_EXP_LNKCTL,\ndrivers/net/wireless/ath/ath10k/pci.c-2826-\t\t\t\t  \u0026ar_pci-\u003elink_ctl);\n--\ndrivers/net/wireless/ath/ath11k/pci.c=609=static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)\n--\ndrivers/net/wireless/ath/ath11k/pci.c-612-\ndrivers/net/wireless/ath/ath11k/pci.c:613:\tpcie_capability_read_word(ab_pci-\u003epdev, PCI_EXP_LNKCTL,\ndrivers/net/wireless/ath/ath11k/pci.c-614-\t\t\t\t  \u0026ab_pci-\u003elink_ctl);\n--\ndrivers/net/wireless/ath/ath12k/pci.c=900=static void ath12k_pci_aspm_disable(struct ath12k_pci *ab_pci)\n--\ndrivers/net/wireless/ath/ath12k/pci.c-903-\ndrivers/net/wireless/ath/ath12k/pci.c:904:\tpcie_capability_read_word(ab_pci-\u003epdev, PCI_EXP_LNKCTL,\ndrivers/net/wireless/ath/ath12k/pci.c-905-\t\t\t\t  \u0026ab_pci-\u003elink_ctl);\n--\ndrivers/net/wireless/ath/ath9k/pci.c=819=static void ath_pci_aspm_init(struct ath_common *common)\n--\ndrivers/net/wireless/ath/ath9k/pci.c-867-\ndrivers/net/wireless/ath/ath9k/pci.c:868:\tret = pcie_capability_read_word(parent, PCI_EXP_LNKCTL, \u0026aspm);\ndrivers/net/wireless/ath/ath9k/pci.c-869-\tif (!ret \u0026\u0026 (aspm \u0026 (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1))) {\n--\ndrivers/net/wireless/intel/iwlegacy/common.c=1219=il_power_initialize(struct il_priv *il)\n--\ndrivers/net/wireless/intel/iwlegacy/common.c-1222-\ndrivers/net/wireless/intel/iwlegacy/common.c:1223:\tpcie_capability_read_word(il-\u003epci_dev, PCI_EXP_LNKCTL, \u0026lctl);\ndrivers/net/wireless/intel/iwlegacy/common.c-1224-\til-\u003epower_data.pci_pm = !(lctl \u0026 PCI_EXP_LNKCTL_ASPM_L0S);\n--\ndrivers/net/wireless/intel/iwlegacy/common.c=4215=il_apm_init(struct il_priv *il)\n--\ndrivers/net/wireless/intel/iwlegacy/common.c-4257-\tif (il-\u003ecfg-\u003eset_l0s) {\ndrivers/net/wireless/intel/iwlegacy/common.c:4258:\t\tret = pcie_capability_read_word(il-\u003epci_dev, PCI_EXP_LNKCTL, \u0026lctl);\ndrivers/net/wireless/intel/iwlegacy/common.c-4259-\t\tif (!ret \u0026\u0026 (lctl \u0026 PCI_EXP_LNKCTL_ASPM_L1)) {\n--\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c=203=void iwl_pcie_apm_config(struct iwl_trans *trans)\n--\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-215-\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c:216:\tpcie_capability_read_word(trans_pcie-\u003epci_dev, PCI_EXP_LNKCTL, \u0026lctl);\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-217-\ttrans_pcie-\u003epm_support = !(lctl \u0026 PCI_EXP_LNKCTL_ASPM_L0S);\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-218-\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c:219:\tpcie_capability_read_word(trans_pcie-\u003epci_dev, PCI_EXP_DEVCTL2, \u0026cap);\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-220-\ttrans_pcie-\u003eltr_enabled = cap \u0026 PCI_EXP_DEVCTL2_LTR_EN;\n--\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c=4232=int iwl_pci_gen1_2_probe(struct pci_dev *pdev,\n--\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-4352-\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c:4353:\t\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026link_status);\ndrivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-4354-\n--\ndrivers/net/wireless/mediatek/mt76/pci.c=9=void mt76_pci_disable_aspm(struct pci_dev *pdev)\n--\ndrivers/net/wireless/mediatek/mt76/pci.c-13-\ndrivers/net/wireless/mediatek/mt76/pci.c:14:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026aspm_conf);\ndrivers/net/wireless/mediatek/mt76/pci.c-15-\taspm_conf \u0026= PCI_EXP_LNKCTL_ASPMC;\ndrivers/net/wireless/mediatek/mt76/pci.c-16-\tif (parent) {\ndrivers/net/wireless/mediatek/mt76/pci.c:17:\t\tpcie_capability_read_word(parent, PCI_EXP_LNKCTL,\ndrivers/net/wireless/mediatek/mt76/pci.c-18-\t\t\t\t\t  \u0026parent_aspm_conf);\n--\ndrivers/net/wireless/mediatek/mt76/pci.c=55=bool mt76_pci_aspm_supported(struct pci_dev *pdev)\n--\ndrivers/net/wireless/mediatek/mt76/pci.c-60-\ndrivers/net/wireless/mediatek/mt76/pci.c:61:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026aspm_conf);\ndrivers/net/wireless/mediatek/mt76/pci.c-62-\taspm_conf \u0026= PCI_EXP_LNKCTL_ASPMC;\ndrivers/net/wireless/mediatek/mt76/pci.c-63-\tif (parent) {\ndrivers/net/wireless/mediatek/mt76/pci.c:64:\t\tpcie_capability_read_word(parent, PCI_EXP_LNKCTL,\ndrivers/net/wireless/mediatek/mt76/pci.c-65-\t\t\t\t\t  \u0026parent_aspm_conf);\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c=68=static void _rtl_pci_update_default_setting(struct ieee80211_hw *hw)\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c-160-\t */\ndrivers/net/wireless/realtek/rtlwifi/pci.c:161:\tpcie_capability_read_word(rtlpci-\u003epdev, PCI_EXP_LNKCTL, \u0026init_aspm);\ndrivers/net/wireless/realtek/rtlwifi/pci.c-162-\tif (rtlpriv-\u003ertlhal.hw_type == HARDWARE_TYPE_RTL8192SE \u0026\u0026\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c=205=static void __rtl_pci_disable_aspm(struct ieee80211_hw *hw)\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c-229-\t/*for promising device will in L0 state after an I/O. */\ndrivers/net/wireless/realtek/rtlwifi/pci.c:230:\tpcie_capability_read_word(rtlpci-\u003epdev, PCI_EXP_LNKCTL, \u0026tmp_u1b);\ndrivers/net/wireless/realtek/rtlwifi/pci.c-231-\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c=314=static void rtl_pci_parse_configuration(struct pci_dev *pdev,\n--\ndrivers/net/wireless/realtek/rtlwifi/pci.c-323-\t/*Link Control Register */\ndrivers/net/wireless/realtek/rtlwifi/pci.c:324:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026linkctrl_reg);\ndrivers/net/wireless/realtek/rtlwifi/pci.c-325-\tpcipriv-\u003endis_adapter.linkctrl_reg = (u8)linkctrl_reg;\n--\ndrivers/net/wireless/realtek/rtw88/pci.c=1405=static void rtw_pci_link_cfg(struct rtw_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw88/pci.c-1434-\t */\ndrivers/net/wireless/realtek/rtw88/pci.c:1435:\tret = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026link_ctrl);\ndrivers/net/wireless/realtek/rtw88/pci.c-1436-\tif (ret) {\n--\ndrivers/net/wireless/realtek/rtw89/pci.c=3042=static bool rtw89_pci_dev_ltr_enabled(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/pci.c-3047-\ndrivers/net/wireless/realtek/rtw89/pci.c:3048:\tpcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, \u0026cap);\ndrivers/net/wireless/realtek/rtw89/pci.c-3049-\n--\ndrivers/net/wireless/realtek/rtw89/pci.c=4286=static void rtw89_pci_link_cfg(struct rtw89_dev *rtwdev)\n--\ndrivers/net/wireless/realtek/rtw89/pci.c-4308-\t */\ndrivers/net/wireless/realtek/rtw89/pci.c:4309:\tret = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026link_ctrl);\ndrivers/net/wireless/realtek/rtw89/pci.c-4310-\tif (ret) {\n--\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c=143=bool ipc_pcie_check_aspm_enabled(struct iosm_pcie *ipc_pcie,\n--\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c-154-\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c:155:\tpcie_capability_read_word(pdev, PCI_EXP_LNKCTL, \u0026value);\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c-156-\tenabled = value \u0026 PCI_EXP_LNKCTL_ASPMC;\n--\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c=163=bool ipc_pcie_check_data_link_active(struct iosm_pcie *ipc_pcie)\n--\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c-174-\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c:175:\tpcie_capability_read_word(parent, PCI_EXP_LNKSTA, \u0026link_status);\ndrivers/net/wwan/iosm/iosm_ipc_pcie.c-176-\tdev_dbg(ipc_pcie-\u003edev, \"Link status: 0x%04X\", link_status);\n--\ndrivers/pci/access.c=386=static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)\n--\ndrivers/pci/access.c-426- */\ndrivers/pci/access.c:427:int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)\ndrivers/pci/access.c-428-{\n--\ndrivers/pci/access.c-459-}\ndrivers/pci/access.c:460:EXPORT_SYMBOL(pcie_capability_read_word);\ndrivers/pci/access.c-461-\n--\ndrivers/pci/access.c=514=int pcie_capability_clear_and_set_word_unlocked(struct pci_dev *dev, int pos,\n--\ndrivers/pci/access.c-519-\ndrivers/pci/access.c:520:\tret = pcie_capability_read_word(dev, pos, \u0026val);\ndrivers/pci/access.c-521-\tif (ret)\n--\ndrivers/pci/controller/pcie-mediatek.c=862=static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)\n--\ndrivers/pci/controller/pcie-mediatek.c-886-\ndrivers/pci/controller/pcie-mediatek.c:887:\t\tpcie_capability_read_word(rp, PCI_EXP_LNKSTA, \u0026lnksta);\ndrivers/pci/controller/pcie-mediatek.c-888-\t\tspeed = lnksta \u0026 PCI_EXP_LNKSTA_CLS;\n--\ndrivers/pci/hotplug/pciehp_hpc.c=86=static int pcie_poll_cmd(struct controller *ctrl, int timeout)\n--\ndrivers/pci/hotplug/pciehp_hpc.c-91-\tdo {\ndrivers/pci/hotplug/pciehp_hpc.c:92:\t\tpcie_capability_read_word(pdev, PCI_EXP_SLTSTA, \u0026slot_status);\ndrivers/pci/hotplug/pciehp_hpc.c-93-\t\tif (PCI_POSSIBLE_ERROR(slot_status)) {\n--\ndrivers/pci/hotplug/pciehp_hpc.c=157=static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,\n--\ndrivers/pci/hotplug/pciehp_hpc.c-169-\ndrivers/pci/hotplug/pciehp_hpc.c:170:\tpcie_capability_read_word(pdev, PCI_EXP_SLTCTL, \u0026slot_ctrl);\ndrivers/pci/hotplug/pciehp_hpc.c-171-\tif (PCI_POSSIBLE_ERROR(slot_ctrl)) {\n--\ndrivers/pci/hotplug/pciehp_hpc.c=235=int pciehp_check_link_active(struct controller *ctrl)\n--\ndrivers/pci/hotplug/pciehp_hpc.c-240-\ndrivers/pci/hotplug/pciehp_hpc.c:241:\tret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026lnk_status);\ndrivers/pci/hotplug/pciehp_hpc.c-242-\tif (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))\n--\ndrivers/pci/hotplug/pciehp_hpc.c=277=static void pcie_wait_for_presence(struct pci_dev *pdev)\n--\ndrivers/pci/hotplug/pciehp_hpc.c-282-\tdo {\ndrivers/pci/hotplug/pciehp_hpc.c:283:\t\tpcie_capability_read_word(pdev, PCI_EXP_SLTSTA, \u0026slot_status);\ndrivers/pci/hotplug/pciehp_hpc.c-284-\t\tif (slot_status \u0026 PCI_EXP_SLTSTA_PDS)\n--\ndrivers/pci/hotplug/pciehp_hpc.c=291=int pciehp_check_link_status(struct controller *ctrl)\n--\ndrivers/pci/hotplug/pciehp_hpc.c-312-\ndrivers/pci/hotplug/pciehp_hpc.c:313:\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA, \u0026lnk_status);\ndrivers/pci/hotplug/pciehp_hpc.c-314-\tctrl_dbg(ctrl, \"%s: lnk_status = %x\\n\", __func__, lnk_status);\n--\ndrivers/pci/hotplug/pciehp_hpc.c-321-\ndrivers/pci/hotplug/pciehp_hpc.c:322:\tpcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, \u0026linksta2);\ndrivers/pci/hotplug/pciehp_hpc.c-323-\t__pcie_update_link_speed(ctrl-\u003epcie-\u003eport-\u003esubordinate, PCIE_HOTPLUG,\n--\ndrivers/pci/hotplug/pciehp_hpc.c=351=int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,\n--\n\n"
}

61/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49092
							output: 36
							thoughts: 24
						

Thoughts:
Here's my summary, as though I'm processing this information:

**Analyzing PCI Access Code: lines 427-459**

Okay, let's dive into `drivers/pci/access.c` and zero in on lines 427-459. My initial expectation is this section likely deals with a crucial aspect of PCI device interaction, probably low-level register access. Given the file and function name, I'd wager it's handling reads or writes to PCI configuration space or BARs. I need to understand the details. It could be a simple read/write implementation, or it could be handling more complex scenarios like MMIO, PIO, or perhaps even bus mastering setup. I'll be looking for any indications of locking mechanisms – crucial to prevent race conditions during concurrent access. I'll also be paying close attention to any error handling; this is low-level code, so robust error detection and reporting are paramount. Let's see how the kernel handles potential PCI bus errors, device not present scenarios, or invalid address ranges. Let's get reading!



62/3 2026/09/21 01:15 tool read-file
0m Args:
{
  "File": "drivers/pci/access.c",
  "FirstLine": 427,
  "LineCount": 35
}

Results:
{
  "Contents": " 427:\tint pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)\n 428:\t{\n 429:\t\tint ret;\n 430:\t\n 431:\t\t*val = 0;\n 432:\t\tif (pos \u0026 1)\n 433:\t\t\treturn PCIBIOS_BAD_REGISTER_NUMBER;\n 434:\t\n 435:\t\tif (pcie_capability_reg_implemented(dev, pos)) {\n 436:\t\t\tret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);\n 437:\t\t\t/*\n 438:\t\t\t * Reset *val to 0 if pci_read_config_word() fails; it may\n 439:\t\t\t * have been written as 0xFFFF (PCI_ERROR_RESPONSE) if the\n 440:\t\t\t * config read failed on PCI.\n 441:\t\t\t */\n 442:\t\t\tif (ret)\n 443:\t\t\t\t*val = 0;\n 444:\t\t\treturn ret;\n 445:\t\t}\n 446:\t\n 447:\t\t/*\n 448:\t\t * For Functions that do not implement the Slot Capabilities,\n 449:\t\t * Slot Status, and Slot Control registers, these spaces must\n 450:\t\t * be hardwired to 0b, with the exception of the Presence Detect\n 451:\t\t * State bit in the Slot Status register of Downstream Ports,\n 452:\t\t * which must be hardwired to 1b.  (PCIe Base Spec 3.0, sec 7.8)\n 453:\t\t */\n 454:\t\tif (pci_is_pcie(dev) \u0026\u0026 pcie_downstream_port(dev) \u0026\u0026\n 455:\t\t    pos == PCI_EXP_SLTSTA)\n 456:\t\t\t*val = PCI_EXP_SLTSTA_PDS;\n 457:\t\n 458:\t\treturn 0;\n 459:\t}\n 460:\tEXPORT_SYMBOL(pcie_capability_read_word);\n 461:\t\n"
}

63/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49952
							output: 23
							thoughts: 237
						

Thoughts:
**Analyzing PCI Configuration Reads**

Okay, here's what I'm thinking as I dive into this code. I need to understand how this `*val` is being handled. Line 431, specifically `*val = 0;`, immediately grabs my attention. It suggests an initialization happening within `pcie_capability_read_word`. That's key. Now, I need to check how the value read by `pcie_capability_read_word` is being used elsewhere and if any other functions could be initializing the output. I'll jump to `pci_read_config_word` within `pci_save_acs_state`.

I see the `pci_save_acs_state` function. The code checks for an ACS capability. It then attempts to find saved state. So far, so good. Then, it calls `pci_read_config_word` – precisely what I'm interested in. I need to quickly ascertain whether `pci_read_config_word` performs any explicit initialization of its output. I'm going to look at the implementation of `pci_read_config_word` to get the definitive answer. I'm confident in my ability to interpret the code now.



64/3 2026/09/21 01:15 tool grepper
0m Args:
{
  "Expression": "int pci_read_config_word"
}

Results:
{
  "Output": "drivers/pci/access.c=568=EXPORT_SYMBOL(pci_read_config_byte);\ndrivers/pci/access.c-569-\ndrivers/pci/access.c:570:int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val)\ndrivers/pci/access.c-571-{\n--\ninclude/linux/pci.h=1354=int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val);\ninclude/linux/pci.h:1355:int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val);\ninclude/linux/pci.h-1356-int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val);\n"
}

65/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 50717
							output: 36
							thoughts: 20
						

Thoughts:
Here's a summary of my thought process, as if I were the one thinking it:

**PCI Access Investigation**

Okay, time to dig into this. I need to refresh my memory on the PCI access routines, so I'm going to jump into the `drivers/pci/access.c` file. Specifically, I recall there was a potential issue around line 570. Let's see what's going on there and jog my memory. That's the first place to start.



66/3 2026/09/21 01:15 tool read-file
0m Args:
{
  "File": "drivers/pci/access.c",
  "FirstLine": 570,
  "LineCount": 15
}

Results:
{
  "Contents": " 570:\tint pci_read_config_word(const struct pci_dev *dev, int where, u16 *val)\n 571:\t{\n 572:\t\tif (pci_dev_is_disconnected(dev)) {\n 573:\t\t\tPCI_SET_ERROR_RESPONSE(val);\n 574:\t\t\treturn PCIBIOS_DEVICE_NOT_FOUND;\n 575:\t\t}\n 576:\t\treturn pci_bus_read_config_word(dev-\u003ebus, dev-\u003edevfn, where, val);\n 577:\t}\n 578:\tEXPORT_SYMBOL(pci_read_config_word);\n 579:\t\n 580:\tint pci_read_config_dword(const struct pci_dev *dev, int where,\n 581:\t\t\t\t\t\tu32 *val)\n 582:\t{\n 583:\t\tif (pci_dev_is_disconnected(dev)) {\n 584:\t\t\tPCI_SET_ERROR_RESPONSE(val);\n"
}

67/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51156
							output: 24
							thoughts: 992
						

Thoughts:
**Analyzing the PCI Live Update Implementation**

Okay, so the `save_state` variable is already zeroed out when it's added, which is good to know. Now, I want to take a closer look at the PCI live update (LUO) mechanism. Let's start by examining the structures defined in `include/linux/kho/abi/pci.h`.  I see the definitions for `PCI_LUO_FLB_COMPATIBLE`, `PCI_LUO_FLB_VERSION`, `struct pci_dev_ser`, and `struct pci_ser`. They're all using `__packed`, which is what I expect to avoid any unexpected padding. The sizes check out: `struct pci_dev_ser` is 8 bytes, and `struct pci_ser` is 16 bytes, which confirms no padding is happening thanks to the attribute. And as anticipated from our previous analysis, `kho_alloc_preserve` is allocating memory and zeroing it.

Next, I need to understand how the data is retrieved. I'm focusing on the `pci_flb_retrieve` function. Here, a `struct pci_ser` is read from `args->data`. I see a version check is performed to ensure compatibility. A new `struct pci_flb_incoming` is allocated using `kzalloc_obj`, which is important for zero initialization. The code initializes an XArray (`incoming->xa`) and a `kho_block_set`. It then attempts to restore a block set of `pci_dev_ser` structs from `ser->devices`. Afterwards, I see that the code iterates through these `pci_dev_ser` entries, skipping those with a zero `refcount`, and inserts them into an XArray. The key for the XArray insertion is generated using the `pci_ser_xa_key` function and consists of the domain and BDF values from the pci device.

Now, I'm thinking about the bigger picture. I need to understand how Live Update interacts with this code. Is any data copied back to userspace? Specifically, is any of this data exposed? I know from prior experience that userspace, using a `LIVEUPDATE_SESSION_PRESERVE_FD` ioctl call, is the starting point, on a file descriptor like those created by vfio-pci.
The control flow seems to be: `luo_preserve_file` -> `luo_flb_file_preserve` -> `luo_flb_file_preserve_one` -> `pci_flb_preserve`, which allocates and preserves the `struct pci_ser` I'm analyzing.
Then, a driver callback `fh->ops->preserve` is called.

The key question is, *does any of the preserved data get copied back to userspace*. I need to investigate the definition and usage of `LIVEUPDATE_SESSION_PRESERVE_FD` to clarify this point.



68/3 2026/09/21 01:15 tool grepper
0m Args:
{
  "Expression": "LIVEUPDATE_SESSION_PRESERVE_FD"
}

Results:
{
  "Output": "drivers/pci/liveupdate.c-25- * and userspace preserves the device with\ndrivers/pci/liveupdate.c:26: * ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)`` on that file. Binding preservation\ndrivers/pci/liveupdate.c-27- * to a file gives it proper lifecycle management, e.g. the preservation is\n--\ndrivers/pci/liveupdate.c-60- *\ndrivers/pci/liveupdate.c:61: *   # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)\ndrivers/pci/liveupdate.c-62- *   luo_preserve_file()\n--\ninclude/uapi/linux/liveupdate.h=113=struct liveupdate_ioctl_retrieve_session {\n--\ninclude/uapi/linux/liveupdate.h-124-/**\ninclude/uapi/linux/liveupdate.h:125: * struct liveupdate_session_preserve_fd - ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)\ninclude/uapi/linux/liveupdate.h-126- * @size:  Input; sizeof(struct liveupdate_session_preserve_fd)\n--\ninclude/uapi/linux/liveupdate.h=148=struct liveupdate_session_preserve_fd {\n--\ninclude/uapi/linux/liveupdate.h-153-\ninclude/uapi/linux/liveupdate.h:154:#define LIVEUPDATE_SESSION_PRESERVE_FD\t\t\t\t\t\\\ninclude/uapi/linux/liveupdate.h-155-\t_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_PRESERVE_FD)\n--\nkernel/liveupdate/luo_session.c=376=static const struct luo_ioctl_op luo_session_ioctl_ops[] = {\n--\nkernel/liveupdate/luo_session.c-378-\t\t struct liveupdate_session_finish, reserved, LUO_IOCTL_INCOMING),\nkernel/liveupdate/luo_session.c:379:\tIOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd,\nkernel/liveupdate/luo_session.c-380-\t\t struct liveupdate_session_preserve_fd, token, LUO_IOCTL_OUTGOING),\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c=82=int luo_session_preserve_fd(int session_fd, int fd, __u64 token)\n--\ntools/testing/selftests/liveupdate/lib/lu_utils.c-89-\ntools/testing/selftests/liveupdate/lib/lu_utils.c:90:\tif (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, \u0026arg))\ntools/testing/selftests/liveupdate/lib/lu_utils.c-91-\t\treturn -errno;\n"
}

69/3 2026/09/21 01:15 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 53544
							output: 39
							thoughts: 38
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the Live Update Session Preservation**

Okay, so I need to understand what's happening with file descriptor preservation within the live update process. I'm going to jump into the `kernel/liveupdate/luo_session.c` file. Specifically, I'm aiming for around line 379. That should be where I can find the implementation of `luo_session_preserve_fd`. Hopefully, that function call will give me a clear picture of how they're handling file descriptors during a live update, and how they're making sure crucial ones don't get lost or closed prematurely. I'm expecting some clever techniques there, considering the complexities of this kind of operation. Time to dig in and see what's what.



70/3 2026/09/21 01:16 tool read-file
0m Args:
{
  "File": "kernel/liveupdate/luo_session.c",
  "FirstLine": 375,
  "LineCount": 40
}

Results:
{
  "Contents": " 375:\t\n 376:\tstatic const struct luo_ioctl_op luo_session_ioctl_ops[] = {\n 377:\t\tIOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish,\n 378:\t\t\t struct liveupdate_session_finish, reserved, LUO_IOCTL_INCOMING),\n 379:\t\tIOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd,\n 380:\t\t\t struct liveupdate_session_preserve_fd, token, LUO_IOCTL_OUTGOING),\n 381:\t\tIOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd,\n 382:\t\t\t struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING),\n 383:\t\tIOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name,\n 384:\t\t\t struct liveupdate_session_get_name, name, LUO_IOCTL_ALL),\n 385:\t};\n 386:\t\n 387:\tstatic bool luo_ioctl_type_valid(struct luo_session *session,\n 388:\t\t\t\t\t const struct luo_ioctl_op *op)\n 389:\t{\n 390:\t\tswitch (op-\u003etype) {\n 391:\t\tcase LUO_IOCTL_INCOMING:\n 392:\t\t\t/* Retrieved is only set on incoming sessions */\n 393:\t\t\treturn session-\u003eretrieved;\n 394:\t\tcase LUO_IOCTL_OUTGOING:\n 395:\t\t\treturn !session-\u003eretrieved;\n 396:\t\tcase LUO_IOCTL_ALL:\n 397:\t\t\treturn true;\n 398:\t\t}\n 399:\t\n 400:\t\t/* Catch-all. */\n 401:\t\treturn false;\n 402:\t}\n 403:\t\n 404:\tstatic long luo_session_ioctl(struct file *filep, unsigned int cmd,\n 405:\t\t\t\t      unsigned long arg)\n 406:\t{\n 407:\t\tstruct luo_session *session = filep-\u003eprivate_data;\n 408:\t\tconst struct luo_ioctl_op *op;\n 409:\t\tstruct luo_ucmd ucmd = {};\n 410:\t\tunion ucmd_buffer buf;\n 411:\t\tunsigned int nr;\n 412:\t\tint ret;\n 413:\t\n 414:\t\tnr = _IOC_NR(cmd);\n"
}

71/3 2026/09/21 01:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54474
							output: 20
							thoughts: 231
						

Thoughts:
**My Assessment of the Liveupdate PCI Integration**

Okay, so I see this patch is focusing on integrating live updates into the PCI subsystem. Importantly, it *doesn't* touch `luo_session.c`, which is good; it means we're dealing with a distinct implementation. This patch creates `drivers/pci/liveupdate.c` and weaves it into the existing PCI infrastructure – `pci.c`, `probe.c`, `quirks.c`, and `pci-driver.c`, from what I can tell. A clean integration, that's what I want to see.

Now, let's drill down into these `pci_dev` additions. Specifically, I'm noting the `CONFIG_PCI_LIVEUPDATE` conditional compilation and the new `struct pci_liveupdate` embedded within `struct pci_dev` in `include/linux/pci.h`. The `pci_liveupdate` struct itself, with its `outgoing`, `incoming`, `was_incoming`, and `frozen` members, suggests a serialization/deserialization approach for handling device state during updates. Makes sense, that's the usual route.

Since a new `struct pci_dev` is allocated using `kzalloc`, my next question is to verify how the `pci_alloc_dev` function handles the allocation process. I need to make sure the liveupdate-specific struct is correctly initialized with zeroes. That's critical for a clean and predictable state.



72/3 2026/09/21 01:16 tool grepper
0m Args:
{
  "Expression": "pci_alloc_dev"
}

Results:
{
  "Output": "Documentation/admin-guide/mm/slab.rst=403=information:\n--\nDocumentation/admin-guide/mm/slab.rst-417-\nDocumentation/admin-guide/mm/slab.rst:418:    338 pci_alloc_dev+0x2c/0xa0 waste=521872/1544 age=290837/291891/293509 pid=1 cpus=106 nodes=0-1\nDocumentation/admin-guide/mm/slab.rst-419-        __kmem_cache_alloc_node+0x11f/0x4e0\nDocumentation/admin-guide/mm/slab.rst-420-        kmalloc_trace+0x26/0xa0\nDocumentation/admin-guide/mm/slab.rst:421:        pci_alloc_dev+0x2c/0xa0\nDocumentation/admin-guide/mm/slab.rst-422-        pci_scan_single_device+0xd2/0x150\n--\narch/powerpc/kernel/pci_of_scan.c=175=struct pci_dev *of_create_pci_dev(struct device_node *node,\n--\narch/powerpc/kernel/pci_of_scan.c-179-\narch/powerpc/kernel/pci_of_scan.c:180:\tdev = pci_alloc_dev(bus);\narch/powerpc/kernel/pci_of_scan.c-181-\tif (!dev)\n--\narch/sparc/kernel/pci.c=287=static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,\n--\narch/sparc/kernel/pci.c-295-\narch/sparc/kernel/pci.c:296:\tdev = pci_alloc_dev(bus);\narch/sparc/kernel/pci.c-297-\tif (!dev)\n--\ndrivers/char/agp/alpha-agp.c=153=alpha_core_agp_setup(void)\n--\ndrivers/char/agp/alpha-agp.c-174-\t */\ndrivers/char/agp/alpha-agp.c:175:\tpdev = pci_alloc_dev(NULL);\ndrivers/char/agp/alpha-agp.c-176-\tif (!pdev)\n--\ndrivers/char/agp/parisc-agp.c=343=parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)\n--\ndrivers/char/agp/parisc-agp.c-348-\ndrivers/char/agp/parisc-agp.c:349:\tfake_bridge_dev = pci_alloc_dev(NULL);\ndrivers/char/agp/parisc-agp.c-350-\tif (!fake_bridge_dev) {\n--\ndrivers/nvme/host/pci.c=3383=static void nvme_free_tagset(struct nvme_dev *dev)\n--\ndrivers/nvme/host/pci.c-3389-\ndrivers/nvme/host/pci.c:3390:/* pairs with nvme_pci_alloc_dev */\ndrivers/nvme/host/pci.c-3391-static void nvme_pci_free_ctrl(struct nvme_ctrl *ctrl)\n--\ndrivers/nvme/host/pci.c=3677=static struct quirk_entry *detect_dynamic_quirks(struct pci_dev *pdev)\n--\ndrivers/nvme/host/pci.c-3688-\ndrivers/nvme/host/pci.c:3689:static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,\ndrivers/nvme/host/pci.c-3690-\t\tconst struct pci_device_id *id)\n--\ndrivers/nvme/host/pci.c=3763=static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/nvme/host/pci.c-3767-\ndrivers/nvme/host/pci.c:3768:\tdev = nvme_pci_alloc_dev(pdev, id);\ndrivers/nvme/host/pci.c-3769-\tif (IS_ERR(dev))\n--\ndrivers/pci/iov.c=314=static struct pci_dev *pci_iov_scan_device(struct pci_dev *dev, int id,\n--\ndrivers/pci/iov.c-320-\ndrivers/pci/iov.c:321:\tvirtfn = pci_alloc_dev(bus);\ndrivers/pci/iov.c-322-\tif (!virtfn)\n--\ndrivers/pci/probe.c=2504=static const struct device_type pci_dev_type = {\n--\ndrivers/pci/probe.c-2507-\ndrivers/pci/probe.c:2508:struct pci_dev *pci_alloc_dev(struct pci_bus *bus)\ndrivers/pci/probe.c-2509-{\n--\ndrivers/pci/probe.c-2530-}\ndrivers/pci/probe.c:2531:EXPORT_SYMBOL(pci_alloc_dev);\ndrivers/pci/probe.c-2532-\n--\ndrivers/pci/probe.c=2605=static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)\n--\ndrivers/pci/probe.c-2612-\ndrivers/pci/probe.c:2613:\tdev = pci_alloc_dev(bus);\ndrivers/pci/probe.c-2614-\tif (!dev)\n--\ndrivers/scsi/megaraid.c=1987=make_local_pdev(adapter_t *adapter, struct pci_dev **pdev)\ndrivers/scsi/megaraid.c-1988-{\ndrivers/scsi/megaraid.c:1989:\t*pdev = pci_alloc_dev(NULL);\ndrivers/scsi/megaraid.c-1990-\n--\ninclude/linux/pci.h=607=static inline struct pci_dev *pci_physfn(struct pci_dev *dev)\n--\ninclude/linux/pci.h-615-\ninclude/linux/pci.h:616:struct pci_dev *pci_alloc_dev(struct pci_bus *bus);\ninclude/linux/pci.h-617-\n"
}

73/3 2026/09/21 01:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56787
							output: 37
							thoughts: 21
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into PCI Probe Logic**

Okay, time to dig in. The issue *should* be localized within the PCI probing sequence, so let's jump straight into the heart of it. Line 2508 in `drivers/pci/probe.c` is the prime suspect - that's where the core of the device discovery and initialization happens. It’s the gatekeeper, deciding what gets added to the system and how. I need to understand what's happening at that specific juncture to pinpoint the root cause of this misbehavior I've been seeing. My focus will be on the conditions, any error handling, and the data structures being manipulated around that call. It's time to follow the execution path and see what's failing.



74/3 2026/09/21 01:16 tool read-file
0m Args:
{
  "File": "drivers/pci/probe.c",
  "FirstLine": 2508,
  "LineCount": 15
}

Results:
{
  "Contents": "2508:\tstruct pci_dev *pci_alloc_dev(struct pci_bus *bus)\n2509:\t{\n2510:\t\tstruct pci_dev *dev;\n2511:\t\n2512:\t\tdev = kzalloc_obj(struct pci_dev);\n2513:\t\tif (!dev)\n2514:\t\t\treturn NULL;\n2515:\t\n2516:\t\tINIT_LIST_HEAD(\u0026dev-\u003ebus_list);\n2517:\t\tdev-\u003edev.type = \u0026pci_dev_type;\n2518:\t\tdev-\u003ebus = pci_bus_get(bus);\n2519:\t\tdev-\u003edriver_exclusive_resource = (struct resource) {\n2520:\t\t\t.name = \"PCI Exclusive\",\n2521:\t\t\t.start = 0,\n2522:\t\t\t.end = -1,\n"
}

75/3 2026/09/21 01:16 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57262
							output: 514
							thoughts: 3019
						

Thoughts:
**PCI Live Update KMSAN Assessment**

Okay, let's break this down. First, I see that `pci_dev` is allocated with `kzalloc_obj`, so that's good; `dev->liveupdate` is zeroed from the start, a fundamental check to ensure there is no garbage.

Now, that `pci_liveupdate_global` is a static global and zero-initialized. Nice. Okay, I'm already seeing a pattern of careful allocation.

Moving on to `pci_flb_retrieve`, specifically, `dev_ser` comes from `kho_block_set`. This is where I have to be careful, but then `kho_block_set_restore` suggests that the memory was *preserved* across kexec using KHO. It looks like it is backed by preserved memory. I'll need to double check how that memory is zeroed out and make sure there are no other holes.

I need to confirm memory is being preserved correctly, even in `pci_quirks.c`, but it's a refactoring, and it looks like clean table matching with some new `pci_dev_acs_ops` helpers.

Then I checked `probe.c`, looking at the new `pci_liveupdate_setup_device` and `pci_liveupdate_cleanup_device` calls, as well as the changes in `pci_release_dev`. Everything looks kosher so far; no obvious uninitialized memory issues leaping out at me.

Next, I look at the changes in `drivers/pci/pci.c`, at `pci_enable_acs`, `pci_restore_state`, and `pci_configure_ari`, and then again the associated `pci_liveupdate_adopt_acs` and `pci_liveupdate_adopt_ari`. I am looking for something missed, like uninitialized memory, but so far I have found none.

Alright, time to apply my expertise. Let's really analyze the KMSAN criteria.

1.  Kernel structures to userspace? No, not here. No `copy_to_user`, no `put_user`, no Netlink, no ioctl handlers, etc.
2.  Conditional logic with uninitialized variables? Nope. All locals are initialized, including `val` in `pcie_capability_read_word`. That's a classic area to watch out for.
3.  Allocations with uninitialized fields? Nope, again. Everything is zero-initialized via `kzalloc_obj` or `kho_alloc_preserve` or explicitly zeroed by `memset`. These primitives are the gold standard here.
4.  Bounds checks that could lead to reading uninitialized memory? No.
5.  All bug types here are logic, refcounting, use-after-free, deadlocks, and synchronization bugs which are caught by KASAN / LOCKDEP.

So, I am pretty sure. Based on that analysis, this patch series does NOT introduce any uninitialized memory risks or information leaks. Running a dedicated KMSAN session is not justified, and I'm ready to mark `NeedsKMSAN = false`. I also have all the supporting information ready.

Let's do this:
*   `NeedsKMSAN`: false
*   `KMSANReasoning`: *Detailed analysis contrasting KASAN vs KMSAN applicability for this patch: 1. Nature of the patch: Introduces PCI Live Update support across kexec using KHO (kexec handover) and LUO (live update orchestrator). 2. Memory allocations: - `pci_flb_outgoing` and `pci_flb_incoming` are allocated with `kzalloc_obj`. - `pci_ser` is allocated using `kho_alloc_preserve()`, which allocates and zeroes the memory, and all fields are explicitly assigned. - `pci_dev_ser` structures are managed in KHO block sets backed by zeroed memory, have all fields initialized, and are explicitly cleared with `memset()` on unpreserve. - New capability save buffer for ACS (`PCI_EXT_CAP_ID_ACS`) is allocated using `kzalloc()` via `_pci_add_cap_save_buffer()`. - `struct pci_dev` embedding `struct pci_liveupdate` is allocated with `kzalloc_obj()`. 3. No kernel-to-userspace copies: No data is copied to userspace via `copy_to_user()`, `put_user()`, netlink, or ioctl outputs. 4. Variables: All local variables (such as `u16 val` in `pci_liveupdate_adopt_ari`) are initialized (e.g. `pcie_capability_read_word()`


76/3 2026/09/21 01:16 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series introduces PCI Live Update support, allowing PCI devices and their bridge topologies to be preserved across kexec using Live Update Orchestrator (LUO) and Kexec Handover (KHO).\n\nAnalysis of memory initialization and sanitizers:\n1. Memory Allocation and Initialization:\n   - `struct pci_flb_outgoing` and `struct pci_flb_incoming` are allocated using `kzalloc_obj()`.\n   - `struct pci_ser` is allocated using `kho_alloc_preserve()`, which allocates and zeroes preserved memory, with all fields explicitly initialized (`version`, `nr_devices`, `devices`). The structure is packed and has no padding.\n   - `struct pci_dev_ser` is packed and allocated from KHO block sets (which allocate zeroed memory via `kho_alloc_preserve()`); all fields are explicitly initialized on preserve, and cleared with `memset()` on unpreserve.\n   - `struct pci_liveupdate` embedded in `struct pci_dev` is zero-initialized as `struct pci_dev` is allocated with `kzalloc_obj()`.\n   - The new ACS capability save buffer is allocated via `pci_add_ext_cap_save_buffer()`, which zeroes memory with `kzalloc()`.\n   - Local variables passed by pointer (e.g. `u16 val` in `pci_liveupdate_adopt_ari()`) are initialized by their callees (`pcie_capability_read_word()` explicitly sets `*val = 0` on entry).\n\n2. Userspace Leaks / Uninitialized Memory:\n   - There are no `copy_to_user()`, `put_user()`, netlink, or userspace-facing serialization paths modified or introduced in this patch.\n   - No branching or conditional logic depends on uninitialized fields or stack variables.\n\n3. Applicable Detectors:\n   - Any potential issues in this patch series are related to synchronization (`rwsem`), refcounting, pointer lifecycle management, or bus scanning logic, which fall under the purview of LOCKDEP, KASAN, and standard kernel debugging tools.\n\nTherefore, the patch introduces no uninitialized memory risks or info-leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series introduces PCI Live Update support, allowing PCI devices and their bridge topologies to be preserved across kexec using Live Update Orchestrator (LUO) and Kexec Handover (KHO).\n\nAnalysis of memory initialization and sanitizers:\n1. Memory Allocation and Initialization:\n   - `struct pci_flb_outgoing` and `struct pci_flb_incoming` are allocated using `kzalloc_obj()`.\n   - `struct pci_ser` is allocated using `kho_alloc_preserve()`, which allocates and zeroes preserved memory, with all fields explicitly initialized (`version`, `nr_devices`, `devices`). The structure is packed and has no padding.\n   - `struct pci_dev_ser` is packed and allocated from KHO block sets (which allocate zeroed memory via `kho_alloc_preserve()`); all fields are explicitly initialized on preserve, and cleared with `memset()` on unpreserve.\n   - `struct pci_liveupdate` embedded in `struct pci_dev` is zero-initialized as `struct pci_dev` is allocated with `kzalloc_obj()`.\n   - The new ACS capability save buffer is allocated via `pci_add_ext_cap_save_buffer()`, which zeroes memory with `kzalloc()`.\n   - Local variables passed by pointer (e.g. `u16 val` in `pci_liveupdate_adopt_ari()`) are initialized by their callees (`pcie_capability_read_word()` explicitly sets `*val = 0` on entry).\n\n2. Userspace Leaks / Uninitialized Memory:\n   - There are no `copy_to_user()`, `put_user()`, netlink, or userspace-facing serialization paths modified or introduced in this patch.\n   - No branching or conditional logic depends on uninitialized fields or stack variables.\n\n3. Applicable Detectors:\n   - Any potential issues in this patch series are related to synchronization (`rwsem`), refcounting, pointer lifecycle management, or bus scanning logic, which fall under the purview of LOCKDEP, KASAN, and standard kernel debugging tools.\n\nTherefore, the patch introduces no uninitialized memory risks or info-leaks, and a dedicated KMSAN fuzzing session is not warranted.",
  "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)