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

Jacob Pan
2026-04-27T20:56:29Z

Hi David,

On Thu, 23 Apr 2026 21:23:12 +0000
David Matlack <dmatlack@google.com> wrote:

> 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;
This may not work for noiommu mode where we could have a null group.
Maybe I can add special case later?

> + mutex_lock(&group->mutex);
> +
> + for_each_group_device(group, d)
> + nr_devices++;
> +
> + mutex_unlock(&group->mutex);
> + iommu_group_put(group);
nit:
scoped_guard(mutex, &group->mutex) {
for_each_group_device(group, d)
nr_devices++;
}
> +
> + 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