Skip to contentSkip to conversation

PCI: liveupdate: PCI core support for Live Update

PATCH Superseded David Matlack 9 participants 83 messages May 22 – Jul 7

Subsystems (5)

v6 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v6 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/v6

This series introduces 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 series allows preserved PCI devices to perform uninterrupted memory transactions (DMA) to/from system memory across a Live Update. These devices can be behind a bridge but must not be VFs. Support for P2P and VF preservation will be addressed 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-preserved 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 retain the same RequesterID (bus, device, function) for the duration of their preservation by inheriting secondary bus numbers, subordinate bus numbers, and ARI Forwarding Enable on preserved bridges.

5. Guarantee that memory transactions to/from preserved devices are routed consistently by inheriting Access Control Services (ACS) flags across a Live Update, from the endpoint up to the root port.

6. Modify the PCI shutdown path to avoid disabling bus mastering on preserved devices, thereby allowing preserved devices to perform uninterrupted during kexec for Live Update.

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 the liveupdate.git tree, which includes two 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 used for testing can be found on GitHub:

https://github.com/dmatlack/linux/tree/liveupdate/pci/base/v6-with-vfio

Testing was performed using the new VFIO selftests:

- vfio_pci_liveupdate_uapi_test
- vfio_pci_liveupdate_kexec_test

Both tests were run in a QEMU-based VM environment (using a single virtio-net PCIe device connected to a root port to exercise bridge support) and on bare metal using an Intel EMR server with 8x Intel DSA PCIe devices and 1x NVMe device.

Future Work
-----------

Following this series, we expect to make further improvements to the PCI core support for Live Update:

- Allow P2P across Live Update by avoiding resizing 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
---------

v6:
- Fix truncated domain during bit shift in pci_ser_xa_key() (Sashiko)
- Replace complex and buggy per-device locks and outgoing mutex with a single rwsem (me)
- Use a loop instead of recursion to walk upstream bridges (Sashiko)
- Correctly account for all devices that need quirks to enable ACS instead of relying on PCI_DEV_FLAGS_ACS_ENABLED_QUIRK. This required adding a patch to refactor pci_dev_acs_ops in quirks.c (Sashiko)
- Fix circular locking dependency between pci_rescan_remove_lock and pci_liveupdate_flb.private->incoming.lock (me)
- Convert several functions to return 0/error instead of true/false.
- Bump PCI_LUO_FLB_COMPATIBLE in the patch that changes the semantics of pci_dev_ser.refcount (me)
- Convert was_prepared and frozen from bitfields to bools to avoid KCSAN warnings (Sashiko)
- Drop experimental verbiage from Kconfig and Documentation (me)
- Ensure that bridges inherit bus numbers during scanning if their parent bridge also inherits bus numbers. Otherwise, a race between a scan and pci_liveupdate_finish() could result in a bridge assigning a bus window larger than its parent while its parent is forced to inherit bus numbers (i.e., cannot expand) (me)
- Free struct pci_ser if an error occurs during pci_flb_retrieve() (Sashiko)
- Detect duplicate entries in incoming FLB when constructing xarray and return an error instead of silently ignoring (me)
- Add a wrapper function for fetching from the outgoing FLB (me)
- Grammatical and spelling fixes (Bjorn)
- Don't bother forcing bus inheritance when only outgoing devices are preserved. If a scan causes a bus number conflict, that is a general PCI core bug; it is not specific to Live Update (me)
- Convert bitfields in struct pci_liveupdate to bools to avoid data races (me)

v5: https://lore.kernel.org/linux-pci/[email protected]/
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 (12):
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: Refactor matching logic for pci_dev_acs_ops
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                           |  15 +
 drivers/pci/Makefile                          |   1 +
 drivers/pci/liveupdate.c                      | 850 ++++++++++++++++++
 drivers/pci/liveupdate.h                      |  68 ++
 drivers/pci/pci-driver.c                      |   9 +-
 drivers/pci/pci.c                             |  13 +-
 drivers/pci/pci.h                             |   5 +
 drivers/pci/probe.c                           |  22 +-
 drivers/pci/quirks.c                          |  57 +-
 include/linux/kho/abi/pci.h                   |  65 ++
 include/linux/pci.h                           |   4 +
 include/linux/pci_liveupdate.h                |  77 ++
 17 files changed, 1197 insertions(+), 38 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.746.g67dd491aae-goog
#1 lore raw

PATCH v6 1/12 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            |  15 ++++
 drivers/pci/Makefile           |   1 +
 drivers/pci/liveupdate.c       | 145 +++++++++++++++++++++++++++++++++
 include/linux/kho/abi/pci.h    |  61 ++++++++++++++
 include/linux/pci.h            |   1 +
 include/linux/pci_liveupdate.h |  30 +++++++
 7 files changed, 263 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
index 2fb1c75afd16..6c618830cf61 100644
@@ -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 +15 -0
index 33c88432b728..10c9b65aa242 100644
@@ -328,6 +328,21 @@ config VGA_ARB_MAX_GPUS
 	  Reserves space in the kernel to maintain resource locking for
 	  multiple GPUS.  The overhead for each GPU is very small.
 
+config PCI_LIVEUPDATE
+	bool "PCI Live Update Support"
+	depends on PCI && LIVEUPDATE
+	help
+	  Enable PCI core support for preserving PCI devices across Live
+	  Update. This, in combination with support in a device's driver,
+	  enables PCI devices to run and perform memory transactions
+	  uninterrupted during a kexec for Live Update.
+
+	  This option should only be enabled by users who plan to use Live
+	  Update for kernel upgrades and require preserving PCI devices during
+	  those upgrades.
+
+	  If unsure, say N.
+
 source "drivers/pci/hotplug/Kconfig"
 source "drivers/pci/controller/Kconfig"
 source "drivers/pci/endpoint/Kconfig"
drivers/pci/Makefile +1 -0
index 41ebc3b9a518..e8d003cb6757 100644
@@ -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 +145 -0

145 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
index 2c4454583c11..8cadeeab86fd 100644
@@ -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
new file mode 100644
index 000000000000..8ec98beefcb4
@@ -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.746.g67dd491aae-goog
#2 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Missing `kho_is_enabled()` check before calling KHO preservation APIs in PCI Live Update FLB…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing kho_is_enabled() check before calling KHO preservation APIs in PCI Live Update FLB operations. --

commit b0cb2fb154fba1aa85d63570fe0186e243353fb7
Author: David Matlack <[email protected]>

PCI: liveupdate: Set up FLB handler for the PCI core

This commit sets up a File-Lifecycle-Bound (FLB) handler for the PCI core to participate in the preservation of PCI devices across Live Update. It allocates and preserves a pci_ser structure using KHO when the first PCI device file is preserved.

Quoted 4 lines
drivers/pci/liveupdate.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_dev *dev = NULL;
+	u32 max_nr_devices = 0;
+	struct pci_ser *ser;
+	unsigned long size;
+
+	/*
+	 * Allocate enough space to preserve all devices that are currently
+	 * present on the system. Extra padding can be added to this in the

drivers/pci/liveupdate.c line 70

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+	 * future to increase the chances that there is enough room to preserve
+	 * devices that are not yet present on the system (e.g. VFs, hot-plugged
+	 * devices).
+	 */
+	for_each_pci_dev(dev)
+		max_nr_devices++;
+
+	size = struct_size_t(struct pci_ser, devices, max_nr_devices);
+
+	ser = kho_alloc_preserve(size);

[Severity: Medium]
Should this code verify kho_is_enabled() before calling kho_alloc_preserve()?

The commit message states:
"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."

Does this assumption conflict with the Kexec Handover subsystem guidelines?

The guidelines mandate that all callers of serialization APIs must explicitly gate their usage with kho_is_enabled().

Calling preservation APIs like kho_alloc_preserve() without the check can silently add tracking state that will never be used if the subsystem is unexpectedly inactive.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 51 lines · On Fri, May 22, 2026 at 1:44 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing kho_is_enabled() check before calling KHO preservation APIs in PCI Live Update FLB operations. --

commit b0cb2fb154fba1aa85d63570fe0186e243353fb7
Author: David Matlack <[email protected]>

PCI: liveupdate: Set up FLB handler for the PCI core

This commit sets up a File-Lifecycle-Bound (FLB) handler for the PCI core to participate in the preservation of PCI devices across Live Update. It allocates and preserves a pci_ser structure using KHO when the first PCI device file is preserved.

Quoted 4 lines
drivers/pci/liveupdate.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+     struct pci_dev *dev = NULL;
+     u32 max_nr_devices = 0;
+     struct pci_ser *ser;
+     unsigned long size;
+
+     /*
+      * Allocate enough space to preserve all devices that are currently
+      * present on the system. Extra padding can be added to this in the

drivers/pci/liveupdate.c line 70

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+      * future to increase the chances that there is enough room to preserve
+      * devices that are not yet present on the system (e.g. VFs, hot-plugged
+      * devices).
+      */
+     for_each_pci_dev(dev)
+             max_nr_devices++;
+
+     size = struct_size_t(struct pci_ser, devices, max_nr_devices);
+
+     ser = kho_alloc_preserve(size);

[Severity: Medium]
Should this code verify kho_is_enabled() before calling kho_alloc_preserve()?

The commit message states:
"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."

Quoted 8 lines

Does this assumption conflict with the Kexec Handover subsystem guidelines?

The guidelines mandate that all callers of serialization APIs must explicitly gate their usage with kho_is_enabled().

Calling preservation APIs like kho_alloc_preserve() without the check can silently add tracking state that will never be used if the subsystem is unexpectedly inactive.

Sashiko keeps leaving this comment. It seems like a false positive to me. KHO maintainers, perhaps the KHO documentation needs adjustment?

maintainer
#4 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 57 lines · David Matlack
Quoted 56 lines · On Fri, May 22, 2026 at 1:44 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing kho_is_enabled() check before calling KHO preservation APIs in PCI Live Update FLB operations. --

commit b0cb2fb154fba1aa85d63570fe0186e243353fb7
Author: David Matlack <[email protected]>

PCI: liveupdate: Set up FLB handler for the PCI core

This commit sets up a File-Lifecycle-Bound (FLB) handler for the PCI core to participate in the preservation of PCI devices across Live Update. It allocates and preserves a pci_ser structure using KHO when the first PCI device file is preserved.

Quoted 4 lines
drivers/pci/liveupdate.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+     struct pci_dev *dev = NULL;
+     u32 max_nr_devices = 0;
+     struct pci_ser *ser;
+     unsigned long size;
+
+     /*
+      * Allocate enough space to preserve all devices that are currently
+      * present on the system. Extra padding can be added to this in the

drivers/pci/liveupdate.c line 70

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+      * future to increase the chances that there is enough room to preserve
+      * devices that are not yet present on the system (e.g. VFs, hot-plugged
+      * devices).
+      */
+     for_each_pci_dev(dev)
+             max_nr_devices++;
+
+     size = struct_size_t(struct pci_ser, devices, max_nr_devices);
+
+     ser = kho_alloc_preserve(size);

[Severity: Medium]
Should this code verify kho_is_enabled() before calling kho_alloc_preserve()?

The commit message states:
"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."

Does this assumption conflict with the Kexec Handover subsystem guidelines?

The guidelines mandate that all callers of serialization APIs must explicitly gate their usage with kho_is_enabled().

Quoted 6 lines
Quoted 3 lines

Calling preservation APIs like kho_alloc_preserve() without the check can silently add tracking state that will never be used if the subsystem is unexpectedly inactive.

Sashiko keeps leaving this comment. It seems like a false positive to me. KHO maintainers, perhaps the KHO documentation needs adjustment?

TBH, I don't remember such guidelines, but patches are welcome ;-)

Signature · 2 lines
Sincerely yours,
Mike.
#5 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 59 lines · Mike Rapoport
Quoted 57 lines · David Matlack
Quoted 56 lines · On Fri, May 22, 2026 at 1:44 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing kho_is_enabled() check before calling KHO preservation APIs in PCI Live Update FLB operations. --

commit b0cb2fb154fba1aa85d63570fe0186e243353fb7
Author: David Matlack <[email protected]>

PCI: liveupdate: Set up FLB handler for the PCI core

This commit sets up a File-Lifecycle-Bound (FLB) handler for the PCI core to participate in the preservation of PCI devices across Live Update. It allocates and preserves a pci_ser structure using KHO when the first PCI device file is preserved.

Quoted 4 lines
drivers/pci/liveupdate.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+     struct pci_dev *dev = NULL;
+     u32 max_nr_devices = 0;
+     struct pci_ser *ser;
+     unsigned long size;
+
+     /*
+      * Allocate enough space to preserve all devices that are currently
+      * present on the system. Extra padding can be added to this in the

drivers/pci/liveupdate.c line 70

Quoted 10 lines
drivers/pci/liveupdate.c new file +10 -0
+      * future to increase the chances that there is enough room to preserve
+      * devices that are not yet present on the system (e.g. VFs, hot-plugged
+      * devices).
+      */
+     for_each_pci_dev(dev)
+             max_nr_devices++;
+
+     size = struct_size_t(struct pci_ser, devices, max_nr_devices);
+
+     ser = kho_alloc_preserve(size);

[Severity: Medium]
Should this code verify kho_is_enabled() before calling kho_alloc_preserve()?

The commit message states:
"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."

Does this assumption conflict with the Kexec Handover subsystem guidelines?

The guidelines mandate that all callers of serialization APIs must explicitly gate their usage with kho_is_enabled().

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

Calling preservation APIs like kho_alloc_preserve() without the check can silently add tracking state that will never be used if the subsystem is unexpectedly inactive.

Sashiko keeps leaving this comment. It seems like a false positive to me. KHO maintainers, perhaps the KHO documentation needs adjustment?

TBH, I don't remember such guidelines, but patches are welcome ;-)

Yeah, on a quick scan of KHO documentation I see nothing of the sort. So possibly the bot is hallucinating?

But perhaps it would help if we update the subsystem prompts for Sashiko about this?

Reviewed-by on v6 1/12
#6 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

In reply to David Matlack
Quoted 36 lines · David Matlack

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.

[...]

Quoted 22 lines
include/linux/kho/abi/pci.h new file +22 -0
+/**
+ * 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.
+ */
+struct pci_dev_ser {
+	u32 domain;
+	u16 bdf;
+	u16 padding;
+} __packed;
+
+/**
+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel.
+ *
+ * @max_nr_devices: The length of the devices[] flexible array.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Flexible array of pci_dev_ser structs for each device.

include/linux/kho/abi/pci.h line 59

Quoted 10 lines
include/linux/kho/abi/pci.h new file +10 -0
+ */
+struct pci_ser {
+	u32 max_nr_devices;
+	u32 nr_devices;
+	struct pci_dev_ser devices[];
+} __packed;
+
+/* Ensure all elements of devices[] are naturally aligned. */
+static_assert(offsetof(struct pci_ser, devices) % sizeof(unsigned long) == 0);
+static_assert(sizeof(struct pci_dev_ser) % sizeof(unsigned long) == 0);

Minor Nit: Shall we consider using specific bitwidth types here? I'm wondering if down the line another u32 field is added to struct pci_dev_ser.. in that case on a 32-bit machine 12 % 4 == 0 but on a 64-bit machine 12 % 8 != 0..

[...]

With the nit:

Thanks,
Praan

#7 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 69 lines · Pranjal Shrivastava
Quoted 36 lines · David Matlack

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.

[...]

Quoted 29 lines
include/linux/kho/abi/pci.h new file +29 -0
+/**
+ * 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.
+ */
+struct pci_dev_ser {
+	u32 domain;
+	u16 bdf;
+	u16 padding;
+} __packed;
+
+/**
+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel.
+ *
+ * @max_nr_devices: The length of the devices[] flexible array.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Flexible array of pci_dev_ser structs for each device.
+ */
+struct pci_ser {
+	u32 max_nr_devices;
+	u32 nr_devices;
+	struct pci_dev_ser devices[];
+} __packed;
+
Quoted 8 lines

include/linux/kho/abi/pci.h line 59

Quoted 3 lines
include/linux/kho/abi/pci.h new file +3 -0
+/* Ensure all elements of devices[] are naturally aligned. */
+static_assert(offsetof(struct pci_ser, devices) % sizeof(unsigned long) == 0);
+static_assert(sizeof(struct pci_dev_ser) % sizeof(unsigned long) == 0);

Minor Nit: Shall we consider using specific bitwidth types here? I'm wondering if down the line another u32 field is added to struct pci_dev_ser.. in that case on a 32-bit machine 12 % 4 == 0 but on a 64-bit machine 12 % 8 != 0..

I think natural alignment is what matters for efficient access of the array elements. So failing the assert only on 64-bit architectures seems like the correct behavior.

Quoted 9 lines

[...]

With the nit:

Thanks,
Praan

#8 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 14 lines · David Matlack
Quoted 13 lines · Pranjal Shrivastava
Quoted 12 lines · David Matlack

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.

[...]

Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel.
+ *
+ * @max_nr_devices: The length of the devices[] flexible array.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Flexible array of pci_dev_ser structs for each device.
+ */

include/linux/kho/abi/pci.h new file +6 -0
+struct pci_ser {
+	u32 max_nr_devices;
+	u32 nr_devices;
+	struct pci_dev_ser devices[];
+} __packed;
+
Quoted 8 lines

include/linux/kho/abi/pci.h line 59

Quoted 3 lines
include/linux/kho/abi/pci.h new file +3 -0
+/* Ensure all elements of devices[] are naturally aligned. */
+static_assert(offsetof(struct pci_ser, devices) % sizeof(unsigned long) == 0);
+static_assert(sizeof(struct pci_dev_ser) % sizeof(unsigned long) == 0);

Minor Nit: Shall we consider using specific bitwidth types here? I'm wondering if down the line another u32 field is added to struct pci_dev_ser.. in that case on a 32-bit machine 12 % 4 == 0 but on a 64-bit machine 12 % 8 != 0..

Quoted 4 lines

I think natural alignment is what matters for efficient access of the array elements. So failing the assert only on 64-bit architectures seems like the correct behavior.

Ack. I guess what I was trying to say was we'd anyway need to keep both architectures in mind, i.e. adding members should be 64-bit aligned (which implicitly also handles 32-bit) as with the current assert any new addition would have to be both 32 & 64-bit aligned. I guess we can keep this as is.

Thanks,
Praan

maintainer
#9 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

In reply to David Matlack

MAINTAINERS L: [email protected] · line 20534

Quoted 10 lines · David Matlack
MAINTAINERS +2 -0
index 2fb1c75afd16..6c618830cf61 100644
@@ -20530,6 +20530,16 @@ L:	[email protected]
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
+PCI LIVE UPDATE
+M:	David Matlack <[email protected]>

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Quoted 22 lines
drivers/pci/liveupdate.c new file +15 -0
new file mode 100644
index 000000000000..737e7b9366db
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <[email protected]>
+ */
+
+/**
+ * DOC: PCI Live Update
+ *
+ * The PCI subsystem participates in the Live Update process to enable drivers
+ * to preserve their PCI devices across kexec.
+ *
+ * File-Lifecycle-Bound (FLB) Data
+ * ===============================

...

Quoted 3 lines

+ *
+ * PCI device preservation across Live Update is built on top of the Live Update
+ * Orchestrator's (LUO) support for file preservation across kexec. Drivers

I prefer to just use acronyms FLB, and LUO, but have links to the actual documentations about them.

So, something like this:

* :ref:FLB <flb> Data
* =====================
*
* PCI device preservation across Live Update is built on top of the
* :ref:LUO <luo> support for file preservation across kexec. Drivers

And also add _luo and _flb to Documentation/core-api/liveupdate.rst

.. _luo:

========================
Live Update Orchestrator
========================

.. _flb:

LUO File Lifecycle Bound Global Data
====================================

drivers/pci/liveupdate.c

Quoted 7 lines

[ ... skip 17 lines ... ]
+ *
+ * * ``pci_liveupdate_register_flb(driver_file_handler)``
+ * * ``pci_liveupdate_unregister_flb(driver_file_handler)``
+ */

drivers/pci/liveupdate.c new file +2 -0
+
+#define pr_fmt(fmt) "PCI: liveupdate: " fmt

Nit, may be:

drivers/pci/liveupdate.c line 48

Quoted 7 lines
drivers/pci/liveupdate.c new file +7 -0
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/pci.h>
+#include <linux/liveupdate.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>

Please sort alphabetically.

drivers/pci/liveupdate.c line 66

Quoted 7 lines

[ ... skip 12 lines ... ]

drivers/pci/liveupdate.c new file +6 -0
+	 * future to increase the chances that there is enough room to preserve
+	 * devices that are not yet present on the system (e.g. VFs, hot-plugged
+	 * devices).
+	 */
+	for_each_pci_dev(dev)
+		max_nr_devices++;

I think, we want to use kho_block [1] (it is in liveupdate/next branch) to allow number of supported devices to be dynamic.

To support this, we would redefine the ABI and tracking structures like so:

/* include/linux/kho/abi/pci.h */
struct pci_ser {
u64 devices; /* Phys address of the first block header of kho_block_set */
u64 nr_devices; /* Total count of active preserved devices */
} __packed;

