]> granicus.if.org Git - spl/commitdiff
Fix taskq_wait_id()
authorBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 29 Apr 2013 20:47:59 +0000 (13:47 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 3 May 2013 21:32:29 +0000 (14:32 -0700)
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 <behlendorf1@llnl.gov>
module/spl/spl-taskq.c

index 4feca0452092a0b37e4075bb00577b2b153b2abf..bcdc98f97be865d4bcb6d6e0c9e3057e5c8ab904 100644 (file)
@@ -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);