summaryrefslogtreecommitdiff
path: root/sys/external/bsd/common/linux/linux_work.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/external/bsd/common/linux/linux_work.c')
-rw-r--r--sys/external/bsd/common/linux/linux_work.c44
1 files changed, 29 insertions, 15 deletions
diff --git a/sys/external/bsd/common/linux/linux_work.c b/sys/external/bsd/common/linux/linux_work.c
index 4ef6f769254..17ce932b5d4 100644
--- a/sys/external/bsd/common/linux/linux_work.c
+++ b/sys/external/bsd/common/linux/linux_work.c
@@ -1,4 +1,4 @@
-/* $NetBSD: linux_work.c,v 1.32 2018/08/27 15:04:19 riastradh Exp $ */
+/* $NetBSD: linux_work.c,v 1.33 2018/08/27 15:04:32 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.32 2018/08/27 15:04:19 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_work.c,v 1.33 2018/08/27 15:04:32 riastradh Exp $");
#include <sys/types.h>
#include <sys/atomic.h>
@@ -65,6 +65,8 @@ static struct workqueue_struct *
struct workqueue_struct *);
static void release_work(struct work_struct *,
struct workqueue_struct *);
+static void wait_for_current_work(struct work_struct *,
+ struct workqueue_struct *);
static void dw_callout_init(struct workqueue_struct *,
struct delayed_work *);
static void dw_callout_destroy(struct workqueue_struct *,
@@ -496,13 +498,9 @@ cancel_work_sync(struct work_struct *work)
} else if (wq->wq_current_work == work) {
/*
* It has already begun execution, so it's too late to
- * cancel now. Wait for it to complete. Don't wait
- * more than one generation in case it gets requeued.
+ * cancel now. Wait for it to complete.
*/
- uint64_t gen = wq->wq_gen;
- do {
- cv_wait(&wq->wq_cv, &wq->wq_lock);
- } while (wq->wq_current_work == work && wq->wq_gen == gen);
+ wait_for_current_work(work, wq);
cancelled_p = false;
} else {
/*
@@ -516,6 +514,27 @@ cancel_work_sync(struct work_struct *work)
out: return cancelled_p;
}
+
+/*
+ * wait_for_current_work(work, wq)
+ *
+ * wq must be currently executing work. Wait for it to finish.
+ */
+static void
+wait_for_current_work(struct work_struct *work, struct workqueue_struct *wq)
+{
+ uint64_t gen;
+
+ KASSERT(mutex_owned(&wq->wq_lock));
+ KASSERT(work->work_queue == wq);
+ KASSERT(wq->wq_current_work == work);
+
+ /* Wait only one generation in case it gets requeued quickly. */
+ gen = wq->wq_gen;
+ do {
+ cv_wait(&wq->wq_cv, &wq->wq_lock);
+ } while (wq->wq_current_work == work && wq->wq_gen == gen);
+}
/*
* Delayed work
@@ -993,19 +1012,14 @@ cancel_delayed_work_sync(struct delayed_work *dw)
/*
* Too late, it's already running.
* First, make sure it's not requeued.
- * Then wait for it to complete, at
- * most one generation.
+ * Then wait for it to complete.
*/
- uint64_t gen = wq->wq_gen;
if (wq->wq_requeued) {
TAILQ_REMOVE(&wq->wq_queue, &dw->work,
work_entry);
wq->wq_requeued = false;
}
- do {
- cv_wait(&wq->wq_cv, &wq->wq_lock);
- } while (wq->wq_current_work == &dw->work &&
- wq->wq_gen == gen);
+ wait_for_current_work(&dw->work, wq);
cancelled_p = false;
} else {
/* Got in before it started. Remove it. */