diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c index d40863e..59573fa 100644 --- a/sys/kern/subr_taskqueue.c +++ b/sys/kern/subr_taskqueue.c @@ -199,6 +199,7 @@ taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task) { struct task *ins; struct task *prev; + int blocked; /* * Count multiple enqueues. @@ -230,9 +231,10 @@ taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task) } task->ta_pending = 1; + blocked = (queue->tq_flags & TQ_FLAGS_BLOCKED) != 0; if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) != 0) TQ_UNLOCK(queue); - if ((queue->tq_flags & TQ_FLAGS_BLOCKED) == 0) + if (!blocked) queue->tq_enqueue(queue->tq_context); if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) == 0) TQ_UNLOCK(queue); @@ -328,6 +330,15 @@ taskqueue_run_locked(struct taskqueue *queue) int pending; TQ_ASSERT_LOCKED(queue); + + /* + * This function could get called after taskqueue_block. + * Don't start running the tasks in that case to honor the expectations. + * taskqueue_unblock will cause this funciton to be called again. + */ + if ((queue->tq_flags & TQ_FLAGS_BLOCKED) != 0) + return; + tb.tb_running = NULL; TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link); @@ -350,6 +361,8 @@ taskqueue_run_locked(struct taskqueue *queue) wakeup(task); } TAILQ_REMOVE(&queue->tq_active, &tb, tb_link); + if (TAILQ_EMPTY(&queue->tq_active)) + wakeup(&queue->tq_active); } void @@ -444,6 +457,20 @@ taskqueue_drain_timeout(struct taskqueue *queue, taskqueue_drain(queue, &timeout_task->t); } +void +taskqueue_drain_running(struct taskqueue *queue) +{ + + if (!queue->tq_spin) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__); + + TQ_LOCK(queue); + while (!TAILQ_EMPTY(&queue->tq_active)) + TQ_SLEEP(queue, &queue->tq_active, &queue->tq_mutex, + PWAIT, "-", 0); + TQ_UNLOCK(queue); +} + static void taskqueue_swi_enqueue(void *context) { diff --git a/sys/sys/taskqueue.h b/sys/sys/taskqueue.h index 7836262..5e8bfd8 100644 --- a/sys/sys/taskqueue.h +++ b/sys/sys/taskqueue.h @@ -81,6 +81,7 @@ int taskqueue_cancel_timeout(struct taskqueue *queue, void taskqueue_drain(struct taskqueue *queue, struct task *task); void taskqueue_drain_timeout(struct taskqueue *queue, struct timeout_task *timeout_task); +void taskqueue_drain_running(struct taskqueue *queue); void taskqueue_free(struct taskqueue *queue); void taskqueue_run(struct taskqueue *queue); void taskqueue_block(struct taskqueue *queue);