Skip to contentSkip to conversation

PCI: liveupdate: PCI core support for Live Update

PATCH Applied David Matlack 8 participants 74 messages Jul 28 – Sep 18

Subsystems (5)

v8 of 10 Applied Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v8 cover PCI: liveupdate: PCI core support for Live Update

NOTE: I am sending v8 out a little earlier than I normally would since I will be AFK from July 29th (tomorrow) through August 6th and would like to post the changes made in response to v7 feedback before then. I will respond to any review feedback on v8 on August 7th or shortly after.

This series can be found on GitHub:

https://github.com/dmatlack/linux/tree/liveupdate/pci/base/v8

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) targeting system memory (specifically, preserved memfds) 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 devices for preservation (outgoing) and for the PCI core to identify preserved devices during enumeration (incoming)

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.

Merge Plan
----------

I propose we merge this series through liveupdate.git for 2 reasons:

1. There are some LUO core changes around how FLBs are refcounted in the next branch of liveupdate.git that this series depends on.

2. We had previously agreed that the files covered by the PCI LIVE UPDATE maintainer entry would be merged through the liveupdate.git tree (which is the majority of this series), so this would be following that process.

This plan still needs to be Acked by Bjorn Helgaas as there are some PCI core changes in this series.

Dependencies
------------

This series is built on top of the next branch of the liveupdate.git tree for the incoming and outgoing FLB reference counting patches.

https://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git/log/?h=next

Testing
-------

This series was tested in conjunction with v5 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/v8-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
---------

v8:
- Use __free() and no_free_ptr() in pci_flb_preserve (Pasha)
- Sort linux/kho_block.h include alphabetically (Pasha)
- Fix reverse-fir-tree variable ordering (Pasha)
- Eliminate unnecessary loops to allocate a new pci_dev_ser (me)
- put() the outgoing FLB refcount now that get() takes a reference (me)
- Document -ENODEV handling in pci_liveupdate_flb_get_incoming() (Alex)
- Detect and panic on PCI FLB version mismatches instead of silently ignoring (Alex)
- Panic if drivers violate contract to call pci_liveupdate_finish() before LUO drops its reference to the preserved device file (Sami, Pasha)
- Use pci_upstream_bridge() instead of raw dev->bus->self (me)
- Add a comment about ari_enabled bitfield safety (Sashiko, Pasha)
- Add drivers/pci/liveupdate.c :internal: kernel-doc to Documentation/PCI/liveupdate.rst (Pasha)
- Collected Reviewed-by tags (Pasha, Bjorn)
- Remove use of KBUILD_BASENAME (Bjorn)
- Make pr_debug()s more useful (Bjorn)
- Refactor bus number preservation logic, variables, and function names (Bjorn)
- Move for_each_pci_dev_in_path() to include/linux/pci.h and drop _end parameter (Bjorn) and _start parameter (me)
- Rename and add kernel-doc comments to functions that adopt ARI enablement and ACS flags established by the previous kernel during enumeration to make the code more readable (Bjorn)

v7: https://lore.kernel.org/linux-pci/[email protected]/
v6: https://lore.kernel.org/linux-pci/[email protected]/
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: Preserve bus numbers during Live Update
PCI: liveupdate: Auto-preserve upstream bridges across Live Update
PCI: Refactor matching logic for pci_dev_acs_ops
PCI: liveupdate: Adopt ACS controls in incoming preserved devices
PCI: liveupdate: Adopt 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              |  35 +
 .../admin-guide/kernel-parameters.txt         |   6 +-
 Documentation/core-api/liveupdate.rst         |   5 +
 MAINTAINERS                                   |  15 +
 drivers/pci/Kconfig                           |  15 +
 drivers/pci/Makefile                          |   1 +
 drivers/pci/liveupdate.c                      | 939 ++++++++++++++++++
 drivers/pci/liveupdate.h                      |  66 ++
 drivers/pci/pci-driver.c                      |   9 +-
 drivers/pci/pci.c                             |  14 +-
 drivers/pci/pci.h                             |   5 +
 drivers/pci/probe.c                           |  23 +-
 drivers/pci/quirks.c                          |  58 +-
 include/linux/kho/abi/pci.h                   |  64 ++
 include/linux/pci.h                           |   7 +
 include/linux/pci_liveupdate.h                |  77 ++
 17 files changed, 1302 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: cecfb90c5f2cc0622b363c2291b69d6cad272897

Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#1 lore raw

PATCH v8 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.

---
 Documentation/core-api/liveupdate.rst |   4 +
 MAINTAINERS                           |  13 +++
 drivers/pci/Kconfig                   |  15 +++
 drivers/pci/Makefile                  |   1 +
 drivers/pci/liveupdate.c              | 158 ++++++++++++++++++++++++++
 include/linux/kho/abi/pci.h           |  63 ++++++++++
 include/linux/pci.h                   |   1 +
 include/linux/pci_liveupdate.h        |  30 +++++
 8 files changed, 285 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
Documentation/core-api/liveupdate.rst +4 -0
index 5a292d0f3706..b3c689e633c1 100644
@@ -1,5 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0
 
+.. _luo:
+
 ========================
 Live Update Orchestrator
 ========================
@@ -18,6 +20,8 @@ LUO Preserving File Descriptors
 .. kernel-doc:: kernel/liveupdate/luo_file.c
    :doc: LUO File Descriptors
 
+.. _flb:
+
 LUO File Lifecycle Bound Global Data
 ====================================
 .. kernel-doc:: kernel/liveupdate/luo_flb.c
MAINTAINERS +13 -0
index a3ed337e827d..9cc7b9291ace 100644
@@ -20824,6 +20824,19 @@ L:	[email protected]
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
+PCI LIVE UPDATE
+M:	David Matlack <[email protected]>
+R:	Pasha Tatashin <[email protected]>
+R:	Mike Rapoport <[email protected]>
+R:	Pratyush Yadav <[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 0c7408509ba2..3781e2b5f095 100644
@@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS
 	  Reserves space in the kernel to maintain resource locking for
 	  multiple GPUS.  The overhead for each GPU is very small.
 
+config PCI_LIVEUPDATE
+	bool "PCI Live Update Support"
+	depends on PCI && LIVEUPDATE
+	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 +158 -0

158 lines not on this page · raw message

include/linux/kho/abi/pci.h new file +63 -0

63 lines not on this page · raw message

include/linux/pci.h +1 -0
index ebb5b9d76360..da58aa101e4c 100644
@@ -43,6 +43,7 @@
 #include <uapi/linux/pci.h>
 
 #include <linux/pci_ids.h>
+#include <linux/pci_liveupdate.h>
 
 #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY  | \
 			       PCI_STATUS_SIG_SYSTEM_ERROR | \
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.55.0.487.gaf234c4eb3-goog
#2 lore raw

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

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 19 lines

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.
[ ... ]
assumed to return true. If KHO was not enabled then Live Update would not be enabled and these routines would never run.

Quoted 2 lines

Sashiko has reviewed this patch and found no issues. It looks great!

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

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

In reply to David Matlack
Quoted 48 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.

---

Documentation/core-api/liveupdate.rst |   4 +
MAINTAINERS                           |  13 +++
drivers/pci/Kconfig                   |  15 +++
drivers/pci/Makefile                  |   1 +
drivers/pci/liveupdate.c              | 158 ++++++++++++++++++++++++++
include/linux/kho/abi/pci.h           |  63 ++++++++++
include/linux/pci.h                   |   1 +
include/linux/pci_liveupdate.h        |  30 +++++
8 files changed, 285 insertions(+)
Quoted 4 lines

create mode 100644 drivers/pci/liveupdate.c
create mode 100644 include/linux/kho/abi/pci.h
create mode 100644 include/linux/pci_liveupdate.h

Thanks,
Sami

#4 lore raw

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

In reply to David Matlack
Quoted 5 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.

I assume pci_ser is the state the PCI core needs to preserve across kexec so the new kernel's enumeration doesn't interrupt the device operation. And that whatever state the endpoint drivers need to adopt/inherit the device in the new kernel is managed without any help from the PCI core?

Quoted 5 lines

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.

Where do drivers expose this file? sysfs? I guess it's a file per preserved device? Thinking like a driver writer, I'm expecting a hint about how to expose this file (should also be in the file doc somehere if it's not already).

Quoted 6 lines

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);

So a driver calls pci_liveupdate_register_flb() once, then pci_liveupdate_preserve() once for each device it wants preserved?

Quoted 3 lines

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

The passive voice here makes the actors a bit obscure. I guess a LIVEUPDATE_SESSION_PRESERVE_FD ioctl on some per-device file kicks this off?

I guess the pci_ser allocation is in pci_liveupdate_flb_ops.preserve(), i.e., pci_flb_preserve()?

So the PCI core tracker (pci_ser) isn't actually allocated at the time of pci_liveupdate_register_flb(); it's allocated on the first LIVEUPDATE_SESSION_PRESERVE_FD ioctl for a driver that has called pci_liveupdate_register_flb()?

IIUC the call tree for that ioctl looks something like this:

<driver>
pci_liveupdate_register_flb
liveupdate_register_flb(fh, &pci_liveupdate_flb)

luo_session_ioctl

    op = &luo_session_ioctl_ops[...]
    op->execute                               # eg luo_session_preserve_fd()
      luo_session_preserve_fd
        luo_preserve_file
          luo_flb_file_preserve
            luo_flb_file_preserve_one
              if (outgoing_count == 0)        # only for first FLB device
                flb->ops->preserve            # eg pci_flb_preserve()
                  pci_flb_preserve
                    ser = kho_alloc_preserve  <-- alloc pci_ser
                outgoing.count = 1
          fh->ops->preserve                   # something not included here

pci_liveupdate_preserve
pci_liveupdate_preserve_device
dev_ser = pci_flb_alloc_dev_ser <-- alloc per-dev PCI core serialized state dev_ser->bdf = pci_dev_id(dev)

Seems like kind of an awkward way to allocate pci_ser. Couldn't it be allocated on the first call to pci_liveupdate_register_flb()? That would be a lot easier for driver writers to trace through.

Quoted 2 lines

When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.

There's a lot going on behind "PCI core will be notified". I assume these refer to the first-time behavior of luo_flb_file_preserve_one() and last-time behavior of liveupdate_flb_put_outgoing(), which is honestly kind of hard to suss out.

This series doesn't include a caller of pci_liveupdate_preserve() (or pci_liveupdate_register_flb()), so I can't figure out the ordering. Obviously pci_liveupdate_register_flb() must be first.

I first thought pci_liveupdate_preserve() would be called via the fh->ops->preserve() in the luo_session_preserve_fd() ioctl path, but it's not. pci_liveupdate_preserve() is intended for the driver to call it directly. But it looks like it has to be called *after* the ioctl? Obviously I'm confused :)

Quoted 395 lines

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.

---
 Documentation/core-api/liveupdate.rst |   4 +
 MAINTAINERS                           |  13 +++
 drivers/pci/Kconfig                   |  15 +++
 drivers/pci/Makefile                  |   1 +
 drivers/pci/liveupdate.c              | 158 ++++++++++++++++++++++++++
 include/linux/kho/abi/pci.h           |  63 ++++++++++
 include/linux/pci.h                   |   1 +
 include/linux/pci_liveupdate.h        |  30 +++++
 8 files changed, 285 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
Documentation/core-api/liveupdate.rst +4 -0
index 5a292d0f3706..b3c689e633c1 100644
@@ -1,5 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0
 
+.. _luo:
+
 ========================
 Live Update Orchestrator
 ========================
@@ -18,6 +20,8 @@ LUO Preserving File Descriptors
 .. kernel-doc:: kernel/liveupdate/luo_file.c
    :doc: LUO File Descriptors
 
+.. _flb:
+
 LUO File Lifecycle Bound Global Data
 ====================================
 .. kernel-doc:: kernel/liveupdate/luo_flb.c
MAINTAINERS +13 -0
index a3ed337e827d..9cc7b9291ace 100644
@@ -20824,6 +20824,19 @@ L:	[email protected]
 S:	Supported
 F:	Documentation/PCI/pci-error-recovery.rst
 
+PCI LIVE UPDATE
+M:	David Matlack <[email protected]>
+R:	Pasha Tatashin <[email protected]>
+R:	Mike Rapoport <[email protected]>
+R:	Pratyush Yadav <[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 0c7408509ba2..3781e2b5f095 100644
@@ -271,6 +271,21 @@ config VGA_ARB_MAX_GPUS
 	  Reserves space in the kernel to maintain resource locking for
 	  multiple GPUS.  The overhead for each GPU is very small.
 
+config PCI_LIVEUPDATE
+	bool "PCI Live Update Support"
+	depends on PCI && LIVEUPDATE
+	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 +158 -0
new file mode 100644
index 000000000000..fffb41a66ca7
@@ -0,0 +1,158 @@
+// 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.
+ *
+ * :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 are
+ * expected to expose a file to represent a single PCI device and support
+ * preservation of that file with ``ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)``.
+ * This allows userspace to control the preservation of devices and ensure
+ * proper lifecycle management while a device is preserved. The first intended
+ * use-case is preserving vfio-pci device files.
+ *
+ * The PCI core maintains its own state about what devices are being preserved
+ * across Live Update using FLB data in LUO. Essentially, this allows the PCI
+ * core to allocate struct pci_ser when the first device (file) is preserved
+ * and free it when the last device (file) is unpreserved. After kexec, the
+ * PCI core can fetch the struct pci_ser (which was constructed by the previous
+ * kernel) from LUO at any time (e.g. during enumeration) so that it knows
+ * which devices were preserved.
+ *
+ * To enable the PCI core to be notified whenever a file representing a device
+ * is preserved, drivers must register their struct liveupdate_file_handler with
+ * the PCI core by using the following APIs:
+ *
+ *  * ``pci_liveupdate_register_flb(driver_file_handler)``
+ *  * ``pci_liveupdate_unregister_flb(driver_file_handler)``
+ */
+
+#define pr_fmt(fmt) "PCI: liveupdate: " fmt
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/pci.h>
+#include <linux/kho_block.h>
+#include <linux/liveupdate.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+/**
+ * struct pci_flb_outgoing - Outgoing PCI FLB object
+ * @ser: Pointer to the preserved struct pci_ser.
+ * @block_set: The KHO block set holding the outgoing devices.
+ *
+ * This structure holds the runtime state for the outgoing PCI Live Update
+ * state. It wraps the serialized pci_ser and the block_set used to manage
+ * the serialized entries.
+ */
+struct pci_flb_outgoing {
+	struct pci_ser *ser;
+	struct kho_block_set block_set;
+};
+
+static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing __free(kfree) = NULL;
+	struct pci_ser *ser;
+
+	outgoing = kzalloc_obj(*outgoing);
+	if (!outgoing)
+		return -ENOMEM;
+
+	ser = kho_alloc_preserve(sizeof(*ser));
+	if (IS_ERR(ser))
+		return PTR_ERR(ser);
+
+	ser->version = PCI_LUO_FLB_VERSION;
+	ser->nr_devices = 0;
+	ser->devices = 0;
+
+	outgoing->ser = ser;
+	kho_block_set_init(&outgoing->block_set, sizeof(struct pci_dev_ser));
+
+	args->obj = no_free_ptr(outgoing);
+	args->data = virt_to_phys(ser);
+	pr_debug("Preserved struct pci_ser (0x%llx)\n", args->data);
+	return 0;
+}
+
+static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
+{
+	struct pci_flb_outgoing *outgoing = args->obj;
+
+	pr_debug("Unpreserving struct pci_ser (0x%llx)\n", args->data);
+
+	WARN_ON(outgoing->ser->nr_devices);
+	kho_block_set_destroy(&outgoing->block_set);
+	kho_unpreserve_free(outgoing->ser);
+	kfree(outgoing);
+}
+
+static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
+{
+	pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
+	args->obj = phys_to_virt(args->data);
+	return 0;
+}
+
+static void pci_flb_finish(struct liveupdate_flb_op_args *args)
+{
+	pr_debug("Finished struct pci_ser (0x%llx)\n", args->data);
+	kho_restore_free(args->obj);
+}
+
+static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {
+	.preserve = pci_flb_preserve,
+	.unpreserve = pci_flb_unpreserve,
+	.retrieve = pci_flb_retrieve,
+	.finish = pci_flb_finish,
+	.owner = THIS_MODULE,
+};
+
+static struct liveupdate_flb pci_liveupdate_flb = {
+	.ops = &pci_liveupdate_flb_ops,
+	.compatible = PCI_LUO_FLB_COMPATIBLE,
+};
+
+/**
+ * pci_liveupdate_register_flb() - Register a file handler with the PCI core
+ * @fh: The file handler to register.
+ *
+ * Drivers should call pci_liveupdate_register_flb() to register their
+ * struct liveupdate_file_handler with the PCI core. This enables the PCI core
+ * to allocate its outgoing struct pci_ser whenever the first device is
+ * preserved, and free it when the last device is unpreserved.
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_register_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Registering file handler \"%s\"\n", fh->compatible);
+	return liveupdate_register_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_register_flb);
+
+/**
+ * pci_liveupdate_unregister_flb() - Unregister a file handler with the PCI core
+ * @fh: The file handler to unregister.
+ */
+void pci_liveupdate_unregister_flb(struct liveupdate_file_handler *fh)
+{
+	pr_debug("Unregistering file handler \"%s\"\n", fh->compatible);
+	liveupdate_unregister_flb(fh, &pci_liveupdate_flb);
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_unregister_flb);
include/linux/kho/abi/pci.h new file +63 -0
new file mode 100644
index 000000000000..d4881c2d2fc9
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * David Matlack <[email protected]>
+ */
+
+#ifndef _LINUX_KHO_ABI_PCI_H
+#define _LINUX_KHO_ABI_PCI_H
+
+#include <linux/bug.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/**
+ * DOC: PCI File-Lifecycle Bound (FLB) Live Update ABI
+ *
+ * This header defines the ABI for preserving core PCI state across kexec using
+ * Live Update File-Lifecycle Bound (FLB) data.
+ *
+ * This interface is a contract. Any modification to any of the serialization
+ * structs defined here constitutes a breaking change. Such changes require
+ * incrementing the version number in the PCI_LUO_FLB_VERSION number.
+ */
+
+#define PCI_LUO_FLB_COMPATIBLE "pci"
+#define PCI_LUO_FLB_VERSION 1
+
+/**
+ * struct pci_dev_ser - Serialized state about a single PCI device.
+ *
+ * @domain: The device's PCI domain number (segment).
+ * @bdf: The device's PCI bus, device, and function number.
+ * @refcount: Reference count used by the PCI core to keep track of whether it
+ *            is done using a device's struct pci_dev_ser. The value of the
+ *            refcount is equal to 1 when the struct pci_dev_ser is in use, and
+ *            0 otherwise.
+ */
+struct pci_dev_ser {
+	u32 domain;
+	u16 bdf;
+	u16 refcount;
+} __packed;
+
+/**
+ * struct pci_ser - PCI Subsystem Live Update State
+ *
+ * This struct tracks state about all devices that are being preserved across
+ * a Live Update for the next kernel.
+ *
+ * @version: The version of the "pci" FLB struct. This field must never be
+ *           deleted, moved, or resized, as the kernel depends on always being
+ *           able to check the struct pci_ser version number.
+ * @nr_devices: The number of devices that were preserved.
+ * @devices: Physical address of the first KHO block containing pci_dev_ser.
+ */
+struct pci_ser {
+	u32 version;
+	u32 nr_devices;
+	u64 devices;
+} __packed;
+
+#endif /* _LINUX_KHO_ABI_PCI_H */
include/linux/pci.h +1 -0
index ebb5b9d76360..da58aa101e4c 100644
@@ -43,6 +43,7 @@
 #include <uapi/linux/pci.h>
 
 #include <linux/pci_ids.h>
+#include <linux/pci_liveupdate.h>
 
 #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY  | \
 			       PCI_STATUS_SIG_SYSTEM_ERROR | \
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.55.0.487.gaf234c4eb3-goog
#5 lore raw

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

Quoted 7 lines · Bjorn Helgaas
Quoted 5 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.

Quoted 5 lines

I assume pci_ser is the state the PCI core needs to preserve across kexec so the new kernel's enumeration doesn't interrupt the device operation. And that whatever state the endpoint drivers need to adopt/inherit the device in the new kernel is managed without any help from the PCI core?

Yes.

Quoted 10 lines
Quoted 5 lines

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.

Where do drivers expose this file? sysfs? I guess it's a file per preserved device? Thinking like a driver writer, I'm expecting a hint about how to expose this file (should also be in the file doc somehere if it's not already).

There is no requirement about how drivers do this from the PCI core perspective. For all intents and purposes, the VFIO PCI variant drivers are the only drivers that are going to be supported in the next 1-2 years. They expose a misc character device for each file.

Quoted 10 lines
Quoted 7 lines

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);

So a driver calls pci_liveupdate_register_flb() once, then pci_liveupdate_preserve() once for each device it wants preserved?

Yes

Quoted 7 lines
Quoted 3 lines

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

The passive voice here makes the actors a bit obscure. I guess a LIVEUPDATE_SESSION_PRESERVE_FD ioctl on some per-device file kicks this off?

Yes. (And I will reduce the passive voice in the next version.)

Quoted 2 lines

I guess the pci_ser allocation is in pci_liveupdate_flb_ops.preserve(), i.e., pci_flb_preserve()?

Yes.

Quoted 4 lines

So the PCI core tracker (pci_ser) isn't actually allocated at the time of pci_liveupdate_register_flb(); it's allocated on the first LIVEUPDATE_SESSION_PRESERVE_FD ioctl for a driver that has called pci_liveupdate_register_flb()?

Yes. The first device that gets preserved triggers the allocation of struct pci_ser. And the last device that gets unpreserved (preservation cancelled) triggers the freeing of struct pci_ser.

Quoted 20 lines

IIUC the call tree for that ioctl looks something like this:

<driver>
pci_liveupdate_register_flb
liveupdate_register_flb(fh, &pci_liveupdate_flb)

luo_session_ioctl

    op = &luo_session_ioctl_ops[...]
    op->execute                               # eg luo_session_preserve_fd()
      luo_session_preserve_fd
        luo_preserve_file
          luo_flb_file_preserve
            luo_flb_file_preserve_one
              if (outgoing_count == 0)        # only for first FLB device
                flb->ops->preserve            # eg pci_flb_preserve()
                  pci_flb_preserve
                    ser = kho_alloc_preserve  <-- alloc pci_ser
                outgoing.count = 1
          fh->ops->preserve                   # something not included here
Quoted 8 lines

pci_liveupdate_preserve
pci_liveupdate_preserve_device
dev_ser = pci_flb_alloc_dev_ser <-- alloc per-dev PCI core serialized state dev_ser->bdf = pci_dev_id(dev)

Seems like kind of an awkward way to allocate pci_ser. Couldn't it be allocated on the first call to pci_liveupdate_register_flb()? That would be a lot easier for driver writers to trace through.

I agree the LUO FLB API is a bit awkward, but this is how it works.

If we allocated it during pci_liveupdate_register_flb() we would then need to stash it in a global variable to hand-off the LUO later. Despite the awkwardness of FLBs, it is useful to avoid globals and have LUO management the lifetime.

Quoted 11 lines
Quoted 2 lines

When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.

There's a lot going on behind "PCI core will be notified". I assume these refer to the first-time behavior of luo_flb_file_preserve_one() and last-time behavior of liveupdate_flb_put_outgoing(), which is honestly kind of hard to suss out.

This series doesn't include a caller of pci_liveupdate_preserve() (or pci_liveupdate_register_flb()), so I can't figure out the ordering. Obviously pci_liveupdate_register_flb() must be first.