/* drivers/pci/liveupdate.c */
struct pci_flb_outgoing {
struct pci_ser *ser; /* Points to the FDT/KHO-allocated ABI struct */
struct kho_block_set block_set; /* Controls the active blocks on the fly */
};

In __pci_liveupdate_preserve_device() , we would search for and reuse any inactive pci_dev_ser slot first, and only call kho_block_set_grow() to expand if no inactive slots are available.

In pci_liveupdate_unpreserve_device(), we would simply mark the pci_dev_ser as inactive.

Quoted 8 lines
include/linux/pci_liveupdate.h new file +0 -0
new file mode 100644
index 000000000000..8ec98beefcb4
@@ -0,0 +1,30 @@

[ ... skip 24 lines ... ]

include/linux/pci_liveupdate.h line 30

Quoted 6 lines
include/linux/pci_liveupdate.h new file +6 -0
+static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+}
+#endif
+
+#endif /* LINUX_PCI_LIVEUPDATE_H */

[1] https://lore.kernel.org/all/[email protected]/

Preserving: In __pci_liveupdate_preserve_device() , we would search for
Unpreserving: In pci_liveupdate_unpreserve_device(), we would simply

Preserving: In __pci_liveupdate_preserve_device() , we would search for
Unpreserving: In pci_liveupdate_unpreserve_device(), we would simply

Signature · 1 line
Pasha Tatashin <[email protected]>
maintainer
#10 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 9 lines · Pasha Tatashin
Quoted 8 lines · David Matlack
MAINTAINERS +0 -0
index 2fb1c75afd16..6c618830cf61 100644
@@ -20530,6 +20530,16 @@ L:	[email protected]
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
Quoted 6 lines

MAINTAINERS L: [email protected] · line 20534

Quoted 2 lines
MAINTAINERS +2 -0
+PCI LIVE UPDATE
+M:	David Matlack <[email protected]>

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Or we can add PCI liveupdate files to LIVEUPDATE entry.

Signature · 2 lines
Sincerely yours,
Mike.
maintainer
#11 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 9 lines · Mike Rapoport
Quoted 5 lines · Pasha Tatashin

On Fri, 22 May 2026 20:23:59 +0000, David Matlack <[email protected]> wrote:

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Or we can add PCI liveupdate files to LIVEUPDATE entry.

That will not work, as we cannot serve as maintainers for PCI/VFIO/IOMMU/KVM, etc. David Matlack will be the maintainer for the PCI components, and we will accept patches once they have been approved by him.

The simplification we could do is to create an email alias for the live-update tree maintainers. This would allow us to use a single entry instead of listing all three of us individually.

#12 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 11 lines · Pasha Tatashin
Quoted 9 lines · Mike Rapoport
Quoted 5 lines · Pasha Tatashin

On Fri, 22 May 2026 20:23:59 +0000, David Matlack <[email protected]> wrote:

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Or we can add PCI liveupdate files to LIVEUPDATE entry.

Quoted 8 lines

That will not work, as we cannot serve as maintainers for PCI/VFIO/IOMMU/KVM, etc. David Matlack will be the maintainer for the PCI components, and we will accept patches once they have been approved by him.

The simplification we could do is to create an email alias for the live-update tree maintainers. This would allow us to use a single entry instead of listing all three of us individually.

We could create a Live Update mailing list for all code that can be CCed on all patches that must be merged through the Live Update tree. I would also be interested in subscribing to that list.

But if you want to create a LUO maintainers-only list that would work fine too.

For now, I'll plan to add you as Reviewers under PCI LIVE UPDATE in v7.

#13 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 17 lines · David Matlack
Quoted 16 lines · Pasha Tatashin
Quoted 9 lines · Mike Rapoport
Quoted 5 lines · Pasha Tatashin

On Fri, 22 May 2026 20:23:59 +0000, David Matlack <[email protected]> wrote:

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Or we can add PCI liveupdate files to LIVEUPDATE entry.

That will not work, as we cannot serve as maintainers for PCI/VFIO/IOMMU/KVM, etc. David Matlack will be the maintainer for the PCI components, and we will accept patches once they have been approved by him.

Quoted 7 lines
Quoted 3 lines

The simplification we could do is to create an email alias for the live-update tree maintainers. This would allow us to use a single entry instead of listing all three of us individually.

We could create a Live Update mailing list for all code that can be CCed on all patches that must be merged through the Live Update tree. I would also be interested in subscribing to that list.

+1. I'd like if there's a specific Live Update mailing list for submissions & discussion about the Live Update tree.

Thanks,
Praan

maintainer
#14 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 22 lines · Pranjal Shrivastava
Quoted 21 lines · David Matlack
Quoted 19 lines · Pasha Tatashin
Quoted 9 lines · Mike Rapoport
Quoted 5 lines · Pasha Tatashin

On Fri, 22 May 2026 20:23:59 +0000, David Matlack <[email protected]> wrote:

Please add Pratyush, Mike, and myself so we are notified directly of incoming patches, the same as with other areas where the liveupdate/ tree is specified.

Or we can add PCI liveupdate files to LIVEUPDATE entry.

That will not work, as we cannot serve as maintainers for PCI/VFIO/IOMMU/KVM, etc. David Matlack will be the maintainer for the PCI components, and we will accept patches once they have been approved by him.

The simplification we could do is to create an email alias for the live-update tree maintainers. This would allow us to use a single entry instead of listing all three of us individually.

Quoted 6 lines
Quoted 3 lines

We could create a Live Update mailing list for all code that can be CCed on all patches that must be merged through the Live Update tree. I would also be interested in subscribing to that list.

+1. I'd like if there's a specific Live Update mailing list for submissions & discussion about the Live Update tree.

We treat [email protected] as the "live update mailing list". We considered getting a separate one, but I reckon the traffic is low enough on kexec@ already that we can re-use it for live update.

So perhaps we just Cc kexec@? Is there anything to be gained by creating an alias?

Signature · 2 lines
Regards,
Pratyush Yadav
#15 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

In reply to Pasha Tatashin
Quoted 18 lines · Pasha Tatashin
Quoted 2 lines · David Matlack

+ * PCI device preservation across Live Update is built on top of the Live Update
+ * Orchestrator's (LUO) support for file preservation across kexec. Drivers

I prefer to just use acronyms FLB, and LUO, but have links to the actual documentations about them.

So, something like this:

* :ref:FLB <flb> Data
* =====================
*
* PCI device preservation across Live Update is built on top of the
* :ref:LUO <luo> support for file preservation across kexec. Drivers

And also add _luo and _flb to Documentation/core-api/liveupdate.rst

Quoted 7 lines

.. _luo:

========================
Live Update Orchestrator
========================

.. _flb:

Will do.

I guess I will need to add another patch to add the link references to liveupdate.rst?

Quoted 4 lines

LUO File Lifecycle Bound Global Data
====================================

Quoted 9 lines

Review

Quoted 7 lines

[ ... skip 17 lines ... ]
+ *
+ * * ``pci_liveupdate_register_flb(driver_file_handler)``
+ * * ``pci_liveupdate_unregister_flb(driver_file_handler)``
+ */

(quoted diff) +2 -0
+
+#define pr_fmt(fmt) "PCI: liveupdate: " fmt

Nit, may be:

Did you have a suggestion here that got lost?

Quoted 10 lines

drivers/pci/liveupdate.c line 48

Quoted 8 lines
drivers/pci/liveupdate.c new file +7 -0
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/pci.h>
+#include <linux/liveupdate.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>

Please sort alphabetically.

Will do.

Quoted 27 lines

drivers/pci/liveupdate.c line 66

Quoted 8 lines

[ ... skip 12 lines ... ]

drivers/pci/liveupdate.c new file +6 -0
+	 * future to increase the chances that there is enough room to preserve
+	 * devices that are not yet present on the system (e.g. VFs, hot-plugged
+	 * devices).
+	 */
+	for_each_pci_dev(dev)
+		max_nr_devices++;

I think, we want to use kho_block [1] (it is in liveupdate/next branch) to allow number of supported devices to be dynamic.

To support this, we would redefine the ABI and tracking structures like so:

/* include/linux/kho/abi/pci.h */
struct pci_ser {
u64 devices; /* Phys address of the first block header of kho_block_set */
u64 nr_devices; /* Total count of active preserved devices */
} __packed;

/* drivers/pci/liveupdate.c */
struct pci_flb_outgoing {
struct pci_ser *ser; /* Points to the FDT/KHO-allocated ABI struct */
struct kho_block_set block_set; /* Controls the active blocks on the fly */
};

Quoted 6 lines

In __pci_liveupdate_preserve_device() , we would search for and reuse any inactive pci_dev_ser slot first, and only call kho_block_set_grow() to expand if no inactive slots are available.

In pci_liveupdate_unpreserve_device(), we would simply mark the pci_dev_ser as inactive.

Makes sense at a high level. I'll work on switching kho_block for v7 and get back to you if I hit any issues.

Quoted 26 lines
Quoted 9 lines
include/linux/pci_liveupdate.h new file +0 -0
new file mode 100644
index 000000000000..8ec98beefcb4
@@ -0,0 +1,30 @@

[ ... skip 24 lines ... ]

include/linux/pci_liveupdate.h line 30

Quoted 6 lines
include/linux/pci_liveupdate.h new file +6 -0
+static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+}
+#endif
+
+#endif /* LINUX_PCI_LIVEUPDATE_H */

[1] https://lore.kernel.org/all/[email protected]/

Preserving: In __pci_liveupdate_preserve_device() , we would search for
Unpreserving: In pci_liveupdate_unpreserve_device(), we would simply

Preserving: In __pci_liveupdate_preserve_device() , we would search for
Unpreserving: In pci_liveupdate_unpreserve_device(), we would simply

Signature · 1 line
Pasha Tatashin <[email protected]>
maintainer
#16 lore raw

Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core

Quoted 27 lines · David Matlack
Quoted 25 lines · Pasha Tatashin
Quoted 1 line · David Matlack

+ * PCI device preservation across Live Update is built on top of the Live Update

I prefer to just use acronyms FLB, and LUO, but have links to the actual documentations about them.

So, something like this:

* :ref:FLB <flb> Data
* =====================
*
* PCI device preservation across Live Update is built on top of the
* :ref:LUO <luo> support for file preservation across kexec. Drivers

And also add _luo and _flb to Documentation/core-api/liveupdate.rst

.. _luo:

========================
Live Update Orchestrator
========================

Quoted 6 lines
Quoted 1 line

.. _flb:

Will do.

I guess I will need to add another patch to add the link references to liveupdate.rst?

Yes, it can be a separate patch, but adding to this patch is also, OK.

Quoted 7 lines
Quoted 5 lines

LUO File Lifecycle Bound Global Data
====================================

Nit, may be:

Did you have a suggestion here that got lost?

Yeah, I meant:
#define pr_fmt(fmt) "PCI: " KBUILD_BASENAME ": " fmt

Quoted 32 lines
Quoted 2 lines

Please sort alphabetically.

Will do.

Quoted 18 lines

I think, we want to use kho_block [1] (it is in liveupdate/next branch) to allow number of supported devices to be dynamic.

To support this, we would redefine the ABI and tracking structures like so:

/* include/linux/kho/abi/pci.h */
struct pci_ser {
u64 devices; /* Phys address of the first block header of kho_block_set */
u64 nr_devices; /* Total count of active preserved devices */
} __packed;

/* drivers/pci/liveupdate.c */
struct pci_flb_outgoing {
struct pci_ser *ser; /* Points to the FDT/KHO-allocated ABI struct */
struct kho_block_set block_set; /* Controls the active blocks on the fly */
};

Quoted 6 lines

In __pci_liveupdate_preserve_device() , we would search for and reuse any inactive pci_dev_ser slot first, and only call kho_block_set_grow() to expand if no inactive slots are available.

In pci_liveupdate_unpreserve_device(), we would simply mark the pci_dev_ser as inactive.

Makes sense at a high level. I'll work on switching kho_block for v7 and get back to you if I hit any issues.

#17 lore raw

PATCH v6 2/12 PCI: liveupdate: Track outgoing preserved PCI devices

In reply to David Matlack

Add 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/liveupdate.h       |  21 +++++
 drivers/pci/probe.c            |   2 +
 include/linux/kho/abi/pci.h    |   9 +-
 include/linux/pci.h            |   3 +
 include/linux/pci_liveupdate.h |  21 +++++
 6 files changed, 220 insertions(+), 3 deletions(-)
 create mode 100644 drivers/pci/liveupdate.h
drivers/pci/liveupdate.c +167 -0
index 737e7b9366db..065d5af822f7 100644
@@ -36,6 +36,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
@@ -48,6 +68,21 @@
 #include <linux/mm.h>
 #include <linux/pci.h>
 
+#include "liveupdate.h"
+
+/**
+ * struct pci_liveupdate_global - Global state for PCI Live Update support
+ * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing
+ *         FLBs, and the references to them in struct pci_dev.
+ */
+struct pci_liveupdate_global {
+	struct rw_semaphore rwsem;
+};
+
+static struct pci_liveupdate_global pci_liveupdate = {
+	.rwsem = __RWSEM_INITIALIZER(pci_liveupdate.rwsem),
+};
+
 static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
 {
 	struct pci_dev *dev = NULL;
@@ -115,6 +150,138 @@ static struct liveupdate_flb pci_liveupdate_flb = {
 	.compatible = PCI_LUO_FLB_COMPATIBLE,
 };
 
+static struct pci_ser *pci_liveupdate_flb_get_outgoing(void)
+{
+	struct pci_ser *ser = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&ser);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!ser)
+		return ERR_PTR(-ENOENT);
+
+	return ser;
+}
+
+static void pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	struct pci_dev_ser *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;
+}
+
+static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	int i;
+
+	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;
+}
+
+/**
+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
+ * @dev: The PCI device to preserve.
+ *
+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be
+ * preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler
+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.
+ *
+ * Returns: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	struct pci_ser *ser = NULL;
+
+	if (dev->is_virtfn)
+		return -EINVAL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	ser = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(ser))
+		return PTR_ERR(ser);
+
+	return pci_liveupdate_preserve_device(ser, dev);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
+
+/**
+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device
+ * @dev: The PCI device to unpreserve.
+ *
+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no
+ * longer be preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set
+ * up.
+ */
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_ser *ser = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	ser = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(ser)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_device(ser, dev);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
+
+void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+	/*
+	 * It should be safe to READ_ONCE() outside of the rwsem during cleanup
+	 * since there should no longer be any references to @dev on the system.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing)) {
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+		pci_liveupdate_unpreserve(dev);
+	}
+}
+
 /**
  * pci_liveupdate_register_flb() - Register a file handler with the PCI core
  * @fh: The file handler to register.
drivers/pci/liveupdate.h new file +21 -0
new file mode 100644
index 000000000000..b2335581f8d0
@@ -0,0 +1,21 @@
+/* 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_cleanup_device(struct pci_dev *dev);
+#else
+static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+}
+#endif
+
+#endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/probe.c +2 -0
index b63cd0c310bc..b88109a8dfe4 100644
@@ -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 = {
@@ -2490,6 +2491,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/kho/abi/pci.h +6 -3
index 6ebcf817fff4..85def616703d 100644
@@ -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 1 when the struct pci_dev_ser is in use, and
+ *            0 otherwise.
  */
 struct pci_dev_ser {
 	u32 domain;
 	u16 bdf;
-	u16 padding;
+	u16 refcount;
 } __packed;
 
 /**
include/linux/pci.h +3 -0
index 8cadeeab86fd..a7c3722b1e77 100644
@@ -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 +21 -0
index 8ec98beefcb4..cfcfbfa73af7 100644
@@ -8,14 +8,26 @@
 #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
+ * @outgoing: State preserved for the next kernel.
+ */
+struct pci_liveupdate {
+	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 +37,15 @@ static inline int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh
 static inline void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
 {
 }
+
+static inline int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+}
 #endif
 
 #endif /* LINUX_PCI_LIVEUPDATE_H */
Signature · 1 line
2.54.0.746.g67dd491aae-goog
#18 lore raw

Re: [PATCH v6 02/12] PCI: liveupdate: Track outgoing preserved PCI devices

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Acquiring a sleepable rw_semaphore (rwsem_write) during PCI device release, which can occur in…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Acquiring a sleepable rw_semaphore (rwsem_write) during PCI device release, which can occur in atomic contexts. --

commit 306396f47fee049b9172957aca3ed638cf2241ab
Author: David Matlack <[email protected]>

PCI: liveupdate: Track outgoing preserved PCI devices

This commit adds APIs to allow drivers to notify the PCI core of which devices are being preserved across a Live Update for the next kernel.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index 737e7b9366dbe..065d5af822f74 100644

[ ... ]

Quoted 1 line
drivers/pci/liveupdate.c +0 -0
@@ -115,6 +150,138 @@ static struct liveupdate_flb pci_liveupdate_flb = {

[ ... ]

Quoted 15 lines
drivers/pci/liveupdate.c +15 -0
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_ser *ser = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	ser = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(ser)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_device(ser, dev);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);

