@@ -233,6 +233,7 @@ int iommufd_ioas_map(struct iommufd_ucmd *ucmd)
mmap_read_unlock(mm);
return -EFAULT;
}
+ ioas->pinned_file_handle = guestmemfs_pin_file(vma->vm_file);
mmap_read_unlock(mm);
#else
return -EFAULT;
@@ -331,6 +332,9 @@ int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd)
&unmapped);
if (rc)
goto out_put;
+
+ if (ioas->pinned_file_handle)
+ guestmemfs_unpin_file(ioas->pinned_file_handle);
}
cmd->length = unmapped;
@@ -260,12 +260,17 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,
* An iommu_domain & iommfd_hw_pagetable will be automatically selected
* for a device based on the hwpt_list. If no suitable iommu_domain
* is found a new iommu_domain will be created.
+ *
+ * If this IOAS is pinning a file for persistent DMA, pinned_file_handle will
+ * be set to a non-zero value. When unmapping this IOAS the file will be
+ * unpinned.
*/
struct iommufd_ioas {
struct iommufd_object obj;
struct io_pagetable iopt;
struct mutex mutex;
struct list_head hwpt_list;
+ unsigned long pinned_file_handle;
};
static inline struct iommufd_ioas *iommufd_get_ioas(struct iommufd_ctx *ictx,
@@ -16,6 +16,7 @@
* account_mode = u8
* ioases = [
* {
+ * pinned_file_handle = u64
* areas = [
* ]
* }
@@ -48,6 +49,9 @@ static int serialise_iommufd(void *fdt, struct iommufd_ctx *ictx)
snprintf(name, sizeof(name), "%lu", obj_idx);
err |= fdt_begin_node(fdt, name);
+ err |= fdt_property(fdt, "pinned-file-handle",
+ &ioas->pinned_file_handle, sizeof(ioas->pinned_file_handle));
+
for (area = iopt_area_iter_first(&ioas->iopt, 0, ULONG_MAX); area;
area = iopt_area_iter_next(area, 0, ULONG_MAX)) {
unsigned long iova_start, iova_len;
@@ -119,15 +123,18 @@ static int rehydrate_iommufd(char *iommufd_name)
snprintf(kho_path, sizeof(kho_path), "/iommufd/iommufds/%s/ioases", iommufd_name);
fdt_for_each_subnode(off, fdt, fdt_path_offset(fdt, kho_path)) {
struct iommufd_ioas *ioas;
+ int len;
int range_off;
+ const unsigned long *pinned_file_handle;
ioas = iommufd_ioas_alloc(ictx);
+ pinned_file_handle = fdt_getprop(fdt, off, "pinned-file-handle", &len);
+ ioas->pinned_file_handle = *pinned_file_handle;
iommufd_object_finalize(ictx, &ioas->obj);
fdt_for_each_subnode(range_off, fdt, off) {
const unsigned long *iova_start, *iova_len;
const int *iommu_prot;
- int len;
struct iopt_area *area = iopt_area_alloc();
iova_start = fdt_getprop(fdt, range_off, "iova-start", &len);
@@ -109,3 +109,23 @@ bool is_guestmemfs_file(struct file const *file)
{
return file && file->f_op == &guestmemfs_file_fops;
}
+
+unsigned long guestmemfs_pin_file(struct file *file)
+{
+ struct guestmemfs_inode *inode =
+ guestmemfs_get_persisted_inode(file->f_inode->i_sb,
+ file->f_inode->i_ino);
+
+ atomic_inc(&inode->long_term_pins);
+ return file->f_inode->i_ino;
+}
+
+void guestmemfs_unpin_file(unsigned long pin_handle)
+{
+ struct guestmemfs_inode *inode =
+ guestmemfs_get_persisted_inode(guestmemfs_sb, pin_handle);
+ int new;
+
+ new = atomic_dec_return(&inode->long_term_pins);
+ WARN_ON(new < 0);
+}
@@ -42,6 +42,7 @@ struct guestmemfs_inode {
char filename[GUESTMEMFS_FILENAME_LEN];
void *mappings;
int num_mappings;
+ atomic_t long_term_pins;
};
void guestmemfs_initialise_inode_store(struct super_block *sb);
@@ -151,6 +151,10 @@ static int guestmemfs_unlink(struct inode *dir, struct dentry *dentry)
ino = guestmemfs_get_persisted_inode(dir->i_sb, dir->i_ino)->child_ino;
+ inode = guestmemfs_get_persisted_inode(dir->i_sb, dentry->d_inode->i_ino);
+ if (atomic_read(&inode->long_term_pins))
+ return -EBUSY;
+
/* Special case for first file in dir */
if (ino == dentry->d_inode->i_ino) {
guestmemfs_get_persisted_inode(dir->i_sb, dir->i_ino)->child_ino =
@@ -20,4 +20,12 @@ inline bool is_guestmemfs_file(struct file const *filp)
}
#endif
+/*
+ * Ensure that the file cannot be deleted or have its memory changed
+ * until it is unpinned. The returned value is a handle which can be
+ * used to un-pin the file.
+ */
+unsigned long guestmemfs_pin_file(struct file *file);
+void guestmemfs_unpin_file(unsigned long pin_handle);
+
#endif