In every version of this patch series I have sent I included a link to the vfio-pci driver changes that build on top of this, rebased that series on top of this one, uploaded it to my GitHub, and included a link in the cover letter. Here is the relevant section from the v8 cover letter:

. This series was tested in conjunction with v5 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/v8-with-vfio

Quoted 5 lines

I first thought pci_liveupdate_preserve() would be called via the fh->ops->preserve() in the luo_session_preserve_fd() ioctl path, but it's not. pci_liveupdate_preserve() is intended for the driver to call it directly. But it looks like it has to be called *after* the ioctl? Obviously I'm confused :)

It is called by the driver during it's fh->ops->preserve() callback. In other words, it is called during the ioctl by the driver.

LUO just knows that a file has been preserved and what preserve() callback it needs to run to preserve that file. It is has no idea which files correspond to devices or which devices. The file could be a memfd for all LUO knows. That's why the driver has to call into the PCI core via pci_liveupdate_preserve() to let it know that a device is being preserved, and which.

#6 lore raw

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

Quoted 27 lines · David Matlack
Quoted 7 lines · Bjorn Helgaas
Quoted 5 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.

Quoted 5 lines

I assume pci_ser is the state the PCI core needs to preserve across kexec so the new kernel's enumeration doesn't interrupt the device operation. And that whatever state the endpoint drivers need to adopt/inherit the device in the new kernel is managed without any help from the PCI core?

Yes.

Quoted 10 lines
Quoted 5 lines

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.

Where do drivers expose this file? sysfs? I guess it's a file per preserved device? Thinking like a driver writer, I'm expecting a hint about how to expose this file (should also be in the file doc somehere if it's not already).

Quoted 4 lines

There is no requirement about how drivers do this from the PCI core perspective. For all intents and purposes, the VFIO PCI variant drivers are the only drivers that are going to be supported in the next 1-2 years. They expose a misc character device for each file.

If this file isn't relevant to the PCI core, maybe we don't need to mention it here. It doesn't seem like it motivates this patch.

Quoted 27 lines
Quoted 9 lines
Quoted 6 lines

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);

So a driver calls pci_liveupdate_register_flb() once, then pci_liveupdate_preserve() once for each device it wants preserved?

Yes

Quoted 7 lines
Quoted 3 lines

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

The passive voice here makes the actors a bit obscure. I guess a LIVEUPDATE_SESSION_PRESERVE_FD ioctl on some per-device file kicks this off?

Yes. (And I will reduce the passive voice in the next version.)

Quoted 2 lines

I guess the pci_ser allocation is in pci_liveupdate_flb_ops.preserve(), i.e., pci_flb_preserve()?

Yes.

Quoted 28 lines
Quoted 4 lines

So the PCI core tracker (pci_ser) isn't actually allocated at the time of pci_liveupdate_register_flb(); it's allocated on the first LIVEUPDATE_SESSION_PRESERVE_FD ioctl for a driver that has called pci_liveupdate_register_flb()?

Yes. The first device that gets preserved triggers the allocation of struct pci_ser. And the last device that gets unpreserved (preservation cancelled) triggers the freeing of struct pci_ser.

Quoted 19 lines

IIUC the call tree for that ioctl looks something like this:

<driver>
pci_liveupdate_register_flb
liveupdate_register_flb(fh, &pci_liveupdate_flb)

luo_session_ioctl

    op = &luo_session_ioctl_ops[...]
    op->execute                               # eg luo_session_preserve_fd()
      luo_session_preserve_fd
        luo_preserve_file
          luo_flb_file_preserve
            luo_flb_file_preserve_one
              if (outgoing_count == 0)        # only for first FLB device
                flb->ops->preserve            # eg pci_flb_preserve()
                  pci_flb_preserve
                    ser = kho_alloc_preserve  <-- alloc pci_ser
                outgoing.count = 1
          fh->ops->preserve                   # something not included here

So IIUC this part of the path looks like this, which answers my question below about ordering of pci_ser and pci_liveupdate_preserve():

              fh->ops->preserve                   # eg vfio_pci_liveupdate_preserve()
                vfio_pci_liveupdate_preserve
                  pci_liveupdate_preserve
                    pci_liveupdate_preserve_device
                      dev_ser = pci_flb_alloc_dev_ser  <-- alloc per-dev PCI core serialized state
                      dev_ser->bdf = pci_dev_id(dev)
Quoted 10 lines
Quoted 3 lines

Seems like kind of an awkward way to allocate pci_ser. Couldn't it be allocated on the first call to pci_liveupdate_register_flb()? That would be a lot easier for driver writers to trace through.

I agree the LUO FLB API is a bit awkward, but this is how it works.

If we allocated it during pci_liveupdate_register_flb() we would then need to stash it in a global variable to hand-off the LUO later. Despite the awkwardness of FLBs, it is useful to avoid globals and have LUO management the lifetime.

It seems like pci_ser is a singleton by design, so a global variable doesn't sound like it would be terrible to me.

Quoted 7 lines
Quoted 7 lines
Quoted 2 lines

When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.

There's a lot going on behind "PCI core will be notified". I assume these refer to the first-time behavior of luo_flb_file_preserve_one() and last-time behavior of liveupdate_flb_put_outgoing(), which is honestly kind of hard to suss out.

Could we say something specific and PCI-related here, to help connect the dots? Most of these paths are outside the PCI core.

IIUC luo_session essentially has a refcount (outgoing.count) incremented by each LIVEUPDATE_SESSION_PRESERVE_FD ioctl, and the 0->1 transition in luo_flb_file_preserve_one() ends up calling pci_flb_preserve(), where pci_ser is allocated.

And the refcount is decremented by luo_flb_file_unpreserve() (in a luo_session .release() function), where the 1->0 transition in liveupdate_flb_put_outgoing() calls pci_flb_unpreserve() where pci_ser is deallocated.

That gets into a lot of detail, probably too much for a commit log. Maybe mentioning the function names by which the PCI core is notified to alloc/free pci_ser would be enough of a bread crumb.

Quoted 20 lines
Quoted 3 lines

This series doesn't include a caller of pci_liveupdate_preserve() (or pci_liveupdate_register_flb()), so I can't figure out the ordering. Obviously pci_liveupdate_register_flb() must be first.

In every version of this patch series I have sent I included a link to the vfio-pci driver changes that build on top of this, rebased that series on top of this one, uploaded it to my GitHub, and included a link in the cover letter. Here is the relevant section from the v8 cover letter:

. This series was tested in conjunction with v5 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/v8-with-vfio

Quoted 8 lines
Quoted 5 lines

I first thought pci_liveupdate_preserve() would be called via the fh->ops->preserve() in the luo_session_preserve_fd() ioctl path, but it's not. pci_liveupdate_preserve() is intended for the driver to call it directly. But it looks like it has to be called *after* the ioctl? Obviously I'm confused :)

It is called by the driver during it's fh->ops->preserve() callback. In other words, it is called during the ioctl by the driver.

I think the updated call tree above shows the connection?

Quoted 6 lines

LUO just knows that a file has been preserved and what preserve() callback it needs to run to preserve that file. It is has no idea which files correspond to devices or which devices. The file could be a memfd for all LUO knows. That's why the driver has to call into the PCI core via pci_liveupdate_preserve() to let it know that a device is being preserved, and which.

#7 lore raw

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

Quoted 168 lines · Bjorn Helgaas
Quoted 27 lines · David Matlack
Quoted 7 lines · Bjorn Helgaas
Quoted 5 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.

Quoted 5 lines

I assume pci_ser is the state the PCI core needs to preserve across kexec so the new kernel's enumeration doesn't interrupt the device operation. And that whatever state the endpoint drivers need to adopt/inherit the device in the new kernel is managed without any help from the PCI core?

Yes.

Quoted 10 lines
Quoted 5 lines

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.

Where do drivers expose this file? sysfs? I guess it's a file per preserved device? Thinking like a driver writer, I'm expecting a hint about how to expose this file (should also be in the file doc somehere if it's not already).

Quoted 4 lines

There is no requirement about how drivers do this from the PCI core perspective. For all intents and purposes, the VFIO PCI variant drivers are the only drivers that are going to be supported in the next 1-2 years. They expose a misc character device for each file.

If this file isn't relevant to the PCI core, maybe we don't need to mention it here. It doesn't seem like it motivates this patch.

Quoted 27 lines
Quoted 9 lines
Quoted 6 lines

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);

So a driver calls pci_liveupdate_register_flb() once, then pci_liveupdate_preserve() once for each device it wants preserved?

Yes

Quoted 7 lines
Quoted 3 lines

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

The passive voice here makes the actors a bit obscure. I guess a LIVEUPDATE_SESSION_PRESERVE_FD ioctl on some per-device file kicks this off?

Yes. (And I will reduce the passive voice in the next version.)

Quoted 2 lines

I guess the pci_ser allocation is in pci_liveupdate_flb_ops.preserve(), i.e., pci_flb_preserve()?

Yes.

Quoted 28 lines
Quoted 4 lines

So the PCI core tracker (pci_ser) isn't actually allocated at the time of pci_liveupdate_register_flb(); it's allocated on the first LIVEUPDATE_SESSION_PRESERVE_FD ioctl for a driver that has called pci_liveupdate_register_flb()?

Yes. The first device that gets preserved triggers the allocation of struct pci_ser. And the last device that gets unpreserved (preservation cancelled) triggers the freeing of struct pci_ser.

Quoted 19 lines

IIUC the call tree for that ioctl looks something like this:

<driver>
pci_liveupdate_register_flb
liveupdate_register_flb(fh, &pci_liveupdate_flb)

luo_session_ioctl

    op = &luo_session_ioctl_ops[...]
    op->execute                               # eg luo_session_preserve_fd()
      luo_session_preserve_fd
        luo_preserve_file
          luo_flb_file_preserve
            luo_flb_file_preserve_one
              if (outgoing_count == 0)        # only for first FLB device
                flb->ops->preserve            # eg pci_flb_preserve()
                  pci_flb_preserve
                    ser = kho_alloc_preserve  <-- alloc pci_ser
                outgoing.count = 1
          fh->ops->preserve                   # something not included here

So IIUC this part of the path looks like this, which answers my question below about ordering of pci_ser and pci_liveupdate_preserve():

              fh->ops->preserve                   # eg vfio_pci_liveupdate_preserve()
                vfio_pci_liveupdate_preserve
                  pci_liveupdate_preserve
                    pci_liveupdate_preserve_device
                      dev_ser = pci_flb_alloc_dev_ser  <-- alloc per-dev PCI core serialized state
                      dev_ser->bdf = pci_dev_id(dev)
Quoted 10 lines
Quoted 3 lines

Seems like kind of an awkward way to allocate pci_ser. Couldn't it be allocated on the first call to pci_liveupdate_register_flb()? That would be a lot easier for driver writers to trace through.

I agree the LUO FLB API is a bit awkward, but this is how it works.

If we allocated it during pci_liveupdate_register_flb() we would then need to stash it in a global variable to hand-off the LUO later. Despite the awkwardness of FLBs, it is useful to avoid globals and have LUO management the lifetime.

It seems like pci_ser is a singleton by design, so a global variable doesn't sound like it would be terrible to me.

Quoted 7 lines
Quoted 7 lines
Quoted 2 lines

When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.

There's a lot going on behind "PCI core will be notified". I assume these refer to the first-time behavior of luo_flb_file_preserve_one() and last-time behavior of liveupdate_flb_put_outgoing(), which is honestly kind of hard to suss out.

Could we say something specific and PCI-related here, to help connect the dots? Most of these paths are outside the PCI core.

IIUC luo_session essentially has a refcount (outgoing.count) incremented by each LIVEUPDATE_SESSION_PRESERVE_FD ioctl, and the 0->1 transition in luo_flb_file_preserve_one() ends up calling pci_flb_preserve(), where pci_ser is allocated.

And the refcount is decremented by luo_flb_file_unpreserve() (in a luo_session .release() function), where the 1->0 transition in liveupdate_flb_put_outgoing() calls pci_flb_unpreserve() where pci_ser is deallocated.

That gets into a lot of detail, probably too much for a commit log. Maybe mentioning the function names by which the PCI core is notified to alloc/free pci_ser would be enough of a bread crumb.

Quoted 26 lines
Quoted 3 lines

This series doesn't include a caller of pci_liveupdate_preserve() (or pci_liveupdate_register_flb()), so I can't figure out the ordering. Obviously pci_liveupdate_register_flb() must be first.

In every version of this patch series I have sent I included a link to the vfio-pci driver changes that build on top of this, rebased that series on top of this one, uploaded it to my GitHub, and included a link in the cover letter. Here is the relevant section from the v8 cover letter:

. This series was tested in conjunction with v5 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/v8-with-vfio

Quoted 5 lines

I first thought pci_liveupdate_preserve() would be called via the fh->ops->preserve() in the luo_session_preserve_fd() ioctl path, but it's not. pci_liveupdate_preserve() is intended for the driver to call it directly. But it looks like it has to be called *after* the ioctl? Obviously I'm confused :)

Quoted 4 lines
Quoted 2 lines

It is called by the driver during it's fh->ops->preserve() callback. In other words, it is called during the ioctl by the driver.

I think the updated call tree above shows the connection?

Yes the call tree you added above is correct. Here is an attempt at the complete picture that I plan to include in the kernel-doc in v9:

* Call Flow
* ---------
*
* ::
*
* # Driver initialization
* pci_liveupdate_register_flb(fh)
*
* # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)
* luo_preserve_file()
* luo_flb_file_preserve()

 *       luo_flb_file_preserve_one()      # first preserved file only
 *         pci_flb_preserve()             # alloc and preserve struct pci_ser
 *     fh->ops->preserve()                # driver callback
 *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser

*
* # Userspace: preservation cancelled or session torn down
* luo_file_unpreserve_files()
* luo_flb_file_unpreserve()
* liveupdate_flb_put_outgoing() # last unpreserved file only
* pci_flb_unpreserve() # free struct pci_ser
*
* # ---------------- kexec ----------------
*
* # New kernel: PCI enumeration
* pci_setup_device()
* pci_liveupdate_setup_device()
* liveupdate_flb_get_incoming()
* luo_flb_retrieve_one() # first request only
* pci_flb_retrieve() # previous kernel's struct pci_ser
*
* # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)
* luo_file_finish_one()
* fh->ops->finish() # driver callback
* pci_liveupdate_finish(dev) # release this device's pci_dev_ser
* luo_flb_file_finish()
* liveupdate_flb_put_incoming() # last incoming file only
* pci_flb_finish() # free struct pci_ser
*

And here is an updated commit message that I hope explains everything more clearly:

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

Set up a File-Lifecycle-Bound (FLB) handler so that the PCI core can preserve its own state across a Live Update kexec.

Preserving a PCI device across kexec requires preserving two independent sets of state:

- Driver state, e.g. everything vfio-pci needs so that userspace can keep using the device in the new kernel. The driver preserves this itself and the PCI core is not involved.

- PCI core state, e.g. which devices are preserved, so that the new kernel knows not to disturb them while they are still running and doing DMA. That is what this commit adds, serialized into struct pci_ser.

Userspace, not the kernel, decides which devices are preserved, and it does so through the Live Update Orchestrator's (LUO) support for file
preservation: a driver exposes a file that represents a single PCI device, and userspace preserves that device with ioctl(LIVEUPDATE_SESSION_PRESERVE_FD) on that file. Binding preservation to a file gives it proper lifecycle management, e.g. the preservation is undone if userspace cancels it or goes away. How a driver exposes that file is up to the driver and invisible to the PCI core (vfio-pci variant drivers, the first intended use-case, use their per-device cdev).

LUO only knows that a file was preserved; it does not know that the represents a PCI device, or which one. Bridging that gap, drivers register their liveupdate_file_handler with the PCI core:

   pci_liveupdate_register_flb(driver_file_handler);
   pci_liveupdate_unregister_flb(driver_file_handler);

LUO then refcounts the PCI core's FLB against the files preserved by that handler, and that refcount drives the lifetime of struct pci_ser:

- On the first preserved file, luo_flb_file_preserve_one() calls pci_flb_preserve(), which allocates struct pci_ser and preserves it with KHO.

- On the last unpreserved file (i.e. preservation cancelled), liveupdate_flb_put_outgoing() calls pci_flb_unpreserve(), which unpreserves and frees struct pci_ser.

- In the next kernel, pci_flb_retrieve() hands the PCI core the struct pci_ser built by the previous kernel, whenever the PCI core asks for it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI core is done with it.

So the flow for preserving a device, once a driver has registered, looks like this:

ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)
luo_session_preserve_fd()
luo_preserve_file()
luo_flb_file_preserve()
luo_flb_file_preserve_one() # only on the first preserved file
pci_flb_preserve() # alloc + KHO-preserve pci_ser
fh->ops->preserve() # driver callback, e.g. vfio-pci

Note that struct pci_ser is deliberately not allocated when a driver calls pci_liveupdate_register_flb(). A driver can be loaded for the lifetime of the machine without ever preserving a device, and there is no reason to allocate memory and hand it to the next kernel in that case. Letting LUO own the lifetime also means the PCI core does not have to duplicate LUO's refcounting and unwind logic for preservation failures, session aborts and fd close, and the incoming side (retrieve/finish) comes from the same object rather than requiring a separate KHO FDT entry owned by the PCI core.

Note: This commit only allocates struct pci_ser and preserves it across Live Update. A subsequent commit adds pci_liveupdate_preserve(), the API drivers call from their fh->ops->preserve() callback 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.

#8 lore raw

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

Quoted 247 lines · David Matlack
Quoted 168 lines · Bjorn Helgaas
Quoted 27 lines · David Matlack
Quoted 7 lines · Bjorn Helgaas
Quoted 5 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.

Quoted 5 lines

I assume pci_ser is the state the PCI core needs to preserve across kexec so the new kernel's enumeration doesn't interrupt the device operation. And that whatever state the endpoint drivers need to adopt/inherit the device in the new kernel is managed without any help from the PCI core?

Yes.

Quoted 10 lines
Quoted 5 lines

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.

Where do drivers expose this file? sysfs? I guess it's a file per preserved device? Thinking like a driver writer, I'm expecting a hint about how to expose this file (should also be in the file doc somehere if it's not already).

Quoted 4 lines

There is no requirement about how drivers do this from the PCI core perspective. For all intents and purposes, the VFIO PCI variant drivers are the only drivers that are going to be supported in the next 1-2 years. They expose a misc character device for each file.

If this file isn't relevant to the PCI core, maybe we don't need to mention it here. It doesn't seem like it motivates this patch.

Quoted 27 lines
Quoted 9 lines
Quoted 6 lines

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);

So a driver calls pci_liveupdate_register_flb() once, then pci_liveupdate_preserve() once for each device it wants preserved?

Yes

Quoted 7 lines
Quoted 3 lines

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

The passive voice here makes the actors a bit obscure. I guess a LIVEUPDATE_SESSION_PRESERVE_FD ioctl on some per-device file kicks this off?

Yes. (And I will reduce the passive voice in the next version.)

Quoted 2 lines

I guess the pci_ser allocation is in pci_liveupdate_flb_ops.preserve(), i.e., pci_flb_preserve()?

Yes.

Quoted 28 lines
Quoted 4 lines

So the PCI core tracker (pci_ser) isn't actually allocated at the time of pci_liveupdate_register_flb(); it's allocated on the first LIVEUPDATE_SESSION_PRESERVE_FD ioctl for a driver that has called pci_liveupdate_register_flb()?

Yes. The first device that gets preserved triggers the allocation of struct pci_ser. And the last device that gets unpreserved (preservation cancelled) triggers the freeing of struct pci_ser.

Quoted 19 lines

IIUC the call tree for that ioctl looks something like this:

<driver>
pci_liveupdate_register_flb
liveupdate_register_flb(fh, &pci_liveupdate_flb)

luo_session_ioctl

    op = &luo_session_ioctl_ops[...]
    op->execute                               # eg luo_session_preserve_fd()
      luo_session_preserve_fd
        luo_preserve_file
          luo_flb_file_preserve
            luo_flb_file_preserve_one
              if (outgoing_count == 0)        # only for first FLB device
                flb->ops->preserve            # eg pci_flb_preserve()
                  pci_flb_preserve
                    ser = kho_alloc_preserve  <-- alloc pci_ser
                outgoing.count = 1
          fh->ops->preserve                   # something not included here

So IIUC this part of the path looks like this, which answers my question below about ordering of pci_ser and pci_liveupdate_preserve():

              fh->ops->preserve                   # eg vfio_pci_liveupdate_preserve()
                vfio_pci_liveupdate_preserve
                  pci_liveupdate_preserve
                    pci_liveupdate_preserve_device
                      dev_ser = pci_flb_alloc_dev_ser  <-- alloc per-dev PCI core serialized state
                      dev_ser->bdf = pci_dev_id(dev)
Quoted 10 lines
Quoted 3 lines

Seems like kind of an awkward way to allocate pci_ser. Couldn't it be allocated on the first call to pci_liveupdate_register_flb()? That would be a lot easier for driver writers to trace through.

I agree the LUO FLB API is a bit awkward, but this is how it works.

If we allocated it during pci_liveupdate_register_flb() we would then need to stash it in a global variable to hand-off the LUO later. Despite the awkwardness of FLBs, it is useful to avoid globals and have LUO management the lifetime.

It seems like pci_ser is a singleton by design, so a global variable doesn't sound like it would be terrible to me.

Quoted 7 lines
Quoted 7 lines
Quoted 2 lines

When the last file is unpreserved (i.e. preservation cancelled) the PCI core will be notified to free struct pci_ser.

There's a lot going on behind "PCI core will be notified". I assume these refer to the first-time behavior of luo_flb_file_preserve_one() and last-time behavior of liveupdate_flb_put_outgoing(), which is honestly kind of hard to suss out.

Could we say something specific and PCI-related here, to help connect the dots? Most of these paths are outside the PCI core.

IIUC luo_session essentially has a refcount (outgoing.count) incremented by each LIVEUPDATE_SESSION_PRESERVE_FD ioctl, and the 0->1 transition in luo_flb_file_preserve_one() ends up calling pci_flb_preserve(), where pci_ser is allocated.

And the refcount is decremented by luo_flb_file_unpreserve() (in a luo_session .release() function), where the 1->0 transition in liveupdate_flb_put_outgoing() calls pci_flb_unpreserve() where pci_ser is deallocated.

That gets into a lot of detail, probably too much for a commit log. Maybe mentioning the function names by which the PCI core is notified to alloc/free pci_ser would be enough of a bread crumb.

Quoted 26 lines
Quoted 3 lines

This series doesn't include a caller of pci_liveupdate_preserve() (or pci_liveupdate_register_flb()), so I can't figure out the ordering. Obviously pci_liveupdate_register_flb() must be first.

In every version of this patch series I have sent I included a link to the vfio-pci driver changes that build on top of this, rebased that series on top of this one, uploaded it to my GitHub, and included a link in the cover letter. Here is the relevant section from the v8 cover letter:

. This series was tested in conjunction with v5 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/v8-with-vfio

Quoted 5 lines

I first thought pci_liveupdate_preserve() would be called via the fh->ops->preserve() in the luo_session_preserve_fd() ioctl path, but it's not. pci_liveupdate_preserve() is intended for the driver to call it directly. But it looks like it has to be called *after* the ioctl? Obviously I'm confused :)

Quoted 4 lines
Quoted 2 lines

It is called by the driver during it's fh->ops->preserve() callback. In other words, it is called during the ioctl by the driver.

I think the updated call tree above shows the connection?

Yes the call tree you added above is correct. Here is an attempt at the complete picture that I plan to include in the kernel-doc in v9:

