diff options
author | Brian Behlendorf <[email protected]> | 2013-04-29 13:47:59 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2013-05-03 14:32:29 -0700 |
commit | 99c452bbbaeaa8fae498da1774d81e146bdd45ed (patch) | |
tree | 8fbe4860ef76a17e383be470d6ab1da27d2ff209 /module/spl/spl-taskq.c | |
parent | ab59be7bc752481db64df07c821e2ae6bf2ae71b (diff) |
Fix taskq_wait_id()
The existing taskq_wait_id() function can incorrectly block
indefinitely. Reimplement it more simply using wait_event()
in a similar fashion to taskq_wait_all().
This flaw was uncovered in the context of moving vn_rdwr() to
a taskq. Previously taskq_wait_id() had no consumers outside
the SPLAT task framework which is why the issue went unnoticed.
Signed-off-by: Brian Behlendorf <[email protected]>
Diffstat (limited to 'module/spl/spl-taskq.c')
-rw-r--r-- | module/spl/spl-taskq.c | 40 |
1 files changed, 14 insertions, 26 deletions
diff --git a/module/spl/spl-taskq.c b/module/spl/spl-taskq.c index 4feca0452..bcdc98f97 100644 --- a/module/spl/spl-taskq.c +++ b/module/spl/spl-taskq.c @@ -342,39 +342,27 @@ taskq_find(taskq_t *tq, taskqid_t id, int *active) SRETURN(NULL); } -/* - * The taskq_wait_id() function blocks until the passed task id completes. - * This does not guarantee that all lower task id's have completed. - */ -void -taskq_wait_id(taskq_t *tq, taskqid_t id) +static int +taskq_wait_id_check(taskq_t *tq, taskqid_t id) { - DEFINE_WAIT(wait); - taskq_ent_t *t; int active = 0; - SENTRY; - - ASSERT(tq); - ASSERT(id > 0); + int rc; spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags); - t = taskq_find(tq, id, &active); - if (t) - prepare_to_wait(&t->tqent_waitq, &wait, TASK_UNINTERRUPTIBLE); + rc = (taskq_find(tq, id, &active) == NULL); spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags); - /* - * We rely on the kernels autoremove_wake_function() function to - * remove us from the wait queue in the context of wake_up(). - * Once woken the taskq_ent_t pointer must never be accessed. - */ - if (t) { - t = NULL; - schedule(); - __set_current_state(TASK_RUNNING); - } + return (rc); +} - SEXIT; +/* + * The taskq_wait_id() function blocks until the passed task id completes. + * This does not guarantee that all lower task ids have completed. + */ +void +taskq_wait_id(taskq_t *tq, taskqid_t id) +{ + wait_event(tq->tq_wait_waitq, taskq_wait_id_check(tq, id)); } EXPORT_SYMBOL(taskq_wait_id); |