diff options
author | julian <julian@FreeBSD.org> | 2016-10-10 04:57:33 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 2016-10-10 04:57:33 +0000 |
commit | f3eddd4e73c64e4b2b7b2ca5b968e6d742f3669a (patch) | |
tree | e36e4229fc95b4975e9d7e94c65f27264cb1fd5f | |
parent | d8fc7ba5ea5f7d4b1f87a24e4b01ecc65d5836d9 (diff) | |
download | FreeBSD-src-f3eddd4e73c64e4b2b7b2ca5b968e6d742f3669a.zip FreeBSD-src-f3eddd4e73c64e4b2b7b2ca5b968e6d742f3669a.tar.gz |
While the thread is sleeping in taskqueue_drain_all() it is
posible that the queue entry it is looking at is removed
from the queue, but we make no effort to account
for this. when we wake up we need to check it's still there.
PR: 209580
Sponsored by: Panzura inc
Differential Revision: D8160
-rw-r--r-- | sys/kern/subr_taskqueue.c | 23 |
1 files changed, 20 insertions, 3 deletions
diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c index bfc941e..c9addb1 100644 --- a/sys/kern/subr_taskqueue.c +++ b/sys/kern/subr_taskqueue.c @@ -454,9 +454,26 @@ taskqueue_drain_all(struct taskqueue *queue) TQ_LOCK(queue); task = STAILQ_LAST(&queue->tq_queue, task, ta_link); - if (task != NULL) - while (task->ta_pending != 0) - TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); + while (task != NULL && task->ta_pending != 0) { + struct task *oldtask; + TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); + /* + * While we were asleeep the last entry may have been freed. + * We need to check if it's still even in the queue. + * Not perfect, but it's better than referencing bad memory. + * first guess is the current 'end of queue' but if a new + * item has been added we need to take the expensive path + * Better fix in 11. + */ + oldtask = task; + if (oldtask != + (task = STAILQ_LAST(&queue->tq_queue, task, ta_link))) { + STAILQ_FOREACH(task, &queue->tq_queue, ta_link) { + if (task == oldtask) + break; + } + } + } taskqueue_drain_running(queue); KASSERT(STAILQ_EMPTY(&queue->tq_queue), ("taskqueue queue is not empty after draining")); |