Commit 998765e5 authored by Zach Brown's avatar Zach Brown Committed by Linus Torvalds

[PATCH] aio: lock around kiocbTryKick()

Only one of the run or kick path is supposed to put an iocb on the run
list.  If both of them do it than one of them can end up referencing a
freed iocb.  The kick patch could set the Kicked bit before acquiring the
ctx_lock and putting the iocb on the run list.  The run path, while holding
the ctx_lock, could see this partial kick and mistake it for a kick that
was deferred while it was doing work with the run_list NULLed out.  It
would then race with the kick thread to add the iocb to the run list.

This patch moves the kick setting under the ctx_lock so that only one of
the kick or run path queues the iocb on the run list, as intended.
Signed-off-by: default avatarZach Brown <zach.brown@oracle.com>
Signed-off-by: default avatarBenjamin LaHaise <bcrl@linux.intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6e3254c4
...@@ -899,15 +899,23 @@ static void aio_kick_handler(void *data) ...@@ -899,15 +899,23 @@ static void aio_kick_handler(void *data)
* and if required activate the aio work queue to process * and if required activate the aio work queue to process
* it * it
*/ */
static void queue_kicked_iocb(struct kiocb *iocb) static void try_queue_kicked_iocb(struct kiocb *iocb)
{ {
struct kioctx *ctx = iocb->ki_ctx; struct kioctx *ctx = iocb->ki_ctx;
unsigned long flags; unsigned long flags;
int run = 0; int run = 0;
WARN_ON((!list_empty(&iocb->ki_wait.task_list))); /* We're supposed to be the only path putting the iocb back on the run
* list. If we find that the iocb is *back* on a wait queue already
* than retry has happened before we could queue the iocb. This also
* means that the retry could have completed and freed our iocb, no
* good. */
BUG_ON((!list_empty(&iocb->ki_wait.task_list)));
spin_lock_irqsave(&ctx->ctx_lock, flags); spin_lock_irqsave(&ctx->ctx_lock, flags);
/* set this inside the lock so that we can't race with aio_run_iocb()
* testing it and putting the iocb on the run list under the lock */
if (!kiocbTryKick(iocb))
run = __queue_kicked_iocb(iocb); run = __queue_kicked_iocb(iocb);
spin_unlock_irqrestore(&ctx->ctx_lock, flags); spin_unlock_irqrestore(&ctx->ctx_lock, flags);
if (run) if (run)
...@@ -931,10 +939,7 @@ void fastcall kick_iocb(struct kiocb *iocb) ...@@ -931,10 +939,7 @@ void fastcall kick_iocb(struct kiocb *iocb)
return; return;
} }
/* If its already kicked we shouldn't queue it again */ try_queue_kicked_iocb(iocb);
if (!kiocbTryKick(iocb)) {
queue_kicked_iocb(iocb);
}
} }
EXPORT_SYMBOL(kick_iocb); EXPORT_SYMBOL(kick_iocb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment