Author: mdf
Date: Fri Nov 12 18:10:27 2010
New Revision: 215191
URL: http://svn.freebsd.org/changeset/base/215191

Log:
  MFC r213813.  This is a direct commit because the diff is against
  several non-MFC'd changes.
  
  Use a safer mechanism for determining if a task is currently running,
  that does not rely on the lifetime of pointers being the same. This
  also restores the task KBI.

Modified:
  stable/7/sys/kern/subr_taskqueue.c

Modified: stable/7/sys/kern/subr_taskqueue.c
==============================================================================
--- stable/7/sys/kern/subr_taskqueue.c  Fri Nov 12 18:09:06 2010        
(r215190)
+++ stable/7/sys/kern/subr_taskqueue.c  Fri Nov 12 18:10:27 2010        
(r215191)
@@ -48,13 +48,18 @@ static void *taskqueue_ih;
 static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues;
 static struct mtx taskqueue_queues_mutex;
 
+struct taskqueue_busy {
+       struct task     *tb_running;
+       TAILQ_ENTRY(taskqueue_busy) tb_link;
+};
+
 struct taskqueue {
        STAILQ_ENTRY(taskqueue) tq_link;
        STAILQ_HEAD(, task)     tq_queue;
        const char              *tq_name;
        taskqueue_enqueue_fn    tq_enqueue;
        void                    *tq_context;
-       struct task             *tq_running;
+       TAILQ_HEAD(, taskqueue_busy) tq_active;
        struct mtx              tq_mutex;
        struct proc             **tq_pproc;
        int                     tq_pcount;
@@ -66,6 +71,8 @@ struct taskqueue {
 #define        TQ_FLAGS_BLOCKED        (1 << 1)
 #define        TQ_FLAGS_PENDING        (1 << 2)
 
+static void    taskqueue_run_locked(struct taskqueue *);
+
 static __inline void
 TQ_LOCK(struct taskqueue *tq)
 {
@@ -117,6 +124,7 @@ _taskqueue_create(const char *name, int 
                return 0;
 
        STAILQ_INIT(&queue->tq_queue);
+       TAILQ_INIT(&queue->tq_active);
        queue->tq_name = name;
        queue->tq_enqueue = enqueue;
        queue->tq_context = context;
@@ -162,8 +170,9 @@ taskqueue_free(struct taskqueue *queue)
 
        TQ_LOCK(queue);
        queue->tq_flags &= ~TQ_FLAGS_ACTIVE;
-       taskqueue_run(queue);
+       taskqueue_run_locked(queue);
        taskqueue_terminate(queue->tq_pproc, queue);
+       KASSERT(TAILQ_EMPTY(&queue->tq_active), ("Tasks still running?"));
        mtx_destroy(&queue->tq_mutex);
        free(queue->tq_pproc, M_TASKQUEUE);
        free(queue, M_TASKQUEUE);
@@ -258,15 +267,17 @@ taskqueue_unblock(struct taskqueue *queu
        TQ_UNLOCK(queue);
 }
 
-void
-taskqueue_run(struct taskqueue *queue)
+static void
+taskqueue_run_locked(struct taskqueue *queue)
 {
+       struct taskqueue_busy tb;
        struct task *task;
-       int owned, pending;
+       int pending;
+
+       mtx_assert(&queue->tq_mutex, MA_OWNED);
+       tb.tb_running = NULL;
+       TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link);
 
-       owned = mtx_owned(&queue->tq_mutex);
-       if (!owned)
-               TQ_LOCK(queue);
        while (STAILQ_FIRST(&queue->tq_queue)) {
                /*
                 * Carefully remove the first task from the queue and
@@ -276,22 +287,38 @@ 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;
+               tb.tb_running = task;
                TQ_UNLOCK(queue);
 
                task->ta_func(task->ta_context, pending);
 
                TQ_LOCK(queue);
-               queue->tq_running = NULL;
+               tb.tb_running = NULL;
                wakeup(task);
        }
+       TAILQ_REMOVE(&queue->tq_active, &tb, tb_link);
+}
 
-       /*
-        * For compatibility, unlock on return if the queue was not locked
-        * on entry, although this opens a race window.
-        */
-       if (!owned)
-               TQ_UNLOCK(queue);
+void
+taskqueue_run(struct taskqueue *queue)
+{
+
+       TQ_LOCK(queue);
+       taskqueue_run_locked(queue);
+       TQ_UNLOCK(queue);
+}
+
+static int
+task_is_running(struct taskqueue *queue, struct task *task)
+{
+       struct taskqueue_busy *tb;
+
+       mtx_assert(&queue->tq_mutex, MA_OWNED);
+       TAILQ_FOREACH(tb, &queue->tq_active, tb_link) {
+               if (tb->tb_running == task)
+                       return (1);
+       }
+       return (0);
 }
 
 void
@@ -299,14 +326,14 @@ 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_is_running(queue, task))
                        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_is_running(queue, task))
                        msleep(task, &queue->tq_mutex, PWAIT, "-", 0);
                mtx_unlock(&queue->tq_mutex);
        }
@@ -398,7 +425,7 @@ taskqueue_thread_loop(void *arg)
        tq = *tqp;
        TQ_LOCK(tq);
        while ((tq->tq_flags & TQ_FLAGS_ACTIVE) != 0) {
-               taskqueue_run(tq);
+               taskqueue_run_locked(tq);
                TQ_SLEEP(tq, tq, &tq->tq_mutex, 0, "-", 0);
        }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to