nfsd: nfsd should drop CAP_MKNOD for non-root
[safe/jmp/linux-2.6] / fs / aio.c
index 0051fd9..8fa77e2 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -191,6 +191,20 @@ static int aio_setup_ring(struct kioctx *ctx)
        kunmap_atomic((void *)((unsigned long)__event & PAGE_MASK), km); \
 } while(0)
 
+static void ctx_rcu_free(struct rcu_head *head)
+{
+       struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
+       unsigned nr_events = ctx->max_reqs;
+
+       kmem_cache_free(kioctx_cachep, ctx);
+
+       if (nr_events) {
+               spin_lock(&aio_nr_lock);
+               BUG_ON(aio_nr - nr_events > aio_nr);
+               aio_nr -= nr_events;
+               spin_unlock(&aio_nr_lock);
+       }
+}
 
 /* __put_ioctx
  *     Called when the last user of an aio context has gone away,
@@ -198,8 +212,6 @@ static int aio_setup_ring(struct kioctx *ctx)
  */
 static void __put_ioctx(struct kioctx *ctx)
 {
-       unsigned nr_events = ctx->max_reqs;
-
        BUG_ON(ctx->reqs_active);
 
        cancel_delayed_work(&ctx->wq);
@@ -208,14 +220,7 @@ static void __put_ioctx(struct kioctx *ctx)
        mmdrop(ctx->mm);
        ctx->mm = NULL;
        pr_debug("__put_ioctx: freeing %p\n", ctx);
-       kmem_cache_free(kioctx_cachep, ctx);
-
-       if (nr_events) {
-               spin_lock(&aio_nr_lock);
-               BUG_ON(aio_nr - nr_events > aio_nr);
-               aio_nr -= nr_events;
-               spin_unlock(&aio_nr_lock);
-       }
+       call_rcu(&ctx->rcu_head, ctx_rcu_free);
 }
 
 #define get_ioctx(kioctx) do {                                         \