drivers/pci/liveupdate.c static struct liveupdate_flb pci_liveupdate_flb = { · line 281

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+
+void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+	/*
+	 * It should be safe to READ_ONCE() outside of the rwsem during cleanup
+	 * since there should no longer be any references to @dev on the system.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing)) {
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+		pci_liveupdate_unpreserve(dev);

[Severity: High]
Can calling pci_liveupdate_unpreserve() here cause a sleep-in-atomic bug?

pci_liveupdate_unpreserve() unconditionally acquires a sleepable rw_semaphore:
guard(rwsem_write)(&pci_liveupdate.rwsem).

Since pci_liveupdate_cleanup_device() is called from pci_release_dev(), this runs when the last reference to a struct pci_dev is dropped via pci_dev_put().

Because pci_dev_put() can be called from atomic contexts (such as hardware interrupt handlers or while holding spinlocks) to drop references acquired during lookups, acquiring a sleepable lock here could trigger a scheduling-while-atomic BUG or deadlock.

Quoted 2 lines
drivers/pci/liveupdate.c +2 -0
+	}
+}

[ ... ]

Quoted 4 lines
drivers/pci/probe.c +0 -0
index b63cd0c310bc0..b88109a8dfe42 100644

[ ... ]

Quoted 8 lines
drivers/pci/probe.c +1 -0
@@ -2490,6 +2491,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);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#19 lore raw

Re: [PATCH v6 02/12] PCI: liveupdate: Track outgoing preserved PCI devices

Quoted 29 lines · On Fri, May 22, 2026 at 1:54 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Acquiring a sleepable rw_semaphore (rwsem_write) during PCI device release, which can occur in atomic contexts. --

drivers/pci/liveupdate.c static struct liveupdate_flb pci_liveupdate_flb = { · line 281

Quoted 9 lines
drivers/pci/liveupdate.c +9 -0
+void pci_liveupdate_cleanup_device(struct pci_dev *dev)
+{
+     /*
+      * It should be safe to READ_ONCE() outside of the rwsem during cleanup
+      * since there should no longer be any references to @dev on the system.
+      */
+     if (READ_ONCE(dev->liveupdate.outgoing)) {
+             pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+             pci_liveupdate_unpreserve(dev);

[Severity: High]
Can calling pci_liveupdate_unpreserve() here cause a sleep-in-atomic bug?

pci_liveupdate_unpreserve() unconditionally acquires a sleepable rw_semaphore:
guard(rwsem_write)(&pci_liveupdate.rwsem).

Since pci_liveupdate_cleanup_device() is called from pci_release_dev(), this runs when the last reference to a struct pci_dev is dropped via pci_dev_put().

Because pci_dev_put() can be called from atomic contexts (such as hardware interrupt handlers or while holding spinlocks) to drop references acquired during lookups, acquiring a sleepable lock here could trigger a scheduling-while-atomic BUG or deadlock.

Quoted 8 lines

drivers/pci/probe.c static void pci_release_dev(struct device *dev) · line 2497

Quoted 8 lines
drivers/pci/probe.c +1 -0
@@ -2490,6 +2491,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);

Gemini found 3 files where pci_dev_put() is called under a spinlock:

- drivers/net/ethernet/microsoft/mana/gdma_main.c (mana_driver_exit())
- arch/x86/pci/i386.c (pcibios_fw_addr_list_del())
- arch/powerpc/platforms/pasemi/dma_lib.c (pasemi_dma_init())

OTOH, pci_release_dev() does not seem atomic-safe. On s390, it can take the zpci_add_remove_lock mutex via pcibios_release_device(). And on all architectures it can call kernfs_remove() via pci_bus_put(), which takes multiple semaphores.

#20 lore raw

Re: [PATCH v6 02/12] PCI: liveupdate: Track outgoing preserved PCI devices

Quoted 11 lines · David Matlack

Gemini found 3 files where pci_dev_put() is called under a spinlock:

- drivers/net/ethernet/microsoft/mana/gdma_main.c (mana_driver_exit())
- arch/x86/pci/i386.c (pcibios_fw_addr_list_del())
- arch/powerpc/platforms/pasemi/dma_lib.c (pasemi_dma_init())

OTOH, pci_release_dev() does not seem atomic-safe. On s390, it can take the zpci_add_remove_lock mutex via pcibios_release_device(). And on all architectures it can call kernfs_remove() via pci_bus_put(), which takes multiple semaphores.

AFAICT, Sashiko seems to be wrong here.. pci_dev_put calls put_device() The put_device() doc clearly mentions might_sleep [1] to warns that dropping the final reference may trigger a sleepable cleanup.

Any existing code calling put_device in atomic context is in violation. put_device / release are *expected* to sleep.

This patch doesn't introduce any sleep-in-atomic issues.

Thanks,
Praan

[1] https://elixir.bootlin.com/linux/v7.1-rc6/source/drivers/base/core.c#L3812

Reviewed-by on v6 2/12
#21 lore raw

Re: [PATCH v6 02/12] PCI: liveupdate: Track outgoing preserved PCI devices

In reply to David Matlack
Quoted 15 lines · David Matlack

Add 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.

[...]

Quoted 11 lines

/**
* 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 1 when the struct pci_dev_ser is in use, and
+ *            0 otherwise.

Note to fellow reviewers: This may seem like a bool instead of refcount, but this is changed in Patch 6.

Thanks,
Praan

maintainer
#22 lore raw

Re: [PATCH v6 02/12] PCI: liveupdate: Track outgoing preserved PCI devices

In reply to David Matlack

drivers/pci/liveupdate.c static struct liveupdate_flb pci_liveupdate_flb = { · line 214

Quoted 12 lines · David Matlack
drivers/pci/liveupdate.c +0 -0
index 737e7b9366db..065d5af822f7 100644
@@ -115,6 +150,138 @@ static struct liveupdate_flb pci_liveupdate_flb = {

[ ... skip 59 lines ... ]

drivers/pci/liveupdate.c +6 -0
+		dev_ser->bdf = pci_dev_id(dev);
+		dev_ser->refcount = 1;
+
+		dev->liveupdate.outgoing = dev_ser;
+		return 0;
+	}

This loop is compatible with KHO block iterators, as mentioned in the previous email. It should be straightforward to convert to them.

include/linux/kho/abi/pci.h line 41

Quoted 13 lines
include/linux/kho/abi/pci.h +0 -0
index 6ebcf817fff4..85def616703d 100644
@@ -23,19 +23,22 @@

[ ... skip 16 lines ... ]
*/
struct pci_dev_ser {

include/linux/kho/abi/pci.h +1 -1
 	u32 domain;
 	u16 bdf;
-	u16 padding;
+	u16 refcount;

Just add refcount to the previous patch, to reduce changes to your own code.

Otherwise looks good

Signature · 1 line
Pasha Tatashin <[email protected]>
#23 lore raw

PATCH v6 3/12 PCI: liveupdate: Track incoming preserved PCI devices

In reply to David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
 drivers/pci/liveupdate.h       |   5 +
 drivers/pci/probe.c            |   3 +
 include/linux/pci_liveupdate.h |  13 ++
 6 files changed, 251 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 6c618830cf61..0e262c0ceb43 100644
@@ -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
index 10c9b65aa242..e68ae5c172d4 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	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 +228 -2

266 lines not on this page · raw message

drivers/pci/liveupdate.h +5 -0
index b2335581f8d0..eaaa3559fd77 100644
@@ -11,8 +11,13 @@
 #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)
 {
 }
drivers/pci/probe.c +3 -0
index b88109a8dfe4..2e2be8af6976 100644
@@ -2070,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);
 
@@ -2193,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;
 
include/linux/pci_liveupdate.h +13 -0
index cfcfbfa73af7..cfdc3d62ec02 100644
@@ -16,9 +16,11 @@
 /**
  * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
  * @outgoing: State preserved for the next kernel.
+ * @incoming: State preserved by the previous kernel.
  */
 struct pci_liveupdate {
 	struct pci_dev_ser *outgoing;
+	struct pci_dev_ser *incoming;
 };
 
 struct pci_dev;
@@ -28,6 +30,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)
 {
@@ -46,6 +50,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.746.g67dd491aae-goog
#24 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Critical] pci_flb_retrieve() frees the incoming preserved memory (ser) on failure, causing a…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] pci_flb_retrieve() frees the incoming preserved memory (ser) on failure, causing a Use-After-Free when the core retries.
- [Medium] Leaked reference to the incoming FLB when a device's preserved refcount is corrupted.
--

commit 5af02c95c00f317cc5c4fa203b8b79373f2759a5
Author: David Matlack <[email protected]>

PCI: liveupdate: Track incoming preserved PCI devices

During PCI enumeration, the PCI core fetches serialized state from the Live Update Orchestrator to identify incoming preserved devices. This patch adds tracking and lifecycle management APIs for these devices.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index 065d5af822f74..96c43b84532cc 100644

[ ... ]

Quoted 28 lines
drivers/pci/liveupdate.c +23 -1
@@ -128,13 +157,49 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
 
 static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
 {
-	args->obj = phys_to_virt(args->data);
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	int ret = -ENOMEM;
+	u32 i;
+
+	incoming = kmalloc_obj(*incoming);
+	if (!incoming)
+		goto err_restore_free;
+
+	incoming->ser = ser;
+	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;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			goto err_xa_destroy;

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 192

Quoted 10 lines
drivers/pci/liveupdate.c +9 -0
+	}
+
+	args->obj = incoming;
 	return 0;
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);

[Severity: Critical]
Does this error path create a use-after-free if the retrieval is retried?

If pci_flb_retrieve() fails here (e.g. from -ENOMEM), kho_restore_free() frees the preserved physical memory for the incoming state. However, the Live Update Orchestrator propagates this error without marking the FLB as retrieved or finished.

When the next PCI device is enumerated, pci_liveupdate_setup_device() calls pci_liveupdate_flb_get_incoming() again, which invokes pci_flb_retrieve() a second time with the exact same data address. The function will then attempt to read incoming->ser->max_nr_devices from memory that was already freed.

Quoted 2 lines
drivers/pci/liveupdate.c +1 -0
+	return ret;
 }

[ ... ]

Quoted 11 lines
drivers/pci/liveupdate.c +7 -0
@@ -280,7 +430,83 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
 		pci_liveupdate_unpreserve(dev);
 	}
+
+	if (READ_ONCE(dev->liveupdate.incoming)) {
+		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
+		pci_liveupdate_finish(dev);
+	}
+}
+

drivers/pci/liveupdate.c void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 449

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	if (!dev->liveupdate.incoming) {
+		pci_warn(dev, "Cannot finish preserving an unpreserved device\n");
+		return;
+	}
+
+	if (dev->liveupdate.incoming->refcount != 1) {
+		pci_WARN(dev, 1, "Preserved device has a corrupted refcount!\n");
+		return;

[Severity: Medium]
Can this early return leak a reference to the incoming FLB?

In pci_liveupdate_setup_device(), a reference is held to protect dev->liveupdate.incoming. This reference is expected to be released via pci_liveupdate_flb_put_incoming() at the end of this function.

By returning early here when the refcount is corrupted, we skip the put call, leaking the reference. Furthermore, dev->liveupdate.incoming is left non-NULL, which means pci_liveupdate_cleanup_device() might later try to finish it again and hit this same leak. Should this error path drop the reference and clear the incoming pointer?

Quoted 14 lines
drivers/pci/liveupdate.c +14 -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;
+
+	pci_info(dev, "Device is finished participating in Live Update\n");
+	dev->liveupdate.incoming = NULL;
+	ser->nr_devices--;
+	pci_liveupdate_flb_put_incoming();
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#25 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 67 lines · On Fri, May 22, 2026 at 2:13 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] pci_flb_retrieve() frees the incoming preserved memory (ser) on failure, causing a Use-After-Free when the core retries.
- [Medium] Leaked reference to the incoming FLB when a device's preserved refcount is corrupted.
--

commit 5af02c95c00f317cc5c4fa203b8b79373f2759a5
Author: David Matlack <[email protected]>

PCI: liveupdate: Track incoming preserved PCI devices

During PCI enumeration, the PCI core fetches serialized state from the Live Update Orchestrator to identify incoming preserved devices. This patch adds tracking and lifecycle management APIs for these devices.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index 065d5af822f74..96c43b84532cc 100644

[ ... ]

Quoted 28 lines
drivers/pci/liveupdate.c +23 -1
@@ -128,13 +157,49 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
 
 static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
 {
-     args->obj = phys_to_virt(args->data);
+     struct pci_ser *ser = phys_to_virt(args->data);
+     struct pci_flb_incoming *incoming;
+     int ret = -ENOMEM;
+     u32 i;
+
+     incoming = kmalloc_obj(*incoming);
+     if (!incoming)
+             goto err_restore_free;
+
+     incoming->ser = ser;
+     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;
+
+             if (!dev_ser->refcount)
+                     continue;
+
+             key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+             ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+             if (ret)
+                     goto err_xa_destroy;

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 192

Quoted 10 lines
drivers/pci/liveupdate.c +9 -0
+     }
+
+     args->obj = incoming;
      return 0;
+
+err_xa_destroy:
+     xa_destroy(&incoming->xa);
+     kfree(incoming);
+err_restore_free:
+     kho_restore_free(ser);

[Severity: Critical]
Does this error path create a use-after-free if the retrieval is retried?

If pci_flb_retrieve() fails here (e.g. from -ENOMEM), kho_restore_free() frees the preserved physical memory for the incoming state. However, the Live Update Orchestrator propagates this error without marking the FLB as retrieved or finished.

Quoted 5 lines

When the next PCI device is enumerated, pci_liveupdate_setup_device() calls pci_liveupdate_flb_get_incoming() again, which invokes pci_flb_retrieve() a second time with the exact same data address. The function will then attempt to read incoming->ser->max_nr_devices from memory that was already freed.

Yes but that is a bug in LUO. It should not be attempting FLB retrieve() multiple times.

(Pasha and Pratyush FYI)

Quoted 33 lines
Quoted 3 lines
drivers/pci/liveupdate.c +1 -0
+     return ret;
 }

[ ... ]

Quoted 11 lines
drivers/pci/liveupdate.c +7 -0
@@ -280,7 +430,83 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
              pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
              pci_liveupdate_unpreserve(dev);
      }
+
+     if (READ_ONCE(dev->liveupdate.incoming)) {
+             pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
+             pci_liveupdate_finish(dev);
+     }
+}
+

drivers/pci/liveupdate.c void pci_liveupdate_cleanup_device(struct pci_dev *dev) · line 449

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+     if (!dev->liveupdate.incoming) {
+             pci_warn(dev, "Cannot finish preserving an unpreserved device\n");
+             return;
+     }
+
+     if (dev->liveupdate.incoming->refcount != 1) {
+             pci_WARN(dev, 1, "Preserved device has a corrupted refcount!\n");
+             return;

[Severity: Medium]
Can this early return leak a reference to the incoming FLB?

In pci_liveupdate_setup_device(), a reference is held to protect dev->liveupdate.incoming. This reference is expected to be released via pci_liveupdate_flb_put_incoming() at the end of this function.

Quoted 5 lines

By returning early here when the refcount is corrupted, we skip the put call, leaking the reference. Furthermore, dev->liveupdate.incoming is left non-NULL, which means pci_liveupdate_cleanup_device() might later try to finish it again and hit this same leak. Should this error path drop the reference and clear the incoming pointer?

Yes but dropping a reference to the incoming FLB when the refcount is corrupted could lead to use-after-free. Logging a warning and leaking FLB seems like the safer approach, aside from panicking.

Reviewed-by on v6 3/12
#26 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

In reply to David Matlack
Quoted 53 lines · David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
 drivers/pci/liveupdate.h       |   5 +
 drivers/pci/probe.c            |   3 +
 include/linux/pci_liveupdate.h |  13 ++
 6 files changed, 251 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 6c618830cf61..0e262c0ceb43 100644
@@ -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 +0 -0
index 10c9b65aa242..e68ae5c172d4 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 333

Quoted 5 lines
drivers/pci/Kconfig +1 -1
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	depends on PCI && LIVEUPDATE
+	depends on PCI && LIVEUPDATE && 64BIT

I see that the static assertions in Patch 1 work because of the 64BIT enforcement here. In that case, should we have the assertions check u64?

Quoted 4 lines
drivers/pci/Kconfig +0 -0
 	help
 	  Enable PCI core support for preserving PCI devices across Live
 	  Update. This, in combination with support in a device's driver,

[...]

Quoted 26 lines

static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) {

drivers/pci/Kconfig +23 -1
-	args->obj = phys_to_virt(args->data);
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	int ret = -ENOMEM;
+	u32 i;
+
+	incoming = kmalloc_obj(*incoming);
+	if (!incoming)
+		goto err_restore_free;
+
+	incoming->ser = ser;
+	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;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			goto err_xa_destroy;

drivers/pci/Kconfig

Quoted 10 lines
drivers/pci/Kconfig +9 -0
+	}
+
+	args->obj = incoming;
 	return 0;
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);

I tend to partly agree with Sashiko[1] here.. it raises a policy-hole. We may need a policy here, the options I have in mind are:

1. Retrieve shall ONLY be tried once, if it fails (like -ENOMEM in the xArray alloc), it's a liveupdate failure. We can't retry liveupdate.

2. Retrying retrieve is allowed.

The only downside with option 1 is, the user may want flexibility due to certain subsystems OR may choose NOT to use the proposed LUOd and instead have its own user-space component which might try funny things or have a different use-case.

In such a situation, the system may have transiently run out of memory during the kexec transition (for e.g. a subsystem uses GFP_ATOMIC to allocate memory and temporarily runs out of the atomic pool). [Note we removed it in IOMMU v1 [2] but subsystems may have a use-case for it]

If the kernel frees the KHO page on the first failure, it removes any chance of recovery. :/

Thus, it might make sense to let the user decide if it wants to fail the liveupdate or retry again based on the failure type / source?

[...]

The changes LGTM, except for policy-based, kho_restore_free discussion.

Thanks,
Praan

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/

#27 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 55 lines · Pranjal Shrivastava
Quoted 54 lines · David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
 drivers/pci/liveupdate.h       |   5 +
 drivers/pci/probe.c            |   3 +
 include/linux/pci_liveupdate.h |  13 ++
 6 files changed, 251 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 6c618830cf61..0e262c0ceb43 100644
@@ -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 +0 -0
index 10c9b65aa242..e68ae5c172d4 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
Quoted 7 lines

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 333

Quoted 4 lines
drivers/pci/Kconfig +1 -1
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	depends on PCI && LIVEUPDATE
+	depends on PCI && LIVEUPDATE && 64BIT

I see that the static assertions in Patch 1 work because of the 64BIT enforcement here. In that case, should we have the assertions check u64?

The static asserts have nothing to do with the 64BIT enforcement here. The static asserts just verify that the array elements in struct pci_ser are naturally aligned (unsigned long) so they can be accessed efficiently. The requirement here for CONFIG_64BIT is for the xarray key.

Theoretically if we got the xarray to work with 32-bit architectures then we could drop the CONFIG_64BIT requirement here.

Quoted 63 lines
Quoted 5 lines
drivers/pci/Kconfig +0 -0
 	help
 	  Enable PCI core support for preserving PCI devices across Live
 	  Update. This, in combination with support in a device's driver,

[...]

Quoted 26 lines

static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) {

drivers/pci/Kconfig +23 -1
-	args->obj = phys_to_virt(args->data);
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	int ret = -ENOMEM;
+	u32 i;
+
+	incoming = kmalloc_obj(*incoming);
+	if (!incoming)
+		goto err_restore_free;
+
+	incoming->ser = ser;
+	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;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			goto err_xa_destroy;

drivers/pci/Kconfig

Quoted 10 lines
drivers/pci/Kconfig +9 -0
+	}
+
+	args->obj = incoming;
 	return 0;
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);

I tend to partly agree with Sashiko[1] here.. it raises a policy-hole. We may need a policy here, the options I have in mind are:

1. Retrieve shall ONLY be tried once, if it fails (like -ENOMEM in the xArray alloc), it's a liveupdate failure. We can't retry liveupdate.

2. Retrying retrieve is allowed.

The only downside with option 1 is, the user may want flexibility due to certain subsystems OR may choose NOT to use the proposed LUOd and instead have its own user-space component which might try funny things or have a different use-case.

In such a situation, the system may have transiently run out of memory during the kexec transition (for e.g. a subsystem uses GFP_ATOMIC to allocate memory and temporarily runs out of the atomic pool). [Note we removed it in IOMMU v1 [2] but subsystems may have a use-case for it]

Quoted 5 lines

If the kernel frees the KHO page on the first failure, it removes any chance of recovery. :/

Thus, it might make sense to let the user decide if it wants to fail the liveupdate or retry again based on the failure type / source?

The plan is to have LUO enforce that retrieve() is only called once:

https://lore.kernel.org/kexec/[email protected]/

Supporting retry gets complicated since there's many different places where retrieve() could have failed.

Quoted 12 lines

[...]

The changes LGTM, except for policy-based, kho_restore_free discussion.

Thanks,
Praan

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/

#28 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 64 lines · David Matlack
Quoted 55 lines · Pranjal Shrivastava
Quoted 54 lines · David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
 drivers/pci/liveupdate.h       |   5 +
 drivers/pci/probe.c            |   3 +
 include/linux/pci_liveupdate.h |  13 ++
 6 files changed, 251 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 6c618830cf61..0e262c0ceb43 100644
@@ -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 +0 -0
index 10c9b65aa242..e68ae5c172d4 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
Quoted 7 lines

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 333

Quoted 4 lines
drivers/pci/Kconfig +1 -1
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	depends on PCI && LIVEUPDATE
+	depends on PCI && LIVEUPDATE && 64BIT

I see that the static assertions in Patch 1 work because of the 64BIT enforcement here. In that case, should we have the assertions check u64?

Quoted 9 lines

The static asserts have nothing to do with the 64BIT enforcement here. The static asserts just verify that the array elements in struct pci_ser are naturally aligned (unsigned long) so they can be accessed efficiently. The requirement here for CONFIG_64BIT is for the xarray key.

Theoretically if we got the xarray to work with 32-bit architectures then we could drop the CONFIG_64BIT requirement here.

Ack. I see.

[...]

Quoted 26 lines
Quoted 20 lines

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 334

Quoted 1 line
drivers/pci/Kconfig +1 -0
+	kho_restore_free(ser);

I tend to partly agree with Sashiko[1] here.. it raises a policy-hole. We may need a policy here, the options I have in mind are:

1. Retrieve shall ONLY be tried once, if it fails (like -ENOMEM in the xArray alloc), it's a liveupdate failure. We can't retry liveupdate.

2. Retrying retrieve is allowed.

The only downside with option 1 is, the user may want flexibility due to certain subsystems OR may choose NOT to use the proposed LUOd and instead have its own user-space component which might try funny things or have a different use-case.

In such a situation, the system may have transiently run out of memory during the kexec transition (for e.g. a subsystem uses GFP_ATOMIC to allocate memory and temporarily runs out of the atomic pool). [Note we removed it in IOMMU v1 [2] but subsystems may have a use-case for it]

Quoted 5 lines

If the kernel frees the KHO page on the first failure, it removes any chance of recovery. :/

Thus, it might make sense to let the user decide if it wants to fail the liveupdate or retry again based on the failure type / source?

Quoted 6 lines

The plan is to have LUO enforce that retrieve() is only called once:

https://lore.kernel.org/kexec/[email protected]/

Supporting retry gets complicated since there's many different places where retrieve() could have failed.

Ack. Thanks for pointing me to the thread.
In that case, no problem.

Thanks,
Praan

maintainer
#29 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

In reply to David Matlack

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 333

Quoted 10 lines · David Matlack
drivers/pci/Kconfig +1 -1
index 10c9b65aa242..e68ae5c172d4 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	depends on PCI && LIVEUPDATE
+	depends on PCI && LIVEUPDATE && 64BIT

Please move this to the first patch, fewer changes between patches, and also KHO does not support anything but 64-bit mode.

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 192

Quoted 13 lines
drivers/pci/liveupdate.c +0 -0
index 065d5af822f7..96c43b84532c 100644
@@ -128,13 +157,49 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)

[ ... skip 31 lines ... ]

drivers/pci/liveupdate.c +6 -0
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);

This is the pattern we have been enforcing in other places in LUO. If the first retrieval fails, return the same error thereafter.

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 341

Quoted 8 lines
drivers/pci/liveupdate.c +4 -0
@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+	struct pci_flb_incoming *incoming = NULL;
+	int ret;

Maybe make the error return static, and avoid another search through compatible FLBs if it failed before?

1. Add "saved_err;"; if it is set, return it right away.
2. Change all errors to use goto save_err;, and at the end of the function, assign ret to saved_err;

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 362

Quoted 7 lines

[ ... skip 15 lines ... ]

drivers/pci/liveupdate.c +6 -0
+	 * This could mean that no PCI FLB data was passed by the previous
+	 * kernel, but it could also mean the previous kernel used a different
+	 * compatibility string (i.e. a different ABI).
+	 */
+	if (ret == -ENOENT) {
+		pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);

