[RFC PATCH v2 27/32] iommu: restore preserved domain and reattach

From: Samiullah Khawaja

Date: Tue Dec 02 2025 - 18:08:18 EST


During boot the preserved iommu domains need to be recreated and
reattached to the preserved devices. Once the device is reattached to
the preserved domain, the preserved_state set in the dev_iommu can be
unset.

Signed-off-by: Samiullah Khawaja <skhawaja@xxxxxxxxxx>
---
drivers/iommu/iommu.c | 39 ++++++++++++++++++++++++++++++++++++--
drivers/iommu/liveupdate.c | 31 ++++++++++++++++++++++++++++++
include/linux/iommu-lu.h | 1 +
3 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 3feb440de40a..07453611cf8e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -18,6 +18,7 @@
#include <linux/errno.h>
#include <linux/host1x_context_bus.h>
#include <linux/iommu.h>
+#include <linux/iommu-lu.h>
#include <linux/iommufd.h>
#include <linux/idr.h>
#include <linux/err.h>
@@ -489,6 +490,10 @@ static int iommu_init_device(struct device *dev)
}
dev->iommu->iommu_dev = iommu_dev;

+#ifdef CONFIG_LIVEUPDATE
+ dev->iommu->device_ser = iommu_get_device_preserved_data(dev);
+#endif
+
ret = iommu_device_link(iommu_dev, dev);
if (ret)
goto err_release;
@@ -2161,6 +2166,12 @@ static int __iommu_attach_device(struct iommu_domain *domain,
if (old)
atomic_dec(&old->attach_count);

+#ifdef CONFIG_LIVEUPDATE
+ /* The associated state can be unset once restored. */
+ if (dev_iommu_restored_state(dev))
+ WRITE_ONCE(dev->iommu->device_ser, NULL);
+#endif
+
dev->iommu->attach_deferred = 0;
trace_attach_device_to_domain(dev);
return 0;
@@ -3006,6 +3017,27 @@ int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids)
}
EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);

+static struct iommu_domain *__iommu_group_maybe_restore_domain(struct iommu_group *group)
+{
+ struct device_ser *device_ser;
+ struct iommu_domain *domain;
+ struct device *dev;
+
+ dev = iommu_group_first_dev(group);
+ if (!dev_is_pci(dev))
+ return NULL;
+
+ device_ser = dev_iommu_restored_state(dev);
+ if (!device_ser)
+ return NULL;
+
+ domain = iommu_restore_domain(dev, device_ser);
+ if (WARN_ON(IS_ERR(domain)))
+ return NULL;
+
+ return domain;
+}
+
/**
* iommu_setup_default_domain - Set the default_domain for the group
* @group: Group to change
@@ -3020,8 +3052,8 @@ static int iommu_setup_default_domain(struct iommu_group *group,
int target_type)
{
struct iommu_domain *old_dom = group->default_domain;
+ struct iommu_domain *dom, *restored_domain;
struct group_device *gdev;
- struct iommu_domain *dom;
bool direct_failed;
int req_type;
int ret;
@@ -3065,6 +3097,9 @@ static int iommu_setup_default_domain(struct iommu_group *group,
/* We must set default_domain early for __iommu_device_set_domain */
group->default_domain = dom;
if (!group->domain) {
+ restored_domain = __iommu_group_maybe_restore_domain(group);
+ if (!restored_domain)
+ restored_domain = dom;
/*
* Drivers are not allowed to fail the first domain attach.
* The only way to recover from this is to fail attaching the
@@ -3072,7 +3107,7 @@ static int iommu_setup_default_domain(struct iommu_group *group,
* in group->default_domain so it is freed after.
*/
ret = __iommu_group_set_domain_internal(
- group, dom, IOMMU_SET_DOMAIN_MUST_SUCCEED);
+ group, restored_domain, IOMMU_SET_DOMAIN_MUST_SUCCEED);
if (WARN_ON(ret))
goto out_free_old;
} else {
diff --git a/drivers/iommu/liveupdate.c b/drivers/iommu/liveupdate.c
index 1ca97612c501..735b7d9417c7 100644
--- a/drivers/iommu/liveupdate.c
+++ b/drivers/iommu/liveupdate.c
@@ -467,3 +467,34 @@ int iommu_unpreserve_device(struct iommu_domain *domain, struct device *dev)
{
return -EOPNOTSUPP;
}
+
+struct iommu_domain *iommu_restore_domain(struct device *dev, struct device_ser *ser)
+{
+ struct iommu_domain_ser *domain_ser;
+ struct iommu_lu_flb_obj *flb_obj;
+ struct iommu_domain *domain;
+ int ret;
+
+ domain_ser = __va(ser->domain_iommu_ser.domain_phys);
+
+ ret = liveupdate_flb_get_incoming(&iommu_flb, (void **)&flb_obj);
+ if (ret)
+ return ERR_PTR(ret);
+
+ guard(mutex)(&flb_obj->lock);
+ if (domain_ser->restored_domain)
+ return domain_ser->restored_domain;
+
+ domain_ser->obj.incoming = true;
+ domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(domain))
+ return domain;
+
+ ret = domain->ops->restore(domain, domain_ser);
+ if (ret)
+ return ERR_PTR(ret);
+
+ domain->preserved_state = domain_ser;
+ domain_ser->restored_domain = domain;
+ return domain;
+}
diff --git a/include/linux/iommu-lu.h b/include/linux/iommu-lu.h
index d0226ec19b2f..1f90f4fb0328 100644
--- a/include/linux/iommu-lu.h
+++ b/include/linux/iommu-lu.h
@@ -78,6 +78,7 @@ static inline void *iommu_domain_restored_state(struct iommu_domain *domain)
}
#endif

+struct iommu_domain *iommu_restore_domain(struct device *dev, struct device_ser *ser);
int iommu_for_each_preserved_device(int (*fn)(struct device_ser *ser, void *arg), void *arg);
struct device_ser *iommu_get_device_preserved_data(struct device *dev);
struct iommu_ser *iommu_get_preserved_data(u64 token, enum iommu_lu_type type);
--
2.52.0.158.g65b55ccf14-goog