diff options
author | Kent Overstreet <koverstreet@google.com> | 2013-03-02 15:25:46 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2013-03-07 14:27:10 +1100 |
commit | d8e64d4aee52a1f69530d2284dd6b14887a508f8 (patch) | |
tree | 36fb8ac3b4d9c0cac11284a1fb3dc82485648ffd /fs | |
parent | 53740cbb4c9f664ace657bdc7e6bb5b24d4a3a15 (diff) |
aio-use-cancellation-list-lazily-fix
The cancellation changes were fubar - we can't cancel a kiocb if it
doesn't actually have a cancellation callback.
The use of xchg() in aio_complete() was right - there we're marking the
kiocb as completed - but we need to use cmpxchg() in kiocb_cancel() - a
lock isn't sufficient since we're synchronizing with aio_complete() which
isn't taking any locks.
Signed-off-by: Kent Overstreet <koverstreet@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/aio.c | 32 |
1 files changed, 22 insertions, 10 deletions
@@ -224,28 +224,40 @@ static int aio_setup_ring(struct kioctx *ctx) void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) { - if (!req->ki_list.next) { - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; + struct kioctx *ctx = req->ki_ctx; + unsigned long flags; - spin_lock_irqsave(&ctx->ctx_lock, flags); + spin_lock_irqsave(&ctx->ctx_lock, flags); + + if (!req->ki_list.next) list_add(&req->ki_list, &ctx->active_reqs); - spin_unlock_irqrestore(&ctx->ctx_lock, flags); - } req->ki_cancel = cancel; + + spin_unlock_irqrestore(&ctx->ctx_lock, flags); } EXPORT_SYMBOL(kiocb_set_cancel_fn); static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb, struct io_event *res) { - kiocb_cancel_fn *cancel; + kiocb_cancel_fn *old, *cancel; int ret = -EINVAL; - cancel = xchg(&kiocb->ki_cancel, KIOCB_CANCELLED); - if (!cancel || cancel == KIOCB_CANCELLED) - return ret; + /* + * Don't want to set kiocb->ki_cancel = KIOCB_CANCELLED unless it + * actually has a cancel function, hence the cmpxchg() + */ + + cancel = ACCESS_ONCE(kiocb->ki_cancel); + do { + if (!cancel || cancel == KIOCB_CANCELLED) + return ret; + + BUG(); + old = cancel; + cancel = cmpxchg(&kiocb->ki_cancel, old, KIOCB_CANCELLED); + } while (cancel != old); atomic_inc(&kiocb->ki_users); spin_unlock_irq(&ctx->ctx_lock); |