#include <linux/highmem.h>
#include <linux/workqueue.h>
#include <linux/security.h>
+#include <linux/rcuref.h>
#include <asm/kmap_types.h>
#include <asm/uaccess.h>
static LIST_HEAD(fput_head);
static void aio_kick_handler(void *);
+static void aio_queue_work(struct kioctx *);
/* aio_setup
* Creates the slab caches used by the aio routines, panic on
/* 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(rcuref_dec_and_test(&req->ki_filp->f_count))) {
get_ioctx(ctx);
spin_lock(&fput_lock);
list_add(&req->ki_list, &fput_head);
return ioctx;
}
+static int lock_kiocb_action(void *param)
+{
+ schedule();
+ return 0;
+}
+
+static inline void lock_kiocb(struct kiocb *iocb)
+{
+ wait_on_bit_lock(&iocb->ki_flags, KIF_LOCKED, lock_kiocb_action,
+ TASK_UNINTERRUPTIBLE);
+}
+
+static inline void unlock_kiocb(struct kiocb *iocb)
+{
+ kiocbClearLocked(iocb);
+ wake_up_bit(&iocb->ki_flags, KIF_LOCKED);
+}
+
/*
* use_mm
* Makes the calling kernel thread take on the specified
atomic_inc(&mm->mm_count);
tsk->mm = mm;
tsk->active_mm = mm;
+ /*
+ * Note that on UML this *requires* PF_BORROWED_MM to be set, otherwise
+ * it won't work. Update it accordingly if you change it here
+ */
activate_mm(active_mm, mm);
task_unlock(tsk);
* has already been kicked */
if (kiocbIsKicked(iocb)) {
__queue_kicked_iocb(iocb);
+
+ /*
+ * __queue_kicked_iocb will always return 1 here, because
+ * iocb->ki_run_list is empty at this point so it should
+ * be safe to unconditionally queue the context into the
+ * work queue.
+ */
+ aio_queue_work(ctx);
}
}
return ret;
* Hold an extra reference while retrying i/o.
*/
iocb->ki_users++; /* grab extra reference */
+ lock_kiocb(iocb);
aio_run_iocb(iocb);
+ unlock_kiocb(iocb);
if (__aio_put_req(ctx, iocb)) /* drop extra ref */
put_ioctx(ctx);
}
goto out_put_req;
spin_lock_irq(&ctx->ctx_lock);
- list_add_tail(&req->ki_run_list, &ctx->run_list);
- /* drain the run list */
- while (__aio_run_iocbs(ctx))
- ;
+ aio_run_iocb(req);
+ unlock_kiocb(req);
+ if (!list_empty(&ctx->run_list)) {
+ /* drain the run list */
+ while (__aio_run_iocbs(ctx))
+ ;
+ }
spin_unlock_irq(&ctx->ctx_lock);
aio_put_req(req); /* drop extra ref to req */
return 0;
if (NULL != cancel) {
struct io_event tmp;
pr_debug("calling cancel\n");
+ lock_kiocb(kiocb);
memset(&tmp, 0, sizeof(tmp));
tmp.obj = (u64)(unsigned long)kiocb->ki_obj.user;
tmp.data = kiocb->ki_user_data;
if (copy_to_user(result, &tmp, sizeof(tmp)))
ret = -EFAULT;
}
+ unlock_kiocb(kiocb);
} else
- printk(KERN_DEBUG "iocb has no cancel operation\n");
+ ret = -EINVAL;
put_ioctx(ctx);