@@ -235,6 +240,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 {
        struct mm_struct *mm;
        struct kioctx *ctx;
+       int did_sync = 0;
 
        /* Prevent overflows */
        if ((nr_events > (0x10000000U / sizeof(struct io_event))) ||
@@ -267,21 +273,30 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
                goto out_freectx;
 
        /* limit the number of system wide aios */
-       spin_lock(&aio_nr_lock);
-       if (aio_nr + ctx->max_reqs > aio_max_nr ||
-           aio_nr + ctx->max_reqs < aio_nr)
-               ctx->max_reqs = 0;
-       else
-               aio_nr += ctx->max_reqs;
-       spin_unlock(&aio_nr_lock);
+       do {
+               spin_lock_bh(&aio_nr_lock);
+               if (aio_nr + nr_events > aio_max_nr ||
+                   aio_nr + nr_events < aio_nr)
+                       ctx->max_reqs = 0;
+               else
+                       aio_nr += ctx->max_reqs;
+               spin_unlock_bh(&aio_nr_lock);
+               if (ctx->max_reqs || did_sync)
+                       break;
+
+               /* wait for rcu callbacks to have completed before giving up */
+               synchronize_rcu();
+               did_sync = 1;
+               ctx->max_reqs = nr_events;
+       } while (1);
+
        if (ctx->max_reqs == 0)
                goto out_cleanup;
 
        /* now link into global list. */
-       write_lock(&mm->ioctx_list_lock);
-       ctx->next = mm->ioctx_list;
-       mm->ioctx_list = ctx;
-       write_unlock(&mm->ioctx_list_lock);
+       spin_lock(&mm->ioctx_lock);
+       hlist_add_head_rcu(&ctx->list, &mm->ioctx_list);
+       spin_unlock(&mm->ioctx_lock);
 
        dprintk("aio: allocated ioctx %p[%ld]: mm=%p mask=0x%x\n",
                ctx, ctx->user_id, current->mm, ctx->ring_info.nr);
@@ -375,11 +390,12 @@ ssize_t wait_on_sync_kiocb(struct kiocb *iocb)
  */
 void exit_aio(struct mm_struct *mm)
 {
-       struct kioctx *ctx = mm->ioctx_list;
-       mm->ioctx_list = NULL;
-       while (ctx) {
-               struct kioctx *next = ctx->next;
-               ctx->next = NULL;
+       struct kioctx *ctx;
+
+       while (!hlist_empty(&mm->ioctx_list)) {
+               ctx = hlist_entry(mm->ioctx_list.first, struct kioctx, list);
+               hlist_del_rcu(&ctx->list);
+
                aio_cancel_all(ctx);
 
                wait_for_all_aios(ctx);
@@ -394,7 +410,6 @@ void exit_aio(struct mm_struct *mm)
                                atomic_read(&ctx->users), ctx->dead,
                                ctx->reqs_active);
                put_ioctx(ctx);
-               ctx = next;
        }
 }
 
@@ -512,8 +527,8 @@ static void aio_fput_routine(struct work_struct *data)
  */
 static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
 {
-       dprintk(KERN_DEBUG "aio_put(%p): f_count=%d\n",
-               req, atomic_read(&req->ki_filp->f_count));
+       dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n",
+               req, atomic_long_read(&req->ki_filp->f_count));
 
        assert_spin_locked(&ctx->ctx_lock);
 
@@ -528,7 +543,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
        /* Must be done under the lock to serialise against cancellation.
         * Call this aio_fput as it duplicates fput via the fput_work.
         */
-       if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) {
+       if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) {
                get_ioctx(ctx);
                spin_lock(&fput_lock);
                list_add(&req->ki_list, &fput_head);
@@ -555,19 +570,21 @@ int aio_put_req(struct kiocb *req)
 
 static struct kioctx *lookup_ioctx(unsigned long ctx_id)
 {
-       struct kioctx *ioctx;
-       struct mm_struct *mm;
+       struct mm_struct *mm = current->mm;
+       struct kioctx *ctx = NULL;
+       struct hlist_node *n;
 
-       mm = current->mm;
-       read_lock(&mm->ioctx_list_lock);
-       for (ioctx = mm->ioctx_list; ioctx; ioctx = ioctx->next)
-               if (likely(ioctx->user_id == ctx_id && !ioctx->dead)) {
-                       get_ioctx(ioctx);
+       rcu_read_lock();
+
+       hlist_for_each_entry_rcu(ctx, n, &mm->ioctx_list, list) {
+               if (ctx->user_id == ctx_id && !ctx->dead) {
+                       get_ioctx(ctx);
                        break;
                }
-       read_unlock(&mm->ioctx_list_lock);
+       }
 
-       return ioctx;
+       rcu_read_unlock();
+       return ctx;
 }
 
 /*
@@ -1215,19 +1232,14 @@ out:
 static void io_destroy(struct kioctx *ioctx)
 {
        struct mm_struct *mm = current->mm;
-       struct kioctx **tmp;
        int was_dead;
 
        /* delete the entry from the list is someone else hasn't already */
-       write_lock(&mm->ioctx_list_lock);
+       spin_lock(&mm->ioctx_lock);
        was_dead = ioctx->dead;
        ioctx->dead = 1;
-       for (tmp = &mm->ioctx_list; *tmp && *tmp != ioctx;
-            tmp = &(*tmp)->next)
-               ;
-       if (*tmp)
-               *tmp = ioctx->next;
-       write_unlock(&mm->ioctx_list_lock);
+       hlist_del_rcu(&ioctx->list);
+       spin_unlock(&mm->ioctx_lock);
 
        dprintk("aio_release(%p)\n", ioctx);
        if (likely(!was_dead))
@@ -1258,7 +1270,7 @@ static void io_destroy(struct kioctx *ioctx)
  *     pointer is passed for ctxp.  Will fail with -ENOSYS if not
  *     implemented.
  */
-asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp)
+SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp)
 {
        struct kioctx *ioctx = NULL;
        unsigned long ctx;
@@ -1296,7 +1308,7 @@ out:
  *     implemented.  May fail with -EFAULT if the context pointed to
  *     is invalid.
  */
-asmlinkage long sys_io_destroy(aio_context_t ctx)
+SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
 {
        struct kioctx *ioctx = lookup_ioctx(ctx);
        if (likely(NULL != ioctx)) {
@@ -1650,8 +1662,8 @@ out_put_req:
  *     are available to queue any iocbs.  Will return 0 if nr is 0.  Will
  *     fail with -ENOSYS if not implemented.
  */
-asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr,
-                             struct iocb __user * __user *iocbpp)
+SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
+               struct iocb __user * __user *, iocbpp)
 {
        struct kioctx *ctx;
        long ret = 0;
@@ -1725,8 +1737,8 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb,
  *     invalid.  May fail with -EAGAIN if the iocb specified was not
  *     cancelled.  Will fail with -ENOSYS if not implemented.
  */
-asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,
-                             struct io_event __user *result)
+SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb,
+               struct io_event __user *, result)
 {
        int (*cancel)(struct kiocb *iocb, struct io_event *res);
        struct kioctx *ctx;
@@ -1787,11 +1799,11 @@ asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,
  *     will be updated if not NULL and the operation blocks.  Will fail
  *     with -ENOSYS if not implemented.
  */
-asmlinkage long sys_io_getevents(aio_context_t ctx_id,
-                                long min_nr,
-                                long nr,
-                                struct io_event __user *events,
-                                struct timespec __user *timeout)
+SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id,
+               long, min_nr,
+               long, nr,
+               struct io_event __user *, events,
+               struct timespec __user *, timeout)
 {
        struct kioctx *ioctx = lookup_ioctx(ctx_id);
        long ret = -EINVAL;