* Call Flow
* ---------
*
* ::
*
* # Driver initialization
* pci_liveupdate_register_flb(fh)
*
* # Userspace: ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, devfd)
* luo_preserve_file()
* luo_flb_file_preserve()

 *       luo_flb_file_preserve_one()      # first preserved file only
 *         pci_flb_preserve()             # alloc and preserve struct pci_ser
 *     fh->ops->preserve()                # driver callback
 *       pci_liveupdate_preserve(dev)     # record this device in struct pci_ser

*
* # Userspace: preservation cancelled or session torn down
* luo_file_unpreserve_files()
* luo_flb_file_unpreserve()
* liveupdate_flb_put_outgoing() # last unpreserved file only
* pci_flb_unpreserve() # free struct pci_ser
*
* # ---------------- kexec ----------------
*
* # New kernel: PCI enumeration
* pci_setup_device()
* pci_liveupdate_setup_device()
* liveupdate_flb_get_incoming()
* luo_flb_retrieve_one() # first request only
* pci_flb_retrieve() # previous kernel's struct pci_ser
*
* # Userspace: ioctl(LIVEUPDATE_SESSION_FINISH)
* luo_file_finish_one()
* fh->ops->finish() # driver callback
* pci_liveupdate_finish(dev) # release this device's pci_dev_ser
* luo_flb_file_finish()
* liveupdate_flb_put_incoming() # last incoming file only
* pci_flb_finish() # free struct pci_ser
*

And here is an updated commit message that I hope explains everything more clearly:

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

Set up a File-Lifecycle-Bound (FLB) handler so that the PCI core can preserve its own state across a Live Update kexec.

Preserving a PCI device across kexec requires preserving two independent sets of state:

- Driver state, e.g. everything vfio-pci needs so that userspace can keep using the device in the new kernel. The driver preserves this itself and the PCI core is not involved.

- PCI core state, e.g. which devices are preserved, so that the new kernel knows not to disturb them while they are still running and doing DMA. That is what this commit adds, serialized into struct pci_ser.

Userspace, not the kernel, decides which devices are preserved, and it does so through the Live Update Orchestrator's (LUO) support for file
preservation: a driver exposes a file that represents a single PCI device, and userspace preserves that device with ioctl(LIVEUPDATE_SESSION_PRESERVE_FD) on that file. Binding preservation to a file gives it proper lifecycle management, e.g. the preservation is undone if userspace cancels it or goes away. How a driver exposes that file is up to the driver and invisible to the PCI core (vfio-pci variant drivers, the first intended use-case, use their per-device cdev).

Quoted 1 line

LUO only knows that a file was preserved; it does not know that the

s/the/it/

Quoted 43 lines

represents a PCI device, or which one. Bridging that gap, drivers register their liveupdate_file_handler with the PCI core:

   pci_liveupdate_register_flb(driver_file_handler);
   pci_liveupdate_unregister_flb(driver_file_handler);

LUO then refcounts the PCI core's FLB against the files preserved by that handler, and that refcount drives the lifetime of struct pci_ser:

- On the first preserved file, luo_flb_file_preserve_one() calls pci_flb_preserve(), which allocates struct pci_ser and preserves it with KHO.

- On the last unpreserved file (i.e. preservation cancelled), liveupdate_flb_put_outgoing() calls pci_flb_unpreserve(), which unpreserves and frees struct pci_ser.

- In the next kernel, pci_flb_retrieve() hands the PCI core the struct pci_ser built by the previous kernel, whenever the PCI core asks for it (e.g. during enumeration), and pci_flb_finish() frees it once the PCI core is done with it.

So the flow for preserving a device, once a driver has registered, looks like this:

ioctl(LIVEUPDATE_SESSION_PRESERVE_FD)
luo_session_preserve_fd()
luo_preserve_file()
luo_flb_file_preserve()
luo_flb_file_preserve_one() # only on the first preserved file
pci_flb_preserve() # alloc + KHO-preserve pci_ser
fh->ops->preserve() # driver callback, e.g. vfio-pci

Note that struct pci_ser is deliberately not allocated when a driver calls pci_liveupdate_register_flb(). A driver can be loaded for the lifetime of the machine without ever preserving a device, and there is no reason to allocate memory and hand it to the next kernel in that case. Letting LUO own the lifetime also means the PCI core does not have to duplicate LUO's refcounting and unwind logic for preservation failures, session aborts and fd close, and the incoming side (retrieve/finish) comes from the same object rather than requiring a separate KHO FDT entry owned by the PCI core.

Quoted 8 lines

Note: This commit only allocates struct pci_ser and preserves it across Live Update. A subsequent commit adds pci_liveupdate_preserve(), the API drivers call from their fh->ops->preserve() callback 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.

Beautiful, thanks for your patience :)

#9 lore raw

PATCH v8 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 the list of preserved devices can be handed off to the next kernel.

For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges).

---
 drivers/pci/liveupdate.c       | 210 +++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       |  21 ++++
 drivers/pci/probe.c            |   2 +
 include/linux/pci.h            |   3 +
 include/linux/pci_liveupdate.h |  21 ++++
 5 files changed, 257 insertions(+)
 create mode 100644 drivers/pci/liveupdate.h
drivers/pci/liveupdate.c +210 -0
index fffb41a66ca7..b003b7069cdb 100644
@@ -36,6 +36,27 @@
  *
  *  * ``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).
+ *  * The device cannot be behind a PCI-to-PCI bridge.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -50,6 +71,21 @@
 #include <linux/pci.h>
 #include <linux/slab.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),
+};
+
 /**
  * struct pci_flb_outgoing - Outgoing PCI FLB object
  * @ser: Pointer to the preserved struct pci_ser.
@@ -128,6 +164,180 @@ static struct liveupdate_flb pci_liveupdate_flb = {
 	.compatible = PCI_LUO_FLB_COMPATIBLE,
 };
 
+static void pci_liveupdate_flb_put_outgoing(void)
+{
+	liveupdate_flb_put_outgoing(&pci_liveupdate_flb);
+}
+
+static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&outgoing);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!outgoing)
+		return ERR_PTR(-ENOENT);
+
+	return outgoing;
+}
+
+static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)
+{
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	u64 count = 0;
+	int err;
+
+	kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* Try to find an existing, previously unpreserved, entry. */
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		if (!dev_ser->refcount)
+			return dev_ser;
+
+		count++;
+	}
+
+	/* Otherwise grow the block set and reserve a new entry. */
+	err = kho_block_set_grow(&outgoing->block_set, count + 1);
+	if (err)
+		return ERR_PTR(err);
+
+	if (!count)
+		kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* This should always succeed since kho_block_set_grow() succeeded. */
+	dev_ser = kho_block_set_it_reserve_entry(&it);
+	if (WARN_ON_ONCE(!dev_ser))
+		return ERR_PTR(-ENOSPC);
+
+	return dev_ser;
+}
+
+static void pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					     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");
+	outgoing->ser->nr_devices--;
+	memset(dev_ser, 0, sizeof(*dev_ser));
+	dev->liveupdate.outgoing = NULL;
+}
+
+static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
+					  struct pci_dev *dev)
+{
+	struct pci_dev_ser *dev_ser;
+
+	if (dev->is_virtfn) {
+		pci_warn(dev, "Cannot preserve Virtual Functions\n");
+		return -EINVAL;
+	}
+
+	if (dev->liveupdate.outgoing) {
+		pci_warn(dev, "Device is already preserved\n");
+		return -EBUSY;
+	}
+
+	if (!pci_is_root_bus(dev->bus)) {
+		pci_warn(dev, "Cannot preserve devices behind bridges\n");
+		return -EINVAL;
+	}
+
+	dev_ser = pci_flb_alloc_dev_ser(outgoing);
+	if (IS_ERR(dev_ser))
+		return PTR_ERR(dev_ser);
+
+	pci_info(dev, "Device will be preserved across next Live Update\n");
+	outgoing->ser->nr_devices++;
+	outgoing->ser->devices = kho_block_set_head_pa(&outgoing->block_set);
+
+	dev_ser->domain = pci_domain_nr(dev->bus);
+	dev_ser->bdf = pci_dev_id(dev);
+	dev_ser->refcount = 1;
+
+	dev->liveupdate.outgoing = dev_ser;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
+ * @dev: The PCI device to preserve.
+ *
+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be
+ * preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler
+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.
+ *
+ * Returns: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing))
+		return PTR_ERR(outgoing);
+
+	ret = pci_liveupdate_preserve_device(outgoing, dev);
+
+	pci_liveupdate_flb_put_outgoing();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
+
+/**
+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device
+ * @dev: The PCI device to unpreserve.
+ *
+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no
+ * longer be preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set
+ * up.
+ */
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_device(outgoing, dev);
+	pci_liveupdate_flb_put_outgoing();
+}
+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.
+	 *
+	 * This should never happen in practice. Drivers should block removal
+	 * while a device is preserved.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing))
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+}
+
 /**
  * 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 dd0abbc63e18..14b66acbdb15 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 = {
@@ -2485,6 +2486,7 @@ static void pci_release_dev(struct device *dev)
 
 	pci_dev = to_pci_dev(dev);
 	pci_release_capabilities(pci_dev);
+	pci_liveupdate_cleanup_device(pci_dev);
 	pci_release_of_node(pci_dev);
 	pcibios_release_device(pci_dev);
 	pci_bus_put(pci_dev->bus);
include/linux/pci.h +3 -0
index da58aa101e4c..b41dd572a2d6 100644
@@ -593,6 +593,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..894052ad6961 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/spinlock_types.h>
 #include <linux/types.h>
 
+/**
+ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
+ * @outgoing: State preserved for the next kernel.
+ */
+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.55.0.487.gaf234c4eb3-goog
#10 lore raw

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

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 14 lines

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 the list of preserved devices can be handed off to the next kernel.

Quoted 7 lines

For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges).

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Reviewed-by on v8 2/12
#11 lore raw

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

In reply to David Matlack
Quoted 21 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 the list of preserved devices can be handed off to the next kernel.

For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges).

Quoted 9 lines

---

drivers/pci/liveupdate.c       | 210 +++++++++++++++++++++++++++++++++
drivers/pci/liveupdate.h       |  21 ++++
drivers/pci/probe.c            |   2 +
include/linux/pci.h            |   3 +
include/linux/pci_liveupdate.h |  21 ++++
5 files changed, 257 insertions(+)

create mode 100644 drivers/pci/liveupdate.h

#12 lore raw

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

In reply to David Matlack
Quoted 10 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).

Quoted 6 lines

This tracking ensures the PCI core is fully aware of which devices may need special handling during shutdown and kexec, and so the list of preserved devices can be handed off to the next kernel.

For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges).

s/an//

Quoted 4 lines
#13 lore raw

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

In reply to David Matlack
Quoted 9 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).

Hi David,

I have a question about the pci_liveupdate_preserve() and pci_liveupdate_unpreserve() APIs.

Is it supported to call these APIs multiple times for the same PCI device? For example, a driver could do:

pci_liveupdate_preserve(pdev) -- > pci_liveupdate_unpreserve(pdev) -- > perform some operations -- > pci_liveupdate_preserve(pdev) again, before the actual Live Update starts.

From the implementation, it looks like an unpreserved pci_dev_ser entry can be reused, so I wanted to confirm whether this preserve -- > unpreserve -- > preserve sequence is an intended and supported usage.

Thanks!

Yanjun.Zhu

Quoted 364 lines

This tracking ensures the PCI core is fully aware of which devices may need special handling during shutdown and kexec, and so the list of preserved devices can be handed off to the next kernel.

For now, the API only supports preserving non-VF devices on a root bus (not behind an PCI-to-PCI bridges).

---
  drivers/pci/liveupdate.c       | 210 +++++++++++++++++++++++++++++++++
  drivers/pci/liveupdate.h       |  21 ++++
  drivers/pci/probe.c            |   2 +
  include/linux/pci.h            |   3 +
  include/linux/pci_liveupdate.h |  21 ++++
  5 files changed, 257 insertions(+)

create mode 100644 drivers/pci/liveupdate.h

drivers/pci/liveupdate.c +210 -0
index fffb41a66ca7..b003b7069cdb 100644
@@ -36,6 +36,27 @@
   *
   *  * ``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).
+ *  * The device cannot be behind a PCI-to-PCI bridge.
   */
 
  #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -50,6 +71,21 @@
  #include <linux/pci.h>
  #include <linux/slab.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),
+};
+
  /**
   * struct pci_flb_outgoing - Outgoing PCI FLB object
   * @ser: Pointer to the preserved struct pci_ser.
@@ -128,6 +164,180 @@ static struct liveupdate_flb pci_liveupdate_flb = {
  	.compatible = PCI_LUO_FLB_COMPATIBLE,
  };
 
+static void pci_liveupdate_flb_put_outgoing(void)
+{
+	liveupdate_flb_put_outgoing(&pci_liveupdate_flb);
+}
+
+static struct pci_flb_outgoing *pci_liveupdate_flb_get_outgoing(void)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	ret = liveupdate_flb_get_outgoing(&pci_liveupdate_flb, (void **)&outgoing);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!outgoing)
+		return ERR_PTR(-ENOENT);
+
+	return outgoing;
+}
+
+static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoing)
+{
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	u64 count = 0;
+	int err;
+
+	kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* Try to find an existing, previously unpreserved, entry. */
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		if (!dev_ser->refcount)
+			return dev_ser;
+
+		count++;
+	}
+
+	/* Otherwise grow the block set and reserve a new entry. */
+	err = kho_block_set_grow(&outgoing->block_set, count + 1);
+	if (err)
+		return ERR_PTR(err);
+
+	if (!count)
+		kho_block_set_it_init(&it, &outgoing->block_set);
+
+	/* This should always succeed since kho_block_set_grow() succeeded. */
+	dev_ser = kho_block_set_it_reserve_entry(&it);
+	if (WARN_ON_ONCE(!dev_ser))
+		return ERR_PTR(-ENOSPC);
+
+	return dev_ser;
+}
+
+static void pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					     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");
+	outgoing->ser->nr_devices--;
+	memset(dev_ser, 0, sizeof(*dev_ser));
+	dev->liveupdate.outgoing = NULL;
+}
+
+static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
+					  struct pci_dev *dev)
+{
+	struct pci_dev_ser *dev_ser;
+
+	if (dev->is_virtfn) {
+		pci_warn(dev, "Cannot preserve Virtual Functions\n");
+		return -EINVAL;
+	}
+
+	if (dev->liveupdate.outgoing) {
+		pci_warn(dev, "Device is already preserved\n");
+		return -EBUSY;
+	}
+
+	if (!pci_is_root_bus(dev->bus)) {
+		pci_warn(dev, "Cannot preserve devices behind bridges\n");
+		return -EINVAL;
+	}
+
+	dev_ser = pci_flb_alloc_dev_ser(outgoing);
+	if (IS_ERR(dev_ser))
+		return PTR_ERR(dev_ser);
+
+	pci_info(dev, "Device will be preserved across next Live Update\n");
+	outgoing->ser->nr_devices++;
+	outgoing->ser->devices = kho_block_set_head_pa(&outgoing->block_set);
+
+	dev_ser->domain = pci_domain_nr(dev->bus);
+	dev_ser->bdf = pci_dev_id(dev);
+	dev_ser->refcount = 1;
+
+	dev->liveupdate.outgoing = dev_ser;
+	return 0;
+}
+
+/**
+ * pci_liveupdate_preserve() - Preserve a PCI device across Live Update
+ * @dev: The PCI device to preserve.
+ *
+ * pci_liveupdate_preserve() notifies the PCI core that a PCI device should be
+ * preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_preserve() from their struct liveupdate_file_handler
+ * preserve() callback to ensure the outgoing struct pci_ser is already set up.
+ *
+ * Returns: 0 on success, <0 on failure.
+ */
+int pci_liveupdate_preserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+	int ret;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing))
+		return PTR_ERR(outgoing);
+
+	ret = pci_liveupdate_preserve_device(outgoing, dev);
+
+	pci_liveupdate_flb_put_outgoing();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_preserve);
+
+/**
+ * pci_liveupdate_unpreserve() - Cancel preservation of a PCI device
+ * @dev: The PCI device to unpreserve.
+ *
+ * pci_liveupdate_unpreserve() notifies the PCI core that a PCI device should no
+ * longer be preserved across the next Live Update. Drivers are expected to call
+ * pci_liveupdate_unpreserve() from their struct liveupdate_file_handler
+ * unpreserve() callback to ensure the outgoing struct pci_ser is already set
+ * up.
+ */
+void pci_liveupdate_unpreserve(struct pci_dev *dev)
+{
+	struct pci_flb_outgoing *outgoing = NULL;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	outgoing = pci_liveupdate_flb_get_outgoing();
+	if (IS_ERR(outgoing)) {
+		pci_warn(dev, "Cannot unpreserve device without outgoing Live Update state\n");
+		return;
+	}
+
+	pci_liveupdate_unpreserve_device(outgoing, dev);
+	pci_liveupdate_flb_put_outgoing();
+}
+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.
+	 *
+	 * This should never happen in practice. Drivers should block removal
+	 * while a device is preserved.
+	 */
+	if (READ_ONCE(dev->liveupdate.outgoing))
+		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+}
+
  /**
   * 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 dd0abbc63e18..14b66acbdb15 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 = {
@@ -2485,6 +2486,7 @@ static void pci_release_dev(struct device *dev)
 
  	pci_dev = to_pci_dev(dev);
  	pci_release_capabilities(pci_dev);
+	pci_liveupdate_cleanup_device(pci_dev);
  	pci_release_of_node(pci_dev);
  	pcibios_release_device(pci_dev);
  	pci_bus_put(pci_dev->bus);
include/linux/pci.h +3 -0
index da58aa101e4c..b41dd572a2d6 100644
@@ -593,6 +593,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..894052ad6961 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/spinlock_types.h>
  #include <linux/types.h>
 
+/**
+ * struct pci_liveupdate - PCI Live Update state for a struct pci_dev
+ * @outgoing: State preserved for the next kernel.
+ */
+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 */
#14 lore raw

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

Quoted 20 lines · Zhu Yanjun
Quoted 9 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).

Hi David,

I have a question about the pci_liveupdate_preserve() and pci_liveupdate_unpreserve() APIs.

Is it supported to call these APIs multiple times for the same PCI device? For example, a driver could do:

Quoted 7 lines

pci_liveupdate_preserve(pdev) -- > pci_liveupdate_unpreserve(pdev) -- > perform some operations -- > pci_liveupdate_preserve(pdev) again, before the actual Live Update starts.

From the implementation, it looks like an unpreserved pci_dev_ser entry
can be reused, so I wanted to confirm whether this preserve -- > unpreserve -- > preserve sequence is an intended and supported usage.

Yes that is a completely supported use-case. A device can be preserved and unpreserved any number of times up until pci_device_shutdown(), where the preservation status of the device is frozen (see patch 10).

#15 lore raw

PATCH v8 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 until pci_liveupdate_finish() is called by the driver. This pointer allows the PCI core and drivers to apply Live Update-specific logic to incoming devices 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       | 256 ++++++++++++++++++++++++++++++++-
 drivers/pci/liveupdate.h       |   5 +
 drivers/pci/probe.c            |   3 +
 include/linux/pci_liveupdate.h |  13 ++
 6 files changed, 277 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 9cc7b9291ace..08a724b860dc 100644
@@ -20834,6 +20834,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 3781e2b5f095..8af20f558086 100644
@@ -273,7 +273,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 +254 -2

294 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 14b66acbdb15..5dc9d86e3597 100644
@@ -2065,6 +2065,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);
 
@@ -2188,6 +2190,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 894052ad6961..710026ada2d5 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.55.0.487.gaf234c4eb3-goog
#16 lore raw

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

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] Unconditional kernel panic in the incoming FLB finish path if a preserved PCI device is missing or…

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Unconditional kernel panic in the incoming FLB finish path if a preserved PCI device is missing or fails setup.
- [High] pci_flb_unpreserve() destroys outgoing FLB memory without clearing device pointers, leading to a Use-After-Free.
- [High] Transient errors or version mismatches during FLB retrieval cause an immediate kernel panic on device setup.
- [Medium] Leaking preserved KHO memory for ser->devices on early error paths in pci_flb_retrieve(). --

--- Patch [3]: [PATCH v8 03/12] PCI: liveupdate: Track incoming preserved PCI devices --- commit 8fdc703d6dd44076cd03589af0bcffa13c79e7f4
Author: David Matlack <[email protected]>

PCI: liveupdate: Track incoming preserved PCI devices

This patch allows the PCI core to track incoming preserved PCI devices during enumeration. It fetches the serialized state from the Live Update Orchestrator and exposes APIs for drivers to handle incoming devices.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index b003b7069cdb0..5ce5f8b369029 100644

[ ... ]

Quoted 23 lines
drivers/pci/liveupdate.c +18 -1
@@ -140,15 +174,91 @@ static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
 {
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	int ret;
 
 	pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
-	args->obj = phys_to_virt(args->data);
+
+	if (ser->version != PCI_LUO_FLB_VERSION) {
+		pr_err("Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\n",
+		       ser->version, PCI_LUO_FLB_VERSION);
+		ret = -EINVAL;
+		goto err_restore_free;
+	}
+
+	incoming = kzalloc_obj(*incoming);
+	if (!incoming) {
+		ret = -ENOMEM;
+		goto err_restore_free;
+	}

[ ... ]

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

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+err_restore_free:
+	kho_restore_free(ser);
+	return ret;
+}

[Severity: Medium]
Does this error path leak the KHO memory for ser->devices?

When jumping to err_restore_free (e.g. due to a version mismatch or kzalloc failure), kho_restore_free(ser) is called, but it doesn't appear to restore or free the memory pointed to by ser->devices.

Since KHO pages are reserved across kexec, will this permanently leak the memory for the uptime of the successor kernel?

[ ... ]

Quoted 8 lines
drivers/pci/liveupdate.c +8 -0
+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)
+{
+	struct pci_dev *dev = NULL;
+
+	if (READ_ONCE(incoming->ser->nr_devices) == 0)
+		return;
+
+	for_each_pci_dev(dev) {

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+		if (READ_ONCE(dev->liveupdate.incoming))
+			pci_emerg(dev, "Preserved device was never finished!\n");
+	}
+
+	/*
+	 * This should only happen if a driver violated the contract to call
+	 * pci_liveupdate_finish() (something is extremely broken).
+	 */
+	panic("Some preserved devices were never finished!\n");
+}

[Severity: High]
Is an unconditional panic here safe if a preserved device is missing or fails to probe?

If a PCI device that was preserved by the previous kernel fails to probe in the new kernel (e.g. the driver module is missing, or it encounters a transient error), pci_liveupdate_finish() will never be called and incoming->ser->nr_devices will not be decremented.

When the Live Update orchestrator concludes the update and calls pci_flb_finish(), pci_check_all_devices_finished() is invoked and sees that nr_devices is still > 0. Does this mean normal system conditions like a missing driver will trigger a kernel panic?

[ ... ]

Quoted 8 lines
drivers/pci/liveupdate.c +4 -0
@@ -325,6 +435,75 @@ 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;

[ ... ]

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but retrieve failed (pci_flb_retrieve() returned an error or LUO
+	 * failed to acquire a reference to pci_liveupdate_flb_ops.owner).
+	 */
+	if (ret)
+		panic("Failed to retrieve incoming FLB data (%d)\n", ret);
+
+	return incoming;
+}

[Severity: High]
Does this panic prevent the new kernel from booting if there's a simple FLB version mismatch?

During PCI enumeration in the new kernel, pci_setup_device() calls pci_liveupdate_setup_device(), which invokes pci_liveupdate_flb_get_incoming().

