From: Brian Behlendorf Date: Mon, 29 Apr 2013 20:47:59 +0000 (-0700) Subject: Fix taskq_wait_id() X-Git-Tag: spl-0.6.2~18 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=99c452bbbaeaa8fae498da1774d81e146bdd45ed;p=spl 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 --- diff --git a/module/spl/spl-taskq.c b/module/spl/spl-taskq.c index 4feca04..bcdc98f 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);