I would assume this is very normal, e.g., no devices were preserved but memfd+hugetlb was preserved. Maybe use pr_debug_once().

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 371

Quoted 9 lines
drivers/pci/liveupdate.c +9 -0
+		return NULL;
+	}
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but it cannot be retrieved.
+	 */
+	if (ret) {
+		WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

No need to print backtrace, please just print a warning:
pr_warn_once("Failed to retrieve incoming FLB data: %pe\n", ERR_PTR(ret));

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 411

Quoted 7 lines

[ ... skip 34 lines ... ]

drivers/pci/liveupdate.c +6 -0
+	 * through pci_liveupdate_finish(). This can happen if PCI core probes
+	 * the same device multiple times, e.g. due to hotplug.
+	 */
+	if (!dev_ser->refcount) {
+		pci_liveupdate_flb_put_incoming();
+		return;

Pleaes use 'goto put_incoming'

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 420

Quoted 9 lines
drivers/pci/liveupdate.c +9 -0
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
+
+	/*
+	 * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
+	 * that dev->liveupdate.incoming does not get freed while it is in use.
+	 */

How would that work? If finish is not called FLB stays around until the next reboot.

Signature · 1 line
Pasha Tatashin <[email protected]>
#30 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 7 lines · Pasha Tatashin
Quoted 6 lines · David Matlack
drivers/pci/Kconfig +0 -0
index 10c9b65aa242..e68ae5c172d4 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
Quoted 7 lines

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 333

Quoted 4 lines
drivers/pci/Kconfig +1 -1
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	depends on PCI && LIVEUPDATE
+	depends on PCI && LIVEUPDATE && 64BIT

Please move this to the first patch, fewer changes between patches, and also KHO does not support anything but 64-bit mode.

I think it is preferred to introduce changes when they are needed. This is the exact patch that requires 64BIT so it makes sense to add the dependency within this patch no?

Quoted 22 lines
Quoted 8 lines
drivers/pci/liveupdate.c +0 -0
index 065d5af822f7..96c43b84532c 100644
@@ -128,13 +157,49 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)

[ ... skip 31 lines ... ]

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 192

Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);

This is the pattern we have been enforcing in other places in LUO. If the first retrieval fails, return the same error thereafter.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
Quoted 7 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 341

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+	struct pci_flb_incoming *incoming = NULL;
+	int ret;

Maybe make the error return static, and avoid another search through compatible FLBs if it failed before?

Good idea, will do.

Quoted 5 lines

1. Add "saved_err;"; if it is set, return it right away.
2. Change all errors to use goto save_err;, and at the end of the function, assign ret to saved_err;

Quoted 10 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 362

Quoted 7 lines

[ ... skip 15 lines ... ]

drivers/pci/liveupdate.c +6 -0
+	 * This could mean that no PCI FLB data was passed by the previous
+	 * kernel, but it could also mean the previous kernel used a different
+	 * compatibility string (i.e. a different ABI).
+	 */
+	if (ret == -ENOENT) {
+		pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);

I would assume this is very normal, e.g., no devices were preserved but memfd+hugetlb was preserved. Maybe use pr_debug_once().

The problem is described in the comment above. The PCI core cannot distinguish between:

1. There was not PCI FLB preserved. and
2. There was PCI FLB preserved, but it was not compatible.

I agree we should not log anything for (1) but (2) definitely requires at least some kind of logging so that's why I went with info-level here and not debug.

Quoted 13 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 371

Quoted 10 lines
drivers/pci/liveupdate.c +9 -0
+		return NULL;
+	}
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but it cannot be retrieved.
+	 */
+	if (ret) {
+		WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

No need to print backtrace, please just print a warning:
pr_warn_once("Failed to retrieve incoming FLB data: %pe\n", ERR_PTR(ret));

SGTM

Quoted 10 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 411

Quoted 8 lines

[ ... skip 34 lines ... ]

drivers/pci/liveupdate.c +6 -0
+	 * through pci_liveupdate_finish(). This can happen if PCI core probes
+	 * the same device multiple times, e.g. due to hotplug.
+	 */
+	if (!dev_ser->refcount) {
+		pci_liveupdate_flb_put_incoming();
+		return;

Pleaes use 'goto put_incoming'

SGTM.

Quoted 13 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 420

Quoted 10 lines
drivers/pci/liveupdate.c +9 -0
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
+
+	/*
+	 * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
+	 * that dev->liveupdate.incoming does not get freed while it is in use.
+	 */

How would that work? If finish is not called FLB stays around until the next reboot.

True... I think if the PCI core trusts drivers to call pci_liveupdate_finish() then we don't need to hold onto the incoming reference here.

Quoted 3 lines
Signature · 2 lines
-- 
Pasha Tatashin <[email protected]>
#31 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 11 lines · David Matlack
Quoted 9 lines · Pasha Tatashin

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 341

Quoted 4 lines · David Matlack
drivers/pci/liveupdate.c +4 -0
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+   struct pci_flb_incoming *incoming = NULL;
+   int ret;

Maybe make the error return static, and avoid another search through compatible FLBs if it failed before?

Quoted 1 line

Good idea, will do.

Actually I'm not so sure this should be handled her. I would have to create a statically allocated variable to cache the result, like you said, and I would also have to invalidate it during FLB finish to avoid use-after-free. That defeats one of the main benefits of FLB which is that we don't have to manage global variables.

Can this be handled by LUO instead?

maintainer
#32 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

In reply to David Matlack

Hi David,

Quoted 2 lines · David Matlack
Quoted 1 line · Pasha Tatashin

On Fri, 22 May 2026 20:24:01 +0000, David Matlack <[email protected]> wrote:

[...]

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 420

Quoted 5 lines
drivers/pci/liveupdate.c +5 -0
+
+	/*
+	 * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
+	 * that dev->liveupdate.incoming does not get freed while it is in use.
+	 */

How would that work? If finish is not called FLB stays around until the next reboot.

Quoted 3 lines

True... I think if the PCI core trusts drivers to call pci_liveupdate_finish() then we don't need to hold onto the incoming reference here.

That was my point when I was arguing against refcounts on outgoing FLBs. This is very easy to abuse, especially when we are talking about device drivers. And this refcounting mechanism makes the FLB no longer file-lifecycle-bound, since now it is entirely up to drivers to decide the lifecycle of this data.

I have been thinking about this a bit more in the last couple days, and I wonder if we are doing this right. Here's an idea I have been thinking of.

We should make live update a first class citizen in PCI. Instead of patching in liveupdate via the liveupdate.incoming field, and letting drivers figure out when to use it, we should separate out probe and retrieve paths entirely.

Probe and retrieve are fundamentally different operations. While they may share some common initialization logic for the _software_ state, how they interface with the hardware is completely different. I think mixing the two will result in driver code being more spaghetti by having liveupdate checks sprayed out all over.

This series doesn't add support for any drivers, but looking at some of the code we have downstream, I see this problem. The liveupdate code is all over the place in the driver and it is very hard to wrap one's head around how the device is actually retrieved.

So I think PCI core should track preserved devices, and if the device is preserved, it should skip the probe and wait for retrieve. Retrieve does the full initialization of the device. This fits in with the LUO model as well. You can make retrieve a callback of struct pci_driver and do some wrappers to talk with LUO, so device drivers don't directly interface with LUO at all.

We should do similar things on the shutdown path. Shutdown is a fundamentally different operation from freeze, and so we should separate them out as well.

This solves the lifetime problem as well. When PCI core is initializing, it knows for sure that no retrievals are going to happen. That's because none of the drivers have registered yet. So it can safely access the FLB and initialize its state. After that, drivers can register themselves and start accepting retrieve() calls. Once the last driver goes away, the FLB is freed automatically.

I am sorry for suggesting a big refactor at v6, but the early versions looked good to me at the time, and I only thought more deeply about this when trying to figure out how we can make the lifetimes cleaner.

What do you think? Does this make sense?

Signature · 2 lines
Regards,
Pratyush Yadav
#33 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 25 lines · Pratyush Yadav

Hi David,

Quoted 2 lines · David Matlack
Quoted 1 line · Pasha Tatashin

On Fri, 22 May 2026 20:24:01 +0000, David Matlack <[email protected]> wrote:

[...]

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+  }
+
+  pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+  dev->liveupdate.incoming = dev_ser;
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 420

Quoted 5 lines
drivers/pci/liveupdate.c +5 -0
+
+  /*
+   * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
+   * that dev->liveupdate.incoming does not get freed while it is in use.
+   */

How would that work? If finish is not called FLB stays around until the next reboot.

Quoted 3 lines

True... I think if the PCI core trusts drivers to call pci_liveupdate_finish() then we don't need to hold onto the incoming reference here.

Quoted 5 lines

That was my point when I was arguing against refcounts on outgoing FLBs. This is very easy to abuse, especially when we are talking about device drivers. And this refcounting mechanism makes the FLB no longer file-lifecycle-bound, since now it is entirely up to drivers to decide the lifecycle of this data.

The PCI core holds a reference to the incoming FLB for as long as it maintains a pointer to that FLB in struct pci_dev (dev->liveupdate.incoming). The lifetime of that pointer is aligned with the lifetime of the file as long as the driver calls pci_liveupdate_finish() in its file finish() callback.

If there is a bug in the driver that causes it to not call pci_liveupdate_finish() then the FLB will leak past the file yes. But the alternative would be to leak a pointer to freed memory in dev->liveupdate.incoming, which could lead to UAF.

Leaking the FLB seems safer than UAF, which is why I went for the refcounting approach.

Another approach entirely would be to drop the dev->liveupdate.incoming and do the xarray lookup everytime instead.

Quoted 10 lines

I have been thinking about this a bit more in the last couple days, and I wonder if we are doing this right. Here's an idea I have been thinking of.

We should make live update a first class citizen in PCI. Instead of patching in liveupdate via the liveupdate.incoming field, and letting drivers figure out when to use it, we should separate out probe and retrieve paths entirely.

Quoted 5 lines

Probe and retrieve are fundamentally different operations. While they may share some common initialization logic for the _software_ state, how they interface with the hardware is completely different. I think mixing the two will result in driver code being more spaghetti by having liveupdate checks sprayed out all over.

We are only planning on supporting Live Update for VFIO drivers for the forseeable future. The VFIO work during probe is almost entirely software state setup. The only hardware logic we need to "if" out in the vfio-pci driver's probe() is putting the device into a low-power mode via the runtime power manager. So I don't think we will get any benefit from this approach, and it would be a lot more intrusive to both the PCI core driver framework, and VFIO itself, to support this.

Quoted 4 lines

This series doesn't add support for any drivers, but looking at some of the code we have downstream, I see this problem. The liveupdate code is all over the place in the driver and it is very hard to wrap one's head around how the device is actually retrieved.

You can find the vfio-pci driver changes here:

https://lore.kernel.org/kvm/[email protected]/

Let's keep the discussion focused on upstream VFIO drivers since that is all we are planning to support right now due to LUO's requirement of file-based preservation. The downstream driver changes we are carrying is not reflective of what we want to support upstream.

Quoted 10 lines

So I think PCI core should track preserved devices, and if the device is preserved, it should skip the probe and wait for retrieve. Retrieve does the full initialization of the device. This fits in with the LUO model as well. You can make retrieve a callback of struct pci_driver and do some wrappers to talk with LUO, so device drivers don't directly interface with LUO at all.

We should do similar things on the shutdown path. Shutdown is a fundamentally different operation from freeze, and so we should separate them out as well.

This is speculative. In practice, we haven't needed to change VFIO's shutdown() or probe() functions so far. The only change I anticipate needing is skipping runtime power management "put" during probe() I mentioned above.

If we actually made retrieve() a first-class callback and used that instead of probe(), VFIO would internally just call its probe() function because that would be the cleanest way to set up all the software state it needs to manage the device.

Quoted 6 lines

This solves the lifetime problem as well. When PCI core is initializing, it knows for sure that no retrievals are going to happen. That's because none of the drivers have registered yet. So it can safely access the FLB and initialize its state. After that, drivers can register themselves and start accepting retrieve() calls. Once the last driver goes away, the FLB is freed automatically.

It's not so simple. The PCI core does not really initialize itself. Scanning devices gets triggered externally, e.g. by ACPI, device trees, runtime hotplug events, etc., and that is when the PCI core gets notified about a device. None of this is synchronized with "when drivers have registered", which I assume you are referring to registering with LUO.

Quoted 10 lines

I am sorry for suggesting a big refactor at v6, but the early versions looked good to me at the time, and I only thought more deeply about this when trying to figure out how we can make the lifetimes cleaner.

What do you think? Does this make sense?

--
Regards,
Pratyush Yadav

maintainer
#34 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 38 lines · David Matlack
Quoted 25 lines · Pratyush Yadav

Hi David,

Quoted 2 lines · David Matlack
Quoted 1 line · Pasha Tatashin

On Fri, 22 May 2026 20:24:01 +0000, David Matlack <[email protected]> wrote:

[...]

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+  }
+
+  pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+  dev->liveupdate.incoming = dev_ser;
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 420

Quoted 5 lines
drivers/pci/liveupdate.c +5 -0
+
+  /*
+   * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
+   * that dev->liveupdate.incoming does not get freed while it is in use.
+   */

How would that work? If finish is not called FLB stays around until the next reboot.

Quoted 3 lines

True... I think if the PCI core trusts drivers to call pci_liveupdate_finish() then we don't need to hold onto the incoming reference here.

Quoted 5 lines

That was my point when I was arguing against refcounts on outgoing FLBs. This is very easy to abuse, especially when we are talking about device drivers. And this refcounting mechanism makes the FLB no longer file-lifecycle-bound, since now it is entirely up to drivers to decide the lifecycle of this data.

The PCI core holds a reference to the incoming FLB for as long as it maintains a pointer to that FLB in struct pci_dev (dev->liveupdate.incoming). The lifetime of that pointer is aligned with the lifetime of the file as long as the driver calls pci_liveupdate_finish() in its file finish() callback.

Quoted 7 lines

If there is a bug in the driver that causes it to not call pci_liveupdate_finish() then the FLB will leak past the file yes. But the alternative would be to leak a pointer to freed memory in dev->liveupdate.incoming, which could lead to UAF.

Leaking the FLB seems safer than UAF, which is why I went for the refcounting approach.

Hmm, that does make sense. I still feel a bit odd doing this, but since I don't have any better ideas, I think let's keep this as-is. If this does turn out to be a real problem, we can fix it later. This is kernel internal API anyway.

Also, I didn't know the plan was to do VFIO only. I thought at some point in the near future we will get support for PF drivers. With only VFIO, I think this is a lot simpler and problems will be a lot easier to fix since there is only one user. I was mainly worried about random drivers holding on to PCI/LUO core data structures.

Quoted 3 lines

Another approach entirely would be to drop the dev->liveupdate.incoming and do the xarray lookup everytime instead.

But then you'd need to have a lifetime for the xarray, no?

Quoted 55 lines
Quoted 11 lines

I have been thinking about this a bit more in the last couple days, and I wonder if we are doing this right. Here's an idea I have been thinking of.

We should make live update a first class citizen in PCI. Instead of patching in liveupdate via the liveupdate.incoming field, and letting drivers figure out when to use it, we should separate out probe and retrieve paths entirely.

Quoted 5 lines

Probe and retrieve are fundamentally different operations. While they may share some common initialization logic for the _software_ state, how they interface with the hardware is completely different. I think mixing the two will result in driver code being more spaghetti by having liveupdate checks sprayed out all over.

We are only planning on supporting Live Update for VFIO drivers for the forseeable future. The VFIO work during probe is almost entirely software state setup. The only hardware logic we need to "if" out in the vfio-pci driver's probe() is putting the device into a low-power mode via the runtime power manager. So I don't think we will get any benefit from this approach, and it would be a lot more intrusive to both the PCI core driver framework, and VFIO itself, to support this.

Quoted 4 lines

This series doesn't add support for any drivers, but looking at some of the code we have downstream, I see this problem. The liveupdate code is all over the place in the driver and it is very hard to wrap one's head around how the device is actually retrieved.

You can find the vfio-pci driver changes here:

https://lore.kernel.org/kvm/[email protected]/

Let's keep the discussion focused on upstream VFIO drivers since that is all we are planning to support right now due to LUO's requirement of file-based preservation. The downstream driver changes we are carrying is not reflective of what we want to support upstream.

Quoted 10 lines

So I think PCI core should track preserved devices, and if the device is preserved, it should skip the probe and wait for retrieve. Retrieve does the full initialization of the device. This fits in with the LUO model as well. You can make retrieve a callback of struct pci_driver and do some wrappers to talk with LUO, so device drivers don't directly interface with LUO at all.

We should do similar things on the shutdown path. Shutdown is a fundamentally different operation from freeze, and so we should separate them out as well.

This is speculative. In practice, we haven't needed to change VFIO's shutdown() or probe() functions so far. The only change I anticipate needing is skipping runtime power management "put" during probe() I mentioned above.

Quoted 4 lines

If we actually made retrieve() a first-class callback and used that instead of probe(), VFIO would internally just call its probe() function because that would be the cleanest way to set up all the software state it needs to manage the device.

Right, for only VFIO this doesn't make much sense. I wrote this thinking we will add PF preservation support at some point.

Quoted 19 lines
Quoted 7 lines

This solves the lifetime problem as well. When PCI core is initializing, it knows for sure that no retrievals are going to happen. That's because none of the drivers have registered yet. So it can safely access the FLB and initialize its state. After that, drivers can register themselves and start accepting retrieve() calls. Once the last driver goes away, the FLB is freed automatically.

It's not so simple. The PCI core does not really initialize itself. Scanning devices gets triggered externally, e.g. by ACPI, device trees, runtime hotplug events, etc., and that is when the PCI core gets notified about a device. None of this is synchronized with "when drivers have registered", which I assume you are referring to registering with LUO.

Quoted 4 lines

I am sorry for suggesting a big refactor at v6, but the early versions looked good to me at the time, and I only thought more deeply about this when trying to figure out how we can make the lifetimes cleaner.

Signature · 2 lines
Regards,
Pratyush Yadav
#35 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

In reply to David Matlack
Quoted 36 lines · David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
drivers/pci/liveupdate.h       |   5 +
drivers/pci/probe.c            |   3 +
include/linux/pci_liveupdate.h |  13 ++
6 files changed, 251 insertions(+), 3 deletions(-)

[snip]

Quoted 31 lines

static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) {

drivers/pci/liveupdate.c +26 -1
-	args->obj = phys_to_virt(args->data);
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	int ret = -ENOMEM;
+	u32 i;
+
+	incoming = kmalloc_obj(*incoming);
+	if (!incoming)
+		goto err_restore_free;
+
+	incoming->ser = ser;
+	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;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			goto err_xa_destroy;
+	}
+
+	args->obj = incoming;
	return 0;

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 193

Quoted 7 lines
drivers/pci/liveupdate.c +7 -0
+
+err_xa_destroy:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);
+	return ret;

Hmm.. This is interesting, so the KHO state is freed and it cannot be reused. I see you already pointed out that we are putting an LUO policy to say that the retry is not allowed.

But what should be the behaviour of liveupdate in this regard? Let the system boot in a normal way? This might break other subsystems as they might depend on PCIe restoring state properly. Also I think some of the PCIe state, like device-id, BAR addresses, ACLs etc, might be used as source of truth by other components.

For example, lets say FLB retrieve() of PCIe fails, but succeeds for VFIO/IOMMU, now VFIO/IOMMU are restoring state of a device that is not restored/preserved?

Should this be considered fatal?

Quoted 41 lines

}

static void pci_flb_finish(struct liveupdate_flb_op_args *args) {

drivers/pci/liveupdate.c +5 -1
-	kho_restore_free(args->obj);
+	struct pci_flb_incoming *incoming = args->obj;
+
+	xa_destroy(&incoming->xa);
+	kho_restore_free(incoming->ser);
+	kfree(incoming);

}

static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {

drivers/pci/liveupdate.c +24 -0
@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+	struct pci_flb_incoming *incoming = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
+
+	/* Live Update is not enabled. */
+	if (ret == -EOPNOTSUPP)
+		return NULL;
+
+	/* Live Update is enabled, but there is no incoming FLB data. */
+	if (ret == -ENODATA)
+		return NULL;
+
+	/*
+	 * Live Update is enabled and there is incoming FLB data, but none of it
+	 * matches pci_liveupdate_flb.compatible.
+	 *
+	 * This could mean that no PCI FLB data was passed by the previous
+	 * kernel, but it could also mean the previous kernel used a different
+	 * compatibility string (i.e. a different ABI).
+	 */
+	if (ret == -ENOENT) {

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 371

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+		pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);
+		return NULL;
+	}
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but it cannot be retrieved.
+	 */
+	if (ret) {
+		WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

I think this should probably be considered fatal as mentioned above or the caller of this function should get an error so it can fail. I think retrievel of preserved state should generally not fail unless there is memory corruption or ABI is incompatible.

Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+		return NULL;
+	}
+
+	return incoming;
+}
+

[snip]

Quoted 11 lines
include/linux/pci_liveupdate.h +5 -0
+
+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.746.g67dd491aae-goog