If pci_flb_retrieve() encounters an error like a version mismatch (-EINVAL) or an allocation failure (-ENOMEM), it returns that error code. This branch then catches the error and unconditionally panics. Will this reliably crash the new kernel during early boot and break the kexec handover fallback mechanism?

[ ... ]

I also have a question about the unpreserve path in pci_flb_unpreserve() which might lead to a regression:

drivers/pci/liveupdate.c:pci_flb_unpreserve() {
    ...
    WARN_ON(outgoing->ser->nr_devices);
    kho_block_set_destroy(&outgoing->block_set);
    kho_unpreserve_free(outgoing->ser);
    kfree(outgoing);
}

[Severity: High]
Does pci_flb_unpreserve() destroy outgoing FLB memory without clearing the device pointers?

If a driver fails to unpreserve its devices before the orchestrator aborts a Live Update, pci_flb_unpreserve() will issue a WARN_ON but proceed to destroy the block set and free the outgoing FLB object.

The dev->liveupdate.outgoing pointers on the pci_dev structs are left dangling. If a new Live Update is subsequently initiated and the driver attempts to preserve the device again, pci_liveupdate_preserve_device() will access dev->liveupdate.outgoing->refcount via the dangling pointer:

drivers/pci/liveupdate.c:pci_liveupdate_preserve_device() {
    ...
    if (dev->liveupdate.outgoing) {
        if (!dev->liveupdate.outgoing->refcount) {
    ...
}

Can this lead to a use-after-free of the KHO block set memory?

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

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

Quoted 55 lines · On Tue, Jul 28, 2026 at 3:22 PM <[email protected]> wrote:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Unconditional kernel panic in the incoming FLB finish path if a preserved PCI device is missing or fails setup.
- [High] pci_flb_unpreserve() destroys outgoing FLB memory without clearing device pointers, leading to a Use-After-Free.
- [High] Transient errors or version mismatches during FLB retrieval cause an immediate kernel panic on device setup.
- [Medium] Leaking preserved KHO memory for ser->devices on early error paths in pci_flb_retrieve(). --

--- Patch [3]: [PATCH v8 03/12] PCI: liveupdate: Track incoming preserved PCI devices --- commit 8fdc703d6dd44076cd03589af0bcffa13c79e7f4
Author: David Matlack <[email protected]>

PCI: liveupdate: Track incoming preserved PCI devices

This patch allows the PCI core to track incoming preserved PCI devices during enumeration. It fetches the serialized state from the Live Update Orchestrator and exposes APIs for drivers to handle incoming devices.

Quoted 4 lines
drivers/pci/liveupdate.c +0 -0
index b003b7069cdb0..5ce5f8b369029 100644

[ ... ]

Quoted 23 lines
drivers/pci/liveupdate.c +18 -1
@@ -140,15 +174,91 @@ static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
 {
+     struct pci_ser *ser = phys_to_virt(args->data);
+     struct pci_flb_incoming *incoming;
+     struct pci_dev_ser *dev_ser;
+     struct kho_block_set_it it;
+     int ret;
 
      pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
-     args->obj = phys_to_virt(args->data);
+
+     if (ser->version != PCI_LUO_FLB_VERSION) {
+             pr_err("Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\n",
+                    ser->version, PCI_LUO_FLB_VERSION);
+             ret = -EINVAL;
+             goto err_restore_free;
+     }
+
+     incoming = kzalloc_obj(*incoming);
+     if (!incoming) {
+             ret = -ENOMEM;
+             goto err_restore_free;
+     }

[ ... ]

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

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+err_restore_free:
+     kho_restore_free(ser);
+     return ret;
+}

[Severity: Medium]
Does this error path leak the KHO memory for ser->devices?

Quoted 6 lines

When jumping to err_restore_free (e.g. due to a version mismatch or kzalloc failure), kho_restore_free(ser) is called, but it doesn't appear to restore or free the memory pointed to by ser->devices.

Since KHO pages are reserved across kexec, will this permanently leak the memory for the uptime of the successor kernel?

Yes, but PCI FLB retrieve errors are fatal (panic).

Quoted 30 lines

[ ... ]

Quoted 8 lines
drivers/pci/liveupdate.c +8 -0
+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)
+{
+     struct pci_dev *dev = NULL;
+
+     if (READ_ONCE(incoming->ser->nr_devices) == 0)
+             return;
+
+     for_each_pci_dev(dev) {

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+             if (READ_ONCE(dev->liveupdate.incoming))
+                     pci_emerg(dev, "Preserved device was never finished!\n");
+     }
+
+     /*
+      * This should only happen if a driver violated the contract to call
+      * pci_liveupdate_finish() (something is extremely broken).
+      */
+     panic("Some preserved devices were never finished!\n");
+}

[Severity: High]
Is an unconditional panic here safe if a preserved device is missing or fails to probe?

If a PCI device that was preserved by the previous kernel fails to probe in the new kernel (e.g. the driver module is missing, or it encounters a transient error), pci_liveupdate_finish() will never be called and incoming->ser->nr_devices will not be decremented.

Quoted 4 lines

When the Live Update orchestrator concludes the update and calls pci_flb_finish(), pci_check_all_devices_finished() is invoked and sees that nr_devices is still > 0. Does this mean normal system conditions like a missing driver will trigger a kernel panic?

The file associated with a device that fails to be enumerated or probed cannever be retrieved by userspace, and therefore never go through its finish() callback. So LUO will never call pci_flb_finish(). This panic will only happen if there is a serious bug in the driver or LUO.

Quoted 30 lines

[ ... ]

Quoted 8 lines
drivers/pci/liveupdate.c +4 -0
@@ -325,6 +435,75 @@ 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;

[ ... ]

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+     /*
+      * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+      * but retrieve failed (pci_flb_retrieve() returned an error or LUO
+      * failed to acquire a reference to pci_liveupdate_flb_ops.owner).
+      */
+     if (ret)
+             panic("Failed to retrieve incoming FLB data (%d)\n", ret);
+
+     return incoming;
+}

[Severity: High]
Does this panic prevent the new kernel from booting if there's a simple FLB version mismatch?

During PCI enumeration in the new kernel, pci_setup_device() calls pci_liveupdate_setup_device(), which invokes pci_liveupdate_flb_get_incoming().

Quoted 5 lines

If pci_flb_retrieve() encounters an error like a version mismatch (-EINVAL) or an allocation failure (-ENOMEM), it returns that error code. This branch then catches the error and unconditionally panics. Will this reliably crash the new kernel during early boot and break the kexec handover fallback mechanism?

Yes all FLB retrieve errors are considered fatal. A version mismatch is considered a retrieve error because it means the current kernel can't understand the data passed by the previous kernel.

We do want more graceful versioning support to handle upgrade/downgrade scenarios, but that requires deeper design work in LUO before the PCI core can use it. We aren't going to build a PCI-specific versioning solution.

Quoted 27 lines

[ ... ]

I also have a question about the unpreserve path in pci_flb_unpreserve() which might lead to a regression:

drivers/pci/liveupdate.c:pci_flb_unpreserve() {
    ...
    WARN_ON(outgoing->ser->nr_devices);
    kho_block_set_destroy(&outgoing->block_set);
    kho_unpreserve_free(outgoing->ser);
    kfree(outgoing);
}

[Severity: High]
Does pci_flb_unpreserve() destroy outgoing FLB memory without clearing the device pointers?

If a driver fails to unpreserve its devices before the orchestrator aborts a Live Update, pci_flb_unpreserve() will issue a WARN_ON but proceed to destroy the block set and free the outgoing FLB object.

The dev->liveupdate.outgoing pointers on the pci_dev structs are left dangling. If a new Live Update is subsequently initiated and the driver attempts to preserve the device again, pci_liveupdate_preserve_device() will access dev->liveupdate.outgoing->refcount via the dangling pointer:

Quoted 8 lines
drivers/pci/liveupdate.c:pci_liveupdate_preserve_device() {
    ...
    if (dev->liveupdate.outgoing) {
        if (!dev->liveupdate.outgoing->refcount) {
    ...
}

Can this lead to a use-after-free of the KHO block set memory?

Yes. We can handle that the same way as dangling incoming pointers and panic. This will only happen if the driver or LUO are very broken.

Quoted 3 lines

--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3

Reviewed-by on v8 3/12
#18 lore raw

Re: [PATCH v8 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 until pci_liveupdate_finish() is called by the driver. This pointer allows the PCI core and drivers to apply Live Update-specific logic to incoming devices 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       | 256 ++++++++++++++++++++++++++++++++-
drivers/pci/liveupdate.h       |   5 +
drivers/pci/probe.c            |   3 +
include/linux/pci_liveupdate.h |  13 ++
6 files changed, 277 insertions(+), 3 deletions(-)

[snip]

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

Quoted 7 lines

static void pci_flb_finish(struct liveupdate_flb_op_args *args) {

(quoted diff) +2 -0
+	struct pci_flb_incoming *incoming = args->obj;
+
	pr_debug("Finished struct pci_ser (0x%llx)\n", args->data);
drivers/pci/liveupdate.c +1 -1
-	kho_restore_free(args->obj);
+	pci_check_all_devices_finished(incoming);

This should handle FLB/FD lifecycle nicely. Thanks for adding this.

Quoted 7 lines
drivers/pci/liveupdate.c +5 -0
+
+	xa_destroy(&incoming->xa);
+	kho_block_set_destroy(&incoming->block_set);
+	kho_restore_free(incoming->ser);
+	kfree(incoming);

}

#19 lore raw

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

In reply to David Matlack
Quoted 11 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.

Quoted 3 lines

+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and
+ * ``pci_liveupdate_preserve()``. i.e. A PCI device can be **outgoing**
+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)

Thinking about how this will be rendered in the formatted doc, I think "i.e." is really part of the sentence, so in U.S. usage this would be:

``pci_liveupdate_preserve()``, i.e., a PCI device can be ...

I understand British usage generally omits the comma after, so that's OK too, but IIUC the first part of the sentence shouldn't be terminated with a period, and the "A" shouldn't be capitalized as if it's a new sentence.

Similar issue in "PCI: liveupdate: Document driver binding responsibilities" later:

bound to the correct driver. i.e. The PCI core does not protect ...

drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 136

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)
+{
+	return (unsigned long)domain << 16 | bdf;
+}

Add blank line.

Quoted 2 lines

static int pci_flb_preserve(struct liveupdate_flb_op_args *args) {

#20 lore raw

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

Quoted 26 lines · Bjorn Helgaas
Quoted 11 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.

Quoted 3 lines

+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and
+ * ``pci_liveupdate_preserve()``. i.e. A PCI device can be **outgoing**
+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)

Thinking about how this will be rendered in the formatted doc, I think "i.e." is really part of the sentence, so in U.S. usage this would be:

``pci_liveupdate_preserve()``, i.e., a PCI device can be ...

I understand British usage generally omits the comma after, so that's OK too, but IIUC the first part of the sentence shouldn't be terminated with a period, and the "A" shouldn't be capitalized as if it's a new sentence.

Quoted 4 lines

Similar issue in "PCI: liveupdate: Document driver binding responsibilities" later:

bound to the correct driver. i.e. The PCI core does not protect ...

Ack I will fix up the use of "i.e." across the series.

Quoted 10 lines

drivers/pci/liveupdate.c struct pci_flb_outgoing { · line 136

Quoted 5 lines
drivers/pci/liveupdate.c +4 -0
+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)
+{
+	return (unsigned long)domain << 16 | bdf;
+}

Add blank line.

Quoted 2 lines

static int pci_flb_preserve(struct liveupdate_flb_op_args *args) {

#21 lore raw

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

In reply to David Matlack
Quoted 52 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 until pci_liveupdate_finish() is called by the driver. This pointer allows the PCI core and drivers to apply Live Update-specific logic to incoming devices 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       | 256 ++++++++++++++++++++++++++++++++-
  drivers/pci/liveupdate.h       |   5 +
  drivers/pci/probe.c            |   3 +
  include/linux/pci_liveupdate.h |  13 ++
  6 files changed, 277 insertions(+), 3 deletions(-)
MAINTAINERS +1 -0
index 9cc7b9291ace..08a724b860dc 100644
@@ -20834,6 +20834,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 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

If this problem has already discussed, I am very sorry about this.

Yanjun Zhu

Quoted 386 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,
drivers/pci/liveupdate.c +254 -2
index b003b7069cdb..5ce5f8b36902 100644
@@ -49,6 +49,20 @@
   * This allows the PCI core to keep its FLB data (struct pci_ser) up to date
   * with the list of **outgoing** preserved devices for the next kernel.
   *
+ * After kexec, whenever a device is enumerated, the PCI core will check if it
+ * is an **incoming** preserved device (i.e. preserved by the previous kernel)
+ * by checking the incoming FLB data (struct pci_ser).
+ *
+ * Drivers must notify the PCI core when an **incoming** device is done
+ * participating in the incoming Live Update with the following API:
+ *
+ *  * ``pci_liveupdate_finish(pci_dev)``
+ *
+ * The PCI core does not enforce any ordering of ``pci_liveupdate_finish()`` and
+ * ``pci_liveupdate_preserve()``. i.e. A PCI device can be **outgoing**
+ * (preserved for next kernel) and **incoming** (preserved by previous kernel)
+ * at the same time.
+ *
   * Restrictions
   * ============
   *
@@ -100,6 +114,26 @@ struct pci_flb_outgoing {
  	struct kho_block_set block_set;
  };
 
+/**
+ * struct pci_flb_incoming - Incoming PCI FLB object
+ * @ser: The incoming struct pci_ser from the previous kernel.
+ * @xa: Xarray used to quickly lookup devices in @ser.
+ * @block_set: The KHO block set holding the incoming devices.
+ *
+ * This structure holds the runtime state for the incoming PCI Live Update
+ * state. It wraps the serialized pci_ser, the block_set used to restore
+ * the serialized entries, and an xarray for fast lookups.
+ */
+struct pci_flb_incoming {
+	struct pci_ser *ser;
+	struct xarray xa;
+	struct kho_block_set block_set;
+};
+
+static unsigned long pci_ser_xa_key(u32 domain, u16 bdf)
+{
+	return (unsigned long)domain << 16 | bdf;
+}
  static int pci_flb_preserve(struct liveupdate_flb_op_args *args)
  {
  	struct pci_flb_outgoing *outgoing __free(kfree) = NULL;
@@ -140,15 +174,91 @@ static void pci_flb_unpreserve(struct liveupdate_flb_op_args *args)
 
  static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
  {
+	struct pci_ser *ser = phys_to_virt(args->data);
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	struct kho_block_set_it it;
+	int ret;
+
  	pr_debug("Retrieving struct pci_ser (0x%llx)\n", args->data);
-	args->obj = phys_to_virt(args->data);
+
+	if (ser->version != PCI_LUO_FLB_VERSION) {
+		pr_err("Incoming PCI FLB version (v%d) is incompatible with this kernel (v%d)\n",
+		       ser->version, PCI_LUO_FLB_VERSION);
+		ret = -EINVAL;
+		goto err_restore_free;
+	}
+
+	incoming = kzalloc_obj(*incoming);
+	if (!incoming) {
+		ret = -ENOMEM;
+		goto err_restore_free;
+	}
+
+	incoming->ser = ser;
+	xa_init(&incoming->xa);
+
+	kho_block_set_init(&incoming->block_set, sizeof(struct pci_dev_ser));
+	ret = kho_block_set_restore(&incoming->block_set, ser->devices);
+	if (ret)
+		goto err_free_incoming;
+
+	kho_block_set_it_init(&it, &incoming->block_set);
+	while ((dev_ser = kho_block_set_it_read_entry(&it))) {
+		unsigned long key;
+
+		if (!dev_ser->refcount)
+			continue;
+
+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
+		if (ret)
+			goto err_block_set_destroy;
+	}
+
+	args->obj = incoming;
  	return 0;
+
+err_block_set_destroy:
+	kho_block_set_destroy(&incoming->block_set);
+err_free_incoming:
+	xa_destroy(&incoming->xa);
+	kfree(incoming);
+err_restore_free:
+	kho_restore_free(ser);
+	return ret;
+}
+
+static void pci_check_all_devices_finished(struct pci_flb_incoming *incoming)
+{
+	struct pci_dev *dev = NULL;
+
+	if (READ_ONCE(incoming->ser->nr_devices) == 0)
+		return;
+
+	for_each_pci_dev(dev) {
+		if (READ_ONCE(dev->liveupdate.incoming))
+			pci_emerg(dev, "Preserved device was never finished!\n");
+	}
+
+	/*
+	 * This should only happen if a driver violated the contract to call
+	 * pci_liveupdate_finish() (something is extremely broken).
+	 */
+	panic("Some preserved devices were never finished!\n");
  }
 
  static void pci_flb_finish(struct liveupdate_flb_op_args *args)
  {
+	struct pci_flb_incoming *incoming = args->obj;
+
  	pr_debug("Finished struct pci_ser (0x%llx)\n", args->data);
-	kho_restore_free(args->obj);
+	pci_check_all_devices_finished(incoming);
+
+	xa_destroy(&incoming->xa);
+	kho_block_set_destroy(&incoming->block_set);
+	kho_restore_free(incoming->ser);
+	kfree(incoming);
  }
 
  static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {
@@ -325,6 +435,75 @@ 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.
+	 */
+	if (ret == -ENOENT)
+		return NULL;
+
+	/*
+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
+	 * but retrieve failed (pci_flb_retrieve() returned an error or LUO
+	 * failed to acquire a reference to pci_liveupdate_flb_ops.owner).
+	 */
+	if (ret)
+		panic("Failed to retrieve incoming FLB data (%d)\n", ret);
+
+	return incoming;
+}
+
+static void pci_liveupdate_flb_put_incoming(void)
+{
+	liveupdate_flb_put_incoming(&pci_liveupdate_flb);
+}
+
+void pci_liveupdate_setup_device(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+	struct pci_dev_ser *dev_ser;
+	unsigned long key;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming)
+		return;
+
+	key = pci_ser_xa_key(pci_domain_nr(dev->bus), pci_dev_id(dev));
+	dev_ser = xa_load(&incoming->xa, key);
+
+	/*
+	 * This device was not preserved across Live Update, or it was preserved
+	 * but has already been probed and gone through pci_liveupdate_finish(),
+	 * e.g. due to removing and re-adding the device. Either way, it's not
+	 * treated as incoming-preserved.
+	 */
+	if (!dev_ser || !dev_ser->refcount) {
+		pci_liveupdate_flb_put_incoming();
+		return;
+	}
+
+	pci_info(dev, "Device was preserved by previous kernel across Live Update\n");
+	dev->liveupdate.incoming = dev_ser;
+	pci_liveupdate_flb_put_incoming();
+}
+
  void pci_liveupdate_cleanup_device(struct pci_dev *dev)
  {
  	/*
@@ -336,7 +515,80 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
  	 */
  	if (READ_ONCE(dev->liveupdate.outgoing))
  		pci_WARN(dev, 1, "Destroying outgoing-preserved device!\n");
+
+	if (READ_ONCE(dev->liveupdate.incoming))
+		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
+}
+
+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;
+	}
+
+	/*
+	 * 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_finish() - Finish the preservation of a PCI device
+ * @dev: The PCI device
+ *
+ * pci_liveupdate_finish() notifies the PCI core that a PCI device that was
+ * preserved across the previous Live Update has finished participating in Live
+ * Update. Drivers must call pci_liveupdate_finish() from their struct
+ * liveupdate_file_handler finish() callback to ensure the incoming struct
+ * pci_ser is allocated.
+ */
+void pci_liveupdate_finish(struct pci_dev *dev)
+{
+	struct pci_flb_incoming *incoming;
+
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+
+	incoming = pci_liveupdate_flb_get_incoming();
+	if (!incoming) {
+		pci_warn(dev, "Cannot finish preserving device without incoming FLB\n");
+		return;
+	}
+
+	pci_liveupdate_finish_device(incoming->ser, dev);
+	pci_liveupdate_flb_put_incoming();
+}
+EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
+
+/**
+ * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
+ * @dev: The PCI device to check
+ *
+ * Check if a device was preserved across Live Update by the previous kernel,
+ * i.e. the device is incoming-preserved. Note that a device is only considered
+ * incoming-preserved prior to pci_liveupdate_finish(). It is up to drivers to
+ * synchronize usage of pci_liveupdate_is_incoming() with their own call to
+ * pci_liveupdate_finish() to avoid acting on stale data.
+ *
+ * Returns: True if the device is incoming-preserved, false otherwise.
+ */
+bool pci_liveupdate_is_incoming(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+	return dev->liveupdate.incoming;
  }
+EXPORT_SYMBOL_GPL(pci_liveupdate_is_incoming);
 
  /**
   * pci_liveupdate_register_flb() - Register a file handler with the PCI core
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 14b66acbdb15..5dc9d86e3597 100644
@@ -2065,6 +2065,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);
 
@@ -2188,6 +2190,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 894052ad6961..710026ada2d5 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 */
#22 lore raw

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

Quoted 23 lines · Zhu Yanjun
Quoted 9 lines · David Matlack

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.

drivers/pci/Kconfig +0 -0
index 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

Quoted 6 lines

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

If this problem has already discussed, I am very sorry about this.

The necessity is that the PCI core needs to store more than 32-bits in the unsigned long xarray key (see the snippet above). The dependency on CONFIG_64BIT ensures that unsigned long is big enough. We could probably remove the dependency but I would rather wait until someone with a 32-bit build has a real use-case for using PCI_LIVEUPDATE before putting any effort into it.

maintainer
#23 lore raw

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

Quoted 29 lines · David Matlack
Quoted 28 lines · Zhu Yanjun
Quoted 9 lines · David Matlack

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.

drivers/pci/Kconfig +0 -0
index 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

Quoted 8 lines
Quoted 1 line

If this problem has already discussed, I am very sorry about this.

The necessity is that the PCI core needs to store more than 32-bits in the unsigned long xarray key (see the snippet above). The dependency on CONFIG_64BIT ensures that unsigned long is big enough. We could probably remove the dependency but I would rather wait until someone with a 32-bit build has a real use-case for using PCI_LIVEUPDATE before putting any effort into it.

KHO or live update themselves don't support 32-bit platforms and there are no plans to do so either. So I don't think you even need to have a dependency on 64BIT in PCI_LIVEUPDATE. Only 64 bit architectures define ARCH_SUPPORTS_KEXEC_HANDOVER, so PCI_LIVEUPDATE and others indirectly inherit the dependency.

If you'd like to be extra safe, then probably you should add a dependency to 64BIT in KEXEC_HANDOVER directly. Though I think that can be a separate patch independent from this series.

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

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

Quoted 47 lines · Pratyush Yadav
Quoted 29 lines · David Matlack
Quoted 28 lines · Zhu Yanjun
Quoted 9 lines · David Matlack

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.

drivers/pci/Kconfig +0 -0
index 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

Quoted 8 lines
Quoted 1 line

If this problem has already discussed, I am very sorry about this.

The necessity is that the PCI core needs to store more than 32-bits in the unsigned long xarray key (see the snippet above). The dependency on CONFIG_64BIT ensures that unsigned long is big enough. We could probably remove the dependency but I would rather wait until someone with a 32-bit build has a real use-case for using PCI_LIVEUPDATE before putting any effort into it.

KHO or live update themselves don't support 32-bit platforms and there are no plans to do so either. So I don't think you even need to have a dependency on 64BIT in PCI_LIVEUPDATE. Only 64 bit architectures define ARCH_SUPPORTS_KEXEC_HANDOVER, so PCI_LIVEUPDATE and others indirectly inherit the dependency.

Quoted 3 lines

If you'd like to be extra safe, then probably you should add a dependency to 64BIT in KEXEC_HANDOVER directly. Though I think that can be a separate patch independent from this series.

It should be in both places then. PCI_LIVEUPDATE explicitly requires 64BIT for its own use of unsigned long, so it should have an explicit dependency. If KEXEC_HANDOVER requires 64-bit for its own specific purposes, then it should have an explicit Kconfig dependency as well. That way if and when someone wants to add 32-bit support we know exactly which configs need to add support.

maintainer
#25 lore raw

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

Quoted 52 lines · David Matlack
Quoted 47 lines · Pratyush Yadav
Quoted 29 lines · David Matlack
Quoted 28 lines · Zhu Yanjun
Quoted 9 lines · David Matlack

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.

drivers/pci/Kconfig +0 -0
index 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

Quoted 8 lines
Quoted 1 line

If this problem has already discussed, I am very sorry about this.

The necessity is that the PCI core needs to store more than 32-bits in the unsigned long xarray key (see the snippet above). The dependency on CONFIG_64BIT ensures that unsigned long is big enough. We could probably remove the dependency but I would rather wait until someone with a 32-bit build has a real use-case for using PCI_LIVEUPDATE before putting any effort into it.

KHO or live update themselves don't support 32-bit platforms and there are no plans to do so either. So I don't think you even need to have a dependency on 64BIT in PCI_LIVEUPDATE. Only 64 bit architectures define ARCH_SUPPORTS_KEXEC_HANDOVER, so PCI_LIVEUPDATE and others indirectly inherit the dependency.

Quoted 3 lines

If you'd like to be extra safe, then probably you should add a dependency to 64BIT in KEXEC_HANDOVER directly. Though I think that can be a separate patch independent from this series.

Quoted 6 lines

It should be in both places then. PCI_LIVEUPDATE explicitly requires 64BIT for its own use of unsigned long, so it should have an explicit dependency. If KEXEC_HANDOVER requires 64-bit for its own specific purposes, then it should have an explicit Kconfig dependency as well. That way if and when someone wants to add 32-bit support we know exactly which configs need to add support.

Well, I don't think we are ever adding 32-bit support. Most 32-bit support in the kernel is being deprecated or removed, so I really don't think it makes much sense to extend KHO/live update to 32-bit architectures.

But anyway, if you'd like to explicitly mark PCI as needing 64-bit, that sounds fine too in the very very unlikely event we do add 32-bit support.

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

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

Quoted 62 lines · Pratyush Yadav
Quoted 52 lines · David Matlack
Quoted 47 lines · Pratyush Yadav
Quoted 29 lines · David Matlack
Quoted 28 lines · Zhu Yanjun
Quoted 9 lines · David Matlack

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.

drivers/pci/Kconfig +0 -0
index 3781e2b5f095..8af20f558086 100644

drivers/pci/Kconfig config VGA_ARB_MAX_GPUS · line 276

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

One question about adding 64BIT to the dependency:

As I understand it, enabling CONFIG_64BIT essentially means that we are building a 64-bit kernel, and a 32-bit architecture cannot normally enable CONFIG_64BIT.

If that is the case, would depends on 64BIT be necessary here? Or is PCI Live Update already inherently restricted to 64-bit architectures by the existing LIVEUPDATE/architecture configuration, so that this dependency would be redundant?

Quoted 8 lines
Quoted 1 line

If this problem has already discussed, I am very sorry about this.

The necessity is that the PCI core needs to store more than 32-bits in the unsigned long xarray key (see the snippet above). The dependency on CONFIG_64BIT ensures that unsigned long is big enough. We could probably remove the dependency but I would rather wait until someone with a 32-bit build has a real use-case for using PCI_LIVEUPDATE before putting any effort into it.

KHO or live update themselves don't support 32-bit platforms and there are no plans to do so either. So I don't think you even need to have a dependency on 64BIT in PCI_LIVEUPDATE. Only 64 bit architectures define ARCH_SUPPORTS_KEXEC_HANDOVER, so PCI_LIVEUPDATE and others indirectly inherit the dependency.

Quoted 3 lines

If you'd like to be extra safe, then probably you should add a dependency to 64BIT in KEXEC_HANDOVER directly. Though I think that can be a separate patch independent from this series.

Quoted 6 lines

It should be in both places then. PCI_LIVEUPDATE explicitly requires 64BIT for its own use of unsigned long, so it should have an explicit dependency. If KEXEC_HANDOVER requires 64-bit for its own specific purposes, then it should have an explicit Kconfig dependency as well. That way if and when someone wants to add 32-bit support we know exactly which configs need to add support.

Quoted 8 lines

Well, I don't think we are ever adding 32-bit support. Most 32-bit support in the kernel is being deprecated or removed, so I really don't think it makes much sense to extend KHO/live update to 32-bit architectures.

But anyway, if you'd like to explicitly mark PCI as needing 64-bit, that sounds fine too in the very very unlikely event we do add 32-bit support.

Agree we will probably never add support for 32-bit to KEXEC_HANDOVER and PCI_LIVEUPDATE. But I do prefer explicitly documenting the dependency. It costs nothing and makes the 64-bit requirement crystal clear, just in case someone tries to do something crazy :)

