commit ecbadc21689bfeb4613fa27fca6c8f04203f6399 Author: Ryota Ozaki Date: Wed Dec 20 15:08:25 2017 +0900 Add workqueue_drain that waits for all pending works to finish It is useful when you want to safely call workqueue_destroy that requires that there remains no work in the queue. Without workqueue_drain the caller had to satisfy the constract by itself somehow. workqueue_drain has a contract that the caller must ensure no new work to be enqueued to the workqueue during workqueue_drain. diff --git a/sys/kern/subr_workqueue.c b/sys/kern/subr_workqueue.c index 7e651d1ebda..d52ba6736ec 100644 --- a/sys/kern/subr_workqueue.c +++ b/sys/kern/subr_workqueue.c @@ -57,6 +57,8 @@ struct workqueue { void (*wq_func)(struct work *, void *); void *wq_arg; int wq_flags; + /* Internal flags */ +#define WQ_DRAINING 0x1000 /* Draining. No new work is allowed. */ char wq_name[MAXCOMLEN]; pri_t wq_prio; @@ -271,6 +273,30 @@ workqueue_create(struct workqueue **wqp, const char *name, return error; } +/* + * Wait for all pending works to finish. The caller must ensure that no new + * work will be enqueued before calling workqueue_drain. + */ +void +workqueue_drain(struct workqueue *wq) +{ + struct workqueue_queue *q; + struct cpu_info *ci; + CPU_INFO_ITERATOR cii; + + wq->wq_flags |= WQ_DRAINING; + + for (CPU_INFO_FOREACH(cii, ci)) { + q = workqueue_queue_lookup(wq, ci); + KASSERT(q->q_worker != NULL); + + mutex_enter(&q->q_mutex); + while (!SIMPLEQ_EMPTY(&q->q_queue)) + cv_wait(&q->q_cv, &q->q_mutex); + mutex_exit(&q->q_mutex); + } +} + void workqueue_destroy(struct workqueue *wq) { @@ -298,6 +324,8 @@ workqueue_enqueue(struct workqueue *wq, struct work *wk0, struct cpu_info *ci) q = workqueue_queue_lookup(wq, ci); mutex_enter(&q->q_mutex); + KASSERTMSG((wq->wq_flags & WQ_DRAINING) == 0, + "adding work to a draining workqueue"); SIMPLEQ_INSERT_TAIL(&q->q_queue, wk, wk_entry); cv_signal(&q->q_cv); mutex_exit(&q->q_mutex); diff --git a/sys/sys/workqueue.h b/sys/sys/workqueue.h index 26d974bced2..10cae3429ce 100644 --- a/sys/sys/workqueue.h +++ b/sys/sys/workqueue.h @@ -51,6 +51,7 @@ struct workqueue; int workqueue_create(struct workqueue **, const char *, void (*)(struct work *, void *), void *, pri_t, int, int); void workqueue_destroy(struct workqueue *); +void workqueue_drain(struct workqueue *); void workqueue_enqueue(struct workqueue *, struct work *, struct cpu_info *);