Sami

#36 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 89 lines · Samiullah Khawaja
Quoted 36 lines · David Matlack

During 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       | 230 ++++++++++++++++++++++++++++++++-
drivers/pci/liveupdate.h       |   5 +
drivers/pci/probe.c            |   3 +
include/linux/pci_liveupdate.h |  13 ++
6 files changed, 251 insertions(+), 3 deletions(-)

[snip]

Quoted 28 lines

static int pci_flb_retrieve(struct liveupdate_flb_op_args *args) {

drivers/pci/liveupdate.c +24 -1
-      args->obj = phys_to_virt(args->data);
+      struct pci_ser *ser = phys_to_virt(args->data);
+      struct pci_flb_incoming *incoming;
+      int ret = -ENOMEM;
+      u32 i;
+
+      incoming = kmalloc_obj(*incoming);
+      if (!incoming)
+              goto err_restore_free;
+
+      incoming->ser = ser;
+      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;
+
+              if (!dev_ser->refcount)
+                      continue;
+
+              key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+              ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+              if (ret)
+                      goto err_xa_destroy;
+      }

drivers/pci/liveupdate.c static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args) · line 193

Quoted 10 lines
drivers/pci/liveupdate.c +9 -0
+
+      args->obj = incoming;
      return 0;
+
+err_xa_destroy:
+      xa_destroy(&incoming->xa);
+      kfree(incoming);
+err_restore_free:
+      kho_restore_free(ser);
+      return ret;

Hmm.. This is interesting, so the KHO state is freed and it cannot be reused. I see you already pointed out that we are putting an LUO policy to say that the retry is not allowed.

But what should be the behaviour of liveupdate in this regard? Let the system boot in a normal way? This might break other subsystems as they might depend on PCIe restoring state properly. Also I think some of the PCIe state, like device-id, BAR addresses, ACLs etc, might be used as source of truth by other components.

Quoted 5 lines

For example, lets say FLB retrieve() of PCIe fails, but succeeds for VFIO/IOMMU, now VFIO/IOMMU are restoring state of a device that is not restored/preserved?

Should this be considered fatal?

If PCI FLB retrieve fails then there are certain things that cannot be guaranteed, such as BDF (B specifically) remaining constant. This could lead to memory corruption as the IOMMU may have live translations in place for those specific RequesterIDs. And, in the future, preserved devices may be doing P2P which depends on BARs not moving. If the PCI core cannot retrieve the FLB saved by the previous kernel, it cannot make these guarantees.

So yeah I think you're right that PCI core should treat FLB retrieve as fatal and just panic.

Quoted 48 lines
Quoted 48 lines

}

static void pci_flb_finish(struct liveupdate_flb_op_args *args) {

drivers/pci/liveupdate.c +5 -1
-      kho_restore_free(args->obj);
+      struct pci_flb_incoming *incoming = args->obj;
+
+      xa_destroy(&incoming->xa);
+      kho_restore_free(incoming->ser);
+      kfree(incoming);

}

static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {

drivers/pci/liveupdate.c +31 -0
@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+      struct pci_flb_incoming *incoming = NULL;
+      int ret;
+
+      ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
+
+      /* Live Update is not enabled. */
+      if (ret == -EOPNOTSUPP)
+              return NULL;
+
+      /* Live Update is enabled, but there is no incoming FLB data. */
+      if (ret == -ENODATA)
+              return NULL;
+
+      /*
+       * Live Update is enabled and there is incoming FLB data, but none of it
+       * matches pci_liveupdate_flb.compatible.
+       *
+       * This could mean that no PCI FLB data was passed by the previous
+       * kernel, but it could also mean the previous kernel used a different
+       * compatibility string (i.e. a different ABI).
+       */
+      if (ret == -ENOENT) {
+              pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);
+              return NULL;
+      }
+
+      /*
+       * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+       * but it cannot be retrieved.
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 371

Quoted 3 lines
drivers/pci/liveupdate.c +3 -0
+       */
+      if (ret) {
+              WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

I think this should probably be considered fatal as mentioned above or the caller of this function should get an error so it can fail. I think retrievel of preserved state should generally not fail unless there is memory corruption or ABI is incompatible.

Yeah. I think I will just call panic() here to cover all cases.

Quoted 21 lines
Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+              return NULL;
+      }
+
+      return incoming;
+}
+

[snip]

Quoted 11 lines
include/linux/pci_liveupdate.h +5 -0
+
+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+      return false;
+}

#endif

#endif /* LINUX_PCI_LIVEUPDATE_H */
--
2.54.0.746.g67dd491aae-goog

Sami

#37 lore raw

Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices

Quoted 2 lines · David Matlack
Quoted 1 line · Samiullah Khawaja

[snip]

Quoted 17 lines
Quoted 11 lines

Hmm.. This is interesting, so the KHO state is freed and it cannot be reused. I see you already pointed out that we are putting an LUO policy to say that the retry is not allowed.

But what should be the behaviour of liveupdate in this regard? Let the system boot in a normal way? This might break other subsystems as they might depend on PCIe restoring state properly. Also I think some of the PCIe state, like device-id, BAR addresses, ACLs etc, might be used as source of truth by other components.

Quoted 5 lines

For example, lets say FLB retrieve() of PCIe fails, but succeeds for VFIO/IOMMU, now VFIO/IOMMU are restoring state of a device that is not restored/preserved?

Should this be considered fatal?

Quoted 7 lines

If PCI FLB retrieve fails then there are certain things that cannot be guaranteed, such as BDF (B specifically) remaining constant. This could lead to memory corruption as the IOMMU may have live translations in place for those specific RequesterIDs. And, in the future, preserved devices may be doing P2P which depends on BARs not moving. If the PCI core cannot retrieve the FLB saved by the previous kernel, it cannot make these guarantees.

Yes, this is what I was worried about.

Quoted 3 lines

So yeah I think you're right that PCI core should treat FLB retrieve as fatal and just panic.

This sounds great.

Quoted 58 lines
Quoted 49 lines
Quoted 49 lines

}

static void pci_flb_finish(struct liveupdate_flb_op_args *args) {

drivers/pci/liveupdate.c +5 -1
-      kho_restore_free(args->obj);
+      struct pci_flb_incoming *incoming = args->obj;
+
+      xa_destroy(&incoming->xa);
+      kho_restore_free(incoming->ser);
+      kfree(incoming);

}

static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {

drivers/pci/liveupdate.c +31 -0
@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
+{
+      struct pci_flb_incoming *incoming = NULL;
+      int ret;
+
+      ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
+
+      /* Live Update is not enabled. */
+      if (ret == -EOPNOTSUPP)
+              return NULL;
+
+      /* Live Update is enabled, but there is no incoming FLB data. */
+      if (ret == -ENODATA)
+              return NULL;
+
+      /*
+       * Live Update is enabled and there is incoming FLB data, but none of it
+       * matches pci_liveupdate_flb.compatible.
+       *
+       * This could mean that no PCI FLB data was passed by the previous
+       * kernel, but it could also mean the previous kernel used a different
+       * compatibility string (i.e. a different ABI).
+       */
+      if (ret == -ENOENT) {
+              pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);
+              return NULL;
+      }
+
+      /*
+       * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+       * but it cannot be retrieved.
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_unpreserve(struct pci_dev *dev) · line 371

Quoted 3 lines
drivers/pci/liveupdate.c +3 -0
+       */
+      if (ret) {
+              WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

I think this should probably be considered fatal as mentioned above or the caller of this function should get an error so it can fail. I think retrievel of preserved state should generally not fail unless there is memory corruption or ABI is incompatible.

Quoted 1 line

Yeah. I think I will just call panic() here to cover all cases.

We have an luo specific panic macro/function that you can use.

luo_restore_fail()

Quoted 22 lines
Quoted 22 lines
Quoted 7 lines
drivers/pci/liveupdate.c +6 -0
+              return NULL;
+      }
+
+      return incoming;
+}
+

[snip]

Quoted 11 lines
include/linux/pci_liveupdate.h +5 -0
+
+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+      return false;
+}

#endif

#endif /* LINUX_PCI_LIVEUPDATE_H */
--
2.54.0.746.g67dd491aae-goog

Sami

#38 lore raw

PATCH v6 4/12 PCI: liveupdate: Document driver binding responsibilities

In reply to David Matlack

Document 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
index 96c43b84532c..4f2ec6ffdd16 100644
@@ -70,6 +70,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.746.g67dd491aae-goog
maintainer
#39 lore raw

Re: [PATCH v6 04/12] PCI: liveupdate: Document driver binding responsibilities

Quoted 4 lines · David Matlack

Document 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.

Nit: Should you also note this in the documentation that this can change in the future?

Quoted 33 lines
---
 drivers/pci/liveupdate.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
drivers/pci/liveupdate.c +16 -0
index 96c43b84532c..4f2ec6ffdd16 100644
@@ -70,6 +70,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 · 2 lines
Regards,
Pratyush Yadav
Reviewed-by on v6 4/12
#40 lore raw

Re: [PATCH v6 04/12] PCI: liveupdate: Document driver binding responsibilities

In reply to David Matlack
Quoted 6 lines · David Matlack

Document 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.

Thanks,
Praan

maintainer Reviewed-by on v6 4/12
#41 lore raw

Re: [PATCH v6 04/12] PCI: liveupdate: Document driver binding responsibilities

In reply to David Matlack
Quoted 4 lines · David Matlack

Document 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.

Signature · 1 line
Pasha Tatashin <[email protected]>
Reviewed-by on v6 4/12
#42 lore raw

Re: [PATCH v6 04/12] PCI: liveupdate: Document driver binding responsibilities

In reply to David Matlack
Quoted 40 lines · David Matlack

Document 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
index 96c43b84532c..4f2ec6ffdd16 100644
@@ -70,6 +70,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.746.g67dd491aae-goog
#43 lore raw

PATCH v6 5/12 PCI: liveupdate: Keep bus numbers constant during Live Update

In reply to David Matlack

During 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 until the Live Update is finished. 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                      | 83 ++++++++++++++++++-
 drivers/pci/liveupdate.h                      | 14 ++++
 drivers/pci/probe.c                           | 17 +++-
 include/linux/pci_liveupdate.h                |  4 +
 6 files changed, 119 insertions(+), 7 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +5 -1
index 4d0f545fb3ec..a64af71c2705 100644
@@ -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
index e68ae5c172d4..a597fede1b3b 100644
@@ -330,7 +330,7 @@ config VGA_ARB_MAX_GPUS
 
 config PCI_LIVEUPDATE
 	bool "PCI Live Update Support"
-	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 +82 -1

101 lines not on this page · raw message

drivers/pci/liveupdate.h +14 -0
index eaaa3559fd77..c763255a8de4 100644
@@ -13,6 +13,9 @@
 #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_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
+				      int pass);
+void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -21,6 +24,17 @@ 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_scan_bridge_begin(struct pci_bus *bus,
+						    struct pci_dev *dev,
+						    int pass)
+{
+	return false;
+}
+
+static inline void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass)
+{
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/probe.c +13 -4
index 2e2be8af6976..19965bfd347d 100644
@@ -1402,6 +1402,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 				  int max, unsigned int available_buses,
 				  int pass)
 {
+	bool liveupdate, assign_new_buses = pcibios_assign_all_busses();
 	struct pci_bus *child;
 	u32 buses;
 	u16 bctl;
@@ -1411,6 +1412,10 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	u8 fixed_sec, fixed_sub;
 	int next_busnr;
 
+	liveupdate = pci_liveupdate_scan_bridge_begin(bus, dev, pass);
+	if (liveupdate)
+		assign_new_buses = false;
+
 	/*
 	 * Make sure the bridge is powered on to be able to access config
 	 * space of devices below it.
@@ -1454,8 +1459,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 +1501,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 +1515,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (liveupdate) {
+			pci_err(dev, "Cannot reconfigure bridge during Live Update, skipping\n");
+			goto out;
+		}
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -1572,6 +1580,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);
 
 	pm_runtime_put(&dev->dev);
+	pci_liveupdate_scan_bridge_end(dev, pass);
 
 	return max;
 }
include/linux/pci_liveupdate.h +4 -0
index cfdc3d62ec02..2be98819e313 100644
@@ -17,10 +17,14 @@
  * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
  * @outgoing: State preserved for the next kernel.
  * @incoming: State preserved by the previous kernel.
+ * @inherit_buses: True if the PCI core should inherit the secondary and
+ *                 subordinate bus numbers assigned to this device due to
+ *                 an ongoing Live Update.
  */
 struct pci_liveupdate {
 	struct pci_dev_ser *outgoing;
 	struct pci_dev_ser *incoming;
+	bool inherit_buses;
 };
 
 struct pci_dev;
Signature · 1 line
2.54.0.746.g67dd491aae-goog
#44 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Mixing goto-based control flow with scope-based cleanup helpers (`scoped_guard`) in the function…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Mixing goto-based control flow with scope-based cleanup helpers (scoped_guard) in the function pci_liveupdate_scan_bridge_begin. --

commit 12ae1f010eb68b0654e38ce2800545f03bf1b835
Author: David Matlack <[email protected]>

PCI: liveupdate: Keep bus numbers constant during Live Update

During a Live Update, preserved devices must be allowed to continue performing memory transactions so the kernel cannot change the fabric topology. To keep bus numbers constant, this patch makes the PCI core inherit the secondary and subordinate bus numbers assigned to bridges during scanning if any PCI devices are being preserved.

Quoted 2 lines
drivers/pci/liveupdate.c +0 -0

[ ... ]

Quoted 30 lines
drivers/pci/liveupdate.c +30 -0
+bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
+				      int pass)
+{
+	struct pci_dev *parent = bus->self;
+
+	/*
+	 * On the second pass, reuse the value that was set on the first pass
+	 * so that the passes are consistent with one another.
+	 */
+	if (pass)
+		return dev->liveupdate.inherit_buses;
+
+	/*
+	 * If the parent bridge is being forced to inherit its bus numbers
+	 * during this scan then this bridge must as well, otherwise the PCI
+	 * core could expand this bridge's reservation beyond its parent (which
+	 * cannot expand).
+	 */
+	if (parent && parent->liveupdate.inherit_buses) {
+		dev->liveupdate.inherit_buses = true;
+		goto out;
+	}
+
+	/*
+	 * Otherwise, if there are any incoming preserved devices, force the
+	 * bus numbers to be inherited to avoid changing the bus numbers
+	 * assigned to those devices during enumeration.
+	 *
+	 * To keep things simple, inherit bus numbers on all bridges if any PCI
+	 * devices are incoming, to ensure that no bridge's reservation is

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 453

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+	 * expanded to overlap with a preserved device downstream of a different
+	 * bridge.
+	 */
+	scoped_guard(rwsem_read, &pci_liveupdate.rwsem) {
+		struct pci_flb_incoming *incoming;
+
+		incoming = pci_liveupdate_flb_get_incoming();
+		if (!incoming) {
+			dev->liveupdate.inherit_buses = false;
+			goto out;

[Severity: Low]
Does this mix goto-based control flow with scope-based cleanup helpers?

According to the cleanup subsystem guidelines, functions should either use scope-based cleanups or goto-based cleanups, but never both in the same function.

Could these goto statements be replaced by directly returning the boolean value instead?

Quoted 15 lines
drivers/pci/liveupdate.c +15 -0
+		}
+
+		/*
+		 * It is safe to sample incoming->ser->nr_devices and then
+		 * drop the rwsem since nr_devices will only decrease. Thus the
+		 * only "race" is that the current scan will be overly
+		 * conservative and force bus inheritance.
+		 */
+		dev->liveupdate.inherit_buses = incoming->ser->nr_devices;
+		pci_liveupdate_flb_put_incoming();
+	}
+
+out:
+	return dev->liveupdate.inherit_buses;
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
#45 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

Quoted 63 lines · On Fri, May 22, 2026 at 2:08 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Mixing goto-based control flow with scope-based cleanup helpers (scoped_guard) in the function pci_liveupdate_scan_bridge_begin. --

commit 12ae1f010eb68b0654e38ce2800545f03bf1b835
Author: David Matlack <[email protected]>

PCI: liveupdate: Keep bus numbers constant during Live Update

During a Live Update, preserved devices must be allowed to continue performing memory transactions so the kernel cannot change the fabric topology. To keep bus numbers constant, this patch makes the PCI core inherit the secondary and subordinate bus numbers assigned to bridges during scanning if any PCI devices are being preserved.

Quoted 2 lines
drivers/pci/liveupdate.c +0 -0

[ ... ]

Quoted 30 lines
drivers/pci/liveupdate.c +30 -0
+bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
+                                   int pass)
+{
+     struct pci_dev *parent = bus->self;
+
+     /*
+      * On the second pass, reuse the value that was set on the first pass
+      * so that the passes are consistent with one another.
+      */
+     if (pass)
+             return dev->liveupdate.inherit_buses;
+
+     /*
+      * If the parent bridge is being forced to inherit its bus numbers
+      * during this scan then this bridge must as well, otherwise the PCI
+      * core could expand this bridge's reservation beyond its parent (which
+      * cannot expand).
+      */
+     if (parent && parent->liveupdate.inherit_buses) {
+             dev->liveupdate.inherit_buses = true;
+             goto out;
+     }
+
+     /*
+      * Otherwise, if there are any incoming preserved devices, force the
+      * bus numbers to be inherited to avoid changing the bus numbers
+      * assigned to those devices during enumeration.
+      *
+      * To keep things simple, inherit bus numbers on all bridges if any PCI
+      * devices are incoming, to ensure that no bridge's reservation is

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 453

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+      * expanded to overlap with a preserved device downstream of a different
+      * bridge.
+      */
+     scoped_guard(rwsem_read, &pci_liveupdate.rwsem) {
+             struct pci_flb_incoming *incoming;
+
+             incoming = pci_liveupdate_flb_get_incoming();
+             if (!incoming) {
+                     dev->liveupdate.inherit_buses = false;
+                     goto out;

[Severity: Low]
Does this mix goto-based control flow with scope-based cleanup helpers?

Quoted 6 lines

According to the cleanup subsystem guidelines, functions should either use scope-based cleanups or goto-based cleanups, but never both in the same function.

Could these goto statements be replaced by directly returning the boolean value instead?

That seems reasonable enough, will fix in v7..

Reviewed-by on v6 5/12
#46 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

In reply to David Matlack
Quoted 32 lines · David Matlack

During 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 until the Live Update is finished. 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                      | 83 ++++++++++++++++++-
 drivers/pci/liveupdate.h                      | 14 ++++
 drivers/pci/probe.c                           | 17 +++-
 include/linux/pci_liveupdate.h                |  4 +
 6 files changed, 119 insertions(+), 7 deletions(-)

[...]

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 462

Quoted 13 lines
drivers/pci/liveupdate.c +13 -0
+		incoming = pci_liveupdate_flb_get_incoming();
+		if (!incoming) {
+			dev->liveupdate.inherit_buses = false;
+			goto out;
+		}
+
+		/*
+		 * It is safe to sample incoming->ser->nr_devices and then
+		 * drop the rwsem since nr_devices will only decrease. Thus the
+		 * only "race" is that the current scan will be overly
+		 * conservative and force bus inheritance.
+		 */
+		dev->liveupdate.inherit_buses = incoming->ser->nr_devices;

Nit: inherit_buses is a bool, while compiler will handle it correctly, maybe we could:

dev->liveupdate.inherit_buses = !!incoming->ser->nr_devices

OR

dev->liveupdate.inherit_buses = (incoming->ser->nr_devices > 0)

for readability?

Quoted 7 lines
drivers/pci/liveupdate.c +7 -0
+		pci_liveupdate_flb_put_incoming();
+	}
+
+out:
+	return dev->liveupdate.inherit_buses;
+}
+

[...]

Quoted 12 lines
drivers/pci/probe.c +1 -2
 		/*
@@ -1497,8 +1501,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

drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1521

Quoted 7 lines
drivers/pci/probe.c +4 -0
@@ -1512,6 +1515,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (liveupdate) {
+			pci_err(dev, "Cannot reconfigure bridge during Live Update, skipping\n");
+			goto out;
+		}

Quite helpful! Thanks :)

drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1586

Quoted 12 lines
drivers/pci/probe.c +2 -0
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -1572,6 +1580,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);
 
 	pm_runtime_put(&dev->dev);
+	pci_liveupdate_scan_bridge_end(dev, pass);
 
 	return max;
 }

With the minor nit above,

Thanks,
Praan

maintainer
#47 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

Quoted 53 lines · Pranjal Shrivastava
Quoted 31 lines · David Matlack

During 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 until the Live Update is finished. 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                      | 83 ++++++++++++++++++-
 drivers/pci/liveupdate.h                      | 14 ++++
 drivers/pci/probe.c                           | 17 +++-
 include/linux/pci_liveupdate.h                |  4 +
 6 files changed, 119 insertions(+), 7 deletions(-)

[...]

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 462

Quoted 13 lines
drivers/pci/liveupdate.c +13 -0
+		incoming = pci_liveupdate_flb_get_incoming();
+		if (!incoming) {
+			dev->liveupdate.inherit_buses = false;
+			goto out;
+		}
+
+		/*
+		 * It is safe to sample incoming->ser->nr_devices and then
+		 * drop the rwsem since nr_devices will only decrease. Thus the
+		 * only "race" is that the current scan will be overly
+		 * conservative and force bus inheritance.
+		 */
+		dev->liveupdate.inherit_buses = incoming->ser->nr_devices;

Nit: inherit_buses is a bool, while compiler will handle it correctly, maybe we could:

Quoted 1 line

dev->liveupdate.inherit_buses = !!incoming->ser->nr_devices

+1

Quoted 57 lines

OR

dev->liveupdate.inherit_buses = (incoming->ser->nr_devices > 0)

for readability?

Quoted 7 lines
drivers/pci/liveupdate.c +7 -0
+		pci_liveupdate_flb_put_incoming();
+	}
+
+out:
+	return dev->liveupdate.inherit_buses;
+}
+