#27 lore raw

PATCH v8 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 5ce5f8b36902..0f7a018ddc66 100644
@@ -71,6 +71,22 @@
  *
  *  * The device cannot be a Virtual Function (VF).
  *  * The device cannot be behind a PCI-to-PCI bridge.
+ *
+ * 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. This may change in the future.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#28 lore raw

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

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines

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.

Sashiko has reviewed this patch and found no issues. It looks great!

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

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

In reply to David Matlack
Quoted 18 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 +0 -0
index 5ce5f8b36902..0f7a018ddc66 100644

drivers/pci/liveupdate.c line 80

Quoted 10 lines
drivers/pci/liveupdate.c +7 -0
@@ -71,6 +71,22 @@
  *
  *  * The device cannot be a Virtual Function (VF).
  *  * The device cannot be behind a PCI-to-PCI bridge.
+ *
+ * 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().

Possibly "the driver must ensure" or even "the driver must not release ..." Also could apply below.

drivers/pci/liveupdate.c line 89

Quoted 8 lines
drivers/pci/liveupdate.c +8 -0
+ * 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. This may change in the future.

Same "i.e." nit.

Not sure we need to be reminded that things may change in the future :)

#30 lore raw

PATCH v8 5/12 PCI: liveupdate: Preserve bus numbers 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 preserve the secondary and subordinate bus numbers assigned to bridges during scanning, instead of assigning new ones, if any PCI devices were preserved. Note that the kernel preserves 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 preserving 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                      | 95 +++++++++++++++++++
 drivers/pci/liveupdate.h                      | 13 +++
 drivers/pci/probe.c                           | 18 +++-
 include/linux/pci_liveupdate.h                |  4 +
 6 files changed, 132 insertions(+), 6 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +5 -1
index 4f65b2a37521..c394a0df8e31 100644
@@ -5105,7 +5105,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
+				preserve the PCI topology (including bus
+				numbers) to avoid interrupting ongoing memory
+				transactions of preserved devices.
 		usepirqmask	[X86] Honor the possible IRQ mask stored
 				in the BIOS $PIR table. This is needed on
 				some systems with broken BIOSes, notably
drivers/pci/Kconfig +1 -1
index 8af20f558086..16fbd4212e0f 100644
@@ -273,7 +273,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 +95 -0

107 lines not on this page · raw message

drivers/pci/liveupdate.h +13 -0
index eaaa3559fd77..107881183fda 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_preserve_bus_numbers(struct pci_bus *bus,
+					 struct pci_dev *dev);
+void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -21,6 +24,16 @@ 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_preserve_bus_numbers(struct pci_bus *bus,
+						       struct pci_dev *dev)
+{
+	return false;
+}
+
+static inline void pci_liveupdate_scan_bridge_end(struct pci_dev *dev)
+{
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/probe.c +14 -4
index 5dc9d86e3597..5ecb55412854 100644
@@ -1397,6 +1397,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 				  int max, unsigned int available_buses,
 				  int pass)
 {
+	bool preserve_bus_numbers = !pcibios_assign_all_busses();
 	struct pci_bus *child;
 	u32 buses;
 	u16 bctl;
@@ -1406,6 +1407,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	u8 fixed_sec, fixed_sub;
 	int next_busnr;
 
+	if (pci_liveupdate_preserve_bus_numbers(bus, dev))
+		preserve_bus_numbers = true;
+
 	/*
 	 * Make sure the bridge is powered on to be able to access config
 	 * space of devices below it.
@@ -1449,8 +1453,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		goto out;
 	}
 
-	if ((secondary || subordinate) &&
-	    !pcibios_assign_all_busses() && !broken) {
+	if ((secondary || subordinate) && preserve_bus_numbers && !broken) {
 		unsigned int cmax, buses;
 
 		/*
@@ -1492,8 +1495,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		 * do in the second pass.
 		 */
 		if (!pass) {
-			if (pcibios_assign_all_busses() || broken)
-
+			if (!preserve_bus_numbers || broken)
 				/*
 				 * Temporarily disable forwarding of the
 				 * configuration cycles on all bridges in
@@ -1507,6 +1509,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			goto out;
 		}
 
+		if (pci_liveupdate_preserve_bus_numbers(bus, dev)) {
+			pci_err(dev, "Cannot reconfigure bridge during Live Update, skipping\n");
+			goto out;
+		}
+
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
@@ -1568,6 +1575,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 
 	pm_runtime_put(&dev->dev);
 
+	if (pass)
+		pci_liveupdate_scan_bridge_end(dev);
+
 	return max;
 }
 
include/linux/pci_liveupdate.h +4 -0
index 710026ada2d5..fc1f5640968a 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.
+ * @preserve_bus_numbers: True if the PCI core should preserve 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 preserve_bus_numbers;
 };
 
 struct pci_dev;
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#31 lore raw

Re: [PATCH v8 05/12] PCI: liveupdate: Preserve bus numbers during Live Update

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines

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 preserve the secondary and subordinate bus numbers assigned to bridges during scanning, instead of assigning new ones, if any PCI devices were preserved. Note that the kernel preserves 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.

Quoted 8 lines

Require that CONFIG_CARDBUS is not enabled to enable CONFIG_PCI_LIVEUPDATE since preserving bus numbers on PCI-to-CardBus bridges requires additional work but is not a priority at the moment.

Sashiko has reviewed this patch and found no issues. It looks great!

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

Re: [PATCH v8 05/12] PCI: liveupdate: Preserve bus numbers during Live Update

In reply to David Matlack
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 preserve the secondary and subordinate bus numbers assigned to bridges during scanning, instead of assigning new ones, if any PCI devices were preserved. Note that the kernel preserves 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.

drivers/pci/liveupdate.c +18 -0
+bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *parent = bus->self;
+
+	if (dev->liveupdate.preserve_bus_numbers)
+		return true;
+
+	if (parent && parent->liveupdate.preserve_bus_numbers) {
+		/*
+		 * Preserve bus numbers if the parent bridge is required to
+		 * preserve bus numbers. Otherwise the PCI core could expand
+		 * this bridge's reservation beyond its parent (which cannot
+		 * expand).
+		 */
+		dev->liveupdate.preserve_bus_numbers = true;
+	} else {
+		/*
+		 * Otherwise preserve bus numbers if there are any incoming

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+		 * preserved devices. This ensures that the PCI core does not
+		 * allocate a bus number to a non-preserved device that
+		 * conflicts with the bus number already assigned to a preserved
+		 * device.
+		 *
+		 * This is slightly more restrictive than it needs to be. For
+		 * example, each host bridges have their own range of bus
+		 * numbers that won't conflict with other host bridges. But the
+		 * previous kernel should have assigned a sane bus topology and
+		 * it is simpler to just adopt that entire topology.

s/each ... bridges have their/each ... bridge has its/

It's true there should be no bus number conflicts between host bridges, but it does require the domain as well.

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

Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+		 */
+		dev->liveupdate.preserve_bus_numbers =
+			pci_has_incoming_preserved_devices();
+	}
+
+	return dev->liveupdate.preserve_bus_numbers;

I'm not sure why you don't just return pci_has_incoming_preserved_devices() in all cases, which is what the commit log suggests this patch does. What's gained by all the logic here? It's not like devices will be hot-added during the kexec.

#33 lore raw

Re: [PATCH v8 05/12] PCI: liveupdate: Preserve bus numbers during Live Update

Quoted 48 lines · Bjorn Helgaas
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 preserve the secondary and subordinate bus numbers assigned to bridges during scanning, instead of assigning new ones, if any PCI devices were preserved. Note that the kernel preserves 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.

drivers/pci/liveupdate.c +18 -0
+bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *parent = bus->self;
+
+	if (dev->liveupdate.preserve_bus_numbers)
+		return true;
+
+	if (parent && parent->liveupdate.preserve_bus_numbers) {
+		/*
+		 * Preserve bus numbers if the parent bridge is required to
+		 * preserve bus numbers. Otherwise the PCI core could expand
+		 * this bridge's reservation beyond its parent (which cannot
+		 * expand).
+		 */
+		dev->liveupdate.preserve_bus_numbers = true;
+	} else {
+		/*
+		 * Otherwise preserve bus numbers if there are any incoming

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

Quoted 10 lines
drivers/pci/liveupdate.c +10 -0
+		 * preserved devices. This ensures that the PCI core does not
+		 * allocate a bus number to a non-preserved device that
+		 * conflicts with the bus number already assigned to a preserved
+		 * device.
+		 *
+		 * This is slightly more restrictive than it needs to be. For
+		 * example, each host bridges have their own range of bus
+		 * numbers that won't conflict with other host bridges. But the
+		 * previous kernel should have assigned a sane bus topology and
+		 * it is simpler to just adopt that entire topology.

s/each ... bridges have their/each ... bridge has its/

It's true there should be no bus number conflicts between host bridges, but it does require the domain as well.

Quoted 11 lines

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

Quoted 6 lines
drivers/pci/liveupdate.c +6 -0
+		 */
+		dev->liveupdate.preserve_bus_numbers =
+			pci_has_incoming_preserved_devices();
+	}
+
+	return dev->liveupdate.preserve_bus_numbers;

I'm not sure why you don't just return pci_has_incoming_preserved_devices() in all cases, which is what the commit log suggests this patch does. What's gained by all the logic here? It's not like devices will be hot-added during the kexec.

To protect against pci_has_incoming_preserved_devices() flipping from true to false while the PCI core is in the middle of a scan. It is not likely to ever happen given most host bridge scanning should happen during early boot, but theoretically possible with the way the PCI core code is structured. I did not see way to structurally ensure these 2 things cannot race. A lot of the host bridge scanning happens without taking the rescan lock, for example.

#34 lore raw

Re: [PATCH v8 05/12] PCI: liveupdate: Preserve bus numbers during Live Update

Quoted 43 lines · David Matlack
Quoted 33 lines · Bjorn Helgaas
Quoted 31 lines · David Matlack
drivers/pci/liveupdate.c +31 -0
+bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *parent = bus->self;
+
+	if (dev->liveupdate.preserve_bus_numbers)
+		return true;
+
+	if (parent && parent->liveupdate.preserve_bus_numbers) {
+		/*
+		 * Preserve bus numbers if the parent bridge is required to
+		 * preserve bus numbers. Otherwise the PCI core could expand
+		 * this bridge's reservation beyond its parent (which cannot
+		 * expand).
+		 */
+		dev->liveupdate.preserve_bus_numbers = true;
+	} else {
+		/*
+		 * Otherwise preserve bus numbers if there are any incoming
+		 * preserved devices. This ensures that the PCI core does not
+		 * allocate a bus number to a non-preserved device that
+		 * conflicts with the bus number already assigned to a preserved
+		 * device.
+		 *
+		 * This is slightly more restrictive than it needs to be. For
+		 * example, each host bridges have their own range of bus
+		 * numbers that won't conflict with other host bridges. But the
+		 * previous kernel should have assigned a sane bus topology and
+		 * it is simpler to just adopt that entire topology.
+		 */
+		dev->liveupdate.preserve_bus_numbers =
+			pci_has_incoming_preserved_devices();
Quoted 8 lines

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

Quoted 3 lines
drivers/pci/liveupdate.c +3 -0
+	}
+
+	return dev->liveupdate.preserve_bus_numbers;

I'm not sure why you don't just return pci_has_incoming_preserved_devices() in all cases, which is what the commit log suggests this patch does. What's gained by all the logic here? It's not like devices will be hot-added during the kexec.

Quoted 7 lines

To protect against pci_has_incoming_preserved_devices() flipping from true to false while the PCI core is in the middle of a scan. It is not likely to ever happen given most host bridge scanning should happen during early boot, but theoretically possible with the way the PCI core code is structured. I did not see way to structurally ensure these 2 things cannot race. A lot of the host bridge scanning happens without taking the rescan lock, for example.

After working on this more, I do see a way to simplify the logic in pci_liveupdate_preserve_bus_numbers().

pci_liveupdate_preserve_bus_numbers() is used in 2 places during scanning. First to decide if the PCI core should preserve bus numbers or is free to allocate new ones, and second to decide if the PCI core is allowed to assign bus numbers to bridges that are missing bus numbers.

The latter case should never happen during initial scanning unless a bridge was somehow reset during the kexec, but could legitimately happen if a bridge is later hot-plugged and I did not want Live Update to unnecessarily break that scenario. But then that creates this problem where pci_has_incoming_preserved_devices() can suddenly flip from true to false at any time and I needed all the complex logic to keep it consistent for a given scan.

Instead we can split the handling of these cases:

1. When the PCI core needs to decide if it should preserve bus numbers due to Live Update, pci_liveupdate_preserve_bus_numbers() can return true forever if any device was preserved by the previous kernel, which simplifies the logic.

2. Then to handle the case of a bridge is enumerated that does not have bus numbers assigned, we can handle that separately. If we reorder this with the next commit so the PCI core knows exactly which bridges have preserved downstream endpoints, then it is possible to determine if it is safe for the PCI core to allow bus numbers to be assigned to an unconfigured bridge.

After re-ordering, we can end up with something like this:

  bool pci_liveupdate_preserve_bus_numbers(void)
  {
  	return pci_liveupdate.had_incoming;
  }

  bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
  {
  	struct pci_dev *bridge;

  	for_each_pci_bridge(bridge, bus) {
  		if (!bridge->liveupdate.was_incoming || bridge->subordinate)
  			continue;

  		pci_err(dev, "Not assigning bus numbers, preserved bridge %s lost its bus number configuration\n",
  			pci_name(bridge));
  		return true;
  	}

  	return false;
  }

The net effect on pci_scan_bridge_extend() is:

  bool preserve_bus_numbers = !pcibios_assign_all_busses() ||
  			    pci_liveupdate_preserve_bus_numbers();
  ...
  	if (pci_liveupdate_refuse_bus_numbers(bus, dev))
  		goto out;

We could further scope pci_liveupdate_preserve_bus_numbers() to only return true for host bridges with preserved endpoints downstream, but that doesn't seem worth the extra complexity. It also seems nice to keep the pci_liveupdate_preserve_bus_numbers() policy global to match how the existing pcibios_assign_all_busses() policy is global.

Does that look reasonable?

#35 lore raw

Re: [PATCH v8 05/12] PCI: liveupdate: Preserve bus numbers during Live Update

Quoted 113 lines · David Matlack
Quoted 43 lines · David Matlack
Quoted 33 lines · Bjorn Helgaas
Quoted 31 lines · David Matlack
drivers/pci/liveupdate.c +31 -0
+bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
+{
+	struct pci_dev *parent = bus->self;
+
+	if (dev->liveupdate.preserve_bus_numbers)
+		return true;
+
+	if (parent && parent->liveupdate.preserve_bus_numbers) {
+		/*
+		 * Preserve bus numbers if the parent bridge is required to
+		 * preserve bus numbers. Otherwise the PCI core could expand
+		 * this bridge's reservation beyond its parent (which cannot
+		 * expand).
+		 */
+		dev->liveupdate.preserve_bus_numbers = true;
+	} else {
+		/*
+		 * Otherwise preserve bus numbers if there are any incoming
+		 * preserved devices. This ensures that the PCI core does not
+		 * allocate a bus number to a non-preserved device that
+		 * conflicts with the bus number already assigned to a preserved
+		 * device.
+		 *
+		 * This is slightly more restrictive than it needs to be. For
+		 * example, each host bridges have their own range of bus
+		 * numbers that won't conflict with other host bridges. But the
+		 * previous kernel should have assigned a sane bus topology and
+		 * it is simpler to just adopt that entire topology.
+		 */
+		dev->liveupdate.preserve_bus_numbers =
+			pci_has_incoming_preserved_devices();
Quoted 8 lines

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

Quoted 3 lines
drivers/pci/liveupdate.c +3 -0
+	}
+
+	return dev->liveupdate.preserve_bus_numbers;

I'm not sure why you don't just return pci_has_incoming_preserved_devices() in all cases, which is what the commit log suggests this patch does. What's gained by all the logic here? It's not like devices will be hot-added during the kexec.

Quoted 7 lines

To protect against pci_has_incoming_preserved_devices() flipping from true to false while the PCI core is in the middle of a scan. It is not likely to ever happen given most host bridge scanning should happen during early boot, but theoretically possible with the way the PCI core code is structured. I did not see way to structurally ensure these 2 things cannot race. A lot of the host bridge scanning happens without taking the rescan lock, for example.

After working on this more, I do see a way to simplify the logic in pci_liveupdate_preserve_bus_numbers().

pci_liveupdate_preserve_bus_numbers() is used in 2 places during scanning. First to decide if the PCI core should preserve bus numbers or is free to allocate new ones, and second to decide if the PCI core is allowed to assign bus numbers to bridges that are missing bus numbers.

The latter case should never happen during initial scanning unless a bridge was somehow reset during the kexec, but could legitimately happen if a bridge is later hot-plugged and I did not want Live Update to unnecessarily break that scenario. But then that creates this problem where pci_has_incoming_preserved_devices() can suddenly flip from true to false at any time and I needed all the complex logic to keep it consistent for a given scan.

Instead we can split the handling of these cases:

1. When the PCI core needs to decide if it should preserve bus numbers due to Live Update, pci_liveupdate_preserve_bus_numbers() can return true forever if any device was preserved by the previous kernel, which simplifies the logic.

2. Then to handle the case of a bridge is enumerated that does not have bus numbers assigned, we can handle that separately. If we reorder this with the next commit so the PCI core knows exactly which bridges have preserved downstream endpoints, then it is possible to determine if it is safe for the PCI core to allow bus numbers to be assigned to an unconfigured bridge.

After re-ordering, we can end up with something like this:

  bool pci_liveupdate_preserve_bus_numbers(void)
  {
  	return pci_liveupdate.had_incoming;
  }

  bool pci_liveupdate_refuse_bus_numbers(struct pci_bus *bus, struct pci_dev *dev)
  {
  	struct pci_dev *bridge;

  	for_each_pci_bridge(bridge, bus) {
  		if (!bridge->liveupdate.was_incoming || bridge->subordinate)
  			continue;

  		pci_err(dev, "Not assigning bus numbers, preserved bridge %s lost its bus number configuration\n",
  			pci_name(bridge));
  		return true;
  	}

  	return false;
  }

The net effect on pci_scan_bridge_extend() is:

  bool preserve_bus_numbers = !pcibios_assign_all_busses() ||
  			    pci_liveupdate_preserve_bus_numbers();
  ...
  	if (pci_liveupdate_refuse_bus_numbers(bus, dev))
  		goto out;
Quoted 7 lines

We could further scope pci_liveupdate_preserve_bus_numbers() to only return true for host bridges with preserved endpoints downstream, but that doesn't seem worth the extra complexity. It also seems nice to keep the pci_liveupdate_preserve_bus_numbers() policy global to match how the existing pcibios_assign_all_busses() policy is global.

Does that look reasonable?

Yep, sounds good to me.

#36 lore raw

PATCH v8 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    | 110 ++++++++++++++++++++++++++++--------
 include/linux/kho/abi/pci.h |   5 +-
 include/linux/pci.h         |   3 +
 3 files changed, 93 insertions(+), 25 deletions(-)
drivers/pci/liveupdate.c +87 -23
index d312381c9e10..08e5ff8c1fe7 100644
@@ -70,7 +70,6 @@
  * preserved. These may be relaxed in the future:
  *
  *  * The device cannot be a Virtual Function (VF).
- *  * The device cannot be behind a PCI-to-PCI bridge.
  *
  * Driver Binding
  * ==============
@@ -102,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
@@ -358,20 +369,42 @@ static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoi
 	return dev_ser;
 }
 
-static void pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
-					     struct pci_dev *dev)
+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					    struct pci_dev *dev)
 {
 	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
 
 	if (!dev_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");
 	outgoing->ser->nr_devices--;
 	memset(dev_ser, 0, sizeof(*dev_ser));
 	dev->liveupdate.outgoing = NULL;
+	return 0;
+}
+
+static void pci_liveupdate_unpreserve_path(struct pci_flb_outgoing *outgoing,
+					   struct pci_dev *dev,
+					   struct pci_dev *end)
+{
+	for_each_pci_dev_in_path(dev) {
+		if (dev == end)
+			break;
+
+		if (pci_liveupdate_unpreserve_device(outgoing, dev))
+			return;
+	}
 }
 
 static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
@@ -385,13 +418,23 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 	}
 
 	if (dev->liveupdate.outgoing) {
-		pci_warn(dev, "Device is already preserved\n");
-		return -EBUSY;
-	}
+		if (!dev->liveupdate.outgoing->refcount) {
+			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
+			return -EINVAL;
+		}
 
