xref: /dflybsd-src/sys/dev/drm/linux_workqueue.c (revision c17dd299cec907bb472a1f824300cad3290019b3)
1df8db295SFrançois Tigeot /*
22cecdd68SFrançois Tigeot  * Copyright (c) 2015-2020 François Tigeot <ftigeot@wolfpond.org>
3*c17dd299SFrançois Tigeot  * Copyright (c) 2020 Matthew Dillon <dillon@backplane.com>
4df8db295SFrançois Tigeot  * All rights reserved.
5df8db295SFrançois Tigeot  *
6df8db295SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
7df8db295SFrançois Tigeot  * modification, are permitted provided that the following conditions
8df8db295SFrançois Tigeot  * are met:
9df8db295SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
10df8db295SFrançois Tigeot  *    notice unmodified, this list of conditions, and the following
11df8db295SFrançois Tigeot  *    disclaimer.
12df8db295SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
13df8db295SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
14df8db295SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
15df8db295SFrançois Tigeot  *
16df8db295SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17df8db295SFrançois Tigeot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18df8db295SFrançois Tigeot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19df8db295SFrançois Tigeot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20df8db295SFrançois Tigeot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21df8db295SFrançois Tigeot  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22df8db295SFrançois Tigeot  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23df8db295SFrançois Tigeot  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24df8db295SFrançois Tigeot  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25df8db295SFrançois Tigeot  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26df8db295SFrançois Tigeot  */
27df8db295SFrançois Tigeot 
28df8db295SFrançois Tigeot #include <drm/drmP.h>
29df8db295SFrançois Tigeot #include <linux/workqueue.h>
30df8db295SFrançois Tigeot 
31*c17dd299SFrançois Tigeot #include <sys/kthread.h>
32*c17dd299SFrançois Tigeot 
33*c17dd299SFrançois Tigeot /*
34*c17dd299SFrançois Tigeot    Running behaviour, from kernel.org docs:
35*c17dd299SFrançois Tigeot    - While there are work items on the workqueue the worker executes the functions
36*c17dd299SFrançois Tigeot    associated with the work items one after the other.
37*c17dd299SFrançois Tigeot    - When there is no work item left on the workqueue the worker becomes idle.
38*c17dd299SFrançois Tigeot 
39*c17dd299SFrançois Tigeot    There are two worker-pools,
40*c17dd299SFrançois Tigeot    one for normal work items
41*c17dd299SFrançois Tigeot    and the other for high priority ones, for each possible CPU
42*c17dd299SFrançois Tigeot    and some extra worker-pools to serve work items queued on unbound workqueues
43*c17dd299SFrançois Tigeot    - the number of these backing pools is dynamic.
44*c17dd299SFrançois Tigeot  */
45*c17dd299SFrançois Tigeot 
46*c17dd299SFrançois Tigeot /* XXX: Linux functions often enable/disable irqs on the CPU they run on
47*c17dd299SFrançois Tigeot  * this should be investigated */
48*c17dd299SFrançois Tigeot 
49df8db295SFrançois Tigeot struct workqueue_struct *system_wq;
502cecdd68SFrançois Tigeot struct workqueue_struct *system_highpri_wq;
51df8db295SFrançois Tigeot struct workqueue_struct *system_long_wq;
5296b26154SFrançois Tigeot struct workqueue_struct *system_unbound_wq;
532cecdd68SFrançois Tigeot struct workqueue_struct *system_power_efficient_wq;
54df8db295SFrançois Tigeot 
55*c17dd299SFrançois Tigeot /*
56*c17dd299SFrançois Tigeot  * Linux now uses these worker pools:
57*c17dd299SFrançois Tigeot  * - (per cpu) regular
58*c17dd299SFrançois Tigeot  * - (per cpu) regular high priority
59*c17dd299SFrançois Tigeot  * - ordered
60*c17dd299SFrançois Tigeot  * - ordered high priority
61*c17dd299SFrançois Tigeot  * - unbound
62*c17dd299SFrançois Tigeot  * - unbound high priority
63*c17dd299SFrançois Tigeot  */
64*c17dd299SFrançois Tigeot 
65*c17dd299SFrançois Tigeot static inline void
process_all_work(struct workqueue_worker * worker)66*c17dd299SFrançois Tigeot process_all_work(struct workqueue_worker *worker)
67*c17dd299SFrançois Tigeot {
68*c17dd299SFrançois Tigeot 	struct work_struct *work;
69*c17dd299SFrançois Tigeot 	bool didcan;
70*c17dd299SFrançois Tigeot 
71*c17dd299SFrançois Tigeot 	while (STAILQ_FIRST(&worker->ws_list_head)) {
72*c17dd299SFrançois Tigeot 		work = STAILQ_FIRST(&worker->ws_list_head);
73*c17dd299SFrançois Tigeot 		STAILQ_REMOVE_HEAD(&worker->ws_list_head, ws_entries);
74*c17dd299SFrançois Tigeot 		work->on_queue = false;
75*c17dd299SFrançois Tigeot 
76*c17dd299SFrançois Tigeot 		/* A work shouldn't be executed concurrently on a single cpu */
77*c17dd299SFrançois Tigeot 		if (work->running)
78*c17dd299SFrançois Tigeot 			continue;
79*c17dd299SFrançois Tigeot 
80*c17dd299SFrançois Tigeot 		/* Do not run canceled works */
81*c17dd299SFrançois Tigeot 		if (work->canceled) {
82*c17dd299SFrançois Tigeot 			/* XXX: should we allow canceled works to be reenabled ? */
83*c17dd299SFrançois Tigeot 			work->canceled = false;
84*c17dd299SFrançois Tigeot 			continue;
85*c17dd299SFrançois Tigeot 		}
86*c17dd299SFrançois Tigeot 
87*c17dd299SFrançois Tigeot 		work->running = true;
88*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_RELEASE);
89*c17dd299SFrançois Tigeot 		work->func(work);
90*c17dd299SFrançois Tigeot 		lwkt_yield();
91*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
92*c17dd299SFrançois Tigeot 		if (work->on_queue == false)
93*c17dd299SFrançois Tigeot 			work->worker = NULL;
94*c17dd299SFrançois Tigeot 		didcan = work->canceled;
95*c17dd299SFrançois Tigeot 		cpu_sfence();
96*c17dd299SFrançois Tigeot 		work->running = false;
97*c17dd299SFrançois Tigeot 		if (didcan == true)
98*c17dd299SFrançois Tigeot 			wakeup(work);
99*c17dd299SFrançois Tigeot 	}
100*c17dd299SFrançois Tigeot }
101*c17dd299SFrançois Tigeot 
102*c17dd299SFrançois Tigeot static void
wq_worker_thread(void * arg)103*c17dd299SFrançois Tigeot wq_worker_thread(void *arg)
104*c17dd299SFrançois Tigeot {
105*c17dd299SFrançois Tigeot 	struct workqueue_worker *worker = arg;
106*c17dd299SFrançois Tigeot 
107*c17dd299SFrançois Tigeot 	lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
108*c17dd299SFrançois Tigeot 	while (1) {
109*c17dd299SFrançois Tigeot 		process_all_work(worker);
110*c17dd299SFrançois Tigeot 		lksleep(worker, &worker->worker_lock, 0, "wqidle", 0);
111*c17dd299SFrançois Tigeot 	}
112*c17dd299SFrançois Tigeot 	lockmgr(&worker->worker_lock, LK_RELEASE);
113*c17dd299SFrançois Tigeot }
114*c17dd299SFrançois Tigeot 
115*c17dd299SFrançois Tigeot /*
116*c17dd299SFrançois Tigeot  * Return false if work was already on a queue
117*c17dd299SFrançois Tigeot  * Return true and queue it if this was not the case
118*c17dd299SFrançois Tigeot  */
119*c17dd299SFrançois Tigeot int
queue_work(struct workqueue_struct * wq,struct work_struct * work)120*c17dd299SFrançois Tigeot queue_work(struct workqueue_struct *wq, struct work_struct *work)
121*c17dd299SFrançois Tigeot {
122*c17dd299SFrançois Tigeot 	struct workqueue_worker *worker;
123*c17dd299SFrançois Tigeot 	int ret = false;
124*c17dd299SFrançois Tigeot 
125*c17dd299SFrançois Tigeot 	/* XXX: should we block instead ? */
126*c17dd299SFrançois Tigeot 	if (wq->is_draining)
127*c17dd299SFrançois Tigeot 		return false;
128*c17dd299SFrançois Tigeot 
129*c17dd299SFrançois Tigeot 	if (wq->num_workers > 1)
130*c17dd299SFrançois Tigeot 		worker = &(*wq->workers)[mycpuid];
131*c17dd299SFrançois Tigeot 	else
132*c17dd299SFrançois Tigeot 		worker = &(*wq->workers)[0];
133*c17dd299SFrançois Tigeot 
134*c17dd299SFrançois Tigeot 	lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
135*c17dd299SFrançois Tigeot 	work->canceled = false;
136*c17dd299SFrançois Tigeot 	if (work->on_queue == false || work->running == false) {
137*c17dd299SFrançois Tigeot 		if (work->on_queue == false) {
138*c17dd299SFrançois Tigeot 			STAILQ_INSERT_TAIL(&worker->ws_list_head, work,
139*c17dd299SFrançois Tigeot 					   ws_entries);
140*c17dd299SFrançois Tigeot 			work->on_queue = true;
141*c17dd299SFrançois Tigeot 			work->worker = worker;
142*c17dd299SFrançois Tigeot 			wakeup_one(worker);
143*c17dd299SFrançois Tigeot 		}
144*c17dd299SFrançois Tigeot 		ret = true;
145*c17dd299SFrançois Tigeot 	}
146*c17dd299SFrançois Tigeot 	lockmgr(&worker->worker_lock, LK_RELEASE);
147*c17dd299SFrançois Tigeot 
148*c17dd299SFrançois Tigeot 	return ret;
149*c17dd299SFrançois Tigeot }
150*c17dd299SFrançois Tigeot 
151*c17dd299SFrançois Tigeot static inline void
_delayed_work_fn(void * arg)152*c17dd299SFrançois Tigeot _delayed_work_fn(void *arg)
153*c17dd299SFrançois Tigeot {
154*c17dd299SFrançois Tigeot 	struct delayed_work *dw = arg;
155*c17dd299SFrançois Tigeot 
156*c17dd299SFrançois Tigeot 	queue_work(system_wq, &dw->work);
157*c17dd299SFrançois Tigeot }
158*c17dd299SFrançois Tigeot 
159*c17dd299SFrançois Tigeot int
queue_delayed_work(struct workqueue_struct * wq,struct delayed_work * work,unsigned long delay)160*c17dd299SFrançois Tigeot queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work,
161*c17dd299SFrançois Tigeot     unsigned long delay)
162*c17dd299SFrançois Tigeot {
163*c17dd299SFrançois Tigeot 	int pending = work->work.on_queue; // XXX: running too ?
164*c17dd299SFrançois Tigeot 	if (delay != 0) {
165*c17dd299SFrançois Tigeot 		callout_reset(&work->timer, delay, _delayed_work_fn, work);
166*c17dd299SFrançois Tigeot 	} else {
167*c17dd299SFrançois Tigeot 		_delayed_work_fn((void *)work);
168*c17dd299SFrançois Tigeot 	}
169*c17dd299SFrançois Tigeot 
170*c17dd299SFrançois Tigeot 	return (!pending);
171*c17dd299SFrançois Tigeot }
172*c17dd299SFrançois Tigeot 
173*c17dd299SFrançois Tigeot static int
init_workqueues(void * arg)174*c17dd299SFrançois Tigeot init_workqueues(void *arg)
175df8db295SFrançois Tigeot {
176df8db295SFrançois Tigeot 	system_wq = alloc_workqueue("system_wq", 0, 1);
177*c17dd299SFrançois Tigeot 	system_highpri_wq = alloc_workqueue("system_highpri_wq", WQ_HIGHPRI, 1);
178df8db295SFrançois Tigeot 	system_long_wq = alloc_workqueue("system_long_wq", 0, 1);
179*c17dd299SFrançois Tigeot 	system_unbound_wq = alloc_workqueue("system_unbound_wq", WQ_UNBOUND, 1);
1802cecdd68SFrançois Tigeot 	system_power_efficient_wq = alloc_workqueue("system_power_efficient_wq", 0, 1);
181df8db295SFrançois Tigeot 
182df8db295SFrançois Tigeot 	return 0;
183df8db295SFrançois Tigeot }
184f5e8ad19SImre Vadász 
destroy_workqueues(void * arg)185f5e8ad19SImre Vadász static int destroy_workqueues(void *arg)
186f5e8ad19SImre Vadász {
187f5e8ad19SImre Vadász 	destroy_workqueue(system_wq);
1882cecdd68SFrançois Tigeot 	destroy_workqueue(system_highpri_wq);
189f5e8ad19SImre Vadász 	destroy_workqueue(system_long_wq);
19096b26154SFrançois Tigeot 	destroy_workqueue(system_unbound_wq);
1912cecdd68SFrançois Tigeot 	destroy_workqueue(system_power_efficient_wq);
192f5e8ad19SImre Vadász 
193f5e8ad19SImre Vadász 	return 0;
194f5e8ad19SImre Vadász }
195f5e8ad19SImre Vadász 
196d6aa1cc5SFrançois Tigeot struct workqueue_struct *
_create_workqueue_common(const char * name,int flags)197*c17dd299SFrançois Tigeot _create_workqueue_common(const char *name, int flags)
198d6aa1cc5SFrançois Tigeot {
199d6aa1cc5SFrançois Tigeot 	struct workqueue_struct *wq;
200*c17dd299SFrançois Tigeot 	int priority, error;
201d6aa1cc5SFrançois Tigeot 
202*c17dd299SFrançois Tigeot 	wq = kmalloc(sizeof(*wq), M_DRM, M_WAITOK | M_ZERO);
203d6aa1cc5SFrançois Tigeot 
204*c17dd299SFrançois Tigeot 	if (flags & WQ_HIGHPRI)
205*c17dd299SFrançois Tigeot 		priority = TDPRI_INT_SUPPORT;
206*c17dd299SFrançois Tigeot 	else
207*c17dd299SFrançois Tigeot 		priority = TDPRI_KERN_DAEMON;
208*c17dd299SFrançois Tigeot 
209*c17dd299SFrançois Tigeot 	if (flags & WQ_UNBOUND) {
210*c17dd299SFrançois Tigeot 		wq->num_workers = 1;
211*c17dd299SFrançois Tigeot 	} else {
212*c17dd299SFrançois Tigeot 		wq->num_workers = ncpus;
213*c17dd299SFrançois Tigeot 	}
214*c17dd299SFrançois Tigeot 	wq->workers = kmalloc(sizeof(struct workqueue_worker) * wq->num_workers,
215*c17dd299SFrançois Tigeot 			M_DRM, M_WAITOK | M_ZERO);
216*c17dd299SFrançois Tigeot 
217*c17dd299SFrançois Tigeot 	for (int i = 0;i < wq->num_workers; i++) {
218*c17dd299SFrançois Tigeot 		struct workqueue_worker *worker = &(*wq->workers)[i];
219*c17dd299SFrançois Tigeot 
220*c17dd299SFrançois Tigeot 		lockinit(&worker->worker_lock, "lwq", 0, 0);
221*c17dd299SFrançois Tigeot 		STAILQ_INIT(&worker->ws_list_head);
222*c17dd299SFrançois Tigeot 		if (wq->num_workers > 1) {
223*c17dd299SFrançois Tigeot 			error = lwkt_create(wq_worker_thread, worker,
224*c17dd299SFrançois Tigeot 				    &worker->worker_thread, NULL, TDF_NOSTART, i, "%s/%d", name, i);
225*c17dd299SFrançois Tigeot 		} else {
226*c17dd299SFrançois Tigeot 			error = lwkt_create(wq_worker_thread, worker,
227*c17dd299SFrançois Tigeot 				    &worker->worker_thread, NULL, TDF_NOSTART, -1, name);
228*c17dd299SFrançois Tigeot 		}
229*c17dd299SFrançois Tigeot 		if (error) {
230*c17dd299SFrançois Tigeot 			kprintf("%s: lwkt_create(%s/%d): error %d",
231*c17dd299SFrançois Tigeot 			    __func__, name, i, error);
232*c17dd299SFrançois Tigeot 			/* XXX: destroy kernel threads and free workers[] if applicable */
233*c17dd299SFrançois Tigeot 			kfree(wq);
234*c17dd299SFrançois Tigeot 			return NULL;
235*c17dd299SFrançois Tigeot 		}
236*c17dd299SFrançois Tigeot 		lwkt_setpri_initial(worker->worker_thread, priority);
237*c17dd299SFrançois Tigeot 		lwkt_schedule(worker->worker_thread);
238*c17dd299SFrançois Tigeot 	}
239*c17dd299SFrançois Tigeot 
240*c17dd299SFrançois Tigeot 	return wq;
241d6aa1cc5SFrançois Tigeot }
242d6aa1cc5SFrançois Tigeot 
243d6aa1cc5SFrançois Tigeot void
destroy_workqueue(struct workqueue_struct * wq)244d6aa1cc5SFrançois Tigeot destroy_workqueue(struct workqueue_struct *wq)
245d6aa1cc5SFrançois Tigeot {
246*c17dd299SFrançois Tigeot 	drain_workqueue(wq);
247*c17dd299SFrançois Tigeot //	wq->is_draining = true;
248*c17dd299SFrançois Tigeot #if 0	/* XXX TODO */
249*c17dd299SFrançois Tigeot 	kill_all_threads;
250*c17dd299SFrançois Tigeot 	kfree(wq->wq_threads);
251d6aa1cc5SFrançois Tigeot 	kfree(wq);
252*c17dd299SFrançois Tigeot #endif
253d6aa1cc5SFrançois Tigeot }
254d6aa1cc5SFrançois Tigeot 
255f5e8ad19SImre Vadász SYSINIT(linux_workqueue_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, init_workqueues, NULL);
256f5e8ad19SImre Vadász SYSUNINIT(linux_workqueue_destroy, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, destroy_workqueues, NULL);
2575dfe0225SFrançois Tigeot 
2585dfe0225SFrançois Tigeot bool
flush_delayed_work(struct delayed_work * dwork)2595dfe0225SFrançois Tigeot flush_delayed_work(struct delayed_work *dwork)
2605dfe0225SFrançois Tigeot {
2615dfe0225SFrançois Tigeot 	callout_drain(&dwork->timer);
2625dfe0225SFrançois Tigeot 	return flush_work(&dwork->work);
2635dfe0225SFrançois Tigeot }
2645ef31fe9SFrançois Tigeot 
265*c17dd299SFrançois Tigeot /* Wait until the wq becomes empty */
2665ef31fe9SFrançois Tigeot void
drain_workqueue(struct workqueue_struct * wq)2675ef31fe9SFrançois Tigeot drain_workqueue(struct workqueue_struct *wq)
2685ef31fe9SFrançois Tigeot {
269*c17dd299SFrançois Tigeot 	struct workqueue_worker *worker;
270*c17dd299SFrançois Tigeot 
2715ef31fe9SFrançois Tigeot 	wq->is_draining = true;
2725ef31fe9SFrançois Tigeot 
273*c17dd299SFrançois Tigeot 	for (int i=0;i < wq->num_workers; i++) {
274*c17dd299SFrançois Tigeot 		worker = &(*wq->workers)[i];
2755ef31fe9SFrançois Tigeot 
276*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
277*c17dd299SFrançois Tigeot 		while (!STAILQ_EMPTY(&worker->ws_list_head)) {
278*c17dd299SFrançois Tigeot 		/* XXX: introduces latency */
279*c17dd299SFrançois Tigeot 			tsleep(&drain_workqueue, 0, "wkdrain", 1);
280*c17dd299SFrançois Tigeot 		}
281*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_RELEASE);
282*c17dd299SFrançois Tigeot 	}
283*c17dd299SFrançois Tigeot 
284*c17dd299SFrançois Tigeot 	/* XXX: No more work will be queued. is that right ? */
285*c17dd299SFrançois Tigeot //	wq->is_draining = false;
286*c17dd299SFrançois Tigeot }
287*c17dd299SFrançois Tigeot 
288*c17dd299SFrançois Tigeot bool
work_pending(struct work_struct * work)289*c17dd299SFrançois Tigeot work_pending(struct work_struct *work)
290*c17dd299SFrançois Tigeot {
291*c17dd299SFrançois Tigeot 	/* XXX: is on_queue the only constraint ? */
292*c17dd299SFrançois Tigeot 	return work->on_queue;
293*c17dd299SFrançois Tigeot }
294*c17dd299SFrançois Tigeot 
295*c17dd299SFrançois Tigeot unsigned int
work_busy(struct work_struct * work)296*c17dd299SFrançois Tigeot work_busy(struct work_struct *work)
297*c17dd299SFrançois Tigeot {
298*c17dd299SFrançois Tigeot 	return (work->on_queue || work->running);
299*c17dd299SFrançois Tigeot }
300*c17dd299SFrançois Tigeot 
301*c17dd299SFrançois Tigeot static inline void
__flush_work_func(struct work_struct * work)302*c17dd299SFrançois Tigeot __flush_work_func(struct work_struct *work)
303*c17dd299SFrançois Tigeot {
304*c17dd299SFrançois Tigeot 	wakeup_one(work);
305*c17dd299SFrançois Tigeot }
306*c17dd299SFrançois Tigeot 
307*c17dd299SFrançois Tigeot /* XXX introduces latency ? */
308*c17dd299SFrançois Tigeot void
flush_workqueue(struct workqueue_struct * wq)309*c17dd299SFrançois Tigeot flush_workqueue(struct workqueue_struct *wq)
310*c17dd299SFrançois Tigeot {
311*c17dd299SFrançois Tigeot 	struct work_struct __flush_work;
312*c17dd299SFrançois Tigeot 
313*c17dd299SFrançois Tigeot 	INIT_WORK(&__flush_work, __flush_work_func);
314*c17dd299SFrançois Tigeot 
315*c17dd299SFrançois Tigeot 	queue_work(wq, &__flush_work);
316*c17dd299SFrançois Tigeot 	while (__flush_work.on_queue || __flush_work.running) {
317*c17dd299SFrançois Tigeot 		tsleep(&__flush_work, 0, "flshwq", 0);
318*c17dd299SFrançois Tigeot 	}
319*c17dd299SFrançois Tigeot }
320*c17dd299SFrançois Tigeot 
321*c17dd299SFrançois Tigeot /*
322*c17dd299SFrançois Tigeot  * Wait until a work is done (has been executed)
323*c17dd299SFrançois Tigeot  * Return true if this function had to wait, and false otherwise
324*c17dd299SFrançois Tigeot  */
325*c17dd299SFrançois Tigeot bool
flush_work(struct work_struct * work)326*c17dd299SFrançois Tigeot flush_work(struct work_struct *work)
327*c17dd299SFrançois Tigeot {
328*c17dd299SFrançois Tigeot 	int ret = false;
329*c17dd299SFrançois Tigeot 
330*c17dd299SFrançois Tigeot 	/* XXX: probably unreliable */
331*c17dd299SFrançois Tigeot 	while (work->on_queue || work->running) {
332*c17dd299SFrançois Tigeot 		ret = true;
333*c17dd299SFrançois Tigeot 		/* XXX: use something more intelligent than tsleep() */
334*c17dd299SFrançois Tigeot 		tsleep(&flush_work, 0, "flshwrk", 1);
335*c17dd299SFrançois Tigeot 	}
336*c17dd299SFrançois Tigeot 
337*c17dd299SFrançois Tigeot 	return ret;
338*c17dd299SFrançois Tigeot }
339*c17dd299SFrançois Tigeot 
340*c17dd299SFrançois Tigeot static inline bool
_cancel_work(struct work_struct * work,bool sync_wait)341*c17dd299SFrançois Tigeot _cancel_work(struct work_struct *work, bool sync_wait)
342*c17dd299SFrançois Tigeot {
343*c17dd299SFrançois Tigeot 	struct workqueue_worker *worker;
344*c17dd299SFrançois Tigeot 	bool ret;
345*c17dd299SFrançois Tigeot 
346*c17dd299SFrançois Tigeot 	ret = false;
347*c17dd299SFrançois Tigeot 
348*c17dd299SFrançois Tigeot 	for (;;) {
349*c17dd299SFrançois Tigeot 		if (work->on_queue) {
350*c17dd299SFrançois Tigeot 			worker = work->worker;
351*c17dd299SFrançois Tigeot 			if (worker == NULL)
352*c17dd299SFrançois Tigeot 				continue;
353*c17dd299SFrançois Tigeot 			lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
354*c17dd299SFrançois Tigeot 			if (worker != work->worker || work->on_queue == false) {
355*c17dd299SFrançois Tigeot 				lockmgr(&worker->worker_lock, LK_RELEASE);
356*c17dd299SFrançois Tigeot 				continue;
357*c17dd299SFrançois Tigeot 			}
358*c17dd299SFrançois Tigeot 			STAILQ_REMOVE(&worker->ws_list_head, work,
359*c17dd299SFrançois Tigeot 				      work_struct, ws_entries);
360*c17dd299SFrançois Tigeot 			work->on_queue = false;
361*c17dd299SFrançois Tigeot 			ret = true;
362*c17dd299SFrançois Tigeot 			lockmgr(&worker->worker_lock, LK_RELEASE);
363*c17dd299SFrançois Tigeot 		}
364*c17dd299SFrançois Tigeot 		if (work->running == false)
365*c17dd299SFrançois Tigeot 			break;
366*c17dd299SFrançois Tigeot 
367*c17dd299SFrançois Tigeot 		worker = work->worker;
368*c17dd299SFrançois Tigeot 		if (worker == NULL)
369*c17dd299SFrançois Tigeot 			continue;
370*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
371*c17dd299SFrançois Tigeot 		if (worker != work->worker || work->running == false) {
372*c17dd299SFrançois Tigeot 			lockmgr(&worker->worker_lock, LK_RELEASE);
373*c17dd299SFrançois Tigeot 			continue;
374*c17dd299SFrançois Tigeot 		}
375*c17dd299SFrançois Tigeot 		work->canceled = true;
376*c17dd299SFrançois Tigeot 		ret = true;
377*c17dd299SFrançois Tigeot 		if (sync_wait == false) {
378*c17dd299SFrançois Tigeot 			lockmgr(&worker->worker_lock, LK_RELEASE);
379*c17dd299SFrançois Tigeot 			break;
380*c17dd299SFrançois Tigeot 		}
381*c17dd299SFrançois Tigeot 		/* XXX this races */
382*c17dd299SFrançois Tigeot 		lksleep(work, &worker->worker_lock, 0, "wqcan", 1);
383*c17dd299SFrançois Tigeot 		lockmgr(&worker->worker_lock, LK_RELEASE);
384*c17dd299SFrançois Tigeot 		/* retest */
385*c17dd299SFrançois Tigeot 	}
386*c17dd299SFrançois Tigeot 
387*c17dd299SFrançois Tigeot 	return ret;
388*c17dd299SFrançois Tigeot }
389*c17dd299SFrançois Tigeot 
390*c17dd299SFrançois Tigeot /*
391*c17dd299SFrançois Tigeot  * If work was queued, remove it from the queue and return true.
392*c17dd299SFrançois Tigeot  * If work was not queued, return false.
393*c17dd299SFrançois Tigeot  * In any case, wait for work to complete or be removed from the workqueue,
394*c17dd299SFrançois Tigeot  * callers may free associated data structures after this call.
395*c17dd299SFrançois Tigeot  */
396*c17dd299SFrançois Tigeot bool
cancel_work_sync(struct work_struct * work)397*c17dd299SFrançois Tigeot cancel_work_sync(struct work_struct *work)
398*c17dd299SFrançois Tigeot {
399*c17dd299SFrançois Tigeot 	return _cancel_work(work, true);
400*c17dd299SFrançois Tigeot }
401*c17dd299SFrançois Tigeot 
402*c17dd299SFrançois Tigeot /* Return false if work wasn't pending
403*c17dd299SFrançois Tigeot  * Return true if work was pending and canceled */
404*c17dd299SFrançois Tigeot bool
cancel_delayed_work(struct delayed_work * dwork)405*c17dd299SFrançois Tigeot cancel_delayed_work(struct delayed_work *dwork)
406*c17dd299SFrançois Tigeot {
407*c17dd299SFrançois Tigeot 	struct work_struct *work = &dwork->work;
408*c17dd299SFrançois Tigeot 
409*c17dd299SFrançois Tigeot 	work->canceled = true;
410*c17dd299SFrançois Tigeot 	callout_cancel(&dwork->timer);
411*c17dd299SFrançois Tigeot 
412*c17dd299SFrançois Tigeot 	return _cancel_work(work, false);
413*c17dd299SFrançois Tigeot }
414*c17dd299SFrançois Tigeot 
415*c17dd299SFrançois Tigeot bool
cancel_delayed_work_sync(struct delayed_work * dwork)416*c17dd299SFrançois Tigeot cancel_delayed_work_sync(struct delayed_work *dwork)
417*c17dd299SFrançois Tigeot {
418*c17dd299SFrançois Tigeot 	struct work_struct *work = &dwork->work;
419*c17dd299SFrançois Tigeot 
420*c17dd299SFrançois Tigeot 	work->canceled = true;
421*c17dd299SFrançois Tigeot 	callout_cancel(&dwork->timer);
422*c17dd299SFrançois Tigeot 
423*c17dd299SFrançois Tigeot 	return _cancel_work(work, true);
424*c17dd299SFrançois Tigeot }
425*c17dd299SFrançois Tigeot 
426*c17dd299SFrançois Tigeot bool
delayed_work_pending(struct delayed_work * dw)427*c17dd299SFrançois Tigeot delayed_work_pending(struct delayed_work *dw)
428*c17dd299SFrançois Tigeot {
429*c17dd299SFrançois Tigeot 	/* XXX: possibly wrong if the timer hasn't yet fired */
430*c17dd299SFrançois Tigeot 	return work_pending(&dw->work);
431*c17dd299SFrançois Tigeot }
432*c17dd299SFrançois Tigeot 
433*c17dd299SFrançois Tigeot void
destroy_work_on_stack(struct work_struct * work)434*c17dd299SFrançois Tigeot destroy_work_on_stack(struct work_struct *work)
435*c17dd299SFrançois Tigeot {
436*c17dd299SFrançois Tigeot }
437*c17dd299SFrançois Tigeot 
438*c17dd299SFrançois Tigeot void
destroy_delayed_work_on_stack(struct delayed_work * work)439*c17dd299SFrançois Tigeot destroy_delayed_work_on_stack(struct delayed_work *work)
440*c17dd299SFrançois Tigeot {
4415ef31fe9SFrançois Tigeot }
442