[...]

Quoted 12 lines
drivers/pci/probe.c +1 -2
 		/*
@@ -1497,8 +1501,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

drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1521

Quoted 7 lines
drivers/pci/probe.c +4 -0
@@ -1512,6 +1515,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (liveupdate) {
+			pci_err(dev, "Cannot reconfigure bridge during Live Update, skipping\n");
+			goto out;
+		}

Quite helpful! Thanks :)

drivers/pci/probe.c static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, · line 1586

Quoted 12 lines
drivers/pci/probe.c +2 -0
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -1572,6 +1580,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);
 
 	pm_runtime_put(&dev->dev);
+	pci_liveupdate_scan_bridge_end(dev, pass);
 
 	return max;
 }

With the minor nit above,

Thanks,
Praan

maintainer
#48 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

In reply to David Matlack

drivers/pci/liveupdate.c line 121

Quoted 10 lines · David Matlack
drivers/pci/liveupdate.c +1 -1
index 4f2ec6ffdd16..2421bc218916 100644
@@ -103,7 +118,7 @@
 /**
  * struct pci_liveupdate_global - Global state for PCI Live Update support
  * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing
- *         FLBs, and the references to them in struct pci_dev.
+ *         FLBs and references to them in struct pci_dev.

This change does not belong to this patch.

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 415

Quoted 6 lines
drivers/pci/liveupdate.c +2 -0
@@ -396,6 +411,72 @@ static void pci_liveupdate_flb_put_incoming(void)
 	liveupdate_flb_put_incoming(&pci_liveupdate_flb);
 }
 
+bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
+				      int pass)

This function requires a header comment; it is public and not self-descriptive.

Signature · 1 line
Pasha Tatashin <[email protected]>
#49 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

Quoted 13 lines · Pasha Tatashin

drivers/pci/liveupdate.c line 121

Quoted 10 lines · David Matlack
drivers/pci/liveupdate.c +1 -1
index 4f2ec6ffdd16..2421bc218916 100644
@@ -103,7 +118,7 @@
 /**
  * struct pci_liveupdate_global - Global state for PCI Live Update support
  * @rwsem: Reader/writer semaphore used to protect the incoming and outgoing
- *         FLBs, and the references to them in struct pci_dev.
+ *         FLBs and references to them in struct pci_dev.

This change does not belong to this patch.

Yes, will fix.

Quoted 9 lines

drivers/pci/liveupdate.c static void pci_liveupdate_flb_put_incoming(void) · line 415

Quoted 7 lines
drivers/pci/liveupdate.c +2 -0
@@ -396,6 +411,72 @@ static void pci_liveupdate_flb_put_incoming(void)
 	liveupdate_flb_put_incoming(&pci_liveupdate_flb);
 }
 
+bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
+				      int pass)

This function requires a header comment; it is public and not self-descriptive.

It is private to the PCI core, but I can still add a comment in v7.

Quoted 3 lines
Signature · 2 lines
-- 
Pasha Tatashin <[email protected]>
Reviewed-by on v6 5/12
#50 lore raw

Re: [PATCH v6 05/12] PCI: liveupdate: Keep bus numbers constant during Live Update

In reply to David Matlack
Quoted 23 lines · David Matlack

During 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 until the Live Update is finished. 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.

Quoted 9 lines

---

.../admin-guide/kernel-parameters.txt         |  6 +-
drivers/pci/Kconfig                           |  2 +-
drivers/pci/liveupdate.c                      | 83 ++++++++++++++++++-
drivers/pci/liveupdate.h                      | 14 ++++
drivers/pci/probe.c                           | 17 +++-
include/linux/pci_liveupdate.h                |  4 +
6 files changed, 119 insertions(+), 7 deletions(-)
#51 lore raw

PATCH v6 6/12 PCI: liveupdate: Auto-preserve upstream bridges across Live Update

In reply to David Matlack

When 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    | 136 +++++++++++++++++++++++++++++++-----
 include/linux/kho/abi/pci.h |   5 +-
 2 files changed, 122 insertions(+), 19 deletions(-)
drivers/pci/liveupdate.c +119 -17
index 2421bc218916..4c79e19b7f98 100644
@@ -101,6 +101,18 @@
  * If a misconfigured or unconfigured bridge is encountered during enumeration
  * while there are preserved devices, its 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
@@ -261,28 +273,52 @@ static struct pci_ser *pci_liveupdate_flb_get_outgoing(void)
 	return ser;
 }
 
-static void pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev *dev)
+static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev *dev)
 {
 	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
 
 	if (!dev_ser) {
 		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
-		return;
+		return -EINVAL;
+	}
+
+	if (!dev_ser->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
 	}
 
+	if (--dev_ser->refcount)
+		return 0;
+
 	pci_info(dev, "Device will no longer be preserved across next Live Update\n");
 	ser->nr_devices--;
 	memset(dev_ser, 0, sizeof(*dev_ser));
 	dev->liveupdate.outgoing = NULL;
+	return 0;
 }
 
-static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *dev)
+static int pci_liveupdate_preserve_device_again(struct pci_dev *dev)
 {
-	int i;
+	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(struct pci_ser *ser, struct pci_dev *dev)
+{
+	int i;
+
 	if (ser->nr_devices == ser->max_nr_devices)
 		return -ENOSPC;
 
@@ -312,6 +348,52 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d
 	return -ENOSPC;
 }
 
+static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *dev)
+{
+	if (dev->liveupdate.outgoing)
+		return pci_liveupdate_preserve_device_again(dev);
+
+	return __pci_liveupdate_preserve_device(ser, dev);
+}
+
+#define for_each_pci_dev_in_path(_d, _start, _end) \
+	for ((_d) = (_start); (_d) != (_end); (_d) = (_d)->bus->self)
+
+static void __pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					     struct pci_dev *start,
+					     struct pci_dev *end)
+{
+	struct pci_dev *dev;
+
+	for_each_pci_dev_in_path(dev, start, end) {
+		if (pci_liveupdate_unpreserve_device(ser, dev))
+			return;
+	}
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					   struct pci_dev *start)
+{
+	__pci_liveupdate_unpreserve_path(ser, start, /*end=*/NULL);
+}
+
+static int pci_liveupdate_preserve_path(struct pci_ser *ser,
+					struct pci_dev *start)
+{
+	struct pci_dev *dev;
+	int ret;
+
+	for_each_pci_dev_in_path(dev, start, NULL) {
+		ret = pci_liveupdate_preserve_device(ser, dev);
+		if (ret) {
+			__pci_liveupdate_unpreserve_path(ser, start, dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
  * @dev: The PCI device to preserve.
@@ -321,6 +403,9 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d
  * pci_liveupdate_preserve() from their struct liveupdate_file_handler
  * preserve() callback to ensure the outgoing struct pci_ser is already set up.
  *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
  * Returns: 0 on success, <0 on failure.
  */
 int pci_liveupdate_preserve(struct pci_dev *dev)
@@ -336,7 +421,7 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
 	if (IS_ERR(ser))
 		return PTR_ERR(ser);
 
-	return pci_liveupdate_preserve_device(ser, dev);
+	return pci_liveupdate_preserve_path(ser, dev);
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
 
@@ -349,6 +434,9 @@ EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
  * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
  * unpreserve() callback to ensure the outgoing struct pci_ser is already set
  * up.
+ *
+ * pci_liveupdate_unpreserve() automatically unpreserves all bridges upstream of
+ * @dev.
  */
 void pci_liveupdate_unpreserve(struct pci_dev *dev)
 {
@@ -362,7 +450,7 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 		return;
 	}
 
-	pci_liveupdate_unpreserve_device(ser, dev);
+	pci_liveupdate_unpreserve_path(ser, dev);
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
 
@@ -534,29 +622,41 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 	}
 }
 
-static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
+static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
 {
 	if (!dev->liveupdate.incoming) {
 		pci_warn(dev, "Cannot finish preserving an unpreserved device\n");
-		return;
+		return -EINVAL;
 	}
 
-	if (dev->liveupdate.incoming->refcount != 1) {
-		pci_WARN(dev, 1, "Preserved device has a corrupted refcount!\n");
-		return;
+	if (!dev->liveupdate.incoming->refcount) {
+		pci_WARN(dev, 1, "Preserved device has a 0 refcount!\n");
+		return -EINVAL;
 	}
 
 	/*
-	 * 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.
+	 * 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.
 	 */
-	dev->liveupdate.incoming->refcount = 0;
+	if (--dev->liveupdate.incoming->refcount)
+		return 0;
 
 	pci_info(dev, "Device is finished participating in Live Update\n");
 	dev->liveupdate.incoming = NULL;
 	ser->nr_devices--;
 	pci_liveupdate_flb_put_incoming();
+	return 0;
+}
+
+static void pci_liveupdate_finish_path(struct pci_ser *ser, struct pci_dev *start)
+{
+	struct pci_dev *dev;
+
+	for_each_pci_dev_in_path(dev, start, NULL) {
+		if (pci_liveupdate_finish_device(ser, dev))
+			return;
+	}
 }
 
 /**
@@ -568,6 +668,8 @@ static void pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *de
  * 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)
 {
@@ -581,7 +683,7 @@ void pci_liveupdate_finish(struct pci_dev *dev)
 		return;
 	}
 
-	pci_liveupdate_finish_device(incoming->ser, dev);
+	pci_liveupdate_finish_path(incoming->ser, dev);
 	pci_liveupdate_flb_put_incoming();
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
include/linux/kho/abi/pci.h +3 -2
index 85def616703d..c86518be4ce7 100644
@@ -23,7 +23,7 @@
  * incrementing the version number in the PCI_LUO_FLB_COMPATIBLE string.
  */
 
-#define PCI_LUO_FLB_COMPATIBLE "pci-v2"
+#define PCI_LUO_FLB_COMPATIBLE "pci-v3"
 
 /**
  * struct pci_dev_ser - Serialized state about a single PCI device.
@@ -32,7 +32,8 @@
  * @bdf: The device's PCI bus, device, and function number.
  * @refcount: Reference count used by the PCI core to keep track of whether it
  *            is done using a device's struct pci_dev_ser. The value of the
- *            refcount is equal to 1 when the struct pci_dev_ser is in use, and
+ *            refcount is equal to the number of preserved devices at or below
+ *            it in the PCI hierarchy when the struct pci_dev_ser is in use, and
  *            0 otherwise.
  */
 struct pci_dev_ser {
Signature · 1 line
2.54.0.746.g67dd491aae-goog
#52 lore raw

Re: [PATCH v6 06/12] PCI: liveupdate: Auto-preserve upstream bridges across Live Update

Quoted 21 lines · David Matlack

When 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    | 136 +++++++++++++++++++++++++++++++-----
 include/linux/kho/abi/pci.h |   5 +-
 2 files changed, 122 insertions(+), 19 deletions(-)

[...]

drivers/pci/liveupdate.c static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d · line 369

Quoted 12 lines
drivers/pci/liveupdate.c +12 -0
+
+#define for_each_pci_dev_in_path(_d, _start, _end) \
+	for ((_d) = (_start); (_d) != (_end); (_d) = (_d)->bus->self)
+
+static void __pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					     struct pci_dev *start,
+					     struct pci_dev *end)
+{
+	struct pci_dev *dev;
+
+	for_each_pci_dev_in_path(dev, start, end) {
+		if (pci_liveupdate_unpreserve_device(ser, dev))

I might be reading this wrong but are we leaking some upstream devs if an intermediate node fails?

			  EP0
			/
Assume we have: RC -> B1 -> B2 
				\
				 EP1

and EP0 & EP1 were preserved successfully.

And then we try unpreserving EP1, we follow:

unpreserve EP1 -> unpreserve B2 failed due to a corruption.

This aborts the loop, skipping B1 and RC completely?
Their refcounts remain elevated, effectively leaking them as preserved state permanently? (i.e. if we unpreserve EP0 after this, B1 & RC will still get preserved).

Quoted 41 lines
drivers/pci/liveupdate.c +30 -0
+			return;
+	}
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					   struct pci_dev *start)
+{
+	__pci_liveupdate_unpreserve_path(ser, start, /*end=*/NULL);
+}
+
+static int pci_liveupdate_preserve_path(struct pci_ser *ser,
+					struct pci_dev *start)
+{
+	struct pci_dev *dev;
+	int ret;
+
+	for_each_pci_dev_in_path(dev, start, NULL) {
+		ret = pci_liveupdate_preserve_device(ser, dev);
+		if (ret) {
+			__pci_liveupdate_unpreserve_path(ser, start, dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
  * @dev: The PCI device to preserve.
@@ -321,6 +403,9 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d
  * pci_liveupdate_preserve() from their struct liveupdate_file_handler
  * preserve() callback to ensure the outgoing struct pci_ser is already set up.
  *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
  * Returns: 0 on success, <0 on failure.
  */
 int pci_liveupdate_preserve(struct pci_dev *dev)

drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 424

Quoted 5 lines
drivers/pci/liveupdate.c +1 -1
@@ -336,7 +421,7 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
 	if (IS_ERR(ser))
 		return PTR_ERR(ser);
 
-	return pci_liveupdate_preserve_device(ser, dev);
+	return pci_liveupdate_preserve_path(ser, dev);

Minor nit: I might be too nitpicky here (and it's NOT a strong opinion) but naming it pci_liveupdate_preserve_path_for_dev() reads better to me.

drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 427

Quoted 3 lines
drivers/pci/liveupdate.c +0 -0
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
 

[...]

Thanks,
Praan

#53 lore raw

Re: [PATCH v6 06/12] PCI: liveupdate: Auto-preserve upstream bridges across Live Update

Quoted 49 lines · Pranjal Shrivastava
Quoted 21 lines · David Matlack

When 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    | 136 +++++++++++++++++++++++++++++++-----
 include/linux/kho/abi/pci.h |   5 +-
 2 files changed, 122 insertions(+), 19 deletions(-)

[...]

drivers/pci/liveupdate.c static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d · line 369

Quoted 12 lines
drivers/pci/liveupdate.c +12 -0
+
+#define for_each_pci_dev_in_path(_d, _start, _end) \
+	for ((_d) = (_start); (_d) != (_end); (_d) = (_d)->bus->self)
+
+static void __pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					     struct pci_dev *start,
+					     struct pci_dev *end)
+{
+	struct pci_dev *dev;
+
+	for_each_pci_dev_in_path(dev, start, end) {
+		if (pci_liveupdate_unpreserve_device(ser, dev))

I might be reading this wrong but are we leaking some upstream devs if an intermediate node fails?

			  EP0
			/
Assume we have: RC -> B1 -> B2 
				\
				 EP1

and EP0 & EP1 were preserved successfully.

Quoted 8 lines

And then we try unpreserving EP1, we follow:

unpreserve EP1 -> unpreserve B2 failed due to a corruption.

This aborts the loop, skipping B1 and RC completely?
Their refcounts remain elevated, effectively leaking them as preserved state permanently? (i.e. if we unpreserve EP0 after this, B1 & RC will still get preserved).

Yes, but that would only happen if there is some sort of kernel bug or silent data corruption. I guess we could proceed with trying to unpreserve the bridges upstream. But I opted to log a big warning and bail immediately.

pci_liveupdate_finish_path() has the same behavior BTW.

Quoted 45 lines
Quoted 45 lines
drivers/pci/liveupdate.c +30 -0
+			return;
+	}
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					   struct pci_dev *start)
+{
+	__pci_liveupdate_unpreserve_path(ser, start, /*end=*/NULL);
+}
+
+static int pci_liveupdate_preserve_path(struct pci_ser *ser,
+					struct pci_dev *start)
+{
+	struct pci_dev *dev;
+	int ret;
+
+	for_each_pci_dev_in_path(dev, start, NULL) {
+		ret = pci_liveupdate_preserve_device(ser, dev);
+		if (ret) {
+			__pci_liveupdate_unpreserve_path(ser, start, dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
  * @dev: The PCI device to preserve.
@@ -321,6 +403,9 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d
  * pci_liveupdate_preserve() from their struct liveupdate_file_handler
  * preserve() callback to ensure the outgoing struct pci_ser is already set up.
  *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
  * Returns: 0 on success, <0 on failure.
  */
 int pci_liveupdate_preserve(struct pci_dev *dev)
@@ -336,7 +421,7 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
 	if (IS_ERR(ser))
 		return PTR_ERR(ser);
 
Quoted 5 lines

drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 424

Quoted 2 lines
drivers/pci/liveupdate.c +1 -1
-	return pci_liveupdate_preserve_device(ser, dev);
+	return pci_liveupdate_preserve_path(ser, dev);

Minor nit: I might be too nitpicky here (and it's NOT a strong opinion) but naming it pci_liveupdate_preserve_path_for_dev() reads better to me.

Noted :). I'll keep the current name for now since that is pretty long, but if anyone else votes for it I'm happy to be overridden.

Quoted 8 lines

drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 427

Quoted 3 lines
drivers/pci/liveupdate.c +0 -0
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
 

[...]

Thanks,
Praan

#54 lore raw

Re: [PATCH v6 06/12] PCI: liveupdate: Auto-preserve upstream bridges across Live Update

Quoted 59 lines · David Matlack
Quoted 49 lines · Pranjal Shrivastava
Quoted 21 lines · David Matlack

When 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    | 136 +++++++++++++++++++++++++++++++-----
 include/linux/kho/abi/pci.h |   5 +-
 2 files changed, 122 insertions(+), 19 deletions(-)

[...]

drivers/pci/liveupdate.c static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d · line 369

Quoted 12 lines
drivers/pci/liveupdate.c +12 -0
+
+#define for_each_pci_dev_in_path(_d, _start, _end) \
+	for ((_d) = (_start); (_d) != (_end); (_d) = (_d)->bus->self)
+
+static void __pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					     struct pci_dev *start,
+					     struct pci_dev *end)
+{
+	struct pci_dev *dev;
+
+	for_each_pci_dev_in_path(dev, start, end) {
+		if (pci_liveupdate_unpreserve_device(ser, dev))

I might be reading this wrong but are we leaking some upstream devs if an intermediate node fails?

			  EP0
			/
Assume we have: RC -> B1 -> B2 
				\
				 EP1

and EP0 & EP1 were preserved successfully.

Quoted 8 lines

And then we try unpreserving EP1, we follow:

unpreserve EP1 -> unpreserve B2 failed due to a corruption.

This aborts the loop, skipping B1 and RC completely?
Their refcounts remain elevated, effectively leaking them as preserved state permanently? (i.e. if we unpreserve EP0 after this, B1 & RC will still get preserved).

Quoted 6 lines

Yes, but that would only happen if there is some sort of kernel bug or silent data corruption. I guess we could proceed with trying to unpreserve the bridges upstream. But I opted to log a big warning and bail immediately.

pci_liveupdate_finish_path() has the same behavior BTW.

Fair point. I agree we are in a broken state if we hit this.

I was originally thinking of a situation where we'd want to keep the failure localized. For example: unpreserve EP1 fails -> user sees the warning -> resets EP1 -> retries preserving it later.

But given the recent discussion/decision that retrieve operations will no longer be retried, I guess there isn't really a use-case for retrying anything. It makes sense to just bail here.

Quoted 46 lines
Quoted 46 lines
Quoted 46 lines
drivers/pci/liveupdate.c +30 -0
+			return;
+	}
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_ser *ser,
+					   struct pci_dev *start)
+{
+	__pci_liveupdate_unpreserve_path(ser, start, /*end=*/NULL);
+}
+
+static int pci_liveupdate_preserve_path(struct pci_ser *ser,
+					struct pci_dev *start)
+{
+	struct pci_dev *dev;
+	int ret;
+
+	for_each_pci_dev_in_path(dev, start, NULL) {
+		ret = pci_liveupdate_preserve_device(ser, dev);
+		if (ret) {
+			__pci_liveupdate_unpreserve_path(ser, start, dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
  * @dev: The PCI device to preserve.
@@ -321,6 +403,9 @@ static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *d
  * pci_liveupdate_preserve() from their struct liveupdate_file_handler
  * preserve() callback to ensure the outgoing struct pci_ser is already set up.
  *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
  * Returns: 0 on success, <0 on failure.
  */
 int pci_liveupdate_preserve(struct pci_dev *dev)
@@ -336,7 +421,7 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
 	if (IS_ERR(ser))
 		return PTR_ERR(ser);
 
Quoted 8 lines
Quoted 5 lines

drivers/pci/liveupdate.c int pci_liveupdate_preserve(struct pci_dev *dev) · line 424

Quoted 2 lines
drivers/pci/liveupdate.c +1 -1
-	return pci_liveupdate_preserve_device(ser, dev);
+	return pci_liveupdate_preserve_path(ser, dev);

Minor nit: I might be too nitpicky here (and it's NOT a strong opinion) but naming it pci_liveupdate_preserve_path_for_dev() reads better to me.

Noted :). I'll keep the current name for now since that is pretty long, but if anyone else votes for it I'm happy to be overridden.

Sounds good.

Thanks,
Praan

#55 lore raw

PATCH v6 7/12 PCI: Refactor matching logic for pci_dev_acs_ops

In reply to David Matlack

Refactor the logic to match devices to pci_dev_acs_ops by factoring out the loop and device matching into its own routine. This eliminates some duplicate code between pci_dev_specific_enable_acs() and pci_dev_specific_disable_acs_redir(), and will also be used in a subsequent commit to check if a device requires device-specific enable_acs() during a Live Update.

No functional change intended.

---
 drivers/pci/quirks.c | 50 ++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 30 deletions(-)
drivers/pci/quirks.c +20 -30
index caaed1a01dc0..171caec2bc47 100644
@@ -5384,9 +5384,6 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
  */
 static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
 {
-	if (!pci_quirk_intel_pch_acs_match(dev))
-		return -ENOTTY;
-
 	if (pci_quirk_enable_intel_lpc_acs(dev)) {
 		pci_warn(dev, "Failed to enable Intel PCH ACS quirk\n");
 		return 0;
@@ -5406,9 +5403,6 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5436,9 +5430,6 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 	int pos;
 	u32 cap, ctrl;
 
-	if (!pci_quirk_intel_spt_pch_acs_match(dev))
-		return -ENOTTY;
-
 	pos = dev->acs_cap;
 	if (!pos)
 		return -ENOTTY;
@@ -5458,22 +5449,25 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 static const struct pci_dev_acs_ops {
 	u16 vendor;
 	u16 device;
+	bool (*match)(struct pci_dev *dev);
 	int (*enable_acs)(struct pci_dev *dev);
 	int (*disable_acs_redir)(struct pci_dev *dev);
 } pci_dev_acs_ops[] = {
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];
@@ -5481,33 +5475,29 @@ int pci_dev_specific_enable_acs(struct pci_dev *dev)
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
 		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
-		}
+		    p->match(dev))
+			return p;
 	}
 
+	return NULL;
+}
+
+int pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	if (p && p->enable_acs)
+		return p->enable_acs(dev);
+
 	return -ENOTTY;
 }
 
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
 {
-	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
 
-	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
-		p = &pci_dev_acs_ops[i];
-		if ((p->vendor == dev->vendor ||
-		     p->vendor == (u16)PCI_ANY_ID) &&
-		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->disable_acs_redir) {
-			ret = p->disable_acs_redir(dev);
-			if (ret >= 0)
-				return ret;
-		}
-	}
+	if (p && p->disable_acs_redir)
+		return p->disable_acs_redir(dev);
 
 	return -ENOTTY;
 }
Signature · 1 line
2.54.0.746.g67dd491aae-goog
Reviewed-by on v6 7/12
#56 lore raw

Re: [PATCH v6 07/12] PCI: Refactor matching logic for pci_dev_acs_ops

Quoted 14 lines · David Matlack

Refactor the logic to match devices to pci_dev_acs_ops by factoring out the loop and device matching into its own routine. This eliminates some duplicate code between pci_dev_specific_enable_acs() and pci_dev_specific_disable_acs_redir(), and will also be used in a subsequent commit to check if a device requires device-specific enable_acs() during a Live Update.

No functional change intended.

---
 drivers/pci/quirks.c | 50 ++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 30 deletions(-)

[...]

Quoted 22 lines

} pci_dev_acs_ops[] = {

drivers/pci/quirks.c +4 -2
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];

drivers/pci/quirks.c int pci_dev_specific_enable_acs(struct pci_dev *dev) · line 5479

Quoted 10 lines
drivers/pci/quirks.c +2 -5
@@ -5481,33 +5475,29 @@ int pci_dev_specific_enable_acs(struct pci_dev *dev)
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
 		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
-		}
+		    p->match(dev))
+			return p;

Nit:
Should we check if (p->match != NULL) like we check for p->enable_acs & p->disable_acs_redir().

Otherwise, it seems like we're mandating the existence of a match op in the pci_dev_acs_ops here? Today, we just have two Intel entries in that array, both of which need the match op. However, AFAICT, it shouldn't be mandatory for future SoCs that might only need a simple vid + devid match

[...]

with that nit:

Thanks,
Praan

#57 lore raw

Re: [PATCH v6 07/12] PCI: Refactor matching logic for pci_dev_acs_ops

Quoted 55 lines · Pranjal Shrivastava
Quoted 14 lines · David Matlack

Refactor the logic to match devices to pci_dev_acs_ops by factoring out the loop and device matching into its own routine. This eliminates some duplicate code between pci_dev_specific_enable_acs() and pci_dev_specific_disable_acs_redir(), and will also be used in a subsequent commit to check if a device requires device-specific enable_acs() during a Live Update.

No functional change intended.

---
 drivers/pci/quirks.c | 50 ++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 30 deletions(-)

[...]

Quoted 22 lines

} pci_dev_acs_ops[] = {

drivers/pci/quirks.c +4 -2
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];

drivers/pci/quirks.c int pci_dev_specific_enable_acs(struct pci_dev *dev) · line 5479

Quoted 10 lines
drivers/pci/quirks.c +2 -5
@@ -5481,33 +5475,29 @@ int pci_dev_specific_enable_acs(struct pci_dev *dev)
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
 		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
-		}
+		    p->match(dev))
+			return p;

Nit:
Should we check if (p->match != NULL) like we check for p->enable_acs & p->disable_acs_redir().

Quoted 4 lines

Otherwise, it seems like we're mandating the existence of a match op in the pci_dev_acs_ops here? Today, we just have two Intel entries in that array, both of which need the match op. However, AFAICT, it shouldn't be mandatory for future SoCs that might only need a simple vid + devid match

*shrug*

I would usually say those future SoCs should be the ones to make it optional if and when they need to.

But making p->matc optional now isn't so bad:

        for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
                p = &pci_dev_acs_ops[i];
                if ((p->vendor == dev->vendor ||
                     p->vendor == (u16)PCI_ANY_ID) &&
                    (p->device == dev->device ||

- p->device == (u16)PCI_ANY_ID) &&
- p->enable_acs) {
- ret = p->enable_acs(dev);
- if (ret >= 0)
- return ret;
+ p->device == (u16)PCI_ANY_ID)) {
+ if (!p->match || p->match(dev))

+                               return p;
                }
        }

I can include this in v7 if you would like.

Quoted 8 lines

[...]

with that nit:

Thanks,
Praan

#58 lore raw

Re: [PATCH v6 07/12] PCI: Refactor matching logic for pci_dev_acs_ops

Quoted 61 lines · David Matlack
Quoted 55 lines · Pranjal Shrivastava
Quoted 14 lines · David Matlack

Refactor the logic to match devices to pci_dev_acs_ops by factoring out the loop and device matching into its own routine. This eliminates some duplicate code between pci_dev_specific_enable_acs() and pci_dev_specific_disable_acs_redir(), and will also be used in a subsequent commit to check if a device requires device-specific enable_acs() during a Live Update.

No functional change intended.

---
 drivers/pci/quirks.c | 50 ++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 30 deletions(-)

[...]

Quoted 22 lines

} pci_dev_acs_ops[] = {

drivers/pci/quirks.c +4 -2
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];

drivers/pci/quirks.c int pci_dev_specific_enable_acs(struct pci_dev *dev) · line 5479

Quoted 10 lines
drivers/pci/quirks.c +2 -5
@@ -5481,33 +5475,29 @@ int pci_dev_specific_enable_acs(struct pci_dev *dev)
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
 		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
-		}
+		    p->match(dev))
+			return p;

