Author: zml
Date: Fri Apr 30 16:29:05 2010
New Revision: 207439
URL: http://svn.freebsd.org/changeset/base/207439

Log:
  Handle taskqueue_drain(9) correctly on a threaded taskqueue:
  
  taskqueue_drain(9) will not correctly detect whether a task is
  currently running.  The check is against a field in the taskqueue
  struct, but for a threaded queue with more than one thread, multiple
  threads can simultaneously be running a task, thus stomping over the
  tq_running field.
  
  Submitted by:       Matthew Fleming <matthew.flem...@isilon.com>
  Reviewed by:        jhb
  Approved by:        dfr (mentor)

Modified:
  head/sys/kern/subr_taskqueue.c
  head/sys/sys/_task.h
  head/sys/sys/taskqueue.h

Modified: head/sys/kern/subr_taskqueue.c
==============================================================================
--- head/sys/kern/subr_taskqueue.c      Fri Apr 30 16:20:14 2010        
(r207438)
+++ head/sys/kern/subr_taskqueue.c      Fri Apr 30 16:29:05 2010        
(r207439)
@@ -51,7 +51,6 @@ struct taskqueue {
        const char              *tq_name;
        taskqueue_enqueue_fn    tq_enqueue;
        void                    *tq_context;
-       struct task             *tq_running;
        struct mtx              tq_mutex;
        struct thread           **tq_threads;
        int                     tq_tcount;
@@ -233,13 +232,13 @@ taskqueue_run(struct taskqueue *queue)
                STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link);
                pending = task->ta_pending;
                task->ta_pending = 0;
-               queue->tq_running = task;
+               task->ta_flags |= TA_FLAGS_RUNNING;
                TQ_UNLOCK(queue);
 
                task->ta_func(task->ta_context, pending);
 
                TQ_LOCK(queue);
-               queue->tq_running = NULL;
+               task->ta_flags &= ~TA_FLAGS_RUNNING;
                wakeup(task);
        }
 
@@ -256,14 +255,16 @@ taskqueue_drain(struct taskqueue *queue,
 {
        if (queue->tq_spin) {           /* XXX */
                mtx_lock_spin(&queue->tq_mutex);
-               while (task->ta_pending != 0 || task == queue->tq_running)
+               while (task->ta_pending != 0 ||
+                   (task->ta_flags & TA_FLAGS_RUNNING) != 0)
                        msleep_spin(task, &queue->tq_mutex, "-", 0);
                mtx_unlock_spin(&queue->tq_mutex);
        } else {
                WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__);
 
                mtx_lock(&queue->tq_mutex);
-               while (task->ta_pending != 0 || task == queue->tq_running)
+               while (task->ta_pending != 0 ||
+                   (task->ta_flags & TA_FLAGS_RUNNING) != 0)
                        msleep(task, &queue->tq_mutex, PWAIT, "-", 0);
                mtx_unlock(&queue->tq_mutex);
        }

Modified: head/sys/sys/_task.h
==============================================================================
--- head/sys/sys/_task.h        Fri Apr 30 16:20:14 2010        (r207438)
+++ head/sys/sys/_task.h        Fri Apr 30 16:29:05 2010        (r207439)
@@ -36,15 +36,21 @@
  * taskqueue_run().  The first argument is taken from the 'ta_context'
  * field of struct task and the second argument is a count of how many
  * times the task was enqueued before the call to taskqueue_run().
+ *
+ * List of locks
+ * (c) const after init
+ * (q) taskqueue lock
  */
 typedef void task_fn_t(void *context, int pending);
 
 struct task {
-       STAILQ_ENTRY(task) ta_link;     /* link for queue */
-       u_short ta_pending;             /* count times queued */
-       u_short ta_priority;            /* Priority */
-       task_fn_t *ta_func;             /* task handler */
-       void    *ta_context;            /* argument for handler */
+       STAILQ_ENTRY(task) ta_link;     /* (q) link for queue */
+       u_int   ta_flags;               /* (q) state of this task */
+#define        TA_FLAGS_RUNNING        0x01
+       u_short ta_pending;             /* (q) count times queued */
+       u_short ta_priority;            /* (c) Priority */
+       task_fn_t *ta_func;             /* (c) task handler */
+       void    *ta_context;            /* (c) argument for handler */
 };
 
 #endif /* !_SYS__TASK_H_ */

Modified: head/sys/sys/taskqueue.h
==============================================================================
--- head/sys/sys/taskqueue.h    Fri Apr 30 16:20:14 2010        (r207438)
+++ head/sys/sys/taskqueue.h    Fri Apr 30 16:29:05 2010        (r207439)
@@ -75,6 +75,7 @@ void  taskqueue_thread_enqueue(void *cont
        (task)->ta_priority = (priority);               \
        (task)->ta_func = (func);                       \
        (task)->ta_context = (context);                 \
+       (task)->ta_flags = 0;                           \
 } while (0)
 
 /*
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to