-	if (!pci_is_root_bus(dev->bus)) {
-		pci_warn(dev, "Cannot preserve devices behind bridges\n");
-		return -EINVAL;
+		/*
+		 * Endpoint devices should not be preserved more than once.
+		 * Bridges are preserved once for every downstream device that
+		 * is preserved.
+		 */
+		if (!dev->subordinate) {
+			pci_warn(dev, "Device is already preserved\n");
+			return -EBUSY;
+		}
+
+		dev->liveupdate.outgoing->refcount++;
+		return 0;
 	}
 
 	dev_ser = pci_flb_alloc_dev_ser(outgoing);
@@ -419,12 +462,16 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
  * pci_liveupdate_preserve() from their struct liveupdate_file_handler
  * preserve() callback to ensure the outgoing struct pci_ser is already set up.
  *
+ * pci_liveupdate_preserve() automatically preserves all bridges upstream of
+ * @dev.
+ *
  * Returns: 0 on success, <0 on failure.
  */
 int pci_liveupdate_preserve(struct pci_dev *dev)
 {
 	struct pci_flb_outgoing *outgoing = NULL;
-	int ret;
+	struct pci_dev *start = dev;
+	int ret = -ENODEV;
 
 	guard(rwsem_write)(&pci_liveupdate.rwsem);
 
@@ -432,7 +479,13 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
 	if (IS_ERR(outgoing))
 		return PTR_ERR(outgoing);
 
-	ret = pci_liveupdate_preserve_device(outgoing, dev);
+	for_each_pci_dev_in_path(dev) {
+		ret = pci_liveupdate_preserve_device(outgoing, dev);
+		if (ret) {
+			pci_liveupdate_unpreserve_path(outgoing, start, dev);
+			break;
+		}
+	}
 
 	pci_liveupdate_flb_put_outgoing();
 	return ret;
@@ -448,6 +501,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)
 {
@@ -461,7 +517,7 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
 		return;
 	}
 
-	pci_liveupdate_unpreserve_device(outgoing, dev);
+	pci_liveupdate_unpreserve_path(outgoing, dev, /*end=*/NULL);
 	pci_liveupdate_flb_put_outgoing();
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
@@ -631,28 +687,30 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
 }
 
-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--;
+	return 0;
 }
 
 /**
@@ -664,6 +722,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)
 {
@@ -677,7 +737,11 @@ void pci_liveupdate_finish(struct pci_dev *dev)
 		return;
 	}
 
-	pci_liveupdate_finish_device(incoming->ser, dev);
+	for_each_pci_dev_in_path(dev) {
+		if (pci_liveupdate_finish_device(incoming->ser, dev))
+			break;
+	}
+
 	pci_liveupdate_flb_put_incoming();
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
include/linux/kho/abi/pci.h +3 -2
index d4881c2d2fc9..aecace02d8ac 100644
@@ -24,7 +24,7 @@
  */
 
 #define PCI_LUO_FLB_COMPATIBLE "pci"
-#define PCI_LUO_FLB_VERSION 1
+#define PCI_LUO_FLB_VERSION 2
 
 /**
  * struct pci_dev_ser - Serialized state about a single PCI device.
@@ -33,7 +33,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 {
include/linux/pci.h +3 -0
index b41dd572a2d6..6267c83813d5 100644
@@ -829,6 +829,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)
 	return dev->bus->self;
 }
 
+#define for_each_pci_dev_in_path(dev) \
+	for (; dev; dev = pci_upstream_bridge(dev))
+
 #ifdef CONFIG_PCI_MSI
 static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev)
 {
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#37 lore raw

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] The `for_each_pci_dev_in_path` macro violates standard kernel iterator conventions by not…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The for_each_pci_dev_in_path macro violates standard kernel iterator conventions by not initializing its iterator variable, creating a confusing API that may lead to misuse. --

--- Patch [6]: [PATCH v8 06/12] PCI: liveupdate: Auto-preserve upstream bridges across Live Update --- commit 5f902c08492796d638f5197db23659b872ce5e10
Author: David Matlack <[email protected]>

PCI: liveupdate: Auto-preserve upstream bridges across Live Update

This commit updates the PCI Live Update mechanism to automatically preserve upstream PCI-to-PCI bridges when a downstream device is preserved. It tracks the number of downstream devices preserved under each bridge using a reference count in struct pci_dev_ser.

include/linux/pci.h static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) · line 833

Quoted 10 lines
include/linux/pci.h +2 -0
index b41dd572a2d6b..6267c83813d55 100644
@@ -829,6 +829,9 @@ static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)
 	return dev->bus->self;
 }
 
+#define for_each_pci_dev_in_path(dev) \
+	for (; dev; dev = pci_upstream_bridge(dev))

[Severity: Low]
Does this macro violate standard kernel iterator conventions by not initializing its iterator variable?

Standard Linux kernel for_each iterators typically initialize their loop variables. By omitting the initialization here, the macro expects the caller to pre-initialize the variable and then mutates it in place.

While the current usages in this patch are functionally correct, could this unexpected behavior lead to confusion and API misuse for future callers?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
Reviewed-by on v8 6/12
#38 lore raw

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

In reply to David Matlack
Quoted 9 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.

Quoted 8 lines

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.

Thanks,
Praan

#39 lore raw

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

In reply to David Matlack
Quoted 9 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.

Quoted 5 lines

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.

This seems to hint that we're going to allow bridge reconfiguration in some cases, e.g., for hot-adds. The simplest case is "leave config of all bridges the same", and I thought that was what the previous patch commit log said.

What's the benefit added by this patch?

#40 lore raw

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

Quoted 16 lines · Bjorn Helgaas
Quoted 9 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.

Quoted 5 lines

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.

Quoted 6 lines

This seems to hint that we're going to allow bridge reconfiguration in some cases, e.g., for hot-adds. The simplest case is "leave config of all bridges the same", and I thought that was what the previous patch commit log said.

What's the benefit added by this patch?

It is used in the following patches:

PCI: liveupdate: Adopt ACS controls in incoming preserved devices
PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges
PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

to preserve certain configuration on bridges that have downstream endpoints that are being preserved. To support P2PDMA we will also have to preserve bridge memory windows (future series).

If we are ok with applying those policies to all bridges on the system whenever one or more endpoints anywhere on the system are being preserved, then I agree we don't need this patch. But I thought it would be cleaner to track things per-device.

#41 lore raw

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

Quoted 30 lines · David Matlack
Quoted 16 lines · Bjorn Helgaas
Quoted 9 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.

Quoted 5 lines

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.

Quoted 6 lines

This seems to hint that we're going to allow bridge reconfiguration in some cases, e.g., for hot-adds. The simplest case is "leave config of all bridges the same", and I thought that was what the previous patch commit log said.

What's the benefit added by this patch?

It is used in the following patches:

PCI: liveupdate: Adopt ACS controls in incoming preserved devices
PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges
PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

Quoted 8 lines

to preserve certain configuration on bridges that have downstream endpoints that are being preserved. To support P2PDMA we will also have to preserve bridge memory windows (future series).

If we are ok with applying those policies to all bridges on the system whenever one or more endpoints anywhere on the system are being preserved, then I agree we don't need this patch. But I thought it would be cleaner to track things per-device.

Yes, I agree tracking it per-device is good. I was looking for a traversal upstream to increment refcounts on bridges, and I guess that happens via for_each_pci_dev_in_path() in pci_liveupdate_preserve().

The actual refcount still confuses me a bit (see
https://lore.kernel.org/all/20260917000723.GA992337@bhelgaas). Maybe it would help if pci_liveupdate_preserve_device() alloc the dev_ser *first* (right after all the bail-out checks)? I wonder if the refcount increment could then happen in exactly one place, separated from the one-time dev_ser housekeeping? E.g., something like:

  if (!dev->liveupdate.outgoing) {
    dev_ser = pci_flb_alloc_dev_ser(outgoing);
    ...
    dev->liveupdate.outgoing = dev_ser;
  }

dev->liveupdate.outgoing->refcount++;

#42 lore raw

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

Quoted 51 lines · Bjorn Helgaas
Quoted 30 lines · David Matlack
Quoted 16 lines · Bjorn Helgaas
Quoted 9 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.

Quoted 5 lines

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.

Quoted 6 lines

This seems to hint that we're going to allow bridge reconfiguration in some cases, e.g., for hot-adds. The simplest case is "leave config of all bridges the same", and I thought that was what the previous patch commit log said.

What's the benefit added by this patch?

It is used in the following patches:

PCI: liveupdate: Adopt ACS controls in incoming preserved devices
PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges
PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

Quoted 8 lines

to preserve certain configuration on bridges that have downstream endpoints that are being preserved. To support P2PDMA we will also have to preserve bridge memory windows (future series).

If we are ok with applying those policies to all bridges on the system whenever one or more endpoints anywhere on the system are being preserved, then I agree we don't need this patch. But I thought it would be cleaner to track things per-device.

Yes, I agree tracking it per-device is good. I was looking for a traversal upstream to increment refcounts on bridges, and I guess that happens via for_each_pci_dev_in_path() in pci_liveupdate_preserve().

The actual refcount still confuses me a bit (see
https://lore.kernel.org/all/20260917000723.GA992337@bhelgaas). Maybe it would help if pci_liveupdate_preserve_device() alloc the dev_ser *first* (right after all the bail-out checks)? I wonder if the refcount increment could then happen in exactly one place, separated from the one-time dev_ser housekeeping? E.g., something like:

Quoted 7 lines
  if (!dev->liveupdate.outgoing) {
    dev_ser = pci_flb_alloc_dev_ser(outgoing);
    ...
    dev->liveupdate.outgoing = dev_ser;
  }

dev->liveupdate.outgoing->refcount++;

Ack, will fix

#43 lore raw

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

In reply to David Matlack
Quoted 24 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 +2 -1
+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					    struct pci_dev *dev)
 {
 	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
 
 	if (!dev_ser) {
 		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
-		return;

drivers/pci/liveupdate.c static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoi · line 388

Quoted 10 lines
drivers/pci/liveupdate.c +8 -0
+		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;

I guess this is a nit: I was looking for a dev_ser->refcount++ in this patch to correspond with this decrement. I *guess* the "dev_ser->refcount = 1" in pci_liveupdate_preserve_device() starts that, and the "dev->liveupdate.outgoing->refcount++" there is an increment of the same refcount? The connections there don't seem quite obvious to me.

But I guess "dev_ser->refcount = 1" was added by a different patch, so probably *not* the same?

Quoted 37 lines
drivers/pci/liveupdate.c +18 -6
 	pci_info(dev, "Device will no longer be preserved across next Live Update\n");
 	outgoing->ser->nr_devices--;
 	memset(dev_ser, 0, sizeof(*dev_ser));
 	dev->liveupdate.outgoing = NULL;
+	return 0;
+}
@@ -385,13 +418,23 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 	}
 
 	if (dev->liveupdate.outgoing) {
-		pci_warn(dev, "Device is already preserved\n");
-		return -EBUSY;
-	}
+		if (!dev->liveupdate.outgoing->refcount) {
+			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
+			return -EINVAL;
+		}
 
-	if (!pci_is_root_bus(dev->bus)) {
-		pci_warn(dev, "Cannot preserve devices behind bridges\n");
-		return -EINVAL;
+		/*
+		 * Endpoint devices should not be preserved more than once.
+		 * Bridges are preserved once for every downstream device that
+		 * is preserved.
+		 */
+		if (!dev->subordinate) {
+			pci_warn(dev, "Device is already preserved\n");
+			return -EBUSY;
+		}
+
+		dev->liveupdate.outgoing->refcount++;
+		return 0;
 	}
 
 	dev_ser = pci_flb_alloc_dev_ser(outgoing);
#44 lore raw

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

Quoted 43 lines · Bjorn Helgaas
Quoted 24 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 +2 -1
+static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
+					    struct pci_dev *dev)
 {
 	struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing;
 
 	if (!dev_ser) {
 		pci_warn(dev, "Cannot unpreserve device that is not preserved\n");
-		return;

drivers/pci/liveupdate.c static struct pci_dev_ser *pci_flb_alloc_dev_ser(struct pci_flb_outgoing *outgoi · line 388

Quoted 10 lines
drivers/pci/liveupdate.c +8 -0
+		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;

I guess this is a nit: I was looking for a dev_ser->refcount++ in this patch to correspond with this decrement. I *guess* the "dev_ser->refcount = 1" in pci_liveupdate_preserve_device() starts that, and the "dev->liveupdate.outgoing->refcount++" there is an increment of the same refcount? The connections there don't seem quite obvious to me.

Quoted 2 lines

But I guess "dev_ser->refcount = 1" was added by a different patch, so probably *not* the same?

As mentioned in the other reply I can combine refcount=1 and refcount++ into a single refcount++ to make this simpler.

The refcount=1 is for when a device is first preserved. refcount++ is for when a device is preserved a subsequent time. Bridges are the only devices that can have a refcount > 1 so that's why this patch introduces the refcount++.

Quoted 38 lines
Quoted 38 lines
drivers/pci/liveupdate.c +18 -6
 	pci_info(dev, "Device will no longer be preserved across next Live Update\n");
 	outgoing->ser->nr_devices--;
 	memset(dev_ser, 0, sizeof(*dev_ser));
 	dev->liveupdate.outgoing = NULL;
+	return 0;
+}
@@ -385,13 +418,23 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 	}
 
 	if (dev->liveupdate.outgoing) {
-		pci_warn(dev, "Device is already preserved\n");
-		return -EBUSY;
-	}
+		if (!dev->liveupdate.outgoing->refcount) {
+			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
+			return -EINVAL;
+		}
 
-	if (!pci_is_root_bus(dev->bus)) {
-		pci_warn(dev, "Cannot preserve devices behind bridges\n");
-		return -EINVAL;
+		/*
+		 * Endpoint devices should not be preserved more than once.
+		 * Bridges are preserved once for every downstream device that
+		 * is preserved.
+		 */
+		if (!dev->subordinate) {
+			pci_warn(dev, "Device is already preserved\n");
+			return -EBUSY;
+		}
+
+		dev->liveupdate.outgoing->refcount++;
+		return 0;
 	}
 
 	dev_ser = pci_flb_alloc_dev_ser(outgoing);
#45 lore raw

PATCH v8 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 device matching loop into its own routine, pci_dev_acs_ops_get(). This eliminates 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 | 51 ++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 30 deletions(-)
drivers/pci/quirks.c +21 -30
index b09f27f7846f..7ac39ec2843e 100644
@@ -5374,9 +5374,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;
@@ -5396,9 +5393,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;
@@ -5426,9 +5420,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;
@@ -5448,56 +5439,56 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
 static const struct pci_dev_acs_ops {
 	u16 vendor;
 	u16 device;
+	bool (*match)(struct pci_dev *dev);
 	int (*enable_acs)(struct pci_dev *dev);
 	int (*disable_acs_redir)(struct pci_dev *dev);
 } pci_dev_acs_ops[] = {
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_pch_acs,
 	},
 	{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+	    .match = pci_quirk_intel_spt_pch_acs_match,
 	    .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
 	    .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
 	},
 };
 
-int pci_dev_specific_enable_acs(struct pci_dev *dev)
+static const struct pci_dev_acs_ops *pci_dev_acs_ops_get(struct pci_dev *dev)
 {
 	const struct pci_dev_acs_ops *p;
-	int i, ret;
+	int i;
 
 	for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
 		p = &pci_dev_acs_ops[i];
 		if ((p->vendor == dev->vendor ||
 		     p->vendor == (u16)PCI_ANY_ID) &&
 		    (p->device == dev->device ||
-		     p->device == (u16)PCI_ANY_ID) &&
-		    p->enable_acs) {
-			ret = p->enable_acs(dev);
-			if (ret >= 0)
-				return ret;
+		     p->device == (u16)PCI_ANY_ID)) {
+			if (!p->match || p->match(dev))
+				return p;
 		}
 	}
 
+	return NULL;
+}
+
+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.55.0.487.gaf234c4eb3-goog
#46 lore raw

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

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 7 lines

Refactor the logic to match devices to pci_dev_acs_ops by factoring out the device matching loop into its own routine, pci_dev_acs_ops_get(). This eliminates 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.

Quoted 6 lines

No functional change intended.

Sashiko has reviewed this patch and found no issues. It looks great!

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

PATCH v8 8/12 PCI: liveupdate: Adopt ACS controls in incoming preserved devices

In reply to David Matlack

Adopt Access Control Services (ACS) controls 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 during or after Live Update.

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       | 87 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 +++++
 drivers/pci/pci.c              |  6 +++
 drivers/pci/pci.h              |  5 ++
 drivers/pci/quirks.c           |  7 +++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 122 insertions(+)
drivers/pci/liveupdate.c +87 -0
index 08e5ff8c1fe7..fb602dd3933e 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 adopts all ACS controls enabled on incoming preserved devices
+ *    rather than assigning new ones. This ensures that TLPs are routed the same
+ *    way after Live Update and ensures that IOMMU groups do not change. Note
+ *    that a device will use its adopted ACS controls for the lifetime of its
+ *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -128,6 +143,7 @@
 #include <linux/slab.h>
 
 #include "liveupdate.h"
+#include "pci.h"
 
 /**
  * struct pci_liveupdate_global - Global state for PCI Live Update support
@@ -417,6 +433,16 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 		return -EINVAL;
 	}
 
+	/*
+	 * Do not preserve devices that rely on device-specific ACS equivalents
+	 * (for now) since that would complicate keeping ACS constant across
+	 * Live Update.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing) {
 		if (!dev->liveupdate.outgoing->refcount) {
 			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
@@ -668,6 +694,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;
 	pci_liveupdate_flb_put_incoming();
 }
 
@@ -746,6 +773,66 @@ void pci_liveupdate_finish(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
 
+/**
+ * pci_liveupdate_cache_adopted_acs_controls() - Cache adopted ACS controls
+ * @dev: The PCI device to cache ACS controls from
+ *
+ * If @dev is an incoming Live Update device, read its current ACS configuration
+ * from hardware (which was set by the previous kernel) and cache it.
+ */
+void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);
+}
+
+/**
+ * pci_liveupdate_enable_adopted_acs_controls() - Enable adopted ACS controls
+ * @dev: The PCI device to enable adopted ACS controls for
+ *
+ * For devices preserved across a Live Update, write the cached ACS controls
+ * back into the hardware's ACS Capability. This ensures that the device
+ * continues to use the ACS rules established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved or requires
+ * device-specific quirks.
+ */
+int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
+{
+	u16 acs_ctrl = dev->liveupdate.acs_ctrl;
+	u16 acs_cap = dev->acs_cap;
+
+	/*
+	 * Check if the device was preserved over a previous Live Update (even
+	 * if it has already gone through pci_liveupdate_finish()). This ensures
+	 * that the device continues to use the ACS controls established by the
+	 * previous kernel.
+	 */
+	if (!dev->liveupdate.was_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 (e.g. new device-specific ACS quirk in the current kernel),
+	 * log a big warning and fall back to the normal enable ACS path.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}
+
+	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 107881183fda..ac5b8bf2edf5 100644
@@ -16,6 +16,8 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev);
 bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 					 struct pci_dev *dev);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
+void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
+int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -34,6 +36,15 @@ static inline bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 static inline void pci_liveupdate_scan_bridge_end(struct pci_dev *dev)
 {
 }
+
+static inline void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev)
+{
+}
+
+static inline int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +6 -0
index 77b17b13ee61..22001bdf4c97 100644
@@ -34,6 +34,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);
@@ -1008,6 +1010,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_enable_adopted_acs_controls(dev))
+		return;
+
 	/* If an iommu is present we start with kernel default caps */
 	if (pci_acs_enable) {
 		if (pci_dev_specific_enable_acs(dev))
@@ -3689,6 +3694,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_cache_adopted_acs_controls(dev);
 }
 
 /**
drivers/pci/pci.h +5 -0
index 4469e1a77f3c..988a18b3204a 100644
@@ -1047,6 +1047,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);
@@ -1057,6 +1058,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 7ac39ec2843e..99b819f38e49 100644
@@ -5473,6 +5473,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 fc1f5640968a..04a2b2a3102a 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.
  * @preserve_bus_numbers: True if the PCI core should preserve 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 preserve_bus_numbers;
+	bool was_preserved;
 };
 
 struct pci_dev;
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#48 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 12 lines

Adopt Access Control Services (ACS) controls 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 during or after Live Update.

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

Quoted 6 lines

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.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
Reviewed-by on v8 8/12
#49 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

In reply to David Matlack
Quoted 12 lines · David Matlack

Adopt Access Control Services (ACS) controls 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 during or after Live Update.

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

Quoted 6 lines

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.

Thanks,
Praan

#50 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

In reply to David Matlack
Quoted 204 lines · David Matlack

Adopt Access Control Services (ACS) controls 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 during or after Live Update.

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       | 87 ++++++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h       | 11 +++++
 drivers/pci/pci.c              |  6 +++
 drivers/pci/pci.h              |  5 ++
 drivers/pci/quirks.c           |  7 +++
 include/linux/pci_liveupdate.h |  6 +++
 6 files changed, 122 insertions(+)
drivers/pci/liveupdate.c +87 -0
index 08e5ff8c1fe7..fb602dd3933e 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 adopts all ACS controls enabled on incoming preserved devices
+ *    rather than assigning new ones. This ensures that TLPs are routed the same
+ *    way after Live Update and ensures that IOMMU groups do not change. Note
+ *    that a device will use its adopted ACS controls for the lifetime of its
+ *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -128,6 +143,7 @@
 #include <linux/slab.h>
 
 #include "liveupdate.h"
+#include "pci.h"
 
 /**
  * struct pci_liveupdate_global - Global state for PCI Live Update support
@@ -417,6 +433,16 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 		return -EINVAL;
 	}
 
+	/*
+	 * Do not preserve devices that rely on device-specific ACS equivalents
+	 * (for now) since that would complicate keeping ACS constant across
+	 * Live Update.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Refusing to preserve device that relies on ACS quirks\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing) {
 		if (!dev->liveupdate.outgoing->refcount) {
 			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
@@ -668,6 +694,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;
 	pci_liveupdate_flb_put_incoming();
 }
 
@@ -746,6 +773,66 @@ void pci_liveupdate_finish(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_liveupdate_finish);
 
+/**
+ * pci_liveupdate_cache_adopted_acs_controls() - Cache adopted ACS controls
+ * @dev: The PCI device to cache ACS controls from
+ *
+ * If @dev is an incoming Live Update device, read its current ACS configuration
+ * from hardware (which was set by the previous kernel) and cache it.
+ */
+void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev)
+{
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL, &dev->liveupdate.acs_ctrl);
+}
+
+/**
+ * pci_liveupdate_enable_adopted_acs_controls() - Enable adopted ACS controls
+ * @dev: The PCI device to enable adopted ACS controls for
+ *
+ * For devices preserved across a Live Update, write the cached ACS controls
+ * back into the hardware's ACS Capability. This ensures that the device
+ * continues to use the ACS rules established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved or requires
+ * device-specific quirks.
+ */
+int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
+{
+	u16 acs_ctrl = dev->liveupdate.acs_ctrl;
+	u16 acs_cap = dev->acs_cap;
+
+	/*
+	 * Check if the device was preserved over a previous Live Update (even
+	 * if it has already gone through pci_liveupdate_finish()). This ensures
+	 * that the device continues to use the ACS controls established by the
+	 * previous kernel.
+	 */
+	if (!dev->liveupdate.was_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 (e.g. new device-specific ACS quirk in the current kernel),
+	 * log a big warning and fall back to the normal enable ACS path.
+	 */
+	if (pci_need_dev_specific_enable_acs(dev)) {
+		pci_warn(dev, "Device-specific quirk required to enable ACS!\n");
+		WARN_ON_ONCE(true);
+		return -EINVAL;
+	}
+
+	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 107881183fda..ac5b8bf2edf5 100644
@@ -16,6 +16,8 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev);
 bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 					 struct pci_dev *dev);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
