Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/external/bsd/common/linux linux: Fix flush_workqueue.
details: https://anonhg.NetBSD.org/src/rev/27b21c88b755
branches: trunk
changeset: 1028844:27b21c88b755
user: riastradh <riastradh%NetBSD.org@localhost>
date: Sun Dec 19 12:11:28 2021 +0000
description:
linux: Fix flush_workqueue.
Simplify mechanism: we know there's only a single thread here that
processes scheduled work in FIFO order (no multi-CPU workqueues --
would have to adapt if there were), so just schedule a work item that
notifies of completion. The previous mechanism of counting 0, 1, or
2 generation numbers was broken by an earlier change to avoid abuse
of tailqs arising from clever TAILQ_CONCAT.
diffstat:
sys/external/bsd/common/linux/linux_work.c | 97 ++++++++++++++---------------
1 files changed, 48 insertions(+), 49 deletions(-)
diffs (154 lines):
diff -r a420a36815c4 -r 27b21c88b755 sys/external/bsd/common/linux/linux_work.c
--- a/sys/external/bsd/common/linux/linux_work.c Sun Dec 19 12:11:21 2021 +0000
+++ b/sys/external/bsd/common/linux/linux_work.c Sun Dec 19 12:11:28 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: linux_work.c,v 1.56 2021/12/19 12:11:21 riastradh Exp $ */
+/* $NetBSD: linux_work.c,v 1.57 2021/12/19 12:11:28 riastradh Exp $ */
/*-
* Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_work.c,v 1.56 2021/12/19 12:11:21 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_work.c,v 1.57 2021/12/19 12:11:28 riastradh Exp $");
#include <sys/types.h>
#include <sys/atomic.h>
@@ -461,7 +461,7 @@
TAILQ_REMOVE(q[i], &marker, work_entry);
}
- /* Notify flush that we've completed a batch of work. */
+ /* Notify cancel that we've completed a batch of work. */
wq->wq_gen++;
cv_broadcast(&wq->wq_cv);
SDT_PROBE1(sdt, linux, work, batch__done, wq);
@@ -1428,46 +1428,22 @@
flush_workqueue(system_wq);
}
-/*
- * flush_workqueue_locked(wq)
- *
- * Wait for all work queued on wq to complete. This does not
- * include delayed work. True if there was work to be flushed,
- * false it the queue was empty.
- *
- * Caller must hold wq's lock.
- */
-static bool
-flush_workqueue_locked(struct workqueue_struct *wq)
-{
- uint64_t gen;
- bool work_queued = false;
-
- KASSERT(mutex_owned(&wq->wq_lock));
-
- /* Get the current generation number. */
- gen = wq->wq_gen;
+struct flush_work {
+ kmutex_t fw_lock;
+ kcondvar_t fw_cv;
+ struct work_struct fw_work;
+ bool fw_done;
+};
- /*
- * If there's any work in progress -- whether currently running
- * or queued to run -- we must wait for the worker thread to
- * finish that batch.
- */
- if (wq->wq_current_work != NULL ||
- !TAILQ_EMPTY(&wq->wq_queue) ||
- !TAILQ_EMPTY(&wq->wq_dqueue)) {
- gen++;
- work_queued = true;
- }
+static void
+flush_work_cb(struct work_struct *work)
+{
+ struct flush_work *fw = container_of(work, struct flush_work, fw_work);
- /* Wait until the generation number has caught up. */
- SDT_PROBE1(sdt, linux, work, flush__start, wq);
- while (wq->wq_gen < gen)
- cv_wait(&wq->wq_cv, &wq->wq_lock);
- SDT_PROBE1(sdt, linux, work, flush__done, wq);
-
- /* Return whether we had to wait for anything. */
- return work_queued;
+ mutex_enter(&fw->fw_lock);
+ fw->fw_done = true;
+ cv_broadcast(&fw->fw_cv);
+ mutex_exit(&fw->fw_lock);
}
/*
@@ -1479,10 +1455,26 @@
void
flush_workqueue(struct workqueue_struct *wq)
{
+ struct flush_work fw;
- mutex_enter(&wq->wq_lock);
- (void)flush_workqueue_locked(wq);
- mutex_exit(&wq->wq_lock);
+ mutex_init(&fw.fw_lock, MUTEX_DEFAULT, IPL_VM);
+ cv_init(&fw.fw_cv, "lxwqflsh");
+ INIT_WORK(&fw.fw_work, &flush_work_cb);
+ fw.fw_done = false;
+
+ SDT_PROBE1(sdt, linux, work, flush__start, wq);
+ queue_work(wq, &fw.fw_work);
+
+ mutex_enter(&fw.fw_lock);
+ while (!fw.fw_done)
+ cv_wait(&fw.fw_cv, &fw.fw_lock);
+ mutex_exit(&fw.fw_lock);
+ SDT_PROBE1(sdt, linux, work, flush__done, wq);
+
+ KASSERT(fw.fw_done);
+ /* no DESTROY_WORK */
+ cv_destroy(&fw.fw_cv);
+ mutex_destroy(&fw.fw_lock);
}
/*
@@ -1494,15 +1486,20 @@
drain_workqueue(struct workqueue_struct *wq)
{
unsigned ntries = 0;
+ bool done;
- mutex_enter(&wq->wq_lock);
- while (flush_workqueue_locked(wq)) {
+ do {
if (ntries++ == 10 || (ntries % 100) == 0)
printf("linux workqueue %s"
": still clogged after %u flushes",
wq->wq_name, ntries);
- }
- mutex_exit(&wq->wq_lock);
+ flush_workqueue(wq);
+ mutex_enter(&wq->wq_lock);
+ done = wq->wq_current_work == NULL;
+ done &= TAILQ_EMPTY(&wq->wq_queue);
+ done &= TAILQ_EMPTY(&wq->wq_dqueue);
+ mutex_exit(&wq->wq_lock);
+ } while (!done);
}
/*
@@ -1599,7 +1596,9 @@
* Waiting for the whole queue to flush is overkill,
* but doesn't hurt.
*/
- (void)flush_workqueue_locked(wq);
+ mutex_exit(&wq->wq_lock);
+ flush_workqueue(wq);
+ mutex_enter(&wq->wq_lock);
waited = true;
}
mutex_exit(&wq->wq_lock);
Home |
Main Index |
Thread Index |
Old Index