Nit:
Should we check if (p->match != NULL) like we check for p->enable_acs & p->disable_acs_redir().

Quoted 4 lines

Otherwise, it seems like we're mandating the existence of a match op in the pci_dev_acs_ops here? Today, we just have two Intel entries in that array, both of which need the match op. However, AFAICT, it shouldn't be mandatory for future SoCs that might only need a simple vid + devid match

Quoted 4 lines

*shrug*

I would usually say those future SoCs should be the ones to make it optional if and when they need to.

Well.. that's fair I guess.

Quoted 19 lines

But making p->matc optional now isn't so bad:

        for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
                p = &pci_dev_acs_ops[i];
                if ((p->vendor == dev->vendor ||
                     p->vendor == (u16)PCI_ANY_ID) &&
                    (p->device == dev->device ||
drivers/pci/quirks.c +3 -5
-                    p->device == (u16)PCI_ANY_ID) &&
-                   p->enable_acs) {
-                       ret = p->enable_acs(dev);
-                       if (ret >= 0)
-                               return ret;
+                    p->device == (u16)PCI_ANY_ID)) {
+                       if (!p->match || p->match(dev))
+                               return p;
                }
        }
Quoted 1 line

I can include this in v7 if you would like.

I don't have a strong opinion here, this should be fine. It's just that we have NULL checks for p->enable_acs and disable_acs_redir too. It'd be nice to keep the same pattern.

Thanks,
Praan

#59 lore raw

PATCH v6 8/12 PCI: liveupdate: Inherit ACS flags in incoming preserved devices

In reply to David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)
drivers/pci/liveupdate.c +68 -0
index 4c79e19b7f98..a93b7ef065f2 100644
@@ -71,6 +71,9 @@
  *
  *  * The device cannot be a Virtual Function (VF).
  *
+ *  * The device cannot require device-specific quirks to enable Access
+ *    Control Services (ACS).
+ *
  * Driver Binding
  * ==============
  *
@@ -113,6 +116,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
@@ -126,6 +141,7 @@
 #include <linux/pci.h>
 
 #include "liveupdate.h"
+#include "pci.h"
 
 /**
  * struct pci_liveupdate_global - Global state for PCI Live Update support
@@ -319,6 +335,16 @@ static int __pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev
 {
 	int i;
 
+	/*
+	 * Do not preserve devices that rely on device-specific ACS equivalents
+	 * (for now) since that would complicate keeping ACS constant across
+	 * Live Update.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n");
+		return -EINVAL;
+	}
+
 	if (ser->nr_devices == ser->max_nr_devices)
 		return -ENOSPC;
 
@@ -598,6 +624,7 @@ void pci_liveupdate_setup_device(struct pci_dev *dev)
 
 	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
 	dev->liveupdate.incoming = dev_ser;
+	dev->liveupdate.was_preserved = true;
 
 	/*
 	 * Hold the ref on the incoming FLB until pci_liveupdate_finish() so
@@ -688,6 +715,47 @@ void pci_liveupdate_finish(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
 
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->acs_cap || !dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);
+}
+
+int pci_liveupdate_enable_acs(struct pci_dev *dev)
+{
+	u16 acs_ctrl = dev->liveupdate.acs_ctrl;
+	u16 acs_cap = dev->acs_cap;
+
+	/*
+	 * 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->liveupdate.was_preserved)
+		return -EINVAL;
+
+	/*
+	 * The previous kernel should not have preserved any devices that
+	 * require device-specific quirks to enable ACS, but if such a device is
+	 * detected, log a big warning and fall back to the normal enable ACS
+	 * path.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}
+
+	if (acs_cap)
+		pci_write_config_word(dev, acs_cap + PCI_ACS_CTRL, acs_ctrl);
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check
drivers/pci/liveupdate.h +11 -0
index c763255a8de4..4e8a01bcb4bb 100644
@@ -16,6 +16,8 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev);
 bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
 				      int pass);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass);
+void pci_liveupdate_init_acs(struct pci_dev *dev);
+int pci_liveupdate_enable_acs(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -35,6 +37,15 @@ static inline bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus,
 static inline void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass)
 {
 }
+
+static inline void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+}
+
+static inline int pci_liveupdate_enable_acs(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +5 -0
index 8f7cfcc00090..211df4618164 100644
@@ -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_enable_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);
 }
 
 /**
drivers/pci/pci.h +5 -0
index 4a14f88e543a..b55f3deddd57 100644
@@ -1062,6 +1062,7 @@ void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 void pci_disable_broken_acs_cap(struct pci_dev *pdev);
@@ -1072,6 +1073,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
 	return -ENOTTY;
 }
+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	return false;
+}
 static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	return -ENOTTY;
drivers/pci/quirks.c +7 -0
index 171caec2bc47..59b0b19c3783 100644
@@ -5482,6 +5482,13 @@ static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 	return NULL;
 }
 
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
+
+	return p && p->enable_acs;
+}
+
 int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p = pci_dev_acs_ops_get(dev);
include/linux/pci_liveupdate.h +6 -0
index 2be98819e313..2446c6d237ca 100644
@@ -17,14 +17,20 @@
  * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
  * @outgoing: State preserved for the next kernel.
  * @incoming: State preserved by the previous kernel.
+ * @acs_ctrl: ACS features established by the previous kernel.
  * @inherit_buses: True if the PCI core should inherit the secondary and
  *                 subordinate bus numbers assigned to this device due to
  *                 an ongoing Live Update.
+ * @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 {
 	struct pci_dev_ser *outgoing;
 	struct pci_dev_ser *incoming;
+	u16 acs_ctrl;
 	bool inherit_buses;
+	bool was_preserved;
 };
 
 struct pci_dev;
Signature · 1 line
2.54.0.746.g67dd491aae-goog
#60 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 9 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->acs_cap || !dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

Quoted 11 lines
drivers/pci/liveupdate.c +11 -0
+}
+
+int pci_liveupdate_enable_acs(struct pci_dev *dev)
+{
+	u16 acs_ctrl = dev->liveupdate.acs_ctrl;
+	u16 acs_cap = dev->acs_cap;
+
+	/*
+	 * 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.

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 746

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+	 */
+	if (!dev->liveupdate.was_preserved)
+		return -EINVAL;
+
+	/*
+	 * The previous kernel should not have preserved any devices that
+	 * require device-specific quirks to enable ACS, but if such a device is
+	 * detected, log a big warning and fall back to the normal enable ACS
+	 * path.
+	 */

