aboutsummaryrefslogtreecommitdiffstats
path: root/module/spl/spl-taskq.c
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2013-04-29 13:47:59 -0700
committerBrian Behlendorf <[email protected]>2013-05-03 14:32:29 -0700
commit99c452bbbaeaa8fae498da1774d81e146bdd45ed (patch)
tree8fbe4860ef76a17e383be470d6ab1da27d2ff209 /module/spl/spl-taskq.c
parentab59be7bc752481db64df07c821e2ae6bf2ae71b (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.c40
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);