[PATCH v4 08/11] PCI: liveupdate: Require preserved devices are in immutable singleton IOMMU groups

David Matlack
2026-04-23T21:23:35Z

Restrict support for preserving PCI devices across Live Update to
devices in immutable singleton IOMMU groups. A device's group is
considered immutable if all bridges upstream from the device up to the
root port have the required ACS features enabled.

Since ACS flags are inherited across a Live Update for preserved devices
and all the way up to the root port, the preserved device should be in a
singleton IOMMU group after kexec in the new kernel.

This change should still permit all the current use-cases for PCI device
preservation across Live Update, since it is intended to be used in
Cloud enviroments which should have the required ACS features enabled
for virtualization purposes.

If a device is part of a multi-device IOMMU group, preserving it will
now fail with an error. This restriction may be lifted in the future if
support for preserving multi-device groups is desired.

Signed-off-by: David Matlack <dmatlack@google.com>
---
drivers/iommu/iommu.c | 35 +++++++++++++++++++++++++++++++++++
drivers/pci/liveupdate.c | 6 ++++++
include/linux/iommu.h | 7 +++++++
3 files changed, 48 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 61c12ba78206..782e73a9d45f 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1664,6 +1664,41 @@ struct iommu_group *pci_device_group(struct device *dev)
}
EXPORT_SYMBOL_GPL(pci_device_group);

+bool pci_device_group_immutable_singleton(struct pci_dev *dev)
+{
+ struct iommu_group *group;
+ struct group_device *d;
+ struct pci_bus *bus;
+ int nr_devices = 0;
+
+ group = iommu_group_get(&dev->dev);
+ if (!group)
+ return false;
+
+ mutex_lock(&group->mutex);
+
+ for_each_group_device(group, d)
+ nr_devices++;
+
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ if (nr_devices != 1)
+ return false;
+
+ for (bus = dev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
+ if (!bus->self)
+ continue;
+
+ if (!pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
/* Get the IOMMU group for device on fsl-mc bus */
struct iommu_group *fsl_mc_device_group(struct device *dev)
{
diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c
index a9a89f7bd3e5..54a90ff02bdd 100644
--- a/drivers/pci/liveupdate.c
+++ b/drivers/pci/liveupdate.c
@@ -133,6 +133,7 @@
#define pr_fmt(fmt) "PCI: liveupdate: " fmt

#include <linux/io.h>
+#include <linux/iommu.h>
#include <linux/kexec_handover.h>
#include <linux/kho/abi/pci.h>
#include <linux/liveupdate.h>
@@ -359,6 +360,11 @@ int pci_liveupdate_preserve(struct pci_dev *dev)
if (dev->is_virtfn)
return -EINVAL;

+ if (!pci_device_group_immutable_singleton(dev)) {
+ pci_warn(dev, "Device preservation limited to immutable singleton iommu groups\n");
+ return -EINVAL;
+ }
+
if (dev->liveupdate_outgoing)
return -EBUSY;

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e587d4ac4d33..6f5d1dec3f89 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1096,6 +1096,8 @@ extern struct iommu_group *generic_device_group(struct device *dev);
struct iommu_group *fsl_mc_device_group(struct device *dev);
extern struct iommu_group *generic_single_device_group(struct device *dev);

+bool pci_device_group_immutable_singleton(struct pci_dev *dev);
+
/**
* struct iommu_fwspec - per-device IOMMU instance data
* @iommu_fwnode: firmware handle for this device's IOMMU
@@ -1528,6 +1530,11 @@ static inline int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
{
}
+
+static inline bool pci_device_group_immutable_singleton(struct pci_dev *dev)
+{
+ return false;
+}
#endif /* CONFIG_IOMMU_API */

#ifdef CONFIG_IRQ_MSI_IOMMU