Nit: It might be worth adding a note here that this can also happen if a new device-specific ACS quirk is introduced in the incoming kernel for a device that was preserved by the old kernel (which didn't have the quirk). In such cases, the two kernels are essentially non-LUO-compatible..

Quoted 5 lines
drivers/pci/liveupdate.c +5 -0
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 761

Quoted 10 lines
drivers/pci/liveupdate.c +7 -0
+
+	if (acs_cap)
+		pci_write_config_word(dev, acs_cap + PCI_ACS_CTRL, acs_ctrl);
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check

[...]

Thanks,
Praan

#61 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 32 lines · Pranjal Shrivastava
Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

Quoted 1 line
Quoted 13 lines

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 8 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->acs_cap || !dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Minor note here: I used "0" as an example value, I'm aware that'll effectively disable ACS and kernel will enforce more security.

My point was that a FW exploit can meddle with the bitfields of the ACS_CTRL to spoof and mis-report the ACS flags.

Additionally, we might give rise to use-cases that start depending on this, for e.g. if someone wants to change ACS policies in the new kernel, the FW may silently update these flags across a kexec.

Quoted 4 lines

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

Thanks,
Praan

#62 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 2 lines · Pranjal Shrivastava

My point was that a FW exploit can meddle with the bitfields of the ACS_CTRL to spoof and mis-report the ACS flags.

Devices can also ignore the ACS flags. I don't think this is an area where we should be worrying about devices being actively hostile.

Jason

#63 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 7 lines · Jason Gunthorpe
Quoted 2 lines · Pranjal Shrivastava

My point was that a FW exploit can meddle with the bitfields of the ACS_CTRL to spoof and mis-report the ACS flags.

Devices can also ignore the ACS flags. I don't think this is an area where we should be worrying about devices being actively hostile.

I'm wondering what happens if we preserve IOMMU groups across a kexec, but a switch's ACS capability is dropped or the ACS_RR bit gets cleared? The incoming kernel assumes that it's the same ACS cap from the old one

Now, the incoming kernel restores the groups assuming they're still isolated, but the hardware no longer enforces it, silently allowing DMAs & breaking isolation?

Thanks,
Praan

#64 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 9 lines · Pranjal Shrivastava
Quoted 7 lines · Jason Gunthorpe
Quoted 2 lines · Pranjal Shrivastava

My point was that a FW exploit can meddle with the bitfields of the ACS_CTRL to spoof and mis-report the ACS flags.

Devices can also ignore the ACS flags. I don't think this is an area where we should be worrying about devices being actively hostile.

Quoted 7 lines

I'm wondering what happens if we preserve IOMMU groups across a kexec, but a switch's ACS capability is dropped or the ACS_RR bit gets cleared? The incoming kernel assumes that it's the same ACS cap from the old one

Now, the incoming kernel restores the groups assuming they're still isolated, but the hardware no longer enforces it, silently allowing DMAs & breaking isolation?

Again, to clarify, I'm aware that we aren't preserving IOMMU groups, the incoming kernel has to rebuild the groups. My concern is that if the ACS_RR bit is cleared during the kexec window, the produced grouping would be different than the old kernel. What happens if two devices on the same bridge were assigned to 2 different VMs?

Thanks,
Praan

#65 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 19 lines · Pranjal Shrivastava
Quoted 9 lines · Pranjal Shrivastava
Quoted 7 lines · Jason Gunthorpe
Quoted 2 lines · Pranjal Shrivastava

My point was that a FW exploit can meddle with the bitfields of the ACS_CTRL to spoof and mis-report the ACS flags.

Devices can also ignore the ACS flags. I don't think this is an area where we should be worrying about devices being actively hostile.

Quoted 7 lines

I'm wondering what happens if we preserve IOMMU groups across a kexec, but a switch's ACS capability is dropped or the ACS_RR bit gets cleared? The incoming kernel assumes that it's the same ACS cap from the old one

Now, the incoming kernel restores the groups assuming they're still isolated, but the hardware no longer enforces it, silently allowing DMAs & breaking isolation?

Quoted 5 lines

Again, to clarify, I'm aware that we aren't preserving IOMMU groups, the incoming kernel has to rebuild the groups. My concern is that if the ACS_RR bit is cleared during the kexec window, the produced grouping would be different than the old kernel. What happens if two devices on the same bridge were assigned to 2 different VMs?

In this patch the kernel reads PCI_ACS_CTRL after kexec to determine the bridge's state and uses that information going forward. We don't assume that if ACS_RR was set on the previous kernel then it is still set now.

So if ACS_RR were cleared during kexec somehow, the new kernel would see that and place those devices into the same group. Someitme after that (I'm a little fuzzy on the iommufd restore path during Live Update) the kernel would try and fail to restore the 2 devices into separate domains because they are in the same group and thus the 2 different VMs would fail to come up.

Of course maybe ACS_RR can somehow get cleared after the kernel reads it, but at that point the issue has nothing to do with Live Update.

So that's what would happen if ACS_RR ever got cleared during kexec and seems reasonable to me?

#66 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

In reply to Pranjal Shrivastava
Quoted 46 lines · Pranjal Shrivastava
Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 9 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->acs_cap || !dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Quoted 2 lines

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

What does "EP FW" mean?

Does such an attacker even need Live Update to attack the system? It seems like such an attacker could route TLPs in whatever malicious way they want regardless of Live Update.

Quoted 19 lines
Quoted 19 lines
drivers/pci/liveupdate.c +18 -0
+}
+
+int pci_liveupdate_enable_acs(struct pci_dev *dev)
+{
+	u16 acs_ctrl = dev->liveupdate.acs_ctrl;
+	u16 acs_cap = dev->acs_cap;
+
+	/*
+	 * 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->liveupdate.was_preserved)
+		return -EINVAL;
+
+	/*
+	 * The previous kernel should not have preserved any devices that
+	 * require device-specific quirks to enable ACS, but if such a device is
Quoted 8 lines

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 746

Quoted 3 lines
drivers/pci/liveupdate.c +3 -0
+	 * detected, log a big warning and fall back to the normal enable ACS
+	 * path.
+	 */

Nit: It might be worth adding a note here that this can also happen if a new device-specific ACS quirk is introduced in the incoming kernel for a device that was preserved by the old kernel (which didn't have the quirk). In such cases, the two kernels are essentially non-LUO-compatible..

Yes will do.

Quoted 21 lines
Quoted 6 lines
drivers/pci/liveupdate.c +5 -0
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 761

Quoted 10 lines
drivers/pci/liveupdate.c +7 -0
+
+	if (acs_cap)
+		pci_write_config_word(dev, acs_cap + PCI_ACS_CTRL, acs_ctrl);
+
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check

[...]

Thanks,
Praan

#67 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 47 lines · David Matlack
Quoted 46 lines · Pranjal Shrivastava
Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 9 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->acs_cap || !dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Quoted 4 lines
Quoted 2 lines

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

What does "EP FW" mean?

I was referring to the Endpoint Firmware (basically any SW running on a downstream device)

Quoted 5 lines

Does such an attacker even need Live Update to attack the system? It seems like such an attacker could route TLPs in whatever malicious way they want regardless of Live Update.

I agree that compromised PCIe devices are a menace anyway. But I was talking about the potential window opened up by Live Update here, suppose we have Device A & B assigned to 2 different VMs (implying they are in separate IOMMU groups because the switch set ACS_RR = 1).

Now, the attacker has an opportunity with Liveupdate, since the devices are already assigned, if *somehow* it flips a bit like ACS_RR, the incoming kernel might see both the devices in the same IOMMU group. Who detects this case and what happens if this happens if the devices are kept assigned to these VMs?

Thanks,
Praan

#68 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 68 lines · Pranjal Shrivastava
Quoted 47 lines · David Matlack
Quoted 46 lines · Pranjal Shrivastava
Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 9 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+ guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+ if (!dev->acs_cap || !dev->liveupdate.incoming)
+         return;
+
+ pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Quoted 4 lines
Quoted 2 lines

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

What does "EP FW" mean?

I was referring to the Endpoint Firmware (basically any SW running on a downstream device)

Quoted 5 lines

Does such an attacker even need Live Update to attack the system? It seems like such an attacker could route TLPs in whatever malicious way they want regardless of Live Update.

I agree that compromised PCIe devices are a menace anyway. But I was talking about the potential window opened up by Live Update here, suppose we have Device A & B assigned to 2 different VMs (implying they are in separate IOMMU groups because the switch set ACS_RR = 1).

Quoted 5 lines

Now, the attacker has an opportunity with Liveupdate, since the devices are already assigned, if *somehow* it flips a bit like ACS_RR, the incoming kernel might see both the devices in the same IOMMU group. Who detects this case and what happens if this happens if the devices are kept assigned to these VMs?

I suspect that would be caught during the restore of the iommufds to which those devices are attached.

The kernel would attempt to restore each device into a separate domain (since that's how they were preserved before the Live Update) but that will fail because they are in the same group now. Even if one of the devices was not preserved, that will still cause a failure when a user tries to start using that device (e.g. to try to attach it to a different VM).

#69 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 78 lines · David Matlack
Quoted 68 lines · Pranjal Shrivastava
Quoted 47 lines · David Matlack
Quoted 46 lines · Pranjal Shrivastava
Quoted 27 lines · David Matlack

Inherit 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()).

To simplify ACS inheritance, reject preserving any devices that require quirks to enable ACS as those quirks would also have to take Live Update into account.

---
 drivers/pci/liveupdate.c       | 68 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 ++++++
 drivers/pci/pci.c              |  5 +++
 drivers/pci/pci.h              |  5 +++
 drivers/pci/quirks.c           |  7 ++++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 102 insertions(+)

[...]

drivers/pci/liveupdate.c void pci_liveupdate_finish(struct pci_dev *dev) · line 725

Quoted 9 lines
drivers/pci/liveupdate.c +8 -0
+void pci_liveupdate_init_acs(struct pci_dev *dev)
+{
+ guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+ if (!dev->acs_cap || !dev->liveupdate.incoming)
+         return;
+
+ pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);

I might be thinking out loud here, but as an attacker, this motivates me to somehow hack the EP FW to mis-report the PCI_ACS_CTRL register across a liveupdate to fool the incoming kernel. If the FW feeds a 0, it silently strips ACS protections.

Quoted 4 lines
Quoted 2 lines

Should we also serialize ACS state in ser somehow to ensure we aren't fooled by something like this?

What does "EP FW" mean?

I was referring to the Endpoint Firmware (basically any SW running on a downstream device)

Quoted 5 lines

Does such an attacker even need Live Update to attack the system? It seems like such an attacker could route TLPs in whatever malicious way they want regardless of Live Update.

I agree that compromised PCIe devices are a menace anyway. But I was talking about the potential window opened up by Live Update here, suppose we have Device A & B assigned to 2 different VMs (implying they are in separate IOMMU groups because the switch set ACS_RR = 1).

Quoted 5 lines

Now, the attacker has an opportunity with Liveupdate, since the devices are already assigned, if *somehow* it flips a bit like ACS_RR, the incoming kernel might see both the devices in the same IOMMU group. Who detects this case and what happens if this happens if the devices are kept assigned to these VMs?

I suspect that would be caught during the restore of the iommufds to which those devices are attached.

Quoted 6 lines

The kernel would attempt to restore each device into a separate domain (since that's how they were preserved before the Live Update) but that will fail because they are in the same group now. Even if one of the devices was not preserved, that will still cause a failure when a user tries to start using that device (e.g. to try to attach it to a different VM).

Yes, IOMMU would eventually catch-up but what about the DMAs that were done already? Say to an NVMe disk? We'll have to wipe the entire disk in such a case? Also, we wouldn't know the offending device..

If such situations aren't a problem, then I guess it's fine.

Thanks,
Praan

#70 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

In reply to Pranjal Shrivastava
Quoted 2 lines · Pranjal Shrivastava

Now, the attacker has an opportunity with Liveupdate, since the devices are already assigned, if *somehow* it flips a bit like ACS_RR, the

If this is possible then your environment is already security broken, no need to involve live update.

Jason

#71 lore raw

Re: [PATCH v6 08/12] PCI: liveupdate: Inherit ACS flags in incoming preserved devices

Quoted 7 lines · Jason Gunthorpe
Quoted 2 lines · Pranjal Shrivastava

Now, the attacker has an opportunity with Liveupdate, since the devices are already assigned, if *somehow* it flips a bit like ACS_RR, the

If this is possible then your environment is already security broken, no need to involve live update.

Ack. Alright.

Thanks,
Praan

#72 lore raw

PATCH v6 9/12 PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

In reply to David Matlack

Inherit 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
index a93b7ef065f2..701276ef6cfb 100644
@@ -128,6 +128,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
@@ -756,6 +760,20 @@ int pci_liveupdate_enable_acs(struct pci_dev *dev)
 	return 0;
 }
 
+int pci_liveupdate_configure_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check
drivers/pci/liveupdate.h +6 -0
index 4e8a01bcb4bb..6f21ec50927b 100644
@@ -18,6 +18,7 @@ bool pci_liveupdate_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass);
 void pci_liveupdate_init_acs(struct pci_dev *dev);
 int pci_liveupdate_enable_acs(struct pci_dev *dev);
+int pci_liveupdate_configure_ari(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -46,6 +47,11 @@ static inline int pci_liveupdate_enable_acs(struct pci_dev *dev)
 {
 	return -EINVAL;
 }
+
+static inline int pci_liveupdate_configure_ari(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +7 -1
index 211df4618164..271da55af270 100644
@@ -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_configure_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.746.g67dd491aae-goog
#73 lore raw

Re: [PATCH v6 09/12] PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Bitfield data race when updating ari_enabled on a shared bridge device. --

commit 9819403e762e1d3e8a471294f5aa07554019d8b4
Author: David Matlack <[email protected]>

PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

Inherit 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.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index a93b7ef065f28..701276ef6cfbd 100644

[ ... ]

Quoted 5 lines
drivers/pci/liveupdate.c +1 -0
@@ -756,6 +760,20 @@ int pci_liveupdate_enable_acs(struct pci_dev *dev)
 	return 0;
 }
 
+int pci_liveupdate_configure_ari(struct pci_dev *dev)

drivers/pci/liveupdate.c int pci_liveupdate_enable_acs(struct pci_dev *dev) · line 773

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);

[Severity: High]
This isn't a bug introduced by this patch, but could this bitfield update cause a data race on the shared bridge device?

Since C bitfield updates are not atomic, modifying dev->ari_enabled compiles down to a read-modify-write of the entire surrounding machine word.

If another thread concurrently modifies adjacent bitfields on the same bridge device (for example, if the pcieport driver is unbound from the bridge via sysfs, invoking pci_disable_msi() and pci_clear_master() to update msi_enabled and is_busmaster), couldn't one of the updates be lost?

Since pci_setup_device(), which calls pci_configure_ari(), does not hold device_lock(bridge), these paths can execute concurrently. This might corrupt the bridge's state by silently dropping updates like MSI enablement, potentially leading to interrupt routing failures or crashes.

The new code appears to duplicate this unprotected modification pattern from the preexisting fallback path in pci_configure_ari().

Quoted 2 lines
drivers/pci/liveupdate.c +2 -0
+	return 0;
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
#74 lore raw

Re: [PATCH v6 09/12] PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

In reply to David Matlack
Quoted 31 lines · David Matlack

Inherit 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 +4 -0
index a93b7ef065f2..701276ef6cfb 100644
@@ -128,6 +128,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
@@ -756,6 +760,20 @@ int pci_liveupdate_enable_acs(struct pci_dev *dev)
 	return 0;
 }
 

drivers/pci/liveupdate.c int pci_liveupdate_enable_acs(struct pci_dev *dev) · line 772

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+int pci_liveupdate_configure_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);

Again, I might be thinking out loud here, but since these are hot-pluggable devices, with some FW / SW running on them, I'm a little worried while assuming the HW registers can be trusted across a kexec.

Say, if the bridge experiences a reset (e.g. link drop etc) during the kexec blackout, the PCI_EXP_DEVCTL2 register could revert to its default state, meaning the ARI bit will be 0.

In that scenario, pci_liveupdate_configure_ari() will read 0, set bridge->ari_enabled = false, and bypass the pci_configure_ari() logic as well, permanently leaving ARI disabled on the bridge. Any preserved downstream devices with function numbers > 7 will instantly become unaddressable, breaking the Live Update. Even if that's desired it gets hard to identify why the Liveupdate broke.

Should ari_enabled be serialized with bridge's pci_ser to ensure we correctly restore / match it in case HW changed it during the kexec?

drivers/pci/liveupdate.c int pci_liveupdate_enable_acs(struct pci_dev *dev) · line 776

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+

[...]

Thanks,
Praan

#75 lore raw

Re: [PATCH v6 09/12] PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

Quoted 47 lines · Pranjal Shrivastava
Quoted 31 lines · David Matlack

Inherit 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 +4 -0
index a93b7ef065f2..701276ef6cfb 100644
@@ -128,6 +128,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
@@ -756,6 +760,20 @@ int pci_liveupdate_enable_acs(struct pci_dev *dev)
 	return 0;
 }
 

drivers/pci/liveupdate.c int pci_liveupdate_enable_acs(struct pci_dev *dev) · line 772

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+int pci_liveupdate_configure_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);

Again, I might be thinking out loud here, but since these are hot-pluggable devices, with some FW / SW running on them, I'm a little worried while assuming the HW registers can be trusted across a kexec.

Quoted 3 lines

Say, if the bridge experiences a reset (e.g. link drop etc) during the kexec blackout, the PCI_EXP_DEVCTL2 register could revert to its default state, meaning the ARI bit will be 0.

This does seem like something to be concerned about, but realistically I think if you get a PCIe error I'm not sure the incoming kernel is equipped to handle it at all :\

Just resuming the driver is going to fail too, I don't know how VFIO can learn and forward the event, and so on..

But maybe it is worth being a little more defensive here

Jason

#76 lore raw

Re: [PATCH v6 09/12] PCI: liveupdate: Inherit ARI Forwarding Enable on preserved bridges

Quoted 34 lines · Jason Gunthorpe
Quoted 32 lines · Pranjal Shrivastava
Quoted 31 lines · David Matlack

Inherit 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 +4 -0
index a93b7ef065f2..701276ef6cfb 100644
@@ -128,6 +128,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
@@ -756,6 +760,20 @@ int pci_liveupdate_enable_acs(struct pci_dev *dev)
    return 0;
 }
 
Quoted 14 lines
Quoted 6 lines
Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+int pci_liveupdate_configure_ari(struct pci_dev *dev)
+{
+   u16 val;
+
+   guard(rwsem_read)(&pci_liveupdate.rwsem);
+
Quoted 8 lines

drivers/pci/liveupdate.c int pci_liveupdate_enable_acs(struct pci_dev *dev) · line 772

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+   if (!dev->liveupdate.incoming)
+           return -EINVAL;
+
+   pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);

Again, I might be thinking out loud here, but since these are hot-pluggable devices, with some FW / SW running on them, I'm a little worried while assuming the HW registers can be trusted across a kexec.

This is, in-part [*], why I explicitly read the capability rather than caching what the previous kernel thinks it was in struct pci_dev_ser.

In other words, the kerenl does not assume that hardware registers remain unchanged across kexec. It reads them after kexec to determine what state the device is in and inherits that.

[*] The other reason is just to keep the ABI as minimal as possible.

Quoted 4 lines
Quoted 3 lines

Say, if the bridge experiences a reset (e.g. link drop etc) during the kexec blackout, the PCI_EXP_DEVCTL2 register could revert to its default state, meaning the ARI bit will be 0.

Quoted 8 lines

This does seem like something to be concerned about, but realistically I think if you get a PCIe error I'm not sure the incoming kernel is equipped to handle it at all :\

Just resuming the driver is going to fail too, I don't know how VFIO can learn and forward the event, and so on..

But maybe it is worth being a little more defensive here

If this were to happen, the kernel would see that ARI is disabled on the bridge after kexec and proceed with that state (ARI disabled). Devices (devfn) 8+ on the bus would not be available to use or restore (the kernel would not discover them during scanning). It should be fairly obvious to the user that something went wrong (devices they are trying to restore do not even exist) and they can reboot at that point to recover.

This should be extremely rare though as it requires a PCIe error that affects the bridge to race with kexec.

This seems like a reasonable way to handle this scenario, at least in the initial version. We can always look at more gracefully handling PCIe errors during kexec for Live Update if-and-when there is value.

#77 lore raw

PATCH v6 10/12 PCI: liveupdate: Freeze preservation status during shutdown

In reply to David Matlack

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.

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
index 701276ef6cfb..d404e64a4e55 100644
@@ -297,6 +297,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_ser *ser, struct pci_dev
 {
 	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
 
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot unpreserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
 	if (!dev_ser) {
 		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
 		return -EINVAL;
@@ -380,6 +385,11 @@ static int __pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev
 
 static int pci_liveupdate_preserve_device(struct pci_ser *ser, struct pci_dev *dev)
 {
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing)
 		return pci_liveupdate_preserve_device_again(dev);
 
@@ -653,6 +663,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 	}
 }
 
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = 1;
+}
+
 static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
 {
 	if (!dev->liveupdate.incoming) {
drivers/pci/liveupdate.h +5 -0
index 6f21ec50927b..bcb0bc73d684 100644
@@ -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_scan_bridge_begin(struct pci_bus *bus, struct pci_dev *dev,
 				      int pass);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass);
@@ -28,6 +29,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_scan_bridge_begin(struct pci_bus *bus,
 						    struct pci_dev *dev,
 						    int pass)
drivers/pci/pci-driver.c +2 -0
index d10ece0889f0..f7a5e65a7c75 100644
@@ -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
index 2446c6d237ca..150993405754 100644
@@ -24,6 +24,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 {
 	struct pci_dev_ser *outgoing;
@@ -31,6 +33,7 @@ struct pci_liveupdate {
 	u16 acs_ctrl;
 	bool inherit_buses;
 	bool was_preserved;
+	bool frozen;
 };
 
 struct pci_dev;
Signature · 1 line
2.54.0.746.g67dd491aae-goog
Reviewed-by on v6 10/12
#78 lore raw

Re: [PATCH v6 10/12] PCI: liveupdate: Freeze preservation status during shutdown

Quoted 11 lines · David Matlack

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.

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).

Thanks,
Praan

#79 lore raw

PATCH v6 11/12 PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

In reply to 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 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 | 11 +++++++++++
 drivers/pci/liveupdate.h |  6 ++++++
 drivers/pci/pci-driver.c |  7 +++++--
 3 files changed, 22 insertions(+), 2 deletions(-)
drivers/pci/liveupdate.c +11 -0
index d404e64a4e55..a6f2790bc1bf 100644
@@ -132,6 +132,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
@@ -790,6 +794,13 @@ int pci_liveupdate_configure_ari(struct pci_dev *dev)
 	return 0;
 }
 
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	pci_WARN_ONCE(dev, !dev->liveupdate.frozen, "Preservation status is unstable!\n");
+	return dev->liveupdate.outgoing;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check
drivers/pci/liveupdate.h +6 -0
index bcb0bc73d684..b266406aaac8 100644
@@ -20,6 +20,7 @@ void pci_liveupdate_scan_bridge_end(struct pci_dev *dev, int pass);
 void pci_liveupdate_init_acs(struct pci_dev *dev);
 int pci_liveupdate_enable_acs(struct pci_dev *dev);
 int pci_liveupdate_configure_ari(struct pci_dev *dev);
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -57,6 +58,11 @@ static inline int pci_liveupdate_configure_ari(struct pci_dev *dev)
 {
 	return -EINVAL;
 }
+
+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	return false;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci-driver.c +5 -2
index f7a5e65a7c75..0b1f8d01d7a5 100644
@@ -546,11 +546,14 @@ static void pci_device_shutdown(struct device *dev)
 	/*
 	 * If this is a kexec reboot, turn off Bus Master bit on the
 	 * device to tell it to not continue to do DMA. Don't touch
-	 * devices in D3cold or unknown states.
+	 * devices being preserved for Live Update or in D3cold or
+	 * unknown states.
+	 *
 	 * If it is not a kexec reboot, firmware will hit the PCI
 	 * devices with big hammer and stop their DMA any way.
 	 */
-	if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
+	if (kexec_in_progress && !pci_liveupdate_is_outgoing(pci_dev) &&
+	    pci_dev->current_state <= PCI_D3hot)
 		pci_clear_master(pci_dev);
 }
 
Signature · 1 line
2.54.0.746.g67dd491aae-goog
Reviewed-by on v6 11/12
#80 lore raw

Re: [PATCH v6 11/12] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

Quoted 48 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 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 | 11 +++++++++++
 drivers/pci/liveupdate.h |  6 ++++++
 drivers/pci/pci-driver.c |  7 +++++--
 3 files changed, 22 insertions(+), 2 deletions(-)
drivers/pci/liveupdate.c +11 -0
index d404e64a4e55..a6f2790bc1bf 100644
@@ -132,6 +132,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
@@ -790,6 +794,13 @@ int pci_liveupdate_configure_ari(struct pci_dev *dev)
 	return 0;
 }
 
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	pci_WARN_ONCE(dev, !dev->liveupdate.frozen, "Preservation status is unstable!\n");
+	return dev->liveupdate.outgoing;
+}
+

[...]

Quoted 10 lines
drivers/pci/pci-driver.c +1 -1
index f7a5e65a7c75..0b1f8d01d7a5 100644
@@ -546,11 +546,14 @@ static void pci_device_shutdown(struct device *dev)
 	/*
 	 * If this is a kexec reboot, turn off Bus Master bit on the
 	 * device to tell it to not continue to do DMA. Don't touch
-	 * devices in D3cold or unknown states.
+	 * devices being preserved for Live Update or in D3cold or

drivers/pci/pci-driver.c static void pci_device_shutdown(struct device *dev) · line 558

Quoted 10 lines
drivers/pci/pci-driver.c +4 -1
+	 * unknown states.
+	 *
 	 * If it is not a kexec reboot, firmware will hit the PCI
 	 * devices with big hammer and stop their DMA any way.
 	 */
-	if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
+	if (kexec_in_progress && !pci_liveupdate_is_outgoing(pci_dev) &&
+	    pci_dev->current_state <= PCI_D3hot)
 		pci_clear_master(pci_dev);
 }

Looks good.

Thanks,
Praan

#81 lore raw

PATCH v6 12/12 Documentation: PCI: Add documentation for Live Update

In reply to David Matlack

Add 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
index 5d720d2a415e..23fb737ac969 100644
@@ -20,3 +20,4 @@ PCI Bus Subsystem
    controller/index
    boot-interrupts
    tph
+   liveupdate
Documentation/PCI/liveupdate.rst new file +29 -0
new file mode 100644
index 000000000000..eba55f8a92ae
@@ -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
index 5a292d0f3706..d56a7760978a 100644
@@ -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
index 0e262c0ceb43..6f0b0ebf67cd 100644
@@ -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.746.g67dd491aae-goog
Reviewed-by on v6 12/12
#82 lore raw

Re: [PATCH v6 12/12] Documentation: PCI: Add documentation for Live Update

Quoted 9 lines · David Matlack

Add 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).

Quoted 7 lines
---
 Documentation/PCI/index.rst           |  1 +
 Documentation/PCI/liveupdate.rst      | 29 +++++++++++++++++++++++++++
 Documentation/core-api/liveupdate.rst |  1 +
 MAINTAINERS                           |  1 +
 4 files changed, 32 insertions(+)

The kernel-doc directives seem to be wired up properly, along with formatting/indentation while keeping MAINTAINERS in sync in the same go.

Thanks,
Praan