PCI: liveupdate: PCI core support for Live Update
Subsystems (5)
v5 cover PCI: liveupdate: PCI core support for Live Update
This series can be found on GitHub:
https://github.com/dmatlack/linux/tree/liveupdate/pci/base/v5
This patch series introduces the initial support in the PCI core for Live Update, enabling drivers to preserve PCI devices across a kexec-based kernel update without interrupting the device. This functionality is critical for minimizing downtime in environments where PCI devices (e.g., those assigned to VMs via VFIO) must continue operating or maintain state across a host kernel upgrade.
Specifically, this patch series allows preserved PCI devices to perform memory transactions to/from system memory (DMA) uninterrupted across a Live Update. The devices can be behind a bridge, but must not be a VF. Support for P2P and preserving VFs will come in future series.
Series Overview
---------------
This series implements the following to support PCI device preservation across Live Update:
1. Set up a File-Lifecycle-Bound (FLB) handler to track and preserve PCI-specific state (struct pci_ser) across Live Update using Kexec Handover (KHO).
2. Add APIs for drivers to register "outgoing" devices for preservation and for the PCI core to identify "incoming" preserved devices during enumeration.
3. Automatically preserve all upstream bridges for any preserved endpoint. Use reference counting to ensure bridges remain preserved as long as any downstream device is preserved.
4. Guarantee that preserved devices can be identified by the same RequesterID (bus, device, function) for as long as they are preserved by always inheriting secondary and subordinate bus numbers and ARI Forwarding Enable on bridges with preserved downstream endpoints.
5. Guarantee the memory transactions to/from preserved devices are routed the same way by inheriting Access Control Services (ACS) flags across a Live Update.
6. Modify the PCI shutdown path to avoid disabling bus mastering on preserved devices and their upstream bridges, allowing memory transactions to continue uninterrupted.
7. Provide comprehensive documentation for the FLB API, device tracking mechanisms, and the division of responsibilities between the PCI core, drivers, and userspace.
Dependencies
------------
This series is built on top of the next branch of liveupdate.git tree which has 2 commits to enable refcounting the incoming FLB:
https://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git/log/?h=next
Testing
-------
This series was tested in conjunction with v4 of the VFIO PCI driver series:
https://lore.kernel.org/kvm/[email protected]/
The full set of patches that I used for testing can be found on GitHub:
https://github.com/dmatlack/linux/tree/liveupdate/pci/base/v5-with-vfio
The full set of patches was tested using the new VFIO selftests:
- vfio_pci_liveupdate_uapi_test
- vfio_pci_liveupdate_kexec_test
Both tests were ran in ran in a QEMU-based VM environment, using a single virtio-net PCIe device connected to a root port (to exercise the bridge support in this series), and in a baremetal environment on an Intel EMR server, using 8x Intel DSA PCIe devices (each on a host bridge) and 1x NVMe device connected to a root port.
Future Work
-----------
After this series we expect to make further improvements to the PCI core support for Live Update.
- Allow P2P across Live Update by avoiding sizing or moving preserved device BARs and preserving all upstream bridge windows.
- Support preserving Virtual Functions, by preserving SR-IOV configuration on PFs and enumerating VFs after Live Update.
Changelog
---------
v5:
- Update PCI LIVE UPDATE entry in MAINTAINERS to use liveupdate.git, add kexec@ mailing list, and drop Bjorn (Pasha, Bjorn, Pratyush)
- Create separate headers for Live Update definitions to avoid future patch conflicts (me)
- Add kernel doc for public (Driver) API (me)
- Rename reserved field to padding (Vipin)
- Reorder checks outside of mutex where possible (Jacob)
- Clarify refcount in struct pci_dev_ser in kernel-doc (Sami)
- Require CONFIG_64BIT to avoid overflowing xarray key (Sashiko)
- Various spelling and grammar fixes (Bjorn)
- Ensure incoming and outgoing devices do not have their bus numbers changed during manual rescans via sysfs (Jacob)
- Fix refcount dropping for upstream bridges during finish (Sashiko)
- Disallow devices with PCI_DEV_FLAGS_ACS_ENABLED_QUIRK to simplify ACS inheritence across Live Update (Sashiko)
- Fix ACS re-enablement via pci_restore_state() (Sashiko)
- Drop commit that requires singleton iommu groups (me, Sami)
- Add per-device lock to protect Live Update fields (Sami, Sashiko)
v4: https://lore.kernel.org/linux-pci/[email protected]/
v3: https://lore.kernel.org/kvm/[email protected]/
v2: https://lore.kernel.org/kvm/[email protected]/
v1: https://lore.kernel.org/kvm/[email protected]/
rfc: https://lore.kernel.org/kvm/[email protected]/
David Matlack (11):
PCI: liveupdate: Set up FLB handler for the PCI core
PCI: liveupdate: Track outgoing preserved PCI devices
PCI: liveupdate: Track incoming preserved PCI devices
PCI: liveupdate: Document driver binding responsibilities
PCI: liveupdate: Keep bus numbers constant during Live Update
PCI: liveupdate: Auto-preserve upstream bridges across Live Update
PCI: liveupdate: Inherit ACS flags in incoming preserved devices
PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges
PCI: liveupdate: Freeze preservation status during shutdown
PCI: liveupdate: Do not disable bus mastering on preserved devices
during kexec
Documentation: PCI: Add documentation for Live Update
Documentation/PCI/index.rst | 1 + Documentation/PCI/liveupdate.rst | 29 + .../admin-guide/kernel-parameters.txt | 6 +- Documentation/core-api/liveupdate.rst | 1 + MAINTAINERS | 12 + drivers/pci/Kconfig | 14 + drivers/pci/Makefile | 1 + drivers/pci/liveupdate.c | 807 ++++++++++++++++++ drivers/pci/liveupdate.h | 66 ++ drivers/pci/pci-driver.c | 33 +- drivers/pci/pci.c | 13 +- drivers/pci/probe.c | 29 +- include/linux/kho/abi/pci.h | 64 ++ include/linux/pci.h | 4 + include/linux/pci_liveupdate.h | 75 ++ 15 files changed, 1140 insertions(+), 15 deletions(-) create mode 100644 Documentation/PCI/liveupdate.rst create mode 100644 drivers/pci/liveupdate.c create mode 100644 drivers/pci/liveupdate.h create mode 100644 include/linux/kho/abi/pci.h create mode 100644 include/linux/pci_liveupdate.h
base-commit: 34e8f02817e31826e76bb2ded48bf28fe921f20b
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
PATCH v5 1/11 PCI: liveupdate: Set up FLB handler for the PCI core
Set up a File-Lifecycle-Bound (FLB) handler for the PCI core to enable it to participate in the preservation of PCI devices across Live Update. Essentially, this commit enables the PCI core to allocate a struct (struct pci_ser) and preserve it across a Live Update whenever at least one device is preserved.
Preserving PCI devices across Live Update is built on top of the Live Update Orchestrator's (LUO) support for file preservation. Drivers are expected to expose a file to userspace to represent a single PCI device and support preservation of that file. This is intended primarily to support preservation of PCI devices bound to VFIO drivers.
This commit enables drivers to register their liveupdate_file_handler with the PCI core so that the PCI core can do its own tracking and enforcement of which devices are preserved.
pci_liveupdate_register_flb(driver_file_handler); pci_liveupdate_unregister_flb(driver_file_handler);
When the first file (with a handler registered with the PCI core) is preserved, the PCI core will be notified to allocate its tracking struct (pci_ser). When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.
This struct is preserved across a Live Update using KHO and can be fetched by the PCI core during early boot (e.g. during device enumeration) so that it knows which devices were preserved.
Note: This commit only allocates struct pci_ser and preserves it across Live Update. A subsequent commit will add an API for drivers to tell the PCI core exactly which devices are being preserved.
Note: There is no reason to check for kho_is_enabled() since it can be assumed to return true. If KHO was not enabled then Live Update would not be enabled and these routines would never run.
--- MAINTAINERS | 10 +++ drivers/pci/Kconfig | 14 +++ drivers/pci/Makefile | 1 + drivers/pci/liveupdate.c | 153 +++++++++++++++++++++++++++++++++ include/linux/kho/abi/pci.h | 61 +++++++++++++ include/linux/pci.h | 1 + include/linux/pci_liveupdate.h | 30 +++++++ 7 files changed, 270 insertions(+) create mode 100644 drivers/pci/liveupdate.c create mode 100644 include/linux/kho/abi/pci.h create mode 100644 include/linux/pci_liveupdate.h
MAINTAINERS +10 -0
@@ -20530,6 +20530,16 @@ L: [email protected] S: Supported F: Documentation/PCI/pci-error-recovery.rst +PCI LIVE UPDATE +M: David Matlack <[email protected]> +L: [email protected] +L: [email protected] +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git +F: drivers/pci/liveupdate.c +F: include/linux/kho/abi/pci.h +F: include/linux/pci_liveupdate.h + PCI MSI DRIVER FOR ALTERA MSI IP L: [email protected] S: Orphan
drivers/pci/Kconfig +14 -0
@@ -328,6 +328,20 @@ 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 (EXPERIMENTAL)" + depends on PCI && LIVEUPDATE + 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 developers working on + implementing this support. + + If unsure, say N. + source "drivers/pci/hotplug/Kconfig" source "drivers/pci/controller/Kconfig" source "drivers/pci/endpoint/Kconfig"
drivers/pci/Makefile +1 -0
@@ -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
drivers/pci/liveupdate.c new file +153 -0
153 lines not on this page · raw message
include/linux/kho/abi/pci.h new file +61 -0
61 lines not on this page · raw message
include/linux/pci.h +1 -0
@@ -42,6 +42,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 | \
include/linux/pci_liveupdate.h new file +30 -0
@@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* PCI Live Update support (Public/Driver API) +* +* Copyright (c) 2026, Google LLC. +* David Matlack <[email protected]> +*/ +#ifndef LINUX_PCI_LIVEUPDATE_H +#define LINUX_PCI_LIVEUPDATE_H + +#include <linux/liveupdate.h> +#include <linux/types.h> + +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); +#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) +{ +} +#endif + +#endif /* LINUX_PCI_LIVEUPDATE_H */
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
PATCH v5 2/11 PCI: liveupdate: Track outgoing preserved PCI devices
In reply to David MatlackAdd APIs to allow drivers to notify the PCI core of which devices are being preserved across a Live Update for the next kernel, i.e. "outgoing" devices.
Drivers must notify the PCI core when devices are preserved so that the PCI core can update its FLB data (struct pci_ser) and track the list of outgoing devices. pci_liveupdate_preserve() notifies the PCI core that a device must be preserved across Live Update. pci_liveupdate_unpreserve() reverses this (cancels the preservation of the device).
This tracking ensures the PCI core is fully aware of which devices may need special handling during shutdown and kexec, and so that it can be handed off to the next kernel.
--- drivers/pci/liveupdate.c | 167 ++++++++++++++++++++++++++++++--- drivers/pci/probe.c | 3 + include/linux/kho/abi/pci.h | 9 +- include/linux/pci.h | 3 + include/linux/pci_liveupdate.h | 23 +++++ 5 files changed, 191 insertions(+), 14 deletions(-)
drivers/pci/liveupdate.c +156 -11
@@ -43,6 +43,26 @@* * * ``pci_liveupdate_register_flb(driver_file_handler)`` * * ``pci_liveupdate_unregister_flb(driver_file_handler)`` +* +* 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. +* +* 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). */ #define pr_fmt (fmt) "PCI: liveupdate: " fmt
@@ -55,13 +75,29 @@#include <linux/mm.h> #include <linux/pci.h> +/** +* struct pci_flb_outgoing - Outgoing PCI FLB object +* @ser: The outgoing struct pci_ser for the next kernel. +* @lock: Lock used to protect against changes to @ser. +*/ +struct pci_flb_outgoing { +struct pci_ser *ser; +struct mutex lock; +}; +static int pci_flb_preserve (struct liveupdate_flb_op_args *args) { +struct pci_flb_outgoing *outgoing;struct pci_dev *dev =NULL ;u32 max_nr_devices =0 ; -struct pci_ser *ser;unsigned long size; + outgoing =kmalloc_obj (*outgoing); +if (!outgoing) +return -ENOMEM ; + +mutex_init (&outgoing->lock); +/* * Allocate enough space to preserve all of the devices that are * currently present on the system. Extra padding can be added to this
@@ -74,27 +110,30 @@ static int pci_flb_preserve(struct liveupdate_flb_op_args *args) size = struct_size_t(struct pci_ser, devices, max_nr_devices); - ser =kho_alloc_preserve (size); -if (IS_ERR (ser)) -return PTR_ERR (ser); + outgoing->ser =kho_alloc_preserve (size); +if (IS_ERR (outgoing->ser)) { +kfree (outgoing); +return PTR_ERR (outgoing->ser); + }pr_debug ("Preserved struct pci_ser with room for %u devices\n" , max_nr_devices); - ser->max_nr_devices = max_nr_devices; - ser->nr_devices =0 ; + outgoing->ser->max_nr_devices = max_nr_devices; + outgoing->ser->nr_devices =0 ; - args->obj = ser; - args->data =virt_to_phys (ser); + args->obj = outgoing; + args->data =virt_to_phys (outgoing->ser);return 0 ; }static void pci_flb_unpreserve (struct liveupdate_flb_op_args *args) { -struct pci_ser *ser = args->obj; +struct pci_flb_outgoing *outgoing = args->obj; -WARN_ON_ONCE (ser->nr_devices); -kho_unpreserve_free (ser); +WARN_ON_ONCE (outgoing->ser->nr_devices); +kho_unpreserve_free (outgoing->ser); +kfree (outgoing);pr_debug ("Unpreserved struct pci_ser\n" ); }
@@ -123,6 +162,112 @@ static struct liveupdate_flb pci_liveupdate_flb = { .compatible = PCI_LUO_FLB_COMPATIBLE, }; +/** +* 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 must call +* pci_liveupdate_preserve() from their struct liveupdate_file_handler +* preserve() callback to ensure the outgoing struct pci_ser is allocated. +* +* Returns: 0 on success, <0 on failure. +*/ +int pci_liveupdate_preserve (struct pci_dev *dev) +{ +struct pci_flb_outgoing *outgoing =NULL ; +struct pci_ser *ser; +int i, ret; + +if (dev->is_virtfn) +return -EINVAL ; + + ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); +if (ret) +return ret; + +if (!outgoing) +return -ENOENT ; + +guard (mutex)(&outgoing->lock); + ser = outgoing->ser; + +guard (write_lock)(&dev->liveupdate.lock); + +if (dev->liveupdate.outgoing) +return -EBUSY ; + +if (ser->nr_devices == ser->max_nr_devices) +return -ENOSPC ; + +for (i =0 ; i < ser->max_nr_devices; i++) { +/* +* Start searching at index ser->nr_devices. This should result +* in a constant time search under expected conditions (devices +* are not getting unpreserved). +*/ +int index = (ser->nr_devices + i) % ser->max_nr_devices; +struct pci_dev_ser *dev_ser = &ser->devices[index]; + +if (dev_ser->refcount) +continue ; + +pci_info (dev,"Device will be preserved across next Live Update\n" ); + ser->nr_devices++; + + dev_ser->domain =pci_domain_nr (dev->bus); + dev_ser->bdf =pci_dev_id (dev); + dev_ser->refcount =1 ; + + dev->liveupdate.outgoing = dev_ser; +return 0 ; + } + +return -ENOSPC ; +} +EXPORT_SYMBOL_GPL (pci_liveupdate_preserve); + +/** +* pci_liveupdate_unpreserve() - Cancel preservation of a PCI device +* @dev: The PCI device to preserve. +* +* pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no +* longer be preserved across the next Live Update. Drivers must call +* pci_liveupdate_unpreserve() from their struct liveupdate_file_handler +* unpreserve() callback to ensure the outgoing struct pci_ser is allocated. +*/ +void pci_liveupdate_unpreserve (struct pci_dev *dev) +{ +struct pci_flb_outgoing *outgoing =NULL ; +struct pci_dev_ser *dev_ser; +struct pci_ser *ser; +int ret; + + ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); + +if (ret || !outgoing) { +pci_warn (dev,"Cannot unpreserve device without outgoing Live Update state\n" ); +return ; + } + +guard (mutex)(&outgoing->lock); + ser = outgoing->ser; + +guard (write_lock)(&dev->liveupdate.lock); + + dev_ser = dev->liveupdate.outgoing; +if (!dev_ser) { +pci_warn (dev,"Cannot unpreserve device that is not preserved\n" ); +return ; + } + +pci_info (dev,"Device will no longer be preserved across next Live Update\n" ); + ser->nr_devices--; +memset (dev_ser,0 ,sizeof (*dev_ser)); + dev->liveupdate.outgoing =NULL ; +} +EXPORT_SYMBOL_GPL (pci_liveupdate_unpreserve); +/** * pci_liveupdate_register_flb() - Register a file handler with the PCI core * @fh: The file handler to register.
drivers/pci/probe.c +3 -0
@@ -2522,6 +2522,9 @@ struct pci_dev *pci_alloc_dev(struct pci_bus *bus)spin_lock_init (&dev->pcie_cap_lock);#ifdef CONFIG_PCI_MSI raw_spin_lock_init (&dev->msi_lock); +#endif +#ifdef CONFIG_PCI_LIVEUPDATE +rwlock_init (&dev->liveupdate.lock);#endif return dev; }
include/linux/kho/abi/pci.h +6 -3
@@ -23,19 +23,22 @@* incrementing the version number in the PCI_LUO_FLB_COMPATIBLE string. */ -#define PCI_LUO_FLB_COMPATIBLE "pci-v1" +#define PCI_LUO_FLB_COMPATIBLE "pci-v2"/** * 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. -* @padding: Padding to naturally align struct pci_dev_ser. +* @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 devices preserved at or below +* this device in the PCI hierarchy. */ struct pci_dev_ser {u32 domain;u16 bdf; -u16 padding; +u16 refcount; } __packed;/**
include/linux/pci.h +3 -0
@@ -594,6 +594,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)
include/linux/pci_liveupdate.h +23 -0
@@ -8,14 +8,28 @@ #ifndef LINUX_PCI_LIVEUPDATE_H #define LINUX_PCI_LIVEUPDATE_H +#include <linux/kho/abi/pci.h> #include <linux/liveupdate.h> #include <linux/types.h> +#include <linux/spinlock_types.h> + +/** + * struct pci_liveupdate - PCI Live Update state for a struct pci_dev + * @lock: Lock used to protect members of struct pci_liveupdate. + * @outgoing: State preserved for the next kernel. + */ +struct pci_liveupdate { + rwlock_t lock; + struct pci_dev_ser *outgoing; +}; 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); #else static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh) {
@@ -25,6 +39,15 @@ static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fhstatic 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) +{ +}#endif #endif /* LINUX_PCI_LIVEUPDATE_H */
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 02/11] PCI: liveupdate: Track outgoing preserved PCI devices
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Critical] Dangling pointer in dev->liveupdate.outgoing causes Use-After-Free memory corruption and state…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Dangling pointer in dev->liveupdate.outgoing causes Use-After-Free memory corruption and state leaks if a device is not properly unpreserved.
- [High] Missing kho_is_enabled() check in pci_flb_preserve() violates KHO subsystem guidelines.
--
commit f946f5d8b0614325899345e29b5315ff6d5139e9
Author: David Matlack <[email protected]>
PCI: liveupdate: Track outgoing preserved PCI devices
This patch adds APIs to allow drivers to notify the PCI core of which devices are being preserved across a Live Update. It allocates a struct pci_ser via the KHO subsystem to track the outgoing preserved devices.
Quoted 3 lines
drivers/pci/liveupdate.c +0
-0
[ ... ]
drivers/pci/liveupdate.c static int pci_flb_preserve(struct liveupdate_flb_op_args *args) · line 113
Quoted 8 lines
drivers/pci/liveupdate.c +1
-3@@ -74,27 +110,30 @@ static int pci_flb_preserve(struct liveupdate_flb_op_args *args) size = struct_size_t(struct pci_ser, devices, max_nr_devices); - ser =kho_alloc_preserve (size); -if (IS_ERR (ser)) -return PTR_ERR (ser); + outgoing->ser =kho_alloc_preserve (size);
Does this allocation need to be gated by kho_is_enabled()?
If KHO is disabled, kho_alloc_preserve() will silently add tracking state that will never be used.
Quoted 7 lines
drivers/pci/liveupdate.c +4
-0+if (IS_ERR (outgoing->ser)) { +kfree (outgoing); +return PTR_ERR (outgoing->ser); + }pr_debug ("Preserved struct pci_ser with room for %u devices\n" , max_nr_devices);
[ ... ]
drivers/pci/liveupdate.c static int pci_flb_preserve(struct liveupdate_flb_op_args *args) · line 136
Quoted 10 lines
static void
pci_flb_unpreserve(struct liveupdate_flb_op_args *args){drivers/pci/liveupdate.c +4
-3-struct pci_ser *ser = args->obj; +struct pci_flb_outgoing *outgoing = args->obj; -WARN_ON_ONCE (ser->nr_devices); -kho_unpreserve_free (ser); +WARN_ON_ONCE (outgoing->ser->nr_devices); +kho_unpreserve_free (outgoing->ser); +kfree (outgoing);
If a driver fails to unpreserve a device before the FLB is torn down, this WARN_ON_ONCE triggers, but the memory is still freed.
Does this leave dev->liveupdate.outgoing pointing to freed memory?
Quoted 3 lines
pr_debug("Unpreserved struct pci_ser\n");
}
[ ... ]
Quoted 18 lines
drivers/pci/liveupdate.c +18
-0+void pci_liveupdate_unpreserve (struct pci_dev *dev) +{ +struct pci_flb_outgoing *outgoing =NULL ; +struct pci_dev_ser *dev_ser; +struct pci_ser *ser; +int ret; + + ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); + +if (ret || !outgoing) { +pci_warn (dev,"Cannot unpreserve device without outgoing Live Update state\n" ); +return ; + } + +guard (mutex)(&outgoing->lock); + ser = outgoing->ser; + +guard (write_lock)(&dev->liveupdate.lock);
drivers/pci/liveupdate.c static struct liveupdate_flb pci_liveupdate_flb = { · line 266
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+ + dev_ser = dev->liveupdate.outgoing; +if (!dev_ser) { +pci_warn (dev,"Cannot unpreserve device that is not preserved\n" ); +return ; + } + +pci_info (dev,"Device will no longer be preserved across next Live Update\n" ); + ser->nr_devices--; +memset (dev_ser,0 ,sizeof (*dev_ser));
If dev->liveupdate.outgoing was left dangling from a previous torn-down session, can this corrupt memory?
If a new Live Update session starts, pci_liveupdate_unpreserve() would fetch the new outgoing FLB but operate on the old dangling dev->liveupdate.outgoing pointer.
Does this code cause a use-after-free in the memset() here, as well as corrupt the new session's counter when decrementing ser->nr_devices?
Quoted 2 lines
drivers/pci/liveupdate.c +2
-0+ dev->liveupdate.outgoing =NULL ; +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v5 02/11] PCI: liveupdate: Track outgoing preserved PCI devices
Quoted 22 lines · On 2026-05-14 12:31 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Dangling pointer indev->liveupdate.outgoingcauses Use-After-Free memory corruption and state leaks if a device is not properly unpreserved.
- [High] Missingkho_is_enabled()check inpci_flb_preserve()violates KHO subsystem guidelines.
--commit
f946f5d8b0614325899345e29b5315ff6d5139e9
Author: David Matlack <[email protected]>PCI: liveupdate: Track outgoing preserved PCI devices
This patch adds APIs to allow drivers to notify the PCI core of which devices are being preserved across a Live Update. It allocates a struct
pci_servia the KHO subsystem to track the outgoing preserved devices.Quoted 3 lines
drivers/pci/liveupdate.c +0
-0[ ... ]
Quoted 2 lines
drivers/pci/liveupdate.c +0
-0@@ -74,27 +110,30 @@ static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
Quoted 8 lines
drivers/pci/liveupdate.c static int pci_flb_preserve(struct liveupdate_flb_op_args *args) · line 113
Quoted 6 lines
drivers/pci/liveupdate.c +1
-3size = struct_size_t(struct pci_ser, devices, max_nr_devices); - ser =kho_alloc_preserve (size); -if (IS_ERR (ser)) -return PTR_ERR (ser); + outgoing->ser =kho_alloc_preserve (size);Does this allocation need to be gated by
kho_is_enabled()?
No, if kho_is_enabled() is false then liveupdate_register_flb() would have returned -EOPNOTSUPP and thus this callback would never be run.
Quoted 28 lines
If KHO is disabled,
kho_alloc_preserve()will silently add tracking state that will never be used.Quoted 7 lines
drivers/pci/liveupdate.c +4
-0+if (IS_ERR (outgoing->ser)) { +kfree (outgoing); +return PTR_ERR (outgoing->ser); + }pr_debug ("Preserved struct pci_ser with room for %u devices\n" , max_nr_devices);[ ... ]
drivers/pci/liveupdate.c static int pci_flb_preserve(struct liveupdate_flb_op_args *args) · line 136
Quoted 10 lines
static void
pci_flb_unpreserve(struct liveupdate_flb_op_args *args){drivers/pci/liveupdate.c +4
-3-struct pci_ser *ser = args->obj; +struct pci_flb_outgoing *outgoing = args->obj; -WARN_ON_ONCE (ser->nr_devices); -kho_unpreserve_free (ser); +WARN_ON_ONCE (outgoing->ser->nr_devices); +kho_unpreserve_free (outgoing->ser); +kfree (outgoing);If a driver fails to unpreserve a device before the FLB is torn down, this
WARN_ON_ONCEtriggers, but the memory is still freed.
Quoted 1 line
Does this leave
dev->liveupdate.outgoingpointing to freed memory?
Yes, if a driver unpreserves a device without notifying the PCI core then that would lead to the PCI core having a pointer to freed memory.
I can make this code more robust against buggy drivers in v6.
Quoted 39 lines
Quoted 4 lines
pr_debug("Unpreserved struct pci_ser\n");
}[ ... ]
Quoted 18 lines
drivers/pci/liveupdate.c +18
-0+void pci_liveupdate_unpreserve (struct pci_dev *dev) +{ +struct pci_flb_outgoing *outgoing =NULL ; +struct pci_dev_ser *dev_ser; +struct pci_ser *ser; +int ret; + + ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); + +if (ret || !outgoing) { +pci_warn (dev,"Cannot unpreserve device without outgoing Live Update state\n" ); +return ; + } + +guard (mutex)(&outgoing->lock); + ser = outgoing->ser; + +guard (write_lock)(&dev->liveupdate.lock);drivers/pci/liveupdate.c static struct liveupdate_flb pci_liveupdate_flb = { · line 266
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+ + dev_ser = dev->liveupdate.outgoing; +if (!dev_ser) { +pci_warn (dev,"Cannot unpreserve device that is not preserved\n" ); +return ; + } + +pci_info (dev,"Device will no longer be preserved across next Live Update\n" ); + ser->nr_devices--; +memset (dev_ser,0 ,sizeof (*dev_ser));If
dev->liveupdate.outgoingwas left dangling from a previous torn-down session, can this corrupt memory?
Quoted 6 lines
If a new Live Update session starts,
pci_liveupdate_unpreserve()would fetch the new outgoing FLB but operate on the old danglingdev->liveupdate.outgoingpointer.Does this code cause a use-after-free in the
memset()here, as well as corrupt the new session's counter when decrementingser->nr_devices?
I will clean up any dangling pointers during pci_flb_unpreserve() in v6 to protect the PCI core against buggy drivers.
Quoted 6 lines
Quoted 3 lines
drivers/pci/liveupdate.c +2
-0+ dev->liveupdate.outgoing =NULL ; +}Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v5 02/11] PCI: liveupdate: Track outgoing preserved PCI devices
Quoted 17 lines · David Matlack
On 2026-05-14 12:31 AM, [email protected] wrote:
...Quoted 14 lines
drivers/pci/liveupdate.c static int pci_flb_preserve(struct liveupdate_flb_op_args *args) · line 136
Quoted 10 lines
static void
pci_flb_unpreserve(struct liveupdate_flb_op_args *args){drivers/pci/liveupdate.c +4
-3-struct pci_ser *ser = args->obj; +struct pci_flb_outgoing *outgoing = args->obj; -WARN_ON_ONCE (ser->nr_devices); -kho_unpreserve_free (ser); +WARN_ON_ONCE (outgoing->ser->nr_devices); +kho_unpreserve_free (outgoing->ser); +kfree (outgoing);If a driver fails to unpreserve a device before the FLB is torn down, this
WARN_ON_ONCEtriggers, but the memory is still freed.
Quoted 4 lines
Quoted 1 line
Does this leave
dev->liveupdate.outgoingpointing to freed memory?Yes, if a driver unpreserves a device without notifying the PCI core then that would lead to the PCI core having a pointer to freed memory.
Just a curiosity, I guess: How is this possible? Is the driver responsible for doing a generic unpreserve and also a PCI unpreserve? Seems like it should do a single unpreserve, which should internally take care of whatever PCI/IOMMU/etc unpreserve is needed.
I guess the requirement is explained in "[02/11] PCI: liveupdate:
Track outgoing ...":
+ * ... Drivers must call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is allocated.
I just don't know what the driver changes will look like.
Re: [PATCH v5 02/11] PCI: liveupdate: Track outgoing preserved PCI devices
Quoted 21 lines · Bjorn Helgaas
Quoted 19 lines · David Matlack
On 2026-05-14 12:31 AM, [email protected] wrote:
...Quoted 15 lines
Quoted 10 lines
static void
pci_flb_unpreserve(struct liveupdate_flb_op_args *args){
- structpci_ser*ser =args->obj;
+ structpci_flb_outgoing*outgoing =args->obj;-
WARN_ON_ONCE(ser->nr_devices);
-kho_unpreserve_free(ser);
+WARN_ON_ONCE(outgoing->ser->nr_devices);
+kho_unpreserve_free(outgoing->ser);
+ kfree(outgoing);If a driver fails to unpreserve a device before the FLB is torn down, this
WARN_ON_ONCEtriggers, but the memory is still freed.Does this leave
dev->liveupdate.outgoingpointing to freed memory?
Quoted 7 lines
Quoted 2 lines
Yes, if a driver unpreserves a device without notifying the PCI core then that would lead to the PCI core having a pointer to freed memory.
Just a curiosity, I guess: How is this possible? Is the driver responsible for doing a generic unpreserve and also a PCI unpreserve? Seems like it should do a single unpreserve, which should internally take care of whatever PCI/IOMMU/etc unpreserve is needed.
When userspace unpreserves a file (e.g. VFIO device file), LUO calls the driver's liveupdate_file_handler unpreserve() callback. The driver needs then to notify the PCI core of which PCI device got unpreserved by calling pci_liveupdate_unpreserve(). If the driver doesn't, it would be a bug, and it would result in LUO later notifying the PCI core to free its outgoing FLB while the PCI core thinks a device is still preserved.
Quoted 8 lines
I guess the requirement is explained in "[02/11] PCI: liveupdate:
Track outgoing ...":+ * ... Drivers must call
+ *pci_liveupdate_unpreserve()from their structliveupdate_file_handler
+ *unpreserve()callback to ensure the outgoing structpci_seris allocated.I just don't know what the driver changes will look like.
Right that's why that line is there. And the WARN_ON_ONCE() above aims to catch future buggy drivers that do the wrong thing.
The VFIO driver changes can be found here that build on top of this series can be found here:
PATCH v5 3/11 PCI: liveupdate: Track incoming preserved PCI devices
In reply to David MatlackDuring PCI enumeration, the previous kernel might have passed state about devices that were preserved across kexec. The PCI core needs to fetch this state to identify which devices are "incoming" and require special handling.
Add pci_liveupdate_setup_device() which is called during device setup to fetch the serialized state (struct pci_ser) from the Live Update Orchestrator. The first time this happens, pci_flb_retrieve() will run and convert the array of pci_dev_ser structs into an xarray so that it can be looked up efficiently.
If a device is found in the xarray, the PCI core stores a pointer to its state in dev->liveupdate_incoming and holds a reference to the incoming FLB until pci_liveupdate_finish() is called by the driver.
This ensures proper lifecycle management for incoming preserved devices and allows the PCI core and drivers to apply specific Live Update logic to them in subsequent commits.
Drivers can check if a device is an incoming preserved device (e.g. during probe) by calling pci_liveupdate_is_incoming().
CONFIG_64BIT is now required to enable CONFIG_PCI_LIVEUPDATE so that the domain and bdf can be guaranteed to fit in an unsigned long and be used as the xarray key.
--- MAINTAINERS | 1 + drivers/pci/Kconfig | 2 +- drivers/pci/liveupdate.c | 223 ++++++++++++++++++++++++++++++++- drivers/pci/liveupdate.h | 26 ++++ drivers/pci/probe.c | 5 + include/linux/pci_liveupdate.h | 13 ++ 6 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 drivers/pci/liveupdate.h
MAINTAINERS +1 -0
@@ -20537,6 +20537,7 @@ L: [email protected] S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git F: drivers/pci/liveupdate.c +F: drivers/pci/liveupdate.h F: include/linux/kho/abi/pci.h F: include/linux/pci_liveupdate.h
drivers/pci/Kconfig +1 -1
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS config PCI_LIVEUPDATE bool "PCI Live Update Support (EXPERIMENTAL)" - depends on PCI && LIVEUPDATE + depends on PCI && LIVEUPDATE && 64BIT help Enable PCI core support for preserving PCI devices across Live Update. This, in combination with support in a device's driver,
drivers/pci/liveupdate.c +221 -2
258 lines not on this page · raw message
drivers/pci/liveupdate.h new file +26 -0
@@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* PCI Live Update support (core API) +* +* Copyright (c) 2026, Google LLC. +* David Matlack <[email protected]> +*/ +#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); +#else +static inline void pci_liveupdate_setup_device (struct pci_dev *dev) +{ +} + +static inline void pci_liveupdate_cleanup_device (struct pci_dev *dev) +{ +} +#endif + +#endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/probe.c +5 -0
@@ -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 = {
@@ -2069,6 +2070,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);
@@ -2192,6 +2195,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 ;
@@ -2490,6 +2494,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);
include/linux/pci_liveupdate.h +13 -0
@@ -17,10 +17,12 @@* struct pci_liveupdate - PCI Live Update state for a struct pci_dev * @lock: Lock used to protect members of struct pci_liveupdate. * @outgoing: State preserved for the next kernel. +* @incoming: State preserved by the previous kernel. */ struct pci_liveupdate {rwlock_t lock;struct pci_dev_ser *outgoing; +struct pci_dev_ser *incoming; };struct pci_dev ;
@@ -30,6 +32,8 @@ 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) {
@@ -48,6 +52,15 @@ static inline int pci_liveupdate_preserve(struct pci_dev *dev)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 */
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 03/11] PCI: liveupdate: Track incoming preserved PCI devices
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [High] Integer truncation in `pci_ser_xa_key()` drops the upper 16 bits of the PCI domain. - [High]…
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Integer truncation in pci_ser_xa_key() drops the upper 16 bits of the PCI domain.
- [High] Memory leak of KHO-preserved physical pages in the error path of pci_flb_retrieve().
- [High] Subsystem guide violation: pci_flb_preserve() calls a KHO serialization API without checking kho_is_enabled().
- [High] Data corruption in pci_liveupdate_unpreserve() due to decrementing ser->nr_devices without compacting the array.
- [High] Use-after-free risk because pci_flb_unpreserve() leaves dangling pointers in dev->liveupdate.outgoing.
- [High] pci_liveupdate_finish() calls pci_liveupdate_flb_put_incoming() under a write lock, risking a sleep-in-atomic bug. --
commit 46fdddc110c525ef02301643989348b7f03a10c0
Author: David Matlack <[email protected]>
PCI: liveupdate: Track incoming preserved PCI devices
This commit adds support for tracking pci devices preserved across a kexec via live update. It introduces setup and cleanup hooks for the pci core to manage the incoming live update state of these devices.
Quoted 12 lines
drivers/pci/liveupdate.c +4
-0@@ -85,6 +101,21 @@ struct pci_flb_outgoing {struct mutex lock; }; +/** +* 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.
drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 117
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+*/ +struct pci_flb_incoming { +struct pci_ser *ser; +struct xarray xa; +}; + +static unsigned long pci_ser_xa_key (u32 domain,u16 bdf) +{ +return domain <<16 | bdf; +}
Can this integer shift truncate the upper 16 bits of the pci domain?
Because domain is a u32, the shift expression evaluates as a 32-bit integer. This discards the upper 16 bits before the result is promoted to the 64-bit unsigned long return type.
If devices are in domains >= 0x10000, this will produce colliding keys, causing them to overwrite each other in the xarray.
Could this be cast before the shift, such as
((unsigned long)domain << 16) | bdf?
drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 121
Quoted 4 lines
drivers/pci/liveupdate.c +1
-0+static int pci_flb_preserve (struct liveupdate_flb_op_args *args) {struct pci_flb_outgoing *outgoing;
Does this function need to check kho_is_enabled() before proceeding?
According to the kho subsystem guidelines, all callers of serialization-side kho APIs (like kho_alloc_preserve() used later in this function) must gate their usage on kho_is_enabled().
Failing to check the enabled state can cause null pointer dereferences or silently add useless tracking state that will never be used.
[ ... ]
drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -140,13 +171,44 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
Can pci_flb_unpreserve() cause a use-after-free risk by leaving dangling pointers?
When an outgoing live update is aborted, this function frees the outgoing->ser memory via kho_unpreserve_free(). However, it does not clear the dev->liveupdate.outgoing pointers in the pci devices that were already preserved.
If a new live update is initiated and pci_liveupdate_unpreserve() is called on those devices, it will execute memset(dev_ser, 0, ...) on the dangling dev->liveupdate.outgoing pointer, corrupting memory.
Quoted 19 lines
drivers/pci/liveupdate.c +15
-1static int pci_flb_retrieve (struct liveupdate_flb_op_args *args) { - args->obj =phys_to_virt (args->data); +struct pci_flb_incoming *incoming; +int i, ret; + + incoming =kmalloc_obj (*incoming); +if (!incoming) +return -ENOMEM ; + + incoming->ser =phys_to_virt (args->data); + +xa_init (&incoming->xa); + +for (i =0 ; i < incoming->ser->max_nr_devices; i++) { +struct pci_dev_ser *dev_ser = &incoming->ser->devices[i]; +unsigned long key; +
drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 198
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+if (!dev_ser->refcount) +continue ; + + key =pci_ser_xa_key (dev_ser->domain, dev_ser->bdf); + ret =xa_err (xa_store (&incoming->xa, key, dev_ser,GFP_KERNEL )); +if (ret) { +xa_destroy (&incoming->xa); +kfree (incoming); +return ret; + }
Does this error path leak the preserved physical pages?
If xa_store() fails, this returns an error code without calling kho_restore_free(incoming->ser). Because the function returns an error and does not populate args->obj, the live update core will not call the .finish callback to clean up the memory.
[ ... ]
drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 331
Quoted 3 lines
drivers/pci/liveupdate.c +0
-0@@ -268,6 +330,163 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev) }EXPORT_SYMBOL_GPL (pci_liveupdate_unpreserve);
Can pci_liveupdate_unpreserve() corrupt the live update tracking state by leaving holes in the array?
Looking at the body of pci_liveupdate_unpreserve():
drivers/pci/liveupdate.c:pci_liveupdate_unpreserve() {
...
ser->nr_devices--;
memset(dev_ser, 0, sizeof(*dev_ser));
...
}If a device other than the most recently preserved one is unpreserved, this leaves a hole in the array and moves the allocation cursor backward. Subsequent device preservations will overwrite currently preserved, valid devices at that index.
[ ... ]
Quoted 12 lines
drivers/pci/liveupdate.c +12
-0+void pci_liveupdate_finish (struct pci_dev *dev) +{ +guard (write_lock)(&dev->liveupdate.lock); + +if (!dev->liveupdate.incoming) { +pci_warn (dev,"Cannot finish preserving an unpreserved device\n" ); +return ; + } + +pci_info (dev,"Device is finished participating in Live Update\n" ); + +/*
drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 468
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+* Drop 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. +*/ + dev->liveupdate.incoming->refcount =0 ; + dev->liveupdate.incoming =NULL ; + +/* Drop this device's reference on the incoming FLB. */ +pci_liveupdate_flb_put_incoming (); +}
Can calling pci_liveupdate_flb_put_incoming() under a write lock cause a sleep-in-atomic bug?
This function calls pci_liveupdate_flb_put_incoming() while holding dev->liveupdate.lock (an rwlock) for writing. If the put function acquires a sleepable lock internally, calling it from atomic context will trigger a bug.
This contrasts with pci_liveupdate_cleanup_device() which drops the lock first using scoped_guard().
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH v5 03/11] PCI: liveupdate: Track incoming preserved PCI devices
Quoted 47 lines · On 2026-05-14 01:05 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Integer truncation inpci_ser_xa_key()drops the upper 16 bits of the PCI domain.
- [High] Memory leak of KHO-preserved physical pages in the error path ofpci_flb_retrieve().
- [High] Subsystem guide violation:pci_flb_preserve()calls a KHO serialization API without checkingkho_is_enabled().
- [High] Data corruption inpci_liveupdate_unpreserve()due to decrementingser->nr_deviceswithout compacting the array.
- [High] Use-after-free risk becausepci_flb_unpreserve()leaves dangling pointers indev->liveupdate.outgoing.
- [High]pci_liveupdate_finish()callspci_liveupdate_flb_put_incoming()under a write lock, risking a sleep-in-atomic bug. --commit
46fdddc110c525ef02301643989348b7f03a10c0
Author: David Matlack <[email protected]>PCI: liveupdate: Track incoming preserved PCI devices
This commit adds support for tracking pci devices preserved across a kexec via live update. It introduces setup and cleanup hooks for the pci core to manage the incoming live update state of these devices.
Quoted 12 lines
drivers/pci/liveupdate.c +4
-0@@ -85,6 +101,21 @@ struct pci_flb_outgoing {struct mutex lock; }; +/** +* 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. drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 117
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+*/ +struct pci_flb_incoming { +struct pci_ser *ser; +struct xarray xa; +}; + +static unsigned long pci_ser_xa_key (u32 domain,u16 bdf) +{ +return domain <<16 | bdf; +}Can this integer shift truncate the upper 16 bits of the pci domain?
Because domain is a u32, the shift expression evaluates as a 32-bit integer. This discards the upper 16 bits before the result is promoted to the 64-bit unsigned long return type.
Quoted 5 lines
If devices are in domains >=
0x10000, this will produce colliding keys, causing them to overwrite each other in the xarray.Could this be cast before the shift, such as
((unsigned long)domain << 16) | bdf?
Ah I thought I had fixed that in v5. Will fix in v6.
Quoted 21 lines
drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 121
Quoted 4 lines
drivers/pci/liveupdate.c +1
-0+static int pci_flb_preserve (struct liveupdate_flb_op_args *args) {struct pci_flb_outgoing *outgoing;Does this function need to check
kho_is_enabled()before proceeding?According to the kho subsystem guidelines, all callers of serialization-side kho APIs (like
kho_alloc_preserve()used later in this function) must gate their usage onkho_is_enabled().Failing to check the enabled state can cause null pointer dereferences or silently add useless tracking state that will never be used.
[ ... ]
drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -140,13 +171,44 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)Can
pci_flb_unpreserve()cause a use-after-free risk by leaving dangling pointers?
Quoted 8 lines
When an outgoing live update is aborted, this function frees the
outgoing->sermemory viakho_unpreserve_free(). However, it does not clear thedev->liveupdate.outgoingpointers in the pci devices that were already preserved.If a new live update is initiated and
pci_liveupdate_unpreserve()is called on those devices, it will executememset(dev_ser, 0, ...)on the danglingdev->liveupdate.outgoingpointer, corrupting memory.
This was addressed in the patch 2 thread.
Quoted 33 lines
Quoted 20 lines
drivers/pci/liveupdate.c +15
-1static int pci_flb_retrieve (struct liveupdate_flb_op_args *args) { - args->obj =phys_to_virt (args->data); +struct pci_flb_incoming *incoming; +int i, ret; + + incoming =kmalloc_obj (*incoming); +if (!incoming) +return -ENOMEM ; + + incoming->ser =phys_to_virt (args->data); + +xa_init (&incoming->xa); + +for (i =0 ; i < incoming->ser->max_nr_devices; i++) { +struct pci_dev_ser *dev_ser = &incoming->ser->devices[i]; +unsigned long key; +drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 198
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+if (!dev_ser->refcount) +continue ; + + key =pci_ser_xa_key (dev_ser->domain, dev_ser->bdf); + ret =xa_err (xa_store (&incoming->xa, key, dev_ser,GFP_KERNEL )); +if (ret) { +xa_destroy (&incoming->xa); +kfree (incoming); +return ret; + }Does this error path leak the preserved physical pages?
Quoted 4 lines
If
xa_store()fails, this returns an error code without callingkho_restore_free(incoming->ser). Because the function returns an error and does not populateargs->obj, the live update core will not call the .finish callback to clean up the memory.
Yes if retrieve fails then the preserved struct pci_ser gets leaked. The user is likely going to reboot the system anyway since the preserved devices will not able to be retrieved, so I don't think leaking is a problem in practice. But I also don't see any reason why we have to leak it. I will free it in v6.
Quoted 19 lines
[ ... ]
drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 331
Quoted 3 lines
drivers/pci/liveupdate.c +0
-0@@ -268,6 +330,163 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev) }EXPORT_SYMBOL_GPL (pci_liveupdate_unpreserve);Can
pci_liveupdate_unpreserve()corrupt the live update tracking state by leaving holes in the array?Looking at the body of
pci_liveupdate_unpreserve():drivers/pci/liveupdate.c:pci_liveupdate_unpreserve() { ... ser->nr_devices--; memset(dev_ser, 0, sizeof(*dev_ser)); ... }
Quoted 4 lines
If a device other than the most recently preserved one is unpreserved, this leaves a hole in the array and moves the allocation cursor backward. Subsequent device preservations will overwrite currently preserved, valid devices at that index.
Leaving a hole in the array is the intention. ser->nr_devices is not a cursor, it is the number of valid (non-hole) elements of the devices array. pci_liveupdate_preserve() specifically walks through the entire array to find the first non-hole to use for the next device.
Quoted 28 lines
[ ... ]
Quoted 12 lines
drivers/pci/liveupdate.c +12
-0+void pci_liveupdate_finish (struct pci_dev *dev) +{ +guard (write_lock)(&dev->liveupdate.lock); + +if (!dev->liveupdate.incoming) { +pci_warn (dev,"Cannot finish preserving an unpreserved device\n" ); +return ; + } + +pci_info (dev,"Device is finished participating in Live Update\n" ); + +/* drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 468
Quoted 10 lines
drivers/pci/liveupdate.c +10
-0+* Drop 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. +*/ + dev->liveupdate.incoming->refcount =0 ; + dev->liveupdate.incoming =NULL ; + +/* Drop this device's reference on the incoming FLB. */ +pci_liveupdate_flb_put_incoming (); +}Can calling
pci_liveupdate_flb_put_incoming()under a write lock cause a sleep-in-atomic bug?
Quoted 6 lines
This function calls
pci_liveupdate_flb_put_incoming()while holdingdev->liveupdate.lock(an rwlock) for writing. If the put function acquires a sleepable lock internally, calling it from atomic context will trigger a bug.This contrasts with
pci_liveupdate_cleanup_device()which drops the lock first usingscoped_guard().
Ah I fixed this in a later patch in the series but forgot to also address it in this intermediate state. Will fix in v6.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
PATCH v5 4/11 PCI: liveupdate: Document driver binding responsibilities
In reply to David MatlackDocument how driver binding works during a Live Update and what the PCI core expects of drivers and users. Note that this is only a description of the current division of responsibilities. These can change in the future if we decide.
--- drivers/pci/liveupdate.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/pci/liveupdate.c +16 -0
@@ -77,6 +77,22 @@* preserved. These may be relaxed in the future: * * * The device cannot be a Virtual Function (VF). +* +* Driver Binding +* ============== +* +* In the outgoing kernel, it is the driver's responsibility to ensure that it +* does not release a device between pci_liveupdate_preserve() and +* pci_liveupdate_unpreserve(). +* +* In the incoming kernel, it is the driver's responsibility to 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. i.e. 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. */ #define pr_fmt (fmt) "PCI: liveupdate: " fmt
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
PATCH v5 5/11 PCI: liveupdate: Keep bus numbers constant during Live Update
In reply to David MatlackDuring a Live Update, preserved devices must be allowed to continue performing memory transactions so the kernel cannot change the fabric topology, including bus numbers, since that would require disabling and flushing any memory transactions first.
To keep bus numbers constant, always inherit the secondary and subordinate bus numbers assigned to bridges during scanning, instead of assigning new ones, if any PCI devices are being preserved. Note that the kernel inherits bus numbers even on bridges without any downstream endpoints that were preserved. This avoids accidentally assigning a bridge a new window that overlaps with a preserved device that is downstream of a different bridge.
If a bridge is scanned with a broken topology or has no bus numbers set during a Live Update, refuse to assign it new bus numbers and refuse to enumerate devices below it. This is a safety measure to prevent topology conflicts.
Require that CONFIG_CARDBUS is not enabled to enable CONFIG_PCI_LIVEUPDATE since inheriting bus numbers on PCI-to-CardBus bridges requires additional work but is not a priority at the moment.
--- .../admin-guide/kernel-parameters.txt | 6 +- drivers/pci/Kconfig | 2 +- drivers/pci/liveupdate.c | 60 +++++++++++++++++++ drivers/pci/liveupdate.h | 6 ++ drivers/pci/probe.c | 21 +++++-- 5 files changed, 89 insertions(+), 6 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +5 -1
@@ -5138,7 +5138,11 @@ 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 + during a Live Update, where the kernel must + inherit 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
drivers/pci/Kconfig +1 -1
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS config PCI_LIVEUPDATE bool "PCI Live Update Support (EXPERIMENTAL)" - depends on PCI && LIVEUPDATE && 64BIT + 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,
drivers/pci/liveupdate.c +60 -0
102 lines not on this page · raw message
drivers/pci/liveupdate.h +6 -0
@@ -13,6 +13,7 @@#ifdef CONFIG_PCI_LIVEUPDATE void pci_liveupdate_setup_device (struct pci_dev *dev);void pci_liveupdate_cleanup_device (struct pci_dev *dev); +bool pci_liveupdate_inherit_buses (void );#else static inline void pci_liveupdate_setup_device (struct pci_dev *dev) {
@@ -21,6 +22,11 @@ static inline void pci_liveupdate_setup_device(struct pci_dev *dev)static inline void pci_liveupdate_cleanup_device (struct pci_dev *dev) { } + +static inline bool pci_liveupdate_inherit_buses (void ) +{ +return false; +}#endif #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/probe.c +17 -4
@@ -1375,6 +1375,14 @@ bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)return true; } +static bool pci_should_assign_new_buses (void ) +{ +if (pci_liveupdate_inherit_buses ()) +return false; + +return pcibios_assign_all_busses (); +} +/* * pci_scan_bridge_extend() - Scan buses behind a bridge * @bus: Parent bus the bridge is on
@@ -1402,6 +1410,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int max, unsigned int available_buses, int pass) { + const bool assign_new_buses = pci_should_assign_new_buses();struct pci_bus *child;u32 buses;u16 bctl;
@@ -1454,8 +1463,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) && !assign_new_buses && !broken) {unsigned int cmax, buses;/*
@@ -1497,8 +1505,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 (assign_new_buses || broken)/* * Temporarily disable forwarding of the * configuration cycles on all bridges in
@@ -1512,6 +1519,12 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, goto out; } +if (pci_liveupdate_inherit_buses ()) { +pci_err (dev,"Cannot reconfigure bridge during Live Update!\n" ); +pci_err (dev,"Downstream devices will not be enumerated!\n" ); + goto out; + } +/* Clear errors */ pci_write_config_word (dev,PCI_STATUS ,0xffff );
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 05/11] PCI: liveupdate: Keep bus numbers constant during Live Update
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Bridge bus numbers are inadvertently cleared for 'broken' topologies during Live Update, modifying…
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Bridge bus numbers are inadvertently cleared for 'broken' topologies during Live Update, modifying the hardware fabric and disrupting active memory transactions. --
commit 0f468978660735d49ff81fb445992a21db06f9ce
Author: David Matlack <[email protected]>
PCI: liveupdate: Keep bus numbers constant during Live Update
During Live Update, preserved devices must be allowed to continue performing memory transactions. This commit modifies the PCI core to always inherit bridge bus numbers when any devices are preserved, avoiding topology changes that could disrupt ongoing operations.
Quoted 4 lines
drivers/pci/probe.c +0
-0
[ ... ]
drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1520
Quoted 13 lines
drivers/pci/probe.c +1
-2@@ -1497,8 +1505,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 (assign_new_buses || broken)/* * Temporarily disable forwarding of the * configuration cycles on all bridges in @@ -1512,6 +1519,12 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, goto out; }
Does this code accidentally clear the secondary and subordinate bus numbers for bridges with a "broken" configuration during a Live Update?
In the first pass (!pass), if the bridge topology is invalid (broken == 1), the code enters the if (assign_new_buses || broken) branch and executes a pci_write_config_dword() that disables configuration cycles.
Since the pci_liveupdate_inherit_buses() safety check happens after this block, the hardware fabric gets modified before the early return. Can this disrupt ongoing memory transactions for preserved downstream devices?
Quoted 9 lines
drivers/pci/probe.c +6
-0+if (pci_liveupdate_inherit_buses ()) { +pci_err (dev,"Cannot reconfigure bridge during Live Update!\n" ); +pci_err (dev,"Downstream devices will not be enumerated!\n" ); + goto out; + } +/* Clear errors */ pci_write_config_word (dev,PCI_STATUS ,0xffff );
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Re: [PATCH v5 05/11] PCI: liveupdate: Keep bus numbers constant during Live Update
Quoted 38 lines · On 2026-05-14 01:36 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Bridge bus numbers are inadvertently cleared for 'broken' topologies during Live Update, modifying the hardware fabric and disrupting active memory transactions. --commit
0f468978660735d49ff81fb445992a21db06f9ce
Author: David Matlack <[email protected]>PCI: liveupdate: Keep bus numbers constant during Live Update
During Live Update, preserved devices must be allowed to continue performing memory transactions. This commit modifies the PCI core to always inherit bridge bus numbers when any devices are preserved, avoiding topology changes that could disrupt ongoing operations.
Quoted 4 lines
drivers/pci/probe.c +0
-0[ ... ]
drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1520
Quoted 13 lines
drivers/pci/probe.c +1
-2@@ -1497,8 +1505,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 (assign_new_buses || broken)/* * Temporarily disable forwarding of the * configuration cycles on all bridges in @@ -1512,6 +1519,12 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, goto out; }Does this code accidentally clear the secondary and subordinate bus numbers for bridges with a "broken" configuration during a Live Update?
Quoted 7 lines
In the first pass (!pass), if the bridge topology is invalid (broken == 1), the code enters the if (
assign_new_buses|| broken) branch and executes apci_write_config_dword()that disables configuration cycles.Since the
pci_liveupdate_inherit_buses()safety check happens after this block, the hardware fabric gets modified before the early return. Can this disrupt ongoing memory transactions for preserved downstream devices?
No, it is on purpose, not accidental.
If the bridges bus secondary and/or subordinate bus numbers are misconfigured during a Live Update then the options are:
1. Proceed with enumeration with the misconfigured secondary/subordinate numbers.
2. Clear the misconfigured numbers and refuse to enumerate further.
3. Clear the misconfigured numbers and then assign new bus numbers.
(1) seems like a bad idea to try to proceed with a misconfigured bridge.
(3) could lead to the bus numbers assigned to preserved endpoints changing, which could lead to corruption (e.g. memory transactions being routed to the wrong devices). (2) seems like the only sane option.
Quoted 13 lines
Quoted 10 lines
drivers/pci/probe.c +6
-0+if (pci_liveupdate_inherit_buses ()) { +pci_err (dev,"Cannot reconfigure bridge during Live Update!\n" ); +pci_err (dev,"Downstream devices will not be enumerated!\n" ); + goto out; + } +/* Clear errors */ pci_write_config_word (dev,PCI_STATUS ,0xffff );Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Re: [PATCH v5 05/11] PCI: liveupdate: Keep bus numbers constant during Live Update
In reply to David MatlackQuoted 13 lines · David Matlack
...
+ * 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 always inherits
+ * the secondary and subordinate bus numbers assigned to bridges during scanning
+ * if any device is preserved. 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.
+ *
+ * If a misconfigured or unconfigured bridge is encountered during enumeration
+ * while there are preserved devices, itss secondary and subordinate bus numbers
+ * will be cleared and devices below it will not be enumerated.
s/itss/its/
Quoted 5 lines
+ * To keep things simple, inherit the secondary and subordinate bus numbers on
+ * _all_ bridges if _any_ PCI devices are preserved (i.e. even bridges without
+ * any downstream endpoints that were preserved). This avoids accidentally
+ * assigning a bridge a new window that overlaps with a preserved device that is
+ * downstream of a different bridge.
s/i.e. even/i.e. even/ (remove extra space)
PATCH v5 6/11 PCI: liveupdate: Auto-preserve upstream bridges across Live Update
In reply to David MatlackWhen a PCI device is preserved across a Live Update, all of its upstream bridges up to the root port must also be preserved. This enables the PCI core and any drivers bound to the bridges to manage bridges correctly across a Live Update.
Notably, this will be used in subsequent commits to ensure that preserved devices can continue performing memory transactions without a disruption or change in routing.
To preserve bridges, the PCI core tracks the number of downstream devices preserved under each bridge using a reference count in struct pci_dev_ser. This allows a bridge to remain preserved until all its downstream preserved devices are unpreserved or finish their participation in the Live Update.
--- drivers/pci/liveupdate.c | 241 ++++++++++++++++++++++++++++++--------- 1 file changed, 184 insertions(+), 57 deletions(-)
drivers/pci/liveupdate.c +184 -57
@@ -108,6 +108,18 @@* If a misconfigured or unconfigured bridge is encountered during enumeration * while there are preserved devices, itss secondary and subordinate bus numbers * will be cleared and devices below it will not be enumerated. +* +* 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. */ #define pr_fmt (fmt) "PCI: liveupdate: " fmt
@@ -300,41 +312,55 @@ static struct liveupdate_flb pci_liveupdate_flb = { .compatible = PCI_LUO_FLB_COMPATIBLE, }; -/** -* 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 must call -* pci_liveupdate_preserve() from their struct liveupdate_file_handler -* preserve() callback to ensure the outgoing struct pci_ser is allocated. -* -* Returns: 0 on success, <0 on failure. -*/ -int pci_liveupdate_preserve (struct pci_dev *dev) +static int pci_liveupdate_unpreserve_device (struct pci_ser *ser,struct pci_dev *dev) { -struct pci_flb_outgoing *outgoing =NULL ; -struct pci_ser *ser; -int i, ret; +struct pci_dev_ser *dev_ser; -if (dev->is_virtfn) +guard (write_lock)(&dev->liveupdate.lock); + + dev_ser = dev->liveupdate.outgoing; +if (!dev_ser) { +pci_warn (dev,"Cannot unpreserve device that is not preserved\n" );return -EINVAL ; + } - ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); -if (ret) -return ret; +if (!dev_ser->refcount) { +pci_WARN (dev,1 ,"Preserved device has a 0 refcount!\n" ); +return -EINVAL ; + } -if (!outgoing) -return -ENOENT ; +if (--dev_ser->refcount) +return 0 ; -guard (mutex)(&outgoing->lock); - ser = outgoing->ser; +pci_info (dev,"Device will no longer be preserved across next Live Update\n" ); + ser->nr_devices--; +memset (dev_ser,0 ,sizeof (*dev_ser)); + dev->liveupdate.outgoing =NULL ; +return 0 ; +} -guard (write_lock)(&dev->liveupdate.lock); +static int pci_liveupdate_preserve_device_existing (struct pci_dev *dev) +{ +if (!dev->liveupdate.outgoing->refcount) { +pci_WARN (dev,1 ,"Preserved device with 0 refcount!\n" ); +return -EINVAL ; + } -if (dev->liveupdate.outgoing) +/* +* Endpoint devices should not be preserved more than once. Bridges are +* preserved once for every downstream device that is preserved. +*/ +if (!dev->subordinate)return -EBUSY ; + dev->liveupdate.outgoing->refcount++; +return 0 ; +} + +static int pci_liveupdate_preserve_device_new (struct pci_ser *ser,struct pci_dev *dev) +{ +int i; +if (ser->nr_devices == ser->max_nr_devices)return -ENOSPC ;
@@ -363,8 +389,82 @@ int pci_liveupdate_preserve(struct pci_dev *dev)return -ENOSPC ; } + +static int pci_liveupdate_preserve_device (struct pci_ser *ser,struct pci_dev *dev) +{ +guard (write_lock)(&dev->liveupdate.lock); + +if (dev->liveupdate.outgoing) +return pci_liveupdate_preserve_device_existing (dev); +else +return pci_liveupdate_preserve_device_new (ser, dev); +} + +static int pci_liveupdate_preserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +int ret; + +if (!dev) +return 0 ; + + ret =pci_liveupdate_preserve_device (ser, dev); +if (ret) +return ret; + + ret =pci_liveupdate_preserve_path (ser, dev->bus->self); +if (ret) { +pci_liveupdate_unpreserve_device (ser, dev); +return ret; + } + +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 must call +* pci_liveupdate_preserve() from their struct liveupdate_file_handler +* preserve() callback to ensure the outgoing struct pci_ser is allocated. +* +* 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 ; +int ret; + +if (dev->is_virtfn) +return -EINVAL ; + + ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing); +if (ret) +return ret; + +if (!outgoing) +return -ENOENT ; + +guard (mutex)(&outgoing->lock); +return pci_liveupdate_preserve_path (outgoing->ser, dev); +}EXPORT_SYMBOL_GPL (pci_liveupdate_preserve); +static void pci_liveupdate_unpreserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +if (!dev) +return ; + +if (pci_liveupdate_unpreserve_device (ser, dev)) +return ; + +pci_liveupdate_unpreserve_path (ser, dev->bus->self); +} +/** * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device * @dev: The PCI device to preserve.
@@ -373,12 +473,13 @@ EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);* longer be preserved across the next Live Update. Drivers must call * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler * unpreserve() callback to ensure the outgoing struct pci_ser is allocated. +* +* pci_liveupdate_unpreserve() automatically unpreserves all bridges upstream of +* @dev. */ void pci_liveupdate_unpreserve (struct pci_dev *dev) {struct pci_flb_outgoing *outgoing =NULL ; -struct pci_dev_ser *dev_ser; -struct pci_ser *ser;int ret; ret =liveupdate_flb_get_outgoing (&pci_liveupdate_flb, (void **)&outgoing);
@@ -389,20 +490,7 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev) }guard (mutex)(&outgoing->lock); - ser = outgoing->ser; - -guard (write_lock)(&dev->liveupdate.lock); - - dev_ser = dev->liveupdate.outgoing; -if (!dev_ser) { -pci_warn (dev,"Cannot unpreserve device that is not preserved\n" ); -return ; - } - -pci_info (dev,"Device will no longer be preserved across next Live Update\n" ); - ser->nr_devices--; -memset (dev_ser,0 ,sizeof (*dev_ser)); - dev->liveupdate.outgoing =NULL ; +pci_liveupdate_unpreserve_path (outgoing->ser, dev); }EXPORT_SYMBOL_GPL (pci_liveupdate_unpreserve);
@@ -510,6 +598,55 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)pci_liveupdate_flb_put_incoming (); } +static int __pci_liveupdate_finish_device (struct pci_dev *dev) +{ +guard (write_lock)(&dev->liveupdate.lock); + +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 -EBUSY ; + +pci_info (dev,"Device is finished participating in Live Update\n" ); + dev->liveupdate.incoming =NULL ; +return 0 ; +} + +static int pci_liveupdate_finish_device (struct pci_dev *dev) +{ +int ret; + +/* +* If ret == -EBUSY the device is still preserved due to remaining +* references. Return 0 up to the caller to indicate it should proceed +* to finish preserving upstream devices but do not drop the device's +* reference on the incoming FLB below. +*/ + ret =__pci_liveupdate_finish_device (dev); +if (ret) +return ret == -EBUSY ?0 : ret; + +/* +* Once the device's refcount reaches zero drop the device's reference +* on the incoming FLB so it can be freed. +*/ +pci_liveupdate_flb_put_incoming (); +return 0 ; +} +/** * pci_liveupdate_finish() - Finish the preservation of a PCI device across Live Update * @dev: The PCI device
@@ -519,28 +656,18 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)* 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) { -guard (write_lock)(&dev->liveupdate.lock); - -if (!dev->liveupdate.incoming) { -pci_warn (dev,"Cannot finish preserving an unpreserved device\n" ); +if (!dev)return ; - } - -pci_info (dev,"Device is finished participating in Live Update\n" ); -/* -* Drop 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. -*/ - dev->liveupdate.incoming->refcount =0 ; - dev->liveupdate.incoming =NULL ; +if (pci_liveupdate_finish_device (dev)) +return ; -/* Drop this device's reference on the incoming FLB. */ -pci_liveupdate_flb_put_incoming (); +pci_liveupdate_finish (dev->bus->self); }EXPORT_SYMBOL_GPL (pci_liveupdate_finish);
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 06/11] PCI: liveupdate: Auto-preserve upstream bridges across Live Update
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Kernel stack overflow via deep recursion in `pci_liveupdate_preserve_path()`. --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Kernel stack overflow via deep recursion in pci_liveupdate_preserve_path(). --
commit 9dcae64241d06ee18edeb26c25b9a7b263aa05a4
Author: David Matlack <[email protected]>
PCI: liveupdate: Auto-preserve upstream bridges across Live Update
This commit introduces tracking to auto-preserve upstream PCI bridges when a device is preserved across a Live Update. It adds functions to preserve, unpreserve, and finish the preservation of devices and recursively applies these to upstream bridges up to the root port.
Quoted 4 lines
drivers/pci/liveupdate.c +0
-0
[ ... ]
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -363,8 +389,82 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
[ ... ]
drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 414
Quoted 12 lines
drivers/pci/liveupdate.c +12
-0+static int pci_liveupdate_preserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +int ret; + +if (!dev) +return 0 ; + + ret =pci_liveupdate_preserve_device (ser, dev); +if (ret) +return ret; + + ret =pci_liveupdate_preserve_path (ser, dev->bus->self);
Can this recursion cause a kernel stack overflow?
The PCI specification allows up to 256 nested buses in a single domain, which can occur with deep Thunderbolt or USB4 topologies.
Because the recursive call to pci_liveupdate_preserve_path() is followed by a potential call to pci_liveupdate_unpreserve_device(), it cannot be optimized as a tail call. This forces a new stack frame for every level of the PCI hierarchy, which could overflow the standard kernel stack, especially with KASAN enabled, and cause a crash.
Could this be rewritten to use an iterative approach instead?
Quoted 7 lines
drivers/pci/liveupdate.c +7
-0+if (ret) { +pci_liveupdate_unpreserve_device (ser, dev); +return ret; + } + +return 0 ; +}
[ ... ]
drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 465
Quoted 9 lines
drivers/pci/liveupdate.c +9
-0+static void pci_liveupdate_unpreserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +if (!dev) +return ; + +if (pci_liveupdate_unpreserve_device (ser, dev)) +return ; + +pci_liveupdate_unpreserve_path (ser, dev->bus->self);
Does this code have the same recursion issue as pci_liveupdate_preserve_path() and require an iterative approach?
Quoted 1 line
+}
[ ... ]
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -519,28 +656,18 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
[ ... ]
Quoted 15 lines
void
pci_liveupdate_finish(struct pci_dev *dev)
{drivers/pci/liveupdate.c +1
-10-guard (write_lock)(&dev->liveupdate.lock); - -if (!dev->liveupdate.incoming) { -pci_warn (dev,"Cannot finish preserving an unpreserved device\n" ); +if (!dev)return ; - } - -pci_info (dev,"Device is finished participating in Live Update\n" ); -/* -* Drop the refcount so this device does not get treated as an incoming -* device again, e.g. in case pci_liveupdate_setup_device() gets called
drivers/pci/liveupdate.c void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 670
Quoted 10 lines
drivers/pci/liveupdate.c +3
-6-* again because the device is hot-plugged. -*/ - dev->liveupdate.incoming->refcount =0 ; - dev->liveupdate.incoming =NULL ; +if (pci_liveupdate_finish_device (dev)) +return ; -/* Drop this device's reference on the incoming FLB. */ -pci_liveupdate_flb_put_incoming (); +pci_liveupdate_finish (dev->bus->self);
Does this code also have the same recursion issue?
Quoted 1 line
}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
Re: [PATCH v5 06/11] PCI: liveupdate: Auto-preserve upstream bridges across Live Update
Quoted 43 lines · On 2026-05-14 02:05 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Kernel stack overflow via deep recursion inpci_liveupdate_preserve_path(). --commit
9dcae64241d06ee18edeb26c25b9a7b263aa05a4
Author: David Matlack <[email protected]>PCI: liveupdate: Auto-preserve upstream bridges across Live Update
This commit introduces tracking to auto-preserve upstream PCI bridges when a device is preserved across a Live Update. It adds functions to preserve, unpreserve, and finish the preservation of devices and recursively applies these to upstream bridges up to the root port.
Quoted 4 lines
drivers/pci/liveupdate.c +0
-0[ ... ]
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -363,8 +389,82 @@ int pci_liveupdate_preserve(struct pci_dev *dev)[ ... ]
drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 414
Quoted 12 lines
drivers/pci/liveupdate.c +12
-0+static int pci_liveupdate_preserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +int ret; + +if (!dev) +return 0 ; + + ret =pci_liveupdate_preserve_device (ser, dev); +if (ret) +return ret; + + ret =pci_liveupdate_preserve_path (ser, dev->bus->self);Can this recursion cause a kernel stack overflow?
The PCI specification allows up to 256 nested buses in a single domain, which can occur with deep Thunderbolt or USB4 topologies.
Quoted 7 lines
Because the recursive call to
pci_liveupdate_preserve_path()is followed by a potential call topci_liveupdate_unpreserve_device(), it cannot be optimized as a tail call. This forces a new stack frame for every level of the PCI hierarchy, which could overflow the standard kernel stack, especially with KASAN enabled, and cause a crash.Could this be rewritten to use an iterative approach instead?
I used recursion to simplify the code and based on the pre-existing examples of using recursion in the PCI core, e.g. pci_scan_bridge_extend(). But I will take a look at an iterative approach for v6.
Quoted 11 lines
Quoted 8 lines
drivers/pci/liveupdate.c +7
-0+if (ret) { +pci_liveupdate_unpreserve_device (ser, dev); +return ret; + } + +return 0 ; +}[ ... ]
Quoted 12 lines
drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 465
Quoted 9 lines
drivers/pci/liveupdate.c +9
-0+static void pci_liveupdate_unpreserve_path (struct pci_ser *ser,struct pci_dev *dev) +{ +if (!dev) +return ; + +if (pci_liveupdate_unpreserve_device (ser, dev)) +return ; + +pci_liveupdate_unpreserve_path (ser, dev->bus->self);Does this code have the same recursion issue as
pci_liveupdate_preserve_path()and require an iterative approach?
See above.
Quoted 31 lines
Quoted 2 lines
+}
[ ... ]
Quoted 1 line
drivers/pci/liveupdate.c +0
-0@@ -519,28 +656,18 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)[ ... ]
Quoted 22 lines
void
pci_liveupdate_finish(struct pci_dev *dev)
{drivers/pci/liveupdate.c +3
-14-guard (write_lock)(&dev->liveupdate.lock); - -if (!dev->liveupdate.incoming) { -pci_warn (dev,"Cannot finish preserving an unpreserved device\n" ); +if (!dev)return ; - } - -pci_info (dev,"Device is finished participating in Live Update\n" ); -/* -* Drop 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. -*/ - dev->liveupdate.incoming->refcount =0 ; - dev->liveupdate.incoming =NULL ; +if (pci_liveupdate_finish_device (dev)) +return ;
Quoted 5 lines
drivers/pci/liveupdate.c void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 670
Quoted 3 lines
drivers/pci/liveupdate.c +1
-2-/* Drop this device's reference on the incoming FLB. */ -pci_liveupdate_flb_put_incoming (); +pci_liveupdate_finish (dev->bus->self);Does this code also have the same recursion issue?
See above.
Quoted 5 lines
Quoted 2 lines
}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
PATCH v5 7/11 PCI: liveupdate: Inherit ACS flags in incoming preserved devices
In reply to David MatlackInherit Access Control Services (ACS) flags on all incoming preserved devices (endpoints and upstream bridges) during a Live Update.
Inheriting ACS flags avoids changing routing rules while memory transactions are in flight from preserved devices. This is also strictly necessary to ensure that IOMMU group assignments do not change across a Live Update for preserved devices, as changing ACS configurations can split or merge IOMMU groups.
Cache the inherited ACS controls established by the previous kernel in struct pci_dev so that ACS controls do not change after a reset (pci_restore_state() calls pci_enable_acs()).
--- drivers/pci/liveupdate.c | 49 ++++++++++++++++++++++++++++++++++ drivers/pci/liveupdate.h | 11 ++++++++ drivers/pci/pci.c | 5 ++++ include/linux/pci_liveupdate.h | 6 +++++ 4 files changed, 71 insertions(+)
drivers/pci/liveupdate.c +49 -0
@@ -120,6 +120,18 @@* 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. +* +* Handling Preserved Devices +* ========================== +* +* The PCI core treats preserved devices differently than non-preserved devices. +* This section enumerates those differences. +* +* * The PCI core inherits all ACS flags 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 inherited ACS flags for the lifetime of its +* struct pci_dev (i.e. even after pci_liveupdate_finish()). */ #define pr_fmt (fmt) "PCI: liveupdate: " fmt
@@ -361,6 +373,16 @@ static int pci_liveupdate_preserve_device_new(struct pci_ser *ser, struct pci_de {int i; +/* +* Do not preserve a devices that rely on device-specific ACS +* equivalents (for now) since that would complicate keeping ACS +* flags constant across Live Update. +*/ +if (dev->dev_flags &PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ) { +pci_warn (dev,"Refusing to preserve device that relies on ACS quirks\n" ); +return -EINVAL ; + } +if (ser->nr_devices == ser->max_nr_devices)return -ENOSPC ;
@@ -571,6 +593,7 @@ void pci_liveupdate_setup_device(struct pci_dev *dev)pci_info (dev,"Device was preserved by previous kernel across Live Update\n" );guard (write_lock)(&dev->liveupdate.lock); dev->liveupdate.incoming = dev_ser; + dev->liveupdate.was_preserved = true;/* * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
@@ -671,6 +694,32 @@ void pci_liveupdate_finish(struct pci_dev *dev) }EXPORT_SYMBOL_GPL (pci_liveupdate_finish); +void pci_liveupdate_init_acs (struct pci_dev *dev) +{ +guard (read_lock)(&dev->liveupdate.lock); + +if (!dev->acs_cap || !dev->liveupdate.incoming) +return ; + +pci_read_config_word (dev, dev->acs_cap +PCI_ACS_CTRL , &dev->liveupdate.acs_ctrl); +} + +bool pci_liveupdate_inherit_acs (struct pci_dev *dev) +{ +guard (read_lock)(&dev->liveupdate.lock); + +/* +* Use liveupdate.was_preserved instead of liveupdate.incoming since the +* device's ACS controls should not change even after the device is +* finished participating in the Live Update. +*/ +if (!dev->acs_cap || !dev->liveupdate.was_preserved) +return false; + +pci_write_config_word (dev, dev->acs_cap +PCI_ACS_CTRL , dev->liveupdate.acs_ctrl); +return true; +} +/** * pci_liveupdate_is_incoming() - Check if a device is incoming preserved * @dev: The PCI device to check
drivers/pci/liveupdate.h +11 -0
@@ -14,6 +14,8 @@void pci_liveupdate_setup_device (struct pci_dev *dev);void pci_liveupdate_cleanup_device (struct pci_dev *dev);bool pci_liveupdate_inherit_buses (void ); +void pci_liveupdate_init_acs (struct pci_dev *dev); +bool pci_liveupdate_inherit_acs (struct pci_dev *dev);#else static inline void pci_liveupdate_setup_device (struct pci_dev *dev) {
@@ -27,6 +29,15 @@ static inline bool pci_liveupdate_inherit_buses(void) {return false; } + +static inline void pci_liveupdate_init_acs (struct pci_dev *dev) +{ +} + +static inline bool pci_liveupdate_inherit_acs (struct pci_dev *dev) +{ +return false; +}#endif #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +5 -0
@@ -33,6 +33,7 @@#include <asm/dma.h> #include <linux/aer.h> #include <linux/bitfield.h> +#include "liveupdate.h" #include "pci.h" DEFINE_MUTEX (pci_slot_mutex);
@@ -1017,6 +1018,9 @@ void pci_enable_acs(struct pci_dev *dev)bool enable_acs = false;int pos; +if (pci_liveupdate_inherit_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))
@@ -3657,6 +3661,7 @@ void pci_acs_init(struct pci_dev *dev)pci_read_config_word (dev, pos +PCI_ACS_CAP , &dev->acs_capabilities);pci_disable_broken_acs_cap (dev); +pci_liveupdate_init_acs (dev); }/**
include/linux/pci_liveupdate.h +6 -0
@@ -18,11 +18,17 @@* @lock: Lock used to protect members of struct pci_liveupdate. * @outgoing: State preserved for the next kernel. * @incoming: State preserved by the previous kernel. +* @acs_ctrl: ACS features established by the previous kernel. +* @was_preserved: True if this struct pci_dev was preserved by the previous +* kernel. Unlike @incoming, this field is not cleared after +* the device is finished participating in Live Update. */ struct pci_liveupdate {rwlock_t lock;struct pci_dev_ser *outgoing;struct pci_dev_ser *incoming; +u16 acs_ctrl; +unsigned int was_preserved:1 ; };struct pci_dev ;
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 07/11] PCI: liveupdate: Inherit ACS flags in incoming preserved devices
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Modifying pci_dev state while holding only a read lock in pci_liveupdate_init_acs(). - [High]…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Modifying pci_dev state while holding only a read lock in pci_liveupdate_init_acs().
- [High] Intel SPT PCH root ports bypass the ACS quirk rejection check, causing their custom ACS control registers to be permanently lost on the new kernel. --
commit 9befbb904977419650ee202e0854fc1452e0a9de
Author: David Matlack <[email protected]>
PCI: liveupdate: Inherit ACS flags in incoming preserved devices
This commit updates the live update process to inherit Access Control Services (ACS) flags on incoming preserved devices. It caches the inherited ACS controls established by the previous kernel so they do not change after a reset.
Quoted 4 lines
drivers/pci/liveupdate.c +0
-0
[ ... ]
drivers/pci/liveupdate.c static int pci_liveupdate_preserve_device_new(struct pci_ser *ser, struct pci_de · line 384
Quoted 13 lines
drivers/pci/liveupdate.c +9
-0@@ -361,6 +373,16 @@ static int pci_liveupdate_preserve_device_new(struct pci_ser *ser, struct pci_de {int i; +/* +* Do not preserve a devices that rely on device-specific ACS +* equivalents (for now) since that would complicate keeping ACS +* flags constant across Live Update. +*/ +if (dev->dev_flags &PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ) { +pci_warn (dev,"Refusing to preserve device that relies on ACS quirks\n" ); +return -EINVAL ; + }
Can Intel SPT PCH root ports bypass this check?
Looking at pci_quirk_enable_intel_spt_pch_acs(), it does not appear to set the PCI_DEV_FLAGS_ACS_ENABLED_QUIRK flag.
If these ports bypass the check and are preserved, pci_liveupdate_init_acs() will only cache the standard PCI_ACS_CTRL register, ignoring the device-specific INTEL_SPT_ACS_CTRL register.
When the new kernel calls pci_enable_acs(), pci_liveupdate_inherit_acs() will restore the standard register and return true, skipping the pci_dev_specific_enable_acs() path entirely.
Will this cause a regression where the custom ACS configuration is lost?
[ ... ]
drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 705
Quoted 13 lines
drivers/pci/liveupdate.c +9
-0@@ -671,6 +694,32 @@ void pci_liveupdate_finish(struct pci_dev *dev) }EXPORT_SYMBOL_GPL (pci_liveupdate_finish); +void pci_liveupdate_init_acs (struct pci_dev *dev) +{ +guard (read_lock)(&dev->liveupdate.lock); + +if (!dev->acs_cap || !dev->liveupdate.incoming) +return ; + +pci_read_config_word (dev, dev->acs_cap +PCI_ACS_CTRL , &dev->liveupdate.acs_ctrl); +}
Does this mutate dev->liveupdate.acs_ctrl while holding a read lock?
Since pci_read_config_word() writes the read value into the acs_ctrl pointer, should this use write_lock to avoid a data race regression?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
Re: [PATCH v5 07/11] PCI: liveupdate: Inherit ACS flags in incoming preserved devices
Quoted 43 lines · On 2026-05-14 02:37 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Modifyingpci_devstate while holding only a read lock inpci_liveupdate_init_acs().
- [High] Intel SPT PCH root ports bypass the ACS quirk rejection check, causing their custom ACS control registers to be permanently lost on the new kernel. --commit
9befbb904977419650ee202e0854fc1452e0a9de
Author: David Matlack <[email protected]>PCI: liveupdate: Inherit ACS flags in incoming preserved devices
This commit updates the live update process to inherit Access Control Services (ACS) flags on incoming preserved devices. It caches the inherited ACS controls established by the previous kernel so they do not change after a reset.
Quoted 4 lines
drivers/pci/liveupdate.c +0
-0[ ... ]
drivers/pci/liveupdate.c static int pci_liveupdate_preserve_device_new(struct pci_ser *ser, struct pci_de · line 384
Quoted 13 lines
drivers/pci/liveupdate.c +9
-0@@ -361,6 +373,16 @@ static int pci_liveupdate_preserve_device_new(struct pci_ser *ser, struct pci_de {int i; +/* +* Do not preserve a devices that rely on device-specific ACS +* equivalents (for now) since that would complicate keeping ACS +* flags constant across Live Update. +*/ +if (dev->dev_flags &PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ) { +pci_warn (dev,"Refusing to preserve device that relies on ACS quirks\n" ); +return -EINVAL ; + }Can Intel SPT PCH root ports bypass this check?
Looking at
pci_quirk_enable_intel_spt_pch_acs(), it does not appear to set thePCI_DEV_FLAGS_ACS_ENABLED_QUIRKflag.If these ports bypass the check and are preserved,
pci_liveupdate_init_acs()will only cache the standardPCI_ACS_CTRLregister, ignoring the device-specificINTEL_SPT_ACS_CTRLregister.
Quoted 5 lines
When the new kernel calls
pci_enable_acs(),pci_liveupdate_inherit_acs()will restore the standard register and return true, skipping thepci_dev_specific_enable_acs()path entirely.Will this cause a regression where the custom ACS configuration is lost?
Yes looks like it, I will account for Intel SPT PCH root ports in v6.
Quoted 18 lines
[ ... ]
drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 705
Quoted 13 lines
drivers/pci/liveupdate.c +9
-0@@ -671,6 +694,32 @@ void pci_liveupdate_finish(struct pci_dev *dev) }EXPORT_SYMBOL_GPL (pci_liveupdate_finish); +void pci_liveupdate_init_acs (struct pci_dev *dev) +{ +guard (read_lock)(&dev->liveupdate.lock); + +if (!dev->acs_cap || !dev->liveupdate.incoming) +return ; + +pci_read_config_word (dev, dev->acs_cap +PCI_ACS_CTRL , &dev->liveupdate.acs_ctrl); +}Does this mutate
dev->liveupdate.acs_ctrlwhile holding a read lock?
Quoted 2 lines
Since
pci_read_config_word()writes the read value into theacs_ctrlpointer, should this usewrite_lockto avoid a data race regression?
Yes, will fix.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
PATCH v5 8/11 PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges
In reply to David MatlackInherit the ARI Forwarding Enable on preserved bridges and update pci_dev->ari_enabled accordingly during a Live Update. This ensures that the preserved devices on the bridge's secondary bus can be identified with the same expanded 8-bit function number after a Live Update.
--- drivers/pci/liveupdate.c | 18 ++++++++++++++++++ drivers/pci/liveupdate.h | 6 ++++++ drivers/pci/pci.c | 8 +++++++- 3 files changed, 31 insertions(+), 1 deletion(-)
drivers/pci/liveupdate.c +18 -0
@@ -132,6 +132,10 @@* way after Live Update and ensures that IOMMU groups do not change. Note * that a device will use its inherited ACS flags for the lifetime of its * struct pci_dev (i.e. even after pci_liveupdate_finish()). +* +* * The PCI core inherits 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. */ #define pr_fmt (fmt) "PCI: liveupdate: " fmt
@@ -720,6 +724,20 @@ bool pci_liveupdate_inherit_acs(struct pci_dev *dev)return true; } +bool pci_liveupdate_inherit_ari (struct pci_dev *dev) +{ +u16 val; + +guard (read_lock)(&dev->liveupdate.lock); + +if (!dev->liveupdate.incoming) +return false; + +pcie_capability_read_word (dev,PCI_EXP_DEVCTL2 , &val); + dev->ari_enabled = !!(val &PCI_EXP_DEVCTL2_ARI ); +return true; +} +/** * pci_liveupdate_is_incoming() - Check if a device is incoming preserved * @dev: The PCI device to check
drivers/pci/liveupdate.h +6 -0
@@ -16,6 +16,7 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev);bool pci_liveupdate_inherit_buses (void );void pci_liveupdate_init_acs (struct pci_dev *dev);bool pci_liveupdate_inherit_acs (struct pci_dev *dev); +bool pci_liveupdate_inherit_ari (struct pci_dev *dev);#else static inline void pci_liveupdate_setup_device (struct pci_dev *dev) {
@@ -38,6 +39,11 @@ static inline bool pci_liveupdate_inherit_acs(struct pci_dev *dev) {return false; } + +static inline bool pci_liveupdate_inherit_ari (struct pci_dev *dev) +{ +return false; +}#endif #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +7 -1
@@ -3495,7 +3495,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;
@@ -3506,6 +3506,12 @@ void pci_configure_ari(struct pci_dev *dev)if (!(cap &PCI_EXP_DEVCAP2_ARI ))return ; +if (pci_liveupdate_inherit_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 );
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
PATCH v5 9/11 PCI: liveupdate: Freeze preservation status during shutdown
In reply to David MatlackFreeze a device's outgoing preservation status (preserved or not preserved) during shutdown. This enables the PCI core and drivers to safely make decisions based on the device's preservation status during shutdown.
Note that pci_liveupdate_freeze() is triggered by the PCI core rather than from drivers participating in Live Update so that all devices can have their status frozen (i.e. prevent non-preserved devices from getting preserved late).
--- drivers/pci/liveupdate.c | 16 ++++++++++++++++ drivers/pci/liveupdate.h | 5 +++++ drivers/pci/pci-driver.c | 2 ++ include/linux/pci_liveupdate.h | 3 +++ 4 files changed, 26 insertions(+)
drivers/pci/liveupdate.c +16 -0
@@ -334,6 +334,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_devguard (write_lock)(&dev->liveupdate.lock); +if (dev->liveupdate.frozen) { +pci_WARN (dev,1 ,"Cannot unpreserve device after it is frozen!\n" ); +return -EINVAL ; + } + dev_ser = dev->liveupdate.outgoing;if (!dev_ser) {pci_warn (dev,"Cannot unpreserve device that is not preserved\n" );
@@ -420,6 +425,11 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d {guard (write_lock)(&dev->liveupdate.lock); +if (dev->liveupdate.frozen) { +pci_WARN (dev,1 ,"Cannot preserve device after it is frozen!\n" ); +return -EINVAL ; + } +if (dev->liveupdate.outgoing)return pci_liveupdate_preserve_device_existing (dev);else
@@ -625,6 +635,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)pci_liveupdate_flb_put_incoming (); } +void pci_liveupdate_freeze (struct pci_dev *dev) +{ +guard (write_lock)(&dev->liveupdate.lock); + dev->liveupdate.frozen =1 ; +} +static int __pci_liveupdate_finish_device (struct pci_dev *dev) {guard (write_lock)(&dev->liveupdate.lock);
drivers/pci/liveupdate.h +5 -0
@@ -13,6 +13,7 @@#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_inherit_buses (void );void pci_liveupdate_init_acs (struct pci_dev *dev);bool pci_liveupdate_inherit_acs (struct pci_dev *dev);
@@ -26,6 +27,10 @@ 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_inherit_buses (void ) {return false;
drivers/pci/pci-driver.c +2 -0
@@ -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"
@@ -536,6 +537,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)
include/linux/pci_liveupdate.h +3 -0
@@ -22,6 +22,8 @@* @was_preserved: True if this struct pci_dev was preserved by the previous * kernel. Unlike @incoming, this field is not cleared after * the device is finished participating in Live Update. +* @frozen: True if the outgoing preservation status of this device is frozen +* and thus cannot be changed. */ struct pci_liveupdate {rwlock_t lock;
@@ -29,6 +31,7 @@ struct pci_liveupdate {struct pci_dev_ser *incoming;u16 acs_ctrl; unsigned int was_preserved:1; + unsigned int frozen:1; };struct pci_dev ;
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
[email protected]
AI reviewRe: [PATCH v5 09/11] PCI: liveupdate: Freeze preservation status during shutdown
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Preservation rollback fails if the device is concurrently frozen, breaking bridge hierarchy…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Preservation rollback fails if the device is concurrently frozen, breaking bridge hierarchy invariants.
- [Low] Syntax error in the !CONFIG_PCI_LIVEUPDATE stub for pci_liveupdate_freeze().
--
commit f49768e50767ced554fd83ab7e5016423c3d87a2
Author: David Matlack <[email protected]>
PCI: liveupdate: Freeze preservation status during shutdown
Freeze a device's outgoing preservation status (preserved or not preserved) during shutdown. This enables the PCI core and drivers to safely make decisions based on the device's preservation status during shutdown.
drivers/pci/liveupdate.c static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev · line 340
Quoted 12 lines
drivers/pci/liveupdate.c +4
-0@@ -334,6 +334,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_devguard (write_lock)(&dev->liveupdate.lock); +if (dev->liveupdate.frozen) { +pci_WARN (dev,1 ,"Cannot unpreserve device after it is frozen!\n" ); +return -EINVAL ; + }
During preservation rollback, can this check leave a child device preserved while its parent is not?
If pci_liveupdate_preserve_path() successfully preserves a child device, but fails to preserve the parent bridge, it attempts to roll back by calling pci_liveupdate_unpreserve_device() on the child.
If a concurrent system shutdown calls pci_liveupdate_freeze() on the child device before this rollback occurs, this check will return -EINVAL and fail the rollback.
Does this result in a broken topology being passed to the incoming kernel?
[ ... ]
Quoted 4 lines
drivers/pci/liveupdate.h +0
-0
[ ... ]
drivers/pci/liveupdate.h static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 32
Quoted 7 lines
drivers/pci/liveupdate.h +3
-0@@ -26,6 +27,10 @@ static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) { } +static inline void pci_liveupdate_freeze (struct pci_dev *dev); +{ +}
Does this stray semicolon after the function signature cause a compilation error for kernels built without CONFIG_PCI_LIVEUPDATE?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
Re: [PATCH v5 09/11] PCI: liveupdate: Freeze preservation status during shutdown
Quoted 34 lines · On 2026-05-14 03:14 AM, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Preservation rollback fails if the device is concurrently frozen, breaking bridge hierarchy invariants.
- [Low] Syntax error in the !CONFIG_PCI_LIVEUPDATE stub forpci_liveupdate_freeze().
--commit
f49768e50767ced554fd83ab7e5016423c3d87a2
Author: David Matlack <[email protected]>PCI: liveupdate: Freeze preservation status during shutdown
Freeze a device's outgoing preservation status (preserved or not preserved) during shutdown. This enables the PCI core and drivers to safely make decisions based on the device's preservation status during shutdown.
drivers/pci/liveupdate.c static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev · line 340
Quoted 12 lines
drivers/pci/liveupdate.c +4
-0@@ -334,6 +334,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_devguard (write_lock)(&dev->liveupdate.lock); +if (dev->liveupdate.frozen) { +pci_WARN (dev,1 ,"Cannot unpreserve device after it is frozen!\n" ); +return -EINVAL ; + }During preservation rollback, can this check leave a child device preserved while its parent is not?
If
pci_liveupdate_preserve_path()successfully preserves a child device, but fails to preserve the parent bridge, it attempts to roll back by callingpci_liveupdate_unpreserve_device()on the child.
Quoted 5 lines
If a concurrent system shutdown calls
pci_liveupdate_freeze()on the child device before this rollback occurs, this check will return-EINVALand fail the rollback.Does this result in a broken topology being passed to the incoming kernel?
Hm yes that race does look possible. Will fix in v6.
Quoted 12 lines
[ ... ]
Quoted 4 lines
drivers/pci/liveupdate.h +0
-0[ ... ]
Quoted 4 lines
drivers/pci/liveupdate.h +0
-0@@ -26,6 +27,10 @@ static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) { }
Quoted 6 lines
drivers/pci/liveupdate.h static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 32
Quoted 3 lines
drivers/pci/liveupdate.h +3
-0+static inline void pci_liveupdate_freeze (struct pci_dev *dev); +{ +}Does this stray semicolon after the function signature cause a compilation error for kernels built without
CONFIG_PCI_LIVEUPDATE?
Yup, Mike Rapoport and kernel-test-robot also flagged this off-list. Will fix in v6.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
PATCH v5 10/11 PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec
In reply to David MatlackDo not disable bus mastering on outgoing preserved devices during pci_device_shutdown() for kexec.
Preserved devices must be allowed to perform memory transactions during a Live Update to minimize downtime and ensure continuous operation. Clearing the bus mastering bit would prevent these devices from issuing any memory requests while the new kernel boots.
Because bridges upstream of preserved endpoint devices are also automatically preserved, this change also avoids clearing bus mastering on them. This is critical because clearing bus mastering on an upstream bridge prevents the bridge from forwarding memory requests upstream (i.e. it would prevent the endpoint device from accessing system RAM and doing peer-to-peer transactions with devices not downstream of the bridge).
--- drivers/pci/liveupdate.c | 4 ++++ drivers/pci/liveupdate.h | 12 ++++++++++++ drivers/pci/pci-driver.c | 31 ++++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 9 deletions(-)
drivers/pci/liveupdate.c +4 -0
@@ -136,6 +136,10 @@* * The PCI core inherits 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
drivers/pci/liveupdate.h +12 -0
@@ -18,6 +18,13 @@ bool pci_liveupdate_inherit_buses(void);void pci_liveupdate_init_acs (struct pci_dev *dev);bool pci_liveupdate_inherit_acs (struct pci_dev *dev);bool pci_liveupdate_inherit_ari (struct pci_dev *dev); + +static inline bool pci_liveupdate_is_outgoing (struct pci_dev *dev) +{ +guard (read_lock)(&dev->liveupdate.lock); +pci_WARN_ONCE (dev, !dev->liveupdate.frozen,"Preservation status is unstable!\n" ); +return dev->liveupdate.outgoing; +}#else static inline void pci_liveupdate_setup_device (struct pci_dev *dev) {
@@ -49,6 +56,11 @@ static inline bool pci_liveupdate_inherit_ari(struct pci_dev *dev) {return false; } + +static inline bool pci_liveupdate_is_outgoing (struct pci_dev *dev) +{ +return false; +}#endif #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci-driver.c +22 -9
@@ -532,6 +532,27 @@ static void pci_device_remove(struct device *dev)pci_dev_put (pci_dev); } +/* +* Disable bus mastering on the device so that it does not perform memory +* transactions during kexec. +* +* Don't touch devices that are being preserved across kexec for Live +* Update or that are in D3cold or unknown states. +*/ +static void pci_clear_master_for_shutdown (struct pci_dev *pci_dev) +{ +if (!kexec_in_progress) +return ; + +if (pci_liveupdate_is_outgoing (pci_dev)) +return ; + +if (pci_dev->current_state > PCI_D3hot) +return ; + +pci_clear_master (pci_dev); +} +static void pci_device_shutdown (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);
@@ -543,15 +564,7 @@ static void pci_device_shutdown(struct device *dev)if (drv && drv->shutdown) drv->shutdown (pci_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. -* 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)) -pci_clear_master (pci_dev); +pci_clear_master_for_shutdown (pci_dev); }#ifdef CONFIG_PM_SLEEP
Signature · 1 line
2.54.0.563.g4f69b47b94-goog
Re: [PATCH v5 10/11] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec
Quoted 7 lines · David Matlack
Do not disable bus mastering on outgoing preserved devices during
pci_device_shutdown()for kexec.Preserved devices must be allowed to perform memory transactions during a Live Update to minimize downtime and ensure continuous operation. Clearing the bus mastering bit would prevent these devices from issuing any memory requests while the new kernel boots.
s/minimize downtime and//
Ensuring continuous operation is the critical piece here. Minimizing downtime is always good but IMO it's not actually a reason for this change.
I guess what this is probably saying is that the continuous operation and Live Update are what minimizes downtime, and preserving bus mastering is a piece of that.
Quoted 6 lines
Because bridges upstream of preserved endpoint devices are also automatically preserved, this change also avoids clearing bus mastering on them. This is critical because clearing bus mastering on an upstream bridge prevents the bridge from forwarding memory requests upstream (i.e. it would prevent the endpoint device from accessing system RAM and doing peer-to-peer transactions with devices not downstream of the bridge).
PATCH v5 11/11 Documentation: PCI: Add documentation for Live Update
In reply to David MatlackAdd documentation files for the PCI subsystem's participation in Live Update.
These documentation files are generated from the kernel-doc comments in the PCI Live Update source code. They describe the File-Lifecycle Bound (FLB) API, the device tracking API, and the specific policies applied to preserved devices (such as bus number inheritance and bus mastering preservation).
--- Documentation/PCI/index.rst | 1 + Documentation/PCI/liveupdate.rst | 29 +++++++++++++++++++++++++++ Documentation/core-api/liveupdate.rst | 1 + MAINTAINERS | 1 + 4 files changed, 32 insertions(+) create mode 100644 Documentation/PCI/liveupdate.rst
Documentation/PCI/index.rst +1 -0
@@ -20,3 +20,4 @@ PCI Bus Subsystem controller/index boot-interrupts tph + liveupdate
Documentation/PCI/liveupdate.rst new file +29 -0
@@ -0,0 +1,29 @@ +.. 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: + +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`
Documentation/core-api/liveupdate.rst +1 -0
@@ -70,3 +70,4 @@ See Also - :doc:`Live Update uAPI </userspace-api/liveupdate>` - :doc:`/core-api/kho/index` +- :doc:`PCI </PCI/liveupdate>`
MAINTAINERS +1 -0
@@ -20536,6 +20536,7 @@ L: [email protected] L: [email protected] 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
Signature · 1 line
2.54.0.563.g4f69b47b94-goog