When IOASes and hardware page tables are made persistent then DMA will
continue accessing memory which is mapped for DMA during and after
kexec. This is only legal if we are sure that the memory being accessed
by that DMA is also persistent. It would not be legal to map normal
buddy-list managed anonymous memory for persistent DMA.
Currently there is one provider of persistent memory: guestmemfs:
https://lore.kernel.org/all/20240805093245.889357-1-jgowans@amazon.com/
This commit ensures that only guestmemfs memory can be mapped into
persistent iommufds. This is almost certainly the wrong way and place to
do it, but something similar to this is needed. Perhaps in page.c?
As more persistent memory providers become available they can be added
to the list to check for.
---
drivers/iommu/iommufd/ioas.c | 22 ++++++++++++++++++++++
fs/guestmemfs/file.c | 5 +++++
include/linux/guestmemfs.h | 7 +++++++
3 files changed, 34 insertions(+)
diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
index 742248276548..ce76b41d2d72 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -2,9 +2,11 @@
/*
* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
*/
+#include <linux/guestmemfs.h>
#include <linux/interval_tree.h>
#include <linux/iommufd.h>
#include <linux/iommu.h>
+#include <linux/mm_types.h>
#include <uapi/linux/iommufd.h>
#include "io_pagetable.h"
@@ -217,6 +219,26 @@ int iommufd_ioas_map(struct iommufd_ucmd *ucmd)
if (IS_ERR(ioas))
return PTR_ERR(ioas);
+ pr_info("iommufd_ioas_map persistent id %lu\n",
+ ucmd->ictx->persistent_id);
+ if (ucmd->ictx->persistent_id) {
+#ifdef CONFIG_GUESTMEMFS_FS
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+
+ mmap_read_lock(mm);
+ vma = find_vma_intersection(current->mm,
+ cmd->user_va, cmd->user_va + cmd->length);
+ if (!vma || !is_guestmemfs_file(vma->vm_file)) {
+ mmap_read_unlock(mm);
+ return -EFAULT;
+ }
+ mmap_read_unlock(mm);
+#else
+ return -EFAULT;
+#endif /* CONFIG_GUESTMEMFS_FS */
+ }
+
if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA))
flags = IOPT_ALLOC_IOVA;
rc = iopt_map_user_pages(ucmd->ictx, &ioas->iopt, &iova,
diff --git a/fs/guestmemfs/file.c b/fs/guestmemfs/file.c
index 8707a9d3ad90..ecacaf200a31 100644
--- a/fs/guestmemfs/file.c
+++ b/fs/guestmemfs/file.c
@@ -104,3 +104,8 @@ const struct file_operations guestmemfs_file_fops = {
.owner = THIS_MODULE,
.mmap = mmap,
};
+
+bool is_guestmemfs_file(struct file const *file)
+{
+ return file && file->f_op == &guestmemfs_file_fops;
+}
diff --git a/include/linux/guestmemfs.h b/include/linux/guestmemfs.h
index 60e769c8e533..c5cd7b6a5630 100644
--- a/include/linux/guestmemfs.h
+++ b/include/linux/guestmemfs.h
@@ -3,14 +3,21 @@
#ifndef _LINUX_GUESTMEMFS_H
#define _LINUX_GUESTMEMFS_H
+#include <linux/fs.h>
+
/*
* Carves out chunks of memory from memblocks for guestmemfs.
* Must be called in early boot before memblocks are freed.
*/
# ifdef CONFIG_GUESTMEMFS_FS
void guestmemfs_reserve_mem(void);
+bool is_guestmemfs_file(struct file const *filp);
#else
void guestmemfs_reserve_mem(void) { }
+inline bool is_guestmemfs_file(struct file const *filp)
+{
+ return 0;
+}
#endif
#endif
--
2.34.1