From d7d8bb9fe0b76875bbdf7a651c10edfe77449cb7 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sun, 19 Jan 2020 23:32:11 +0000 Subject: [PATCH] Fix abuse of TAILQ_CONCAT. Other parts of this expect that the entries will be on wq->wq_queue or wq->wq_dqueue, so we can't just move a batch of entries onto a temporary queue. Instead, use a marker node to delimit when the batch ends. XXX pullup --- sys/external/bsd/common/linux/linux_work.c | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sys/external/bsd/common/linux/linux_work.c b/sys/external/bsd/common/linux/linux_work.c index 3dd10f51c415..43ebdee111ce 100644 --- a/sys/external/bsd/common/linux/linux_work.c +++ b/sys/external/bsd/common/linux/linux_work.c @@ -348,8 +348,8 @@ static void __dead linux_workqueue_thread(void *cookie) { struct workqueue_struct *const wq = cookie; - struct work_head queue, dqueue; - struct work_head *const q[2] = { &queue, &dqueue }; + struct work_head *const q[2] = { &wq->wq_queue, &wq->wq_dqueue }; + struct work_struct marker, *work; unsigned i; lwp_setspecific(workqueue_key, wq); @@ -368,22 +368,22 @@ linux_workqueue_thread(void *cookie) continue; } - /* Grab a batch of work off the queue. */ + /* + * Start a batch of work. Use a marker to delimit when + * the batch ends so we can advance the generation + * after the batch. + */ SDT_PROBE1(sdt, linux, work, batch__start, wq); - TAILQ_INIT(&queue); - TAILQ_INIT(&dqueue); - TAILQ_CONCAT(&queue, &wq->wq_queue, work_entry); - TAILQ_CONCAT(&dqueue, &wq->wq_dqueue, work_entry); - - /* Process each work item in the batch. */ for (i = 0; i < 2; i++) { - while (!TAILQ_EMPTY(q[i])) { - struct work_struct *work = TAILQ_FIRST(q[i]); + if (TAILQ_EMPTY(q[i])) + continue; + TAILQ_INSERT_TAIL(q[i], &marker, work_entry); + while ((work = TAILQ_FIRST(q[i])) != &marker) { void (*func)(struct work_struct *); KASSERT(work_queue(work) == wq); KASSERT(work_claimed(work, wq)); - KASSERTMSG((q[i] != &dqueue || + KASSERTMSG((q[i] != &wq->wq_dqueue || container_of(work, struct delayed_work, work)->dw_state == DELAYED_WORK_IDLE), @@ -407,6 +407,7 @@ linux_workqueue_thread(void *cookie) wq->wq_current_work = NULL; cv_broadcast(&wq->wq_cv); } + TAILQ_REMOVE(q[i], &marker, work_entry); } /* Notify flush that we've completed a batch of work. */