+void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
+int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -34,6 +36,15 @@ static inline bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 static inline void pci_liveupdate_scan_bridge_end(struct pci_dev *dev)
 {
 }
+
+static inline void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev)
+{
+}
+
+static inline int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +2 -0
index 77b17b13ee61..22001bdf4c97 100644
@@ -34,6 +34,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);

drivers/pci/pci.c void pci_enable_acs(struct pci_dev *dev) · line 1014

Quoted 5 lines
drivers/pci/pci.c +2 -0
@@ -1008,6 +1010,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_enable_adopted_acs_controls(dev))
+		return;

Ugh. The asymmetry between pci_save_state(), which does nothing ACS-related, and pci_restore_state(), which enables it, is sort of sketchy to begin with.

The command-line parsing in this path feels like kind of a wart (not that you're touching it).

It just seems like this path is already hard to analyze, and liveupdate is making it harder.

If we could save/restore the ACS state around the reset, wouldn't that solve this without any liveupdate specials here?

Quoted 80 lines
drivers/pci/pci.c +1 -0
 	/* If an iommu is present we start with kernel default caps */
 	if (pci_acs_enable) {
 		if (pci_dev_specific_enable_acs(dev))
@@ -3689,6 +3694,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_cache_adopted_acs_controls(dev);
 }
 
 /**
drivers/pci/pci.h +5 -0
index 4469e1a77f3c..988a18b3204a 100644
@@ -1047,6 +1047,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);
@@ -1057,6 +1058,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 7ac39ec2843e..99b819f38e49 100644
@@ -5473,6 +5473,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 fc1f5640968a..04a2b2a3102a 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.
  * @preserve_bus_numbers: True if the PCI core should preserve 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 preserve_bus_numbers;
+	bool was_preserved;
 };
 
 struct pci_dev;
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#51 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

Quoted 24 lines · Bjorn Helgaas
Quoted 14 lines
drivers/pci/pci.c +2 -0
index 77b17b13ee61..22001bdf4c97 100644
@@ -34,6 +34,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);

drivers/pci/pci.c void pci_enable_acs(struct pci_dev *dev) · line 1014

Quoted 5 lines
drivers/pci/pci.c +2 -0
@@ -1008,6 +1010,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_enable_adopted_acs_controls(dev))
+		return;

Ugh. The asymmetry between pci_save_state(), which does nothing ACS-related, and pci_restore_state(), which enables it, is sort of sketchy to begin with.

Quoted 8 lines

The command-line parsing in this path feels like kind of a wart (not that you're touching it).

It just seems like this path is already hard to analyze, and liveupdate is making it harder.

If we could save/restore the ACS state around the reset, wouldn't that solve this without any liveupdate specials here?

Yeah that would simplify the liveupdate support greatly. Let me work on that for v9.

#52 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

Quoted 29 lines · David Matlack
Quoted 27 lines · Bjorn Helgaas
Quoted 14 lines
drivers/pci/pci.c +2 -0
index 77b17b13ee61..22001bdf4c97 100644
@@ -34,6 +34,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);

drivers/pci/pci.c void pci_enable_acs(struct pci_dev *dev) · line 1014

Quoted 5 lines
drivers/pci/pci.c +2 -0
@@ -1008,6 +1010,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_enable_adopted_acs_controls(dev))
+		return;

Ugh. The asymmetry between pci_save_state(), which does nothing ACS-related, and pci_restore_state(), which enables it, is sort of sketchy to begin with.

The command-line parsing in this path feels like kind of a wart (not that you're touching it).

Quoted 8 lines
Quoted 5 lines

It just seems like this path is already hard to analyze, and liveupdate is making it harder.

If we could save/restore the ACS state around the reset, wouldn't that solve this without any liveupdate specials here?

Yeah that would simplify the liveupdate support greatly. Let me work on that for v9.

Here's the patch I have prepped for v9 to save/restore ACS controls around reset:

From 0c69424454de0c569aca67bc69f496db915a9bcf Mon Sep 17 00:00:00 2001
From: David Matlack <[email protected]>
Date: Fri, 11 Sep 2026 20:05:04 +0000
Subject: [PATCH] PCI: Save and restore the ACS Control register

Save the ACS Control register in pci_save_state() and write it back in pci_restore_state(), instead of recomputing the ACS controls from scratch with pci_enable_acs().

This makes ACS symmetric with the rest of a device's saved state. Today pci_save_state() ignores ACS entirely and pci_restore_state() re-enables the ACS controls from the kernel's current ACS policy. As a result, a device can come out of a reset with different ACS controls than it went in with, e.g. any controls programmed outside of pci_enable_acs() are silently dropped.

pci_enable_acs() runs when a driver binds to a device (pci_dma_configure()), i.e. after pci_bus_add_device() has already saved the device's state. Refresh the saved ACS Control register there as well, otherwise a subsequent reset would revert ACS back to the configuration left behind by firmware.

Devices that rely on device-specific quirks to enable an ACS equivalent keep that configuration outside of the ACS Control register, so keep configuring ACS from scratch for them. Do the same for devices that have no saved ACS state at all.

Assisted-by Claude:claude-opus-5
---
 drivers/pci/pci.c    | 66 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/pci/pci.h    |  5 ++++
 drivers/pci/quirks.c |  7 +++++
 3 files changed, 77 insertions(+), 1 deletion(-)
drivers/pci/pci.c +65 -1
index b2879a6be5f8..dd25c01736b4 100644
@@ -1021,6 +1021,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
 		caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
 }
 
+/**
+ * pci_save_acs_state - save the ACS Control register
+ * @dev: the PCI device
+ *
+ * Record the ACS controls currently programmed in hardware so that
+ * pci_restore_acs_state() can reapply them after a reset.
+ */
+static void pci_save_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state;
+
+	if (!dev->acs_cap)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+	if (!save_state)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			     (u16 *)&save_state->cap.data[0]);
+}
+
+/**
+ * pci_restore_acs_state - restore the ACS Control register
+ * @dev: the PCI device
+ */
+static void pci_restore_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state = NULL;
+
+	if (dev->acs_cap && !pci_need_dev_specific_enable_acs(dev))
+		save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+
+	/*
+	 * Devices that rely on device-specific quirks to enable an ACS
+	 * equivalent keep that configuration outside of the ACS Control
+	 * register, so there is nothing useful to restore for them. Configure
+	 * ACS from scratch instead, which also covers devices that have no
+	 * saved ACS state at all.
+	 */
+	if (!save_state) {
+		pci_enable_acs(dev);
+		return;
+	}
+
+	pci_write_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			      *(u16 *)&save_state->cap.data[0]);
+}
+
 /**
  * pci_enable_acs - enable ACS if hardware support it
  * @dev: the PCI device
@@ -1057,6 +1106,15 @@ void pci_enable_acs(struct pci_dev *dev)
 	__pci_config_acs(dev, &caps, config_acs_param, 0, 0);
 
 	pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
+
+	/*
+	 * pci_enable_acs() runs when a driver binds to the device, i.e. after
+	 * pci_bus_add_device() has already saved the device's state. Refresh
+	 * the saved ACS Control register so that a subsequent reset restores
+	 * the controls programmed here rather than the ones left behind by
+	 * firmware.
+	 */
+	pci_save_acs_state(dev);
 }
 
 /**
@@ -1800,6 +1858,7 @@ int pci_save_state(struct pci_dev *dev)
 	pci_save_aer_state(dev);
 	pci_save_ptm_state(dev);
 	pci_save_tph_state(dev);
+	pci_save_acs_state(dev);
 	return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1877,7 +1936,7 @@ void pci_restore_state(struct pci_dev *dev)
 	pci_restore_msi_state(dev);
 
 	/* Restore ACS and IOV configuration state */
-	pci_enable_acs(dev);
+	pci_restore_acs_state(dev);
 	pci_restore_iov_state(dev);
 
 	dev->state_saved = false;
@@ -3532,6 +3591,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
 	if (error)
 		pci_err(dev, "unable to allocate suspend buffer for LTR\n");
 
+	error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,
+					    sizeof(u16));
+	if (error)
+		pci_err(dev, "unable to allocate suspend buffer for ACS\n");
+
 	pci_allocate_vc_save_buffers(dev);
 }
 
drivers/pci/pci.h +5 -0
index ba3c3fddddc2..037c1674f164 100644
@@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 void pci_disable_broken_acs_cap(struct pci_dev *pdev);
@@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
 	return -ENOTTY;
 }
+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	return false;
+}
 static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	return -ENOTTY;
drivers/pci/quirks.c +7 -0
index 7aee30734303..e500c202d2ec 100644
@@ -5476,6 +5476,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);
Reviewed-by
#53 lore raw

Re: [PATCH v8 08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices

Quoted 64 lines · David Matlack
Quoted 29 lines · David Matlack
Quoted 27 lines · Bjorn Helgaas
Quoted 14 lines
drivers/pci/pci.c +2 -0
index 77b17b13ee61..22001bdf4c97 100644
@@ -34,6 +34,8 @@
 #include <linux/aer.h>
 #include <linux/bitfield.h>
 #include <linux/suspend.h>
+
+#include "liveupdate.h"
 #include "pci.h"
 
 DEFINE_MUTEX(pci_slot_mutex);

drivers/pci/pci.c void pci_enable_acs(struct pci_dev *dev) · line 1014

Quoted 5 lines
drivers/pci/pci.c +2 -0
@@ -1008,6 +1010,9 @@ void pci_enable_acs(struct pci_dev *dev)
 	bool enable_acs = false;
 	int pos;
 
+	if (!pci_liveupdate_enable_adopted_acs_controls(dev))
+		return;

Ugh. The asymmetry between pci_save_state(), which does nothing ACS-related, and pci_restore_state(), which enables it, is sort of sketchy to begin with.

The command-line parsing in this path feels like kind of a wart (not that you're touching it).

Quoted 8 lines
Quoted 5 lines

It just seems like this path is already hard to analyze, and liveupdate is making it harder.

If we could save/restore the ACS state around the reset, wouldn't that solve this without any liveupdate specials here?

Yeah that would simplify the liveupdate support greatly. Let me work on that for v9.

Here's the patch I have prepped for v9 to save/restore ACS controls around reset:

From 0c69424454de0c569aca67bc69f496db915a9bcf Mon Sep 17 00:00:00 2001
From: David Matlack <[email protected]>
Date: Fri, 11 Sep 2026 20:05:04 +0000
Subject: [PATCH] PCI: Save and restore the ACS Control register

Save the ACS Control register in pci_save_state() and write it back in pci_restore_state(), instead of recomputing the ACS controls from scratch with pci_enable_acs().

This makes ACS symmetric with the rest of a device's saved state. Today pci_save_state() ignores ACS entirely and pci_restore_state() re-enables the ACS controls from the kernel's current ACS policy. As a result, a device can come out of a reset with different ACS controls than it went in with, e.g. any controls programmed outside of pci_enable_acs() are silently dropped.

pci_enable_acs() runs when a driver binds to a device (pci_dma_configure()), i.e. after pci_bus_add_device() has already saved the device's state. Refresh the saved ACS Control register there as well, otherwise a subsequent reset would revert ACS back to the configuration left behind by firmware.

Quoted 7 lines

Devices that rely on device-specific quirks to enable an ACS equivalent keep that configuration outside of the ACS Control register, so keep configuring ACS from scratch for them. Do the same for devices that have no saved ACS state at all.

Assisted-by Claude:claude-opus-5

I like this a lot, thanks! It would be good to get Alex W's ack, too.

Quoted 152 lines
---
 drivers/pci/pci.c    | 66 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/pci/pci.h    |  5 ++++
 drivers/pci/quirks.c |  7 +++++
 3 files changed, 77 insertions(+), 1 deletion(-)
drivers/pci/pci.c +65 -1
index b2879a6be5f8..dd25c01736b4 100644
@@ -1021,6 +1021,55 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
 		caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
 }
 
+/**
+ * pci_save_acs_state - save the ACS Control register
+ * @dev: the PCI device
+ *
+ * Record the ACS controls currently programmed in hardware so that
+ * pci_restore_acs_state() can reapply them after a reset.
+ */
+static void pci_save_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state;
+
+	if (!dev->acs_cap)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+	if (!save_state)
+		return;
+
+	pci_read_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			     (u16 *)&save_state->cap.data[0]);
+}
+
+/**
+ * pci_restore_acs_state - restore the ACS Control register
+ * @dev: the PCI device
+ */
+static void pci_restore_acs_state(struct pci_dev *dev)
+{
+	struct pci_cap_saved_state *save_state = NULL;
+
+	if (dev->acs_cap && !pci_need_dev_specific_enable_acs(dev))
+		save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ACS);
+
+	/*
+	 * Devices that rely on device-specific quirks to enable an ACS
+	 * equivalent keep that configuration outside of the ACS Control
+	 * register, so there is nothing useful to restore for them. Configure
+	 * ACS from scratch instead, which also covers devices that have no
+	 * saved ACS state at all.
+	 */
+	if (!save_state) {
+		pci_enable_acs(dev);
+		return;
+	}
+
+	pci_write_config_word(dev, dev->acs_cap + PCI_ACS_CTRL,
+			      *(u16 *)&save_state->cap.data[0]);
+}
+
 /**
  * pci_enable_acs - enable ACS if hardware support it
  * @dev: the PCI device
@@ -1057,6 +1106,15 @@ void pci_enable_acs(struct pci_dev *dev)
 	__pci_config_acs(dev, &caps, config_acs_param, 0, 0);
 
 	pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
+
+	/*
+	 * pci_enable_acs() runs when a driver binds to the device, i.e. after
+	 * pci_bus_add_device() has already saved the device's state. Refresh
+	 * the saved ACS Control register so that a subsequent reset restores
+	 * the controls programmed here rather than the ones left behind by
+	 * firmware.
+	 */
+	pci_save_acs_state(dev);
 }
 
 /**
@@ -1800,6 +1858,7 @@ int pci_save_state(struct pci_dev *dev)
 	pci_save_aer_state(dev);
 	pci_save_ptm_state(dev);
 	pci_save_tph_state(dev);
+	pci_save_acs_state(dev);
 	return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1877,7 +1936,7 @@ void pci_restore_state(struct pci_dev *dev)
 	pci_restore_msi_state(dev);
 
 	/* Restore ACS and IOV configuration state */
-	pci_enable_acs(dev);
+	pci_restore_acs_state(dev);
 	pci_restore_iov_state(dev);
 
 	dev->state_saved = false;
@@ -3532,6 +3591,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
 	if (error)
 		pci_err(dev, "unable to allocate suspend buffer for LTR\n");
 
+	error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ACS,
+					    sizeof(u16));
+	if (error)
+		pci_err(dev, "unable to allocate suspend buffer for ACS\n");
+
 	pci_allocate_vc_save_buffers(dev);
 }
 
drivers/pci/pci.h +5 -0
index ba3c3fddddc2..037c1674f164 100644
@@ -1095,6 +1095,7 @@ void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+bool pci_need_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
 int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
 void pci_disable_broken_acs_cap(struct pci_dev *pdev);
@@ -1105,6 +1106,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
 	return -ENOTTY;
 }
+static inline bool pci_need_dev_specific_enable_acs(struct pci_dev *dev)
+{
+	return false;
+}
 static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
 	return -ENOTTY;
drivers/pci/quirks.c +7 -0
index 7aee30734303..e500c202d2ec 100644
@@ -5476,6 +5476,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);
#54 lore raw

PATCH v8 9/12 PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges

In reply to David Matlack

Adopt the ARI Forwarding Enable bit 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 | 30 ++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h |  6 ++++++
 drivers/pci/pci.c        |  8 +++++++-
 3 files changed, 43 insertions(+), 1 deletion(-)
drivers/pci/liveupdate.c +30 -0
index fb602dd3933e..744513ee11ed 100644
@@ -128,6 +128,10 @@
  *    way after Live Update and ensures that IOMMU groups do not change. Note
  *    that a device will use its adopted ACS controls for the lifetime of its
  *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
+ *
+ *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
+ *    preserved devices to ensure that all preserved devices on the bridge's
+ *    secondary bus are addressable after the Live Update.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -833,6 +837,32 @@ int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
 	return 0;
 }
 
+/**
+ * pci_liveupdate_adopt_ari() - Adopt ARI configuration
+ * @dev: The PCI device to adopt ARI configuration for
+ *
+ * For devices preserved across a Live Update, read the ARI state from
+ * hardware and adopt it. This ensures the device continues to use the ARI
+ * configuration established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved.
+ */
+int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+
+	/* Safe to modify dev->ari_enabled bitfield during enumeration. */
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check
drivers/pci/liveupdate.h +6 -0
index ac5b8bf2edf5..ccc23031e99d 100644
@@ -18,6 +18,7 @@ bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
 void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
+int pci_liveupdate_adopt_ari(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -45,6 +46,11 @@ static inline int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev
 {
 	return -EINVAL;
 }
+
+static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +7 -1
index 22001bdf4c97..6922f361a89e 100644
@@ -3528,7 +3528,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;
@@ -3539,6 +3539,12 @@ void pci_configure_ari(struct pci_dev *dev)
 	if (!(cap & PCI_EXP_DEVCAP2_ARI))
 		return;
 
+	if (!pci_liveupdate_adopt_ari(bridge))
+		return;
+
+	if (pcie_ari_disabled)
+		return;
+
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
 		pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
 					 PCI_EXP_DEVCTL2_ARI);
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#55 lore raw

Re: [PATCH v8 09/12] PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 6 lines

Adopt the ARI Forwarding Enable bit 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.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
Reviewed-by on v8 9/12
#56 lore raw

Re: [PATCH v8 09/12] PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges

In reply to David Matlack
Quoted 6 lines · David Matlack

Adopt the ARI Forwarding Enable bit 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.

Thanks,
Praan

Reviewed-by on v8 9/12
#57 lore raw

Re: [PATCH v8 09/12] PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges

In reply to David Matlack
Quoted 6 lines · David Matlack

Adopt the ARI Forwarding Enable bit 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 107 lines
---
 drivers/pci/liveupdate.c | 30 ++++++++++++++++++++++++++++++
 drivers/pci/liveupdate.h |  6 ++++++
 drivers/pci/pci.c        |  8 +++++++-
 3 files changed, 43 insertions(+), 1 deletion(-)
drivers/pci/liveupdate.c +30 -0
index fb602dd3933e..744513ee11ed 100644
@@ -128,6 +128,10 @@
  *    way after Live Update and ensures that IOMMU groups do not change. Note
  *    that a device will use its adopted ACS controls for the lifetime of its
  *    struct pci_dev (i.e. even after pci_liveupdate_finish()).
+ *
+ *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
+ *    preserved devices to ensure that all preserved devices on the bridge's
+ *    secondary bus are addressable after the Live Update.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -833,6 +837,32 @@ int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev)
 	return 0;
 }
 
+/**
+ * pci_liveupdate_adopt_ari() - Adopt ARI configuration
+ * @dev: The PCI device to adopt ARI configuration for
+ *
+ * For devices preserved across a Live Update, read the ARI state from
+ * hardware and adopt it. This ensures the device continues to use the ARI
+ * configuration established by the previous kernel.
+ *
+ * Return: 0 on success, or -EINVAL if the device was not preserved.
+ */
+int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	u16 val;
+
+	guard(rwsem_read)(&pci_liveupdate.rwsem);
+
+	if (!dev->liveupdate.incoming)
+		return -EINVAL;
+
+	pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &val);
+
+	/* Safe to modify dev->ari_enabled bitfield during enumeration. */
+	dev->ari_enabled = !!(val & PCI_EXP_DEVCTL2_ARI);
+	return 0;
+}
+
 /**
  * pci_liveupdate_is_incoming() - Check if a device is incoming-preserved
  * @dev: The PCI device to check
drivers/pci/liveupdate.h +6 -0
index ac5b8bf2edf5..ccc23031e99d 100644
@@ -18,6 +18,7 @@ bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
 void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
+int pci_liveupdate_adopt_ari(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -45,6 +46,11 @@ static inline int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev
 {
 	return -EINVAL;
 }
+
+static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
+{
+	return -EINVAL;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci.c +7 -1
index 22001bdf4c97..6922f361a89e 100644
@@ -3528,7 +3528,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;
@@ -3539,6 +3539,12 @@ void pci_configure_ari(struct pci_dev *dev)
 	if (!(cap & PCI_EXP_DEVCAP2_ARI))
 		return;
 
+	if (!pci_liveupdate_adopt_ari(bridge))
+		return;
+
+	if (pcie_ari_disabled)
+		return;
+
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
 		pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
 					 PCI_EXP_DEVCTL2_ARI);
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#58 lore raw

PATCH v8 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       |  4 ++++
 drivers/pci/pci-driver.c       |  2 ++
 include/linux/pci_liveupdate.h |  3 +++
 4 files changed, 25 insertions(+)
drivers/pci/liveupdate.c +16 -0
index 744513ee11ed..65bcecb1b64e 100644
@@ -394,6 +394,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
 {
 	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;
@@ -447,6 +452,11 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 		return -EINVAL;
 	}
 
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing) {
 		if (!dev->liveupdate.outgoing->refcount) {
 			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
@@ -718,6 +728,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
 }
 
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = 1;
+}
+
 static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
 {
 	if (!dev->liveupdate.incoming) {
drivers/pci/liveupdate.h +4 -0
index ccc23031e99d..49d07fd02132 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_preserve_bus_numbers(struct pci_bus *bus,
 					 struct pci_dev *dev);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
@@ -28,6 +29,9 @@ static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 {
 }
 
+static inline void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+}
 static inline bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 						       struct pci_dev *dev)
 {
drivers/pci/pci-driver.c +2 -0
index f36778e62ac1..51616e4327d3 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 04a2b2a3102a..31898685a406 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 preserve_bus_numbers;
 	bool was_preserved;
+	bool frozen;
 };
 
 struct pci_dev;
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#59 lore raw

Re: [PATCH v8 10/12] PCI: liveupdate: Freeze preservation status during shutdown

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

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.

Quoted 8 lines

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

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
Reviewed-by on v8 10/12
#60 lore raw

Re: [PATCH v8 10/12] PCI: liveupdate: Freeze preservation status during shutdown

In reply to David Matlack
Quoted 13 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).

Quoted 7 lines

---

drivers/pci/liveupdate.c       | 16 ++++++++++++++++
drivers/pci/liveupdate.h       |  4 ++++
drivers/pci/pci-driver.c       |  2 ++
include/linux/pci_liveupdate.h |  3 +++
4 files changed, 25 insertions(+)
Reviewed-by on v8 10/12
#61 lore raw

Re: [PATCH v8 10/12] PCI: liveupdate: Freeze preservation status during shutdown

In reply to David Matlack
Quoted 9 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).

This calls pci_liveupdate_freeze() from pci_device_shutdown(), the pci_bus_type .shutdown() method, which I think is only called from device_shutdown() in the kexec or restart paths.

Since there's no error exit after we get as far as device_shutdown(), and this applies to all devices and dev->liveupdate.frozen is never set to false (at least at this point in the series), is there benefit to tracking this per-device?

Quoted 3 lines
Quoted 36 lines
---
 drivers/pci/liveupdate.c       | 16 ++++++++++++++++
 drivers/pci/liveupdate.h       |  4 ++++
 drivers/pci/pci-driver.c       |  2 ++
 include/linux/pci_liveupdate.h |  3 +++
 4 files changed, 25 insertions(+)
drivers/pci/liveupdate.c +10 -0
index 744513ee11ed..65bcecb1b64e 100644
@@ -394,6 +394,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
 {
 	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;
@@ -447,6 +452,11 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 		return -EINVAL;
 	}
 
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing) {
 		if (!dev->liveupdate.outgoing->refcount) {
 			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");

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

Quoted 7 lines
drivers/pci/liveupdate.c +4 -0
@@ -718,6 +728,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
 }
 
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = 1;

Nit, the series assigns "true" to other booleans
(preserve_bus_numbers, was_preserved).

Quoted 71 lines
drivers/pci/liveupdate.c +2 -0
+}
+
 static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev *dev)
 {
 	if (!dev->liveupdate.incoming) {
drivers/pci/liveupdate.h +4 -0
index ccc23031e99d..49d07fd02132 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_preserve_bus_numbers(struct pci_bus *bus,
 					 struct pci_dev *dev);
 void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
@@ -28,6 +29,9 @@ static inline void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 {
 }
 
+static inline void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+}
 static inline bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus,
 						       struct pci_dev *dev)
 {
drivers/pci/pci-driver.c +2 -0
index f36778e62ac1..51616e4327d3 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 04a2b2a3102a..31898685a406 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 preserve_bus_numbers;
 	bool was_preserved;
+	bool frozen;
 };
 
 struct pci_dev;
Signature · 1 line
2.55.0.487.gaf234c4eb3-goog
#62 lore raw

Re: [PATCH v8 10/12] PCI: liveupdate: Freeze preservation status during shutdown

Quoted 11 lines · Bjorn Helgaas
Quoted 9 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).

Quoted 8 lines

This calls pci_liveupdate_freeze() from pci_device_shutdown(), the pci_bus_type .shutdown() method, which I think is only called from device_shutdown() in the kexec or restart paths.

Since there's no error exit after we get as far as device_shutdown(), and this applies to all devices and dev->liveupdate.frozen is never set to false (at least at this point in the series), is there benefit to tracking this per-device?

I think this could be global. But I don't see any obvious place to add that hook and keeping it per device is one less global variable to worry about.

Quoted 45 lines
Quoted 3 lines
Quoted 39 lines
---
 drivers/pci/liveupdate.c       | 16 ++++++++++++++++
 drivers/pci/liveupdate.h       |  4 ++++
 drivers/pci/pci-driver.c       |  2 ++
 include/linux/pci_liveupdate.h |  3 +++
 4 files changed, 25 insertions(+)
drivers/pci/liveupdate.c +10 -0
index 744513ee11ed..65bcecb1b64e 100644
@@ -394,6 +394,11 @@ static int pci_liveupdate_unpreserve_device(struct pci_flb_outgoing *outgoing,
 {
 	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;
@@ -447,6 +452,11 @@ static int pci_liveupdate_preserve_device(struct pci_flb_outgoing *outgoing,
 		return -EINVAL;
 	}
 
+	if (dev->liveupdate.frozen) {
+		pci_warn(dev, "Cannot preserve device after it is frozen!\n");
+		return -EINVAL;
+	}
+
 	if (dev->liveupdate.outgoing) {
 		if (!dev->liveupdate.outgoing->refcount) {
 			pci_WARN(dev, 1, "Preserved device with 0 refcount!\n");
@@ -718,6 +728,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev)
 		pci_WARN(dev, 1, "Destroying incoming-preserved device!\n");
 }
 
Quoted 7 lines

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

Quoted 4 lines
drivers/pci/liveupdate.c +4 -0
+void pci_liveupdate_freeze(struct pci_dev *dev)
+{
+	guard(rwsem_write)(&pci_liveupdate.rwsem);
+	dev->liveupdate.frozen = 1;

Nit, the series assigns "true" to other booleans
(preserve_bus_numbers, was_preserved).

Ack, will fix.

#63 lore raw

PATCH v8 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 65bcecb1b64e..32b73ebf71f7 100644
@@ -132,6 +132,10 @@
  *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
  *    preserved devices to ensure that all preserved devices on the bridge's
  *    secondary bus are addressable after the Live Update.
+ *
+ *  * The PCI core does not disable bus mastering on outgoing preserved devices
+ *    during kexec. This allows preserved devices to issue memory transactions
+ *    throughout the Live Update.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -879,6 +883,13 @@ int pci_liveupdate_adopt_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 49d07fd02132..9c31fb60207d 100644
@@ -20,6 +20,7 @@ void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
 void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_adopt_ari(struct pci_dev *dev);
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -55,6 +56,11 @@ static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
 {
 	return -EINVAL;
 }
+
+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	return false;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci-driver.c +5 -2
index 51616e4327d3..47cb3dcaa927 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.55.0.487.gaf234c4eb3-goog
#64 lore raw

Re: [PATCH v8 11/12] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines

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

Quoted 3 lines

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=11
Reviewed-by on v8 11/12
#65 lore raw

Re: [PATCH v8 11/12] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

In reply to David Matlack
Quoted 15 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).

Quoted 9 lines

---

drivers/pci/liveupdate.c | 11 +++++++++++
drivers/pci/liveupdate.h |  6 ++++++
drivers/pci/pci-driver.c |  7 +++++--
3 files changed, 22 insertions(+), 2 deletions(-)
Reviewed-by on v8 11/12
#66 lore raw

Re: [PATCH v8 11/12] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec

In reply to David Matlack
Quoted 15 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).

Quoted 3 lines
Quoted 83 lines
---
 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 65bcecb1b64e..32b73ebf71f7 100644
@@ -132,6 +132,10 @@
  *  * The PCI core adopts ARI Forwarding Enable on all bridges with downstream
  *    preserved devices to ensure that all preserved devices on the bridge's
  *    secondary bus are addressable after the Live Update.
+ *
+ *  * The PCI core does not disable bus mastering on outgoing preserved devices
+ *    during kexec. This allows preserved devices to issue memory transactions
+ *    throughout the Live Update.
  */
 
 #define pr_fmt(fmt) "PCI: liveupdate: " fmt
@@ -879,6 +883,13 @@ int pci_liveupdate_adopt_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 49d07fd02132..9c31fb60207d 100644
@@ -20,6 +20,7 @@ void pci_liveupdate_scan_bridge_end(struct pci_dev *dev);
 void pci_liveupdate_cache_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_enable_adopted_acs_controls(struct pci_dev *dev);
 int pci_liveupdate_adopt_ari(struct pci_dev *dev);
+bool pci_liveupdate_is_outgoing(struct pci_dev *dev);
 #else
 static inline void pci_liveupdate_setup_device(struct pci_dev *dev)
 {
@@ -55,6 +56,11 @@ static inline int pci_liveupdate_adopt_ari(struct pci_dev *dev)
 {
 	return -EINVAL;
 }
+
+static inline bool pci_liveupdate_is_outgoing(struct pci_dev *dev)
+{
+	return false;
+}
 #endif
 
 #endif /* DRIVERS_PCI_LIVEUPDATE_H */
drivers/pci/pci-driver.c +5 -2
index 51616e4327d3..47cb3dcaa927 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.55.0.487.gaf234c4eb3-goog
#67 lore raw

PATCH v8 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      | 35 +++++++++++++++++++++++++++
 Documentation/core-api/liveupdate.rst |  1 +
 MAINTAINERS                           |  1 +
 4 files changed, 38 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 +35 -0
new file mode 100644
index 000000000000..96b1d7f5df3a
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+PCI Support for Live Update
+===========================
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :doc: PCI Live Update
+
+Driver API
+==========
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :export:
+
+Internal API
+============
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :internal:
+
+Live Update ABI
+===============
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :internal:
+
+See Also
+========
+
+ * :doc:`/core-api/liveupdate`
+ * :doc:`/core-api/kho/index`
Documentation/core-api/liveupdate.rst +1 -0
index b3c689e633c1..2bce2644eba2 100644
@@ -74,3 +74,4 @@ See Also
 
 - :doc:`Live Update uAPI </userspace-api/liveupdate>`
 - :doc:`/core-api/kho/index`
+- :doc:`PCI </PCI/liveupdate>`
MAINTAINERS +1 -0
index 08a724b860dc..347c435ca404 100644
@@ -20833,6 +20833,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.55.0.487.gaf234c4eb3-goog
#68 lore raw

Re: [PATCH v8 12/12] Documentation: PCI: Add documentation for Live Update

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 11 lines

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

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=12
Reviewed-by on v8 12/12
#69 lore raw

Re: [PATCH v8 12/12] Documentation: PCI: Add documentation for Live Update

In reply to David Matlack
Quoted 11 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 8 lines

---

Documentation/PCI/index.rst           |  1 +
Documentation/PCI/liveupdate.rst      | 35 +++++++++++++++++++++++++++
Documentation/core-api/liveupdate.rst |  1 +
MAINTAINERS                           |  1 +
4 files changed, 38 insertions(+)

create mode 100644 Documentation/PCI/liveupdate.rst

Reviewed-by on v8 12/12
#70 lore raw

Re: [PATCH v8 12/12] Documentation: PCI: Add documentation for Live Update

In reply to David Matlack
Quoted 6 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

Most uses fully hyphenate this: "File-Lifecycle-Bound" data, object, etc.

Quoted 5 lines

applied to preserved devices (such as bus number inheritance and bus mastering preservation).

Quoted 39 lines
---
 Documentation/PCI/index.rst           |  1 +
 Documentation/PCI/liveupdate.rst      | 35 +++++++++++++++++++++++++++
 Documentation/core-api/liveupdate.rst |  1 +
 MAINTAINERS                           |  1 +
 4 files changed, 38 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 +16 -0
new file mode 100644
index 000000000000..96b1d7f5df3a
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+PCI Support for Live Update
+===========================
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :doc: PCI Live Update
+
+Driver API
+==========
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :export:
+
+Internal API

Documentation/PCI/liveupdate.rst line 26

Quoted 10 lines
Documentation/PCI/liveupdate.rst new file +10 -0
+============
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :internal:
+
+Live Update ABI
+===============
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI

Ditto (and in include/linux/kho/abi/pci.h itself).

Trying to understand the FLB concept, I found kernel/liveupdate/luo_flb.c. I know that's already merged so this isn't really the place to ask about it. But FWIW here are some questions from this naive reader:

File-Lifecycle-Bound (FLB) objects provide a mechanism for managing global state that is shared across multiple live-updatable files. The lifecycle of this shared state is tied to the preservation of the files that depend on it.

I understand "global state", but I don't know whether "global" is relevant here.

I don't know what "shared across live-updatable files" means. Is the sharing a fundamental aspect or just a typical use case reflecting the level the data is for (e.g., bus vs device)?

I'm imagining a *kernel* being "live-updated", i.e., a kernel being updated while things around it (devices, some user-space things) stay alive, so I guess "live-updatable files" would be preserved across a kexec? I don't think of devices as being "live-updated" since they themselves aren't being updated; in fact, the whole point is that they *aren't* updated.

Do these FLB objects appear in a filesystem? Or are they merely blobs of data that are preserved across kexec? I suppose there must be a mechanism for the new kernel to identify and request one of the several FLB objects saved by the pre-kexec kernel?

What does "lifecycle is tied to preservation of files" mean? I expected to learn about the beginning and end of the object lifetime.

An FLB represents a global resource, such as the IOMMU core state, that is required by multiple file descriptors (e.g., all VFIO fds).

I have the impression that the important thing about FLB is the lifetime of some data, e.g., something that lasts longer than the kernel that produced it.

The preservation of the FLB's state is triggered when the *first* file depending on it is preserved. The cleanup of this state (unpreserve or finish) is triggered when the *last* file depending on it is unpreserved or finished.

Maybe this means .unpreserve() (in pre-kexec kernel) or .finish() (in new post-kexec kernel) is the end of an FLB object lifetime?

Quoted 32 lines
Documentation/PCI/liveupdate.rst new file +8 -0
+.. 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 b3c689e633c1..2bce2644eba2 100644
@@ -74,3 +74,4 @@ See Also
 
 - :doc:`Live Update uAPI </userspace-api/liveupdate>`
 - :doc:`/core-api/kho/index`
+- :doc:`PCI </PCI/liveupdate>`
MAINTAINERS +1 -0
index 08a724b860dc..347c435ca404 100644
@@ -20833,6 +20833,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.55.0.487.gaf234c4eb3-goog
#71 lore raw

Re: [PATCH v8 12/12] Documentation: PCI: Add documentation for Live Update

Quoted 10 lines · Bjorn Helgaas
Quoted 6 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

Most uses fully hyphenate this: "File-Lifecycle-Bound" data, object, etc.

Ack, will fix.

Quoted 71 lines
Quoted 6 lines

applied to preserved devices (such as bus number inheritance and bus mastering preservation).

Quoted 39 lines
---
 Documentation/PCI/index.rst           |  1 +
 Documentation/PCI/liveupdate.rst      | 35 +++++++++++++++++++++++++++
 Documentation/core-api/liveupdate.rst |  1 +
 MAINTAINERS                           |  1 +
 4 files changed, 38 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 +16 -0
new file mode 100644
index 000000000000..96b1d7f5df3a
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+PCI Support for Live Update
+===========================
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :doc: PCI Live Update
+
+Driver API
+==========
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :export:
+
+Internal API

Documentation/PCI/liveupdate.rst line 26

Quoted 10 lines
Documentation/PCI/liveupdate.rst new file +10 -0
+============
+
+.. kernel-doc:: drivers/pci/liveupdate.c
+   :internal:
+
+Live Update ABI
+===============
+
+.. kernel-doc:: include/linux/kho/abi/pci.h
+   :doc: PCI File-Lifecycle Bound (FLB) Live Update ABI

Ditto (and in include/linux/kho/abi/pci.h itself).

Trying to understand the FLB concept, I found kernel/liveupdate/luo_flb.c. I know that's already merged so this isn't really the place to ask about it. But FWIW here are some questions from this naive reader:

File-Lifecycle-Bound (FLB) objects provide a mechanism for managing global state that is shared across multiple live-updatable files. The lifecycle of this shared state is tied to the preservation of the files that depend on it.

Quoted 6 lines

I understand "global state", but I don't know whether "global" is relevant here.

I don't know what "shared across live-updatable files" means. Is the sharing a fundamental aspect or just a typical use case reflecting the level the data is for (e.g., bus vs device)?

LUO is fundamentally a system for preserving files across a kexec. FLB solves 2 related but independent problems that arise when trying to preserve files:

1. For certain types of files, there is some data that needs to be preserved that is not necessarily tied to one specific file. Instead it is shared across multiple files. FLB gives you a way to allocate and preserve such shared data. The association between FLB and file handlers tells LUO which types of files require which FLBs to be allocated.

2. For certain types of files, the next kernel needs information about preserved state before userspace has come up and is ready to restore the preserved files. FLB solves this by existing separate from the preserved files and thus can be fetched during early boot.

For the PCI core we use FLB to solve both these problems.

1. The files userspace is preserving are VFIO device files that correspond with endpoint PCI devices. Bridges can be shared across multiple endpoints so the PCI FLB gives us a way to track those independent of the VFIO endpoint state that is inside the file.

2. The PCI core needs to know during enumeration which devices were preserved by the previous kernel so that it can set up those devices differently, e.g., adopt ACS controls instead of programming new ones. The PCI FLB contains the full list of preserved devices, making this possible.

Quoted 6 lines

I'm imagining a *kernel* being "live-updated", i.e., a kernel being updated while things around it (devices, some user-space things) stay alive, so I guess "live-updatable files" would be preserved across a kexec? I don't think of devices as being "live-updated" since they themselves aren't being updated; in fact, the whole point is that they *aren't* updated.

Yeah I think live-updatable is not an accurate way to describe these files. I would say "preserved files" instead.

Quoted 4 lines

Do these FLB objects appear in a filesystem? Or are they merely blobs of data that are preserved across kexec? I suppose there must be a mechanism for the new kernel to identify and request one of the several FLB objects saved by the pre-kexec kernel?

They are not exposed anywhere in the filesystem. They are blobs of data (e.g. struct pci_ser) that the kernel hands off to the next kernel across the kexec. To request a specific incoming FLB (i.e., FLB passed by the previous kernel) liveupdate_flb_get_incoming(). Strings are used to uniquely identify an FLB. PCI_LUO_FLB_COMPATIBLE ("pci") is the string used for the PCI core's FLB.

Quoted 2 lines

What does "lifecycle is tied to preservation of files" mean? I expected to learn about the beginning and end of the object lifetime.

Specifically it means:

- Just before the first file goes through liveupdate_file_ops::preserve(), the outgoing FLB is allocated (liveupdate_flb_ops::preserve()).

- Just after the last file goes through liveupdate_file_ops::unpreserve(), the outgoing FLB is freed (liveupdate_flb_ops::unpreserve()).

- Just after the last file goes through liveupdate_file_ops::finish(), the incoming FLB is freed (liveupdate_flb_ops::finish()).

Quoted 6 lines

An FLB represents a global resource, such as the IOMMU core state, that is required by multiple file descriptors (e.g., all VFIO fds).

I have the impression that the important thing about FLB is the lifetime of some data, e.g., something that lasts longer than the kernel that produced it.

I think that is fair way to think about it.

Quoted 7 lines

The preservation of the FLB's state is triggered when the *first* file depending on it is preserved. The cleanup of this state (unpreserve or finish) is triggered when the *last* file depending on it is unpreserved or finished.

Maybe this means .unpreserve() (in pre-kexec kernel) or .finish() (in new post-kexec kernel) is the end of an FLB object lifetime?

Hopefully my comment about explains this more clearly.

Quoted 33 lines
Quoted 32 lines
Documentation/PCI/liveupdate.rst new file +8 -0
+.. 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 b3c689e633c1..2bce2644eba2 100644
@@ -74,3 +74,4 @@ See Also
 
 - :doc:`Live Update uAPI </userspace-api/liveupdate>`
 - :doc:`/core-api/kho/index`
+- :doc:`PCI </PCI/liveupdate>`
MAINTAINERS +1 -0
index 08a724b860dc..347c435ca404 100644
@@ -20833,6 +20833,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.55.0.487.gaf234c4eb3-goog
#72 lore raw
In reply to David Matlack

On Tue, Jul 28, 2026 at 3:10 PM David Matlack <[email protected]> wrote:
...

Quoted 15 lines

Merge Plan
----------

I propose we merge this series through liveupdate.git for 2 reasons:

1. There are some LUO core changes around how FLBs are refcounted in the next branch of liveupdate.git that this series depends on.

2. We had previously agreed that the files covered by the PCI LIVE UPDATE maintainer entry would be merged through the liveupdate.git tree (which is the majority of this series), so this would be following that process.

This plan still needs to be Acked by Bjorn Helgaas as there are some PCI core changes in this series.

...

Quoted 6 lines

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: Preserve bus numbers during Live Update

Quoted 8 lines

PCI: liveupdate: Auto-preserve upstream bridges across Live Update
PCI: Refactor matching logic for pci_dev_acs_ops
PCI: liveupdate: Adopt ACS controls in incoming preserved devices
PCI: liveupdate: Adopt 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

Hi Bjorn,

Pasha and I discussed this series and are hoping to merge it into the liveupdate tree by the first week of September so that it has enough time to soak in linux-next before 7.4. I was wondering if you think that is a reasonable plan and if you have any lingering concerns.

Thanks,
--David

maintainer Applied
#73 lore raw
In reply to David Matlack
Quoted 8 lines · David Matlack

NOTE: I am sending v8 out a little earlier than I normally would since I will be AFK from July 29th (tomorrow) through August 6th and would like to post the changes made in response to v7 feedback before then. I will respond to any review feedback on v8 on August 7th or shortly after.

This series can be found on GitHub:

[...]

Applied,

https://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git/log/?h=lu-pci-preservation

Thanks.

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

        commit: acff3a35a49097424762f255afdaf2a122e926a3
[02/12] PCI: liveupdate: Track outgoing preserved PCI devices
        commit: ced0d433a98ccc33495a985580bfefb62d73b16c
[03/12] PCI: liveupdate: Track incoming preserved PCI devices
        commit: d96e637021905ee22ef75f3608b46287b3d44508
[04/12] PCI: liveupdate: Document driver binding responsibilities
        commit: 4d9305a6938a66ccc1154dc5fa5a8a9ac56eb549
[05/12] PCI: liveupdate: Preserve bus numbers during Live Update
        commit: 8db5cb4011d0ebde5fbadf1236dfcb23bb8ce007
[06/12] PCI: liveupdate: Auto-preserve upstream bridges across Live Update
        commit: a7709b31ea29cb7d65b75beb844e0b92a20d4ab0
[07/12] PCI: Refactor matching logic for pci_dev_acs_ops
        commit: d2cc005d11de82f352e1f30f7b9aa2a4c463dec0
[08/12] PCI: liveupdate: Adopt ACS controls in incoming preserved devices
        commit: bd5970669fa8a2c1bc04c3fa516a0667a090c604
[09/12] PCI: liveupdate: Adopt ARI Forwarding Enable on preserved bridges
        commit: 48928296f529d15ecf66c263f3cc5fc657894e26
[10/12] PCI: liveupdate: Freeze preservation status during shutdown
        commit: 09907e211d2d63cc74f5a00e5949866f1078da76
[11/12] PCI: liveupdate: Do not disable bus mastering on preserved devices during kexec
        commit: 7f44eb94b1a85d2827610726e43de5554795cbdc
[12/12] Documentation: PCI: Add documentation for Live Update
        commit: c08eff447b966b21511c997272612e9665b0bd6e

Best regards,

Signature · 1 line
Pasha Tatashin <[email protected]>