aio: Make it possible to remap aio ring
authorPavel Emelyanov <xemul@parallels.com>
Thu, 18 Sep 2014 15:56:17 +0000 (19:56 +0400)
committerBenjamin LaHaise <bcrl@kvack.org>
Sat, 13 Dec 2014 22:49:50 +0000 (17:49 -0500)
There are actually two issues this patch addresses. Let me start with
the one I tried to solve in the beginning.

So, in the checkpoint-restore project (criu) we try to dump tasks'
state and restore one back exactly as it was. One of the tasks' state
bits is rings set up with io_setup() call. There's (almost) no problems
in dumping them, there's a problem restoring them -- if I dump a task
with aio ring originally mapped at address A, I want to restore one
back at exactly the same address A. Unfortunately, the io_setup() does
not allow for that -- it mmaps the ring at whatever place mm finds
appropriate (it calls do_mmap_pgoff() with zero address and without
the MAP_FIXED flag).

To make restore possible I'm going to mremap() the freshly created ring
into the address A (under which it was seen before dump). The problem is
that the ring's virtual address is passed back to the user-space as the
context ID and this ID is then used as search key by all the other io_foo()
calls. Reworking this ID to be just some integer doesn't seem to work, as
this value is already used by libaio as a pointer using which this library
accesses memory for aio meta-data.

So, to make restore work we need to make sure that

a) ring is mapped at desired virtual address
b) kioctx->user_id matches this value

Having said that, the patch makes mremap() on aio region update the
kioctx's user_id and mmap_base values.

Here appears the 2nd issue I mentioned in the beginning of this mail.
If (regardless of the C/R dances I do) someone creates an io context
with io_setup(), then mremap()-s the ring and then destroys the context,
the kill_ioctx() routine will call munmap() on wrong (old) address.
This will result in a) aio ring remaining in memory and b) some other
vma get unexpectedly unmapped.

What do you think?

Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Acked-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
fs/aio.c
include/linux/fs.h
mm/mremap.c

index 14b93159ef83a140483bb31a4a6c70286d209ed8..bfab55607a4d57f19964d667800f1492ba8ab04f 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -286,12 +286,37 @@ static void aio_free_ring(struct kioctx *ctx)
 
 static int aio_ring_mmap(struct file *file, struct vm_area_struct *vma)
 {
+       vma->vm_flags |= VM_DONTEXPAND;
        vma->vm_ops = &generic_file_vm_ops;
        return 0;
 }
 
+static void aio_ring_remap(struct file *file, struct vm_area_struct *vma)
+{
+       struct mm_struct *mm = vma->vm_mm;
+       struct kioctx_table *table;
+       int i;
+
+       spin_lock(&mm->ioctx_lock);
+       rcu_read_lock();
+       table = rcu_dereference(mm->ioctx_table);
+       for (i = 0; i < table->nr; i++) {
+               struct kioctx *ctx;
+
+               ctx = table->table[i];
+               if (ctx && ctx->aio_ring_file == file) {
+                       ctx->user_id = ctx->mmap_base = vma->vm_start;
+                       break;
+               }
+       }
+
+       rcu_read_unlock();
+       spin_unlock(&mm->ioctx_lock);
+}
+
 static const struct file_operations aio_ring_fops = {
        .mmap = aio_ring_mmap,
+       .mremap = aio_ring_remap,
 };
 
 #if IS_ENABLED(CONFIG_MIGRATION)
index 9ab779e8a63ccd7785637dd9017fc3bc69ff260f..85f378c55c2674dd85e2ec72c0fc474022ef9e82 100644 (file)
@@ -1497,6 +1497,7 @@ struct file_operations {
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
+       void (*mremap)(struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
index b147f66f4c40f8a639def879df125f48e8c5595c..c855922497a385adb1ccb210b6dfee37b48fd92b 100644 (file)
@@ -288,7 +288,8 @@ static unsigned long move_vma(struct vm_area_struct *vma,
                old_len = new_len;
                old_addr = new_addr;
                new_addr = -ENOMEM;
-       }
+       } else if (vma->vm_file && vma->vm_file->f_op->mremap)
+               vma->vm_file->f_op->mremap(vma->vm_file, new_vma);
 
        /* Conceal VM_ACCOUNT so old reservation is not undone */
        if (vm_flags & VM_ACCOUNT) {