[RFC PATCH 12/15] iommufd-luo: Implement basic prepare/cancel/finish/retrieve using folios

Samiullah Khawaja
2025-09-28T19:07:59Z

From: YiFei Zhu <zhuyifei@google.com>

The actual serialization and de-serialization is implemented in
follow up commits.

- On prepare, a single folio is created and preserved to store
all the structs.
- On cancel, the folio is unpreserved and freed.
- On retrieve, the folio is restored, then an fd with anon_inode
is created, with data pointing to the folio.
- On finish, the folio is freed.

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
drivers/iommu/iommufd/iommufd_private.h | 12 +++
drivers/iommu/iommufd/liveupdate.c | 127 +++++++++++++++++++++++-
drivers/iommu/iommufd/main.c | 2 +-
3 files changed, 137 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index faf48ca9e555..dfa17bfc9933 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -19,6 +19,9 @@ struct iommu_domain;
struct iommu_group;
struct iommu_option;
struct iommufd_device;
+struct iommufd_lu;
+
+extern const struct file_operations iommufd_fops;

struct iommufd_sw_msi_map {
struct list_head sw_msi_item;
@@ -55,6 +58,10 @@ struct iommufd_ctx {
/* Compatibility with VFIO no iommu */
u8 no_iommu_mode;
struct iommufd_ioas *vfio_ioas;
+
+#ifdef CONFIG_LIVEUPDATE
+ struct iommufd_lu *lu;
+#endif
};

/* Entry for iommufd_ctx::mt_mmap */
@@ -703,6 +710,11 @@ iommufd_get_vdevice(struct iommufd_ctx *ictx, u32 id)
}

#ifdef CONFIG_LIVEUPDATE
+struct iommufd_lu {
+ /* Only valid in restore, for lifetime purposes */
+ struct folio *folio_lu;
+};
+
int iommufd_liveupdate_register_lufs(void);
int iommufd_liveupdate_unregister_lufs(void);
#else
diff --git a/drivers/iommu/iommufd/liveupdate.c b/drivers/iommu/iommufd/liveupdate.c
index 6d2a64966335..1bdd5a82af90 100644
--- a/drivers/iommu/iommufd/liveupdate.c
+++ b/drivers/iommu/iommufd/liveupdate.c
@@ -2,16 +2,52 @@

#define pr_fmt(fmt) "iommufd: " fmt

+#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/iommufd.h>
+#include <linux/kexec_handover.h>
#include <linux/liveupdate.h>
+#include <linux/mm.h>

#include "iommufd_private.h"

static int iommufd_liveupdate_prepare(struct liveupdate_file_handler *handler,
struct file *file, u64 *data)
{
- return -EOPNOTSUPP;
+ struct iommufd_ctx *ictx = iommufd_ctx_from_file(file);
+ struct iommufd_lu *iommufd_lu;
+ struct folio *folio_lu;
+ size_t serial_size;
+ int rc;
+
+ if (IS_ERR(ictx))
+ return PTR_ERR(ictx);
+
+ serial_size = sizeof(*iommufd_lu);
+
+ folio_lu = folio_alloc(GFP_KERNEL, get_order(serial_size));
+ if (!folio_lu) {
+ rc = -ENOMEM;
+ goto err_ctx_put;
+ }
+
+ iommufd_lu = folio_address(folio_lu);
+
+ rc = kho_preserve_folio(folio_lu);
+ if (rc)
+ goto err_folio_put;
+
+ *data = virt_to_phys(iommufd_lu);
+
+ iommufd_ctx_put(ictx);
+ return 0;
+
+err_folio_put:
+ folio_put(folio_lu);
+
+err_ctx_put:
+ iommufd_ctx_put(ictx);
+ return rc;
}

static int iommufd_liveupdate_freeze(struct liveupdate_file_handler *handler,
@@ -24,23 +60,108 @@ static int iommufd_liveupdate_freeze(struct liveupdate_file_handler *handler,
static void iommufd_liveupdate_cancel(struct liveupdate_file_handler *handler,
struct file *file, u64 data)
{
+ struct iommufd_ctx *ictx = iommufd_ctx_from_file(file);
+ struct folio *folio_lu;
+
+ if (WARN_ON(IS_ERR(ictx)))
+ return;
+
+ folio_lu = pfn_folio(PHYS_PFN(data));
+ WARN_ON(kho_unpreserve_folio(folio_lu));
+ folio_put(folio_lu);
+
+ iommufd_ctx_put(ictx);
}

static void iommufd_liveupdate_finish(struct liveupdate_file_handler *handler,
struct file *file, u64 data, bool reclaimed)
{
+ struct iommufd_lu *iommufd_lu;
+ struct iommufd_ctx *ictx;
+ struct folio *folio_lu;
+
+ if (!reclaimed || !file) {
+ pr_warn("%s: fd not reclaimed\n", __func__);
+
+ folio_lu = kho_restore_folio(data);
+ if (WARN_ON_ONCE(IS_ERR_OR_NULL(folio_lu)))
+ return;
+
+ iommufd_lu = folio_address(folio_lu);
+ } else {
+ ictx = iommufd_ctx_from_file(file);
+ iommufd_lu = ictx->lu;
+ ictx->lu = NULL;
+ iommufd_ctx_put(ictx);
+ }
+
+ folio_put(iommufd_lu->folio_lu);
}

static int iommufd_liveupdate_retrieve(struct liveupdate_file_handler *handler,
u64 data, struct file **file_p)
{
- return -EOPNOTSUPP;
+ struct iommufd_lu *iommufd_lu;
+ struct iommufd_ctx *ictx;
+ struct folio *folio_lu;
+ struct file *file;
+ int rc;
+
+ folio_lu = kho_restore_folio(data);
+ if (IS_ERR_OR_NULL(folio_lu))
+ return -EFAULT;
+
+ iommufd_lu = folio_address(folio_lu);
+ iommufd_lu->folio_lu = folio_lu;
+
+ file = anon_inode_create_getfile("iommufd", &iommufd_fops,
+ NULL, O_RDWR, NULL);
+ if (IS_ERR(file)) {
+ rc = PTR_ERR(file);
+ goto err_folio_put;
+ }
+
+ rc = iommufd_fops.open(file->f_inode, file);
+ if (rc)
+ goto err_fput;
+
+ ictx = iommufd_ctx_from_file(file);
+ if (WARN_ON(IS_ERR(ictx))) {
+ rc = PTR_ERR(ictx);
+ goto err_fput;
+ }
+
+ if (WARN_ON(ictx->lu)) {
+ rc = -EEXIST;
+ goto err_ctx_put;
+ }
+ ictx->lu = iommufd_lu;
+
+ iommufd_ctx_put(ictx);
+
+ *file_p = file;
+
+ return 0;
+
+err_ctx_put:
+ iommufd_ctx_put(ictx);
+err_fput:
+ fput(file);
+err_folio_put:
+ folio_put(folio_lu);
+ return rc;
}

static bool iommufd_liveupdate_can_preserve(struct liveupdate_file_handler *handler,
struct file *file)
{
- return false;
+ struct iommufd_ctx *ictx = iommufd_ctx_from_file(file);
+
+ if (IS_ERR(ictx))
+ return false;
+
+ iommufd_ctx_put(ictx);
+ return true;
}

static struct liveupdate_file_ops iommufd_lu_file_ops = {
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index b3bf65bc8da4..a8b6daaca11f 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -577,7 +577,7 @@ static int iommufd_fops_mmap(struct file *filp, struct vm_area_struct *vma)
return rc;
}

-static const struct file_operations iommufd_fops = {
+const struct file_operations iommufd_fops = {
.owner = THIS_MODULE,
.open = iommufd_fops_open,
.release = iommufd_fops_release,
--
2.51.0.536.g15c5d4f767-goog