mm: rcu-protected get_mm_exe_file()
[linux-2.6-block.git] / fs / aio.c
index 435ca29eca31431649b4df125e933488b8d4073e..5785c4b58fea5ffdeae17d43b6b00df2572dc638 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -310,11 +310,11 @@ static int aio_ring_mmap(struct file *file, struct vm_area_struct *vma)
        return 0;
 }
 
-static void aio_ring_remap(struct file *file, struct vm_area_struct *vma)
+static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)
 {
        struct mm_struct *mm = vma->vm_mm;
        struct kioctx_table *table;
-       int i;
+       int i, res = -EINVAL;
 
        spin_lock(&mm->ioctx_lock);
        rcu_read_lock();
@@ -324,13 +324,17 @@ static void aio_ring_remap(struct file *file, struct vm_area_struct *vma)
 
                ctx = table->table[i];
                if (ctx && ctx->aio_ring_file == file) {
-                       ctx->user_id = ctx->mmap_base = vma->vm_start;
+                       if (!atomic_read(&ctx->dead)) {
+                               ctx->user_id = ctx->mmap_base = vma->vm_start;
+                               res = 0;
+                       }
                        break;
                }
        }
 
        rcu_read_unlock();
        spin_unlock(&mm->ioctx_lock);
+       return res;
 }
 
 static const struct file_operations aio_ring_fops = {
@@ -688,8 +692,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
        nr_events *= 2;
 
        /* Prevent overflows */
-       if ((nr_events > (0x10000000U / sizeof(struct io_event))) ||
-           (nr_events > (0x10000000U / sizeof(struct kiocb)))) {
+       if (nr_events > (0x10000000U / sizeof(struct io_event))) {
                pr_debug("ENOMEM: nr_events too high\n");
                return ERR_PTR(-EINVAL);
        }
@@ -760,6 +763,9 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 err_cleanup:
        aio_nr_sub(ctx->max_reqs);
 err_ctx:
+       atomic_set(&ctx->dead, 1);
+       if (ctx->mmap_size)
+               vm_munmap(ctx->mmap_base, ctx->mmap_size);
        aio_free_ring(ctx);
 err:
        mutex_unlock(&ctx->ring_lock);
@@ -781,11 +787,12 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx,
 {
        struct kioctx_table *table;
 
-       if (atomic_xchg(&ctx->dead, 1))
+       spin_lock(&mm->ioctx_lock);
+       if (atomic_xchg(&ctx->dead, 1)) {
+               spin_unlock(&mm->ioctx_lock);
                return -EINVAL;
+       }
 
-
-       spin_lock(&mm->ioctx_lock);
        table = rcu_dereference_raw(mm->ioctx_table);
        WARN_ON(ctx != table->table[ctx->id]);
        table->table[ctx->id] = NULL;
@@ -1348,52 +1355,21 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
        return -EINVAL;
 }
 
-typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *,
-                           unsigned long, loff_t);
 typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *);
 
-static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb,
-                                    int rw, char __user *buf,
-                                    unsigned long *nr_segs,
-                                    size_t *len,
-                                    struct iovec **iovec,
-                                    bool compat)
+static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len,
+                                struct iovec **iovec,
+                                bool compat,
+                                struct iov_iter *iter)
 {
-       ssize_t ret;
-
-       *nr_segs = *len;
-
 #ifdef CONFIG_COMPAT
        if (compat)
-               ret = compat_rw_copy_check_uvector(rw,
+               return compat_import_iovec(rw,
                                (struct compat_iovec __user *)buf,
-                               *nr_segs, UIO_FASTIOV, *iovec, iovec);
-       else
+                               len, UIO_FASTIOV, iovec, iter);
 #endif
-               ret = rw_copy_check_uvector(rw,
-                               (struct iovec __user *)buf,
-                               *nr_segs, UIO_FASTIOV, *iovec, iovec);
-       if (ret < 0)
-               return ret;
-
-       /* len now reflect bytes instead of segs */
-       *len = ret;
-       return 0;
-}
-
-static ssize_t aio_setup_single_vector(struct kiocb *kiocb,
-                                      int rw, char __user *buf,
-                                      unsigned long *nr_segs,
-                                      size_t len,
-                                      struct iovec *iovec)
-{
-       if (unlikely(!access_ok(!rw, buf, len)))
-               return -EFAULT;
-
-       iovec->iov_base = buf;
-       iovec->iov_len = len;
-       *nr_segs = 1;
-       return 0;
+       return import_iovec(rw, (struct iovec __user *)buf,
+                               len, UIO_FASTIOV, iovec, iter);
 }
 
 /*
@@ -1405,10 +1381,8 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
 {
        struct file *file = req->ki_filp;
        ssize_t ret;
-       unsigned long nr_segs;
        int rw;
        fmode_t mode;
-       aio_rw_op *rw_op;
        rw_iter_op *iter_op;
        struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
        struct iov_iter iter;
@@ -1418,7 +1392,6 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
        case IOCB_CMD_PREADV:
                mode    = FMODE_READ;
                rw      = READ;
-               rw_op   = file->f_op->aio_read;
                iter_op = file->f_op->read_iter;
                goto rw_common;
 
@@ -1426,51 +1399,40 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
        case IOCB_CMD_PWRITEV:
                mode    = FMODE_WRITE;
                rw      = WRITE;
-               rw_op   = file->f_op->aio_write;
                iter_op = file->f_op->write_iter;
                goto rw_common;
 rw_common:
                if (unlikely(!(file->f_mode & mode)))
                        return -EBADF;
 
-               if (!rw_op && !iter_op)
+               if (!iter_op)
                        return -EINVAL;
 
                if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV)
-                       ret = aio_setup_vectored_rw(req, rw, buf, &nr_segs,
-                                               &len, &iovec, compat);
-               else
-                       ret = aio_setup_single_vector(req, rw, buf, &nr_segs,
-                                                 len, iovec);
+                       ret = aio_setup_vectored_rw(rw, buf, len,
+                                               &iovec, compat, &iter);
+               else {
+                       ret = import_single_range(rw, buf, len, iovec, &iter);
+                       iovec = NULL;
+               }
                if (!ret)
-                       ret = rw_verify_area(rw, file, &req->ki_pos, len);
+                       ret = rw_verify_area(rw, file, &req->ki_pos,
+                                            iov_iter_count(&iter));
                if (ret < 0) {
-                       if (iovec != inline_vecs)
-                               kfree(iovec);
+                       kfree(iovec);
                        return ret;
                }
 
                len = ret;
 
-               /* XXX: move/kill - rw_verify_area()? */
-               /* This matches the pread()/pwrite() logic */
-               if (req->ki_pos < 0) {
-                       ret = -EINVAL;
-                       break;
-               }
-
                if (rw == WRITE)
                        file_start_write(file);
 
-               if (iter_op) {
-                       iov_iter_init(&iter, rw, iovec, nr_segs, len);
-                       ret = iter_op(req, &iter);
-               } else {
-                       ret = rw_op(req, iovec, nr_segs, req->ki_pos);
-               }
+               ret = iter_op(req, &iter);
 
                if (rw == WRITE)
                        file_end_write(file);
+               kfree(iovec);
                break;
 
        case IOCB_CMD_FDSYNC:
@@ -1492,9 +1454,6 @@ rw_common:
                return -EINVAL;
        }
 
-       if (iovec != inline_vecs)
-               kfree(iovec);
-
        if (ret != -EIOCBQUEUED) {
                /*
                 * There's no easy way to restart the syscall since other AIO's