xref: /openbsd-src/sys/kern/kern_task.c (revision 2ee1bbb5f6a87ed949b08a2fee3cd4465428539c)
1*2ee1bbb5Smvs /*	$OpenBSD: kern_task.c,v 1.36 2025/01/13 03:21:10 mvs Exp $ */
2e72c42a1Sdlg 
3e72c42a1Sdlg /*
4e72c42a1Sdlg  * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
5e72c42a1Sdlg  *
6e72c42a1Sdlg  * Permission to use, copy, modify, and distribute this software for any
7e72c42a1Sdlg  * purpose with or without fee is hereby granted, provided that the above
8e72c42a1Sdlg  * copyright notice and this permission notice appear in all copies.
9e72c42a1Sdlg  *
10e72c42a1Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11e72c42a1Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12e72c42a1Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13e72c42a1Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14e72c42a1Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15e72c42a1Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16e72c42a1Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e72c42a1Sdlg  */
18e72c42a1Sdlg 
19e72c42a1Sdlg #include <sys/param.h>
20e72c42a1Sdlg #include <sys/systm.h>
21e72c42a1Sdlg #include <sys/malloc.h>
22e72c42a1Sdlg #include <sys/mutex.h>
23e72c42a1Sdlg #include <sys/kthread.h>
24e72c42a1Sdlg #include <sys/task.h>
256f1a6781Sdlg #include <sys/proc.h>
2660aa962eSdlg #include <sys/witness.h>
2760aa962eSdlg 
288430bc4bSanton #include "kcov.h"
298430bc4bSanton #if NKCOV > 0
308430bc4bSanton #include <sys/kcov.h>
318430bc4bSanton #endif
328430bc4bSanton 
3360aa962eSdlg #ifdef WITNESS
3460aa962eSdlg 
3560aa962eSdlg static struct lock_type taskq_lock_type = {
3660aa962eSdlg 	.lt_name = "taskq"
3760aa962eSdlg };
3860aa962eSdlg 
3960aa962eSdlg #define TASKQ_LOCK_FLAGS LO_WITNESS | LO_INITIALIZED | LO_SLEEPABLE | \
4060aa962eSdlg     (LO_CLASS_RWLOCK << LO_CLASSSHIFT)
4160aa962eSdlg 
4260aa962eSdlg #endif /* WITNESS */
43e72c42a1Sdlg 
44ba614123Sdlg struct taskq_thread {
45ba614123Sdlg 	SLIST_ENTRY(taskq_thread)
46ba614123Sdlg 				 tt_entry;
47ba614123Sdlg 	struct proc		*tt_thread;
48ba614123Sdlg };
49ba614123Sdlg SLIST_HEAD(taskq_threads, taskq_thread);
50ba614123Sdlg 
51e72c42a1Sdlg struct taskq {
52e72c42a1Sdlg 	enum {
53e72c42a1Sdlg 		TQ_S_CREATED,
54e72c42a1Sdlg 		TQ_S_RUNNING,
55e72c42a1Sdlg 		TQ_S_DESTROYED
56e72c42a1Sdlg 	}			 tq_state;
573c9f708dSdlg 	unsigned int		 tq_running;
583c9f708dSdlg 	unsigned int		 tq_nthreads;
5979ea9c08Sdlg 	unsigned int		 tq_flags;
60e72c42a1Sdlg 	const char		*tq_name;
61e72c42a1Sdlg 
62e72c42a1Sdlg 	struct mutex		 tq_mtx;
63d3bdd90fSdlg 	struct task_list	 tq_worklist;
64ba614123Sdlg 
65ba614123Sdlg 	struct taskq_threads	 tq_threads;
66ba614123Sdlg 	unsigned int		 tq_barriers;
67ba614123Sdlg 	unsigned int		 tq_bgen;
68ba614123Sdlg 	unsigned int		 tq_bthreads;
69ba614123Sdlg 
7060aa962eSdlg #ifdef WITNESS
7160aa962eSdlg 	struct lock_object	 tq_lock_object;
7260aa962eSdlg #endif
73e72c42a1Sdlg };
74e72c42a1Sdlg 
7560aa962eSdlg static const char taskq_sys_name[] = "systq";
7660aa962eSdlg 
77e72c42a1Sdlg struct taskq taskq_sys = {
78ba614123Sdlg 	.tq_state	= TQ_S_CREATED,
79ba614123Sdlg 	.tq_running	= 0,
80ba614123Sdlg 	.tq_nthreads	= 1,
81ba614123Sdlg 	.tq_flags	= 0,
82ba614123Sdlg 	.tq_name	= taskq_sys_name,
83ba614123Sdlg 	.tq_mtx		= MUTEX_INITIALIZER_FLAGS(IPL_HIGH,
84ba614123Sdlg 			      taskq_sys_name, 0),
85ba614123Sdlg 	.tq_worklist	= TAILQ_HEAD_INITIALIZER(taskq_sys.tq_worklist),
86ba614123Sdlg 
87ba614123Sdlg 	.tq_threads	= SLIST_HEAD_INITIALIZER(taskq_sys.tq_threads),
88ba614123Sdlg 	.tq_barriers	= 0,
89ba614123Sdlg 	.tq_bgen	= 0,
90ba614123Sdlg 	.tq_bthreads	= 0,
91ba614123Sdlg 
9260aa962eSdlg #ifdef WITNESS
93ba614123Sdlg 	.tq_lock_object	= {
9460aa962eSdlg 		.lo_name	= taskq_sys_name,
9560aa962eSdlg 		.lo_flags	= TASKQ_LOCK_FLAGS,
9660aa962eSdlg 	},
9760aa962eSdlg #endif
98e72c42a1Sdlg };
99e72c42a1Sdlg 
10060aa962eSdlg static const char taskq_sys_mp_name[] = "systqmp";
10160aa962eSdlg 
10253eebcffSblambert struct taskq taskq_sys_mp = {
103ba614123Sdlg 	.tq_state	= TQ_S_CREATED,
104ba614123Sdlg 	.tq_running	= 0,
105ba614123Sdlg 	.tq_nthreads	= 1,
106ba614123Sdlg 	.tq_flags	= TASKQ_MPSAFE,
107ba614123Sdlg 	.tq_name	= taskq_sys_mp_name,
108ba614123Sdlg 	.tq_mtx		= MUTEX_INITIALIZER_FLAGS(IPL_HIGH,
109ba614123Sdlg 			      taskq_sys_mp_name, 0),
110ba614123Sdlg 	.tq_worklist	= TAILQ_HEAD_INITIALIZER(taskq_sys_mp.tq_worklist),
111ba614123Sdlg 
112ba614123Sdlg 	.tq_threads	= SLIST_HEAD_INITIALIZER(taskq_sys_mp.tq_threads),
113ba614123Sdlg 	.tq_barriers	= 0,
114ba614123Sdlg 	.tq_bgen	= 0,
115ba614123Sdlg 	.tq_bthreads	= 0,
116ba614123Sdlg 
11760aa962eSdlg #ifdef WITNESS
118ba614123Sdlg 	.tq_lock_object = {
11960aa962eSdlg 		.lo_name	= taskq_sys_mp_name,
12060aa962eSdlg 		.lo_flags	= TASKQ_LOCK_FLAGS,
12160aa962eSdlg 	},
12260aa962eSdlg #endif
12353eebcffSblambert };
12453eebcffSblambert 
1251c81877cSdlg struct taskq *const systq = &taskq_sys;
12653eebcffSblambert struct taskq *const systqmp = &taskq_sys_mp;
1271c81877cSdlg 
128e72c42a1Sdlg void	taskq_init(void); /* called in init_main.c */
129e72c42a1Sdlg void	taskq_create_thread(void *);
1306f1a6781Sdlg void	taskq_barrier_task(void *);
131f07ea34cSdlg int	taskq_next_work(struct taskq *, struct task *);
132e72c42a1Sdlg void	taskq_thread(void *);
133e72c42a1Sdlg 
134e72c42a1Sdlg void
135e72c42a1Sdlg taskq_init(void)
136e72c42a1Sdlg {
13760aa962eSdlg 	WITNESS_INIT(&systq->tq_lock_object, &taskq_lock_type);
1381c81877cSdlg 	kthread_create_deferred(taskq_create_thread, systq);
13960aa962eSdlg 	WITNESS_INIT(&systqmp->tq_lock_object, &taskq_lock_type);
14053eebcffSblambert 	kthread_create_deferred(taskq_create_thread, systqmp);
141e72c42a1Sdlg }
142e72c42a1Sdlg 
143e72c42a1Sdlg struct taskq *
14479ea9c08Sdlg taskq_create(const char *name, unsigned int nthreads, int ipl,
14579ea9c08Sdlg     unsigned int flags)
146e72c42a1Sdlg {
147e72c42a1Sdlg 	struct taskq *tq;
148e72c42a1Sdlg 
1490bfe7560Sdlg 	tq = malloc(sizeof(*tq), M_DEVBUF, M_WAITOK);
150e72c42a1Sdlg 	if (tq == NULL)
151e72c42a1Sdlg 		return (NULL);
152e72c42a1Sdlg 
153e72c42a1Sdlg 	tq->tq_state = TQ_S_CREATED;
154e72c42a1Sdlg 	tq->tq_running = 0;
155e72c42a1Sdlg 	tq->tq_nthreads = nthreads;
156e72c42a1Sdlg 	tq->tq_name = name;
15779ea9c08Sdlg 	tq->tq_flags = flags;
1584e29e7adSkettenis 
159bd1acdabSvisa 	mtx_init_flags(&tq->tq_mtx, ipl, name, 0);
160e72c42a1Sdlg 	TAILQ_INIT(&tq->tq_worklist);
161e72c42a1Sdlg 
162ba614123Sdlg 	SLIST_INIT(&tq->tq_threads);
163ba614123Sdlg 	tq->tq_barriers = 0;
164ba614123Sdlg 	tq->tq_bgen = 0;
165ba614123Sdlg 	tq->tq_bthreads = 0;
166ba614123Sdlg 
16760aa962eSdlg #ifdef WITNESS
16860aa962eSdlg 	memset(&tq->tq_lock_object, 0, sizeof(tq->tq_lock_object));
16960aa962eSdlg 	tq->tq_lock_object.lo_name = name;
17060aa962eSdlg 	tq->tq_lock_object.lo_flags = TASKQ_LOCK_FLAGS;
17160aa962eSdlg 	witness_init(&tq->tq_lock_object, &taskq_lock_type);
17260aa962eSdlg #endif
17360aa962eSdlg 
174e72c42a1Sdlg 	/* try to create a thread to guarantee that tasks will be serviced */
175e72c42a1Sdlg 	kthread_create_deferred(taskq_create_thread, tq);
176e72c42a1Sdlg 
177e72c42a1Sdlg 	return (tq);
178e72c42a1Sdlg }
179e72c42a1Sdlg 
180e72c42a1Sdlg void
181e72c42a1Sdlg taskq_destroy(struct taskq *tq)
182e72c42a1Sdlg {
183e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
184e72c42a1Sdlg 	switch (tq->tq_state) {
185e72c42a1Sdlg 	case TQ_S_CREATED:
186e72c42a1Sdlg 		/* tq is still referenced by taskq_create_thread */
187e72c42a1Sdlg 		tq->tq_state = TQ_S_DESTROYED;
188e72c42a1Sdlg 		mtx_leave(&tq->tq_mtx);
189e72c42a1Sdlg 		return;
190e72c42a1Sdlg 
191e72c42a1Sdlg 	case TQ_S_RUNNING:
192e72c42a1Sdlg 		tq->tq_state = TQ_S_DESTROYED;
193e72c42a1Sdlg 		break;
194e72c42a1Sdlg 
195e72c42a1Sdlg 	default:
196e72c42a1Sdlg 		panic("unexpected %s tq state %u", tq->tq_name, tq->tq_state);
197e72c42a1Sdlg 	}
198e72c42a1Sdlg 
199e72c42a1Sdlg 	while (tq->tq_running > 0) {
200e72c42a1Sdlg 		wakeup(tq);
201babb761dSmpi 		msleep_nsec(&tq->tq_running, &tq->tq_mtx, PWAIT, "tqdestroy",
202babb761dSmpi 		    INFSLP);
203e72c42a1Sdlg 	}
204e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
205e72c42a1Sdlg 
206fc62de09Stedu 	free(tq, M_DEVBUF, sizeof(*tq));
207e72c42a1Sdlg }
208e72c42a1Sdlg 
209e72c42a1Sdlg void
210e72c42a1Sdlg taskq_create_thread(void *arg)
211e72c42a1Sdlg {
212e72c42a1Sdlg 	struct taskq *tq = arg;
213e72c42a1Sdlg 	int rv;
214e72c42a1Sdlg 
215e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
216e72c42a1Sdlg 
217e72c42a1Sdlg 	switch (tq->tq_state) {
218e72c42a1Sdlg 	case TQ_S_DESTROYED:
219e72c42a1Sdlg 		mtx_leave(&tq->tq_mtx);
220fc62de09Stedu 		free(tq, M_DEVBUF, sizeof(*tq));
221e72c42a1Sdlg 		return;
222e72c42a1Sdlg 
223e72c42a1Sdlg 	case TQ_S_CREATED:
224e72c42a1Sdlg 		tq->tq_state = TQ_S_RUNNING;
225e72c42a1Sdlg 		break;
226e72c42a1Sdlg 
227e72c42a1Sdlg 	default:
228e72c42a1Sdlg 		panic("unexpected %s tq state %d", tq->tq_name, tq->tq_state);
229e72c42a1Sdlg 	}
230e72c42a1Sdlg 
231e72c42a1Sdlg 	do {
232e72c42a1Sdlg 		tq->tq_running++;
233e72c42a1Sdlg 		mtx_leave(&tq->tq_mtx);
234e72c42a1Sdlg 
2352d257949Sderaadt 		rv = kthread_create(taskq_thread, tq, NULL, tq->tq_name);
236e72c42a1Sdlg 
237e72c42a1Sdlg 		mtx_enter(&tq->tq_mtx);
238e72c42a1Sdlg 		if (rv != 0) {
239e72c42a1Sdlg 			printf("unable to create thread for \"%s\" taskq\n",
240e72c42a1Sdlg 			    tq->tq_name);
241e72c42a1Sdlg 
242e72c42a1Sdlg 			tq->tq_running--;
243e72c42a1Sdlg 			/* could have been destroyed during kthread_create */
244e72c42a1Sdlg 			if (tq->tq_state == TQ_S_DESTROYED &&
245e72c42a1Sdlg 			    tq->tq_running == 0)
246e72c42a1Sdlg 				wakeup_one(&tq->tq_running);
247e72c42a1Sdlg 			break;
248e72c42a1Sdlg 		}
249e72c42a1Sdlg 	} while (tq->tq_running < tq->tq_nthreads);
250e72c42a1Sdlg 
251e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
252e72c42a1Sdlg }
253e72c42a1Sdlg 
254e72c42a1Sdlg void
2556f1a6781Sdlg taskq_barrier_task(void *p)
2566f1a6781Sdlg {
257ba614123Sdlg 	struct taskq *tq = p;
258ba614123Sdlg 	unsigned int gen;
259ba614123Sdlg 
260ba614123Sdlg 	mtx_enter(&tq->tq_mtx);
261ba614123Sdlg 	tq->tq_bthreads++;
262ba614123Sdlg 	wakeup(&tq->tq_bthreads);
263ba614123Sdlg 
264ba614123Sdlg 	gen = tq->tq_bgen;
265ba614123Sdlg 	do {
266ba614123Sdlg 		msleep_nsec(&tq->tq_bgen, &tq->tq_mtx,
267ba614123Sdlg 		    PWAIT, "tqbarend", INFSLP);
268ba614123Sdlg 	} while (gen == tq->tq_bgen);
269ba614123Sdlg 	mtx_leave(&tq->tq_mtx);
270ba614123Sdlg }
271ba614123Sdlg 
272ba614123Sdlg static void
273ba614123Sdlg taskq_do_barrier(struct taskq *tq)
274ba614123Sdlg {
275ba614123Sdlg 	struct task t = TASK_INITIALIZER(taskq_barrier_task, tq);
276ba614123Sdlg 	struct proc *thread = curproc;
277ba614123Sdlg 	struct taskq_thread *tt;
278ba614123Sdlg 
279ba614123Sdlg 	mtx_enter(&tq->tq_mtx);
280ba614123Sdlg 	tq->tq_barriers++;
281ba614123Sdlg 
282ba614123Sdlg 	/* is the barrier being run from a task inside the taskq? */
283ba614123Sdlg 	SLIST_FOREACH(tt, &tq->tq_threads, tt_entry) {
284ba614123Sdlg 		if (tt->tt_thread == thread) {
285ba614123Sdlg 			tq->tq_bthreads++;
286ba614123Sdlg 			wakeup(&tq->tq_bthreads);
287ba614123Sdlg 			break;
288ba614123Sdlg 		}
289ba614123Sdlg 	}
290ba614123Sdlg 
291ba614123Sdlg 	while (tq->tq_bthreads < tq->tq_nthreads) {
292ba614123Sdlg 		/* shove the task into the queue for a worker to pick up */
293ba614123Sdlg 		SET(t.t_flags, TASK_ONQUEUE);
2941330a2b3Sdlg 		TAILQ_INSERT_TAIL(&tq->tq_worklist, &t, t_entry);
295ba614123Sdlg 		wakeup_one(tq);
296ba614123Sdlg 
297ba614123Sdlg 		msleep_nsec(&tq->tq_bthreads, &tq->tq_mtx,
298ba614123Sdlg 		    PWAIT, "tqbar", INFSLP);
299ba614123Sdlg 
300ba614123Sdlg 		/*
301c3947ab6Sdlg 		 * another thread running a barrier might have
302ba614123Sdlg 		 * done this work for us.
303ba614123Sdlg 		 */
304ba614123Sdlg 		if (ISSET(t.t_flags, TASK_ONQUEUE))
305ba614123Sdlg 			TAILQ_REMOVE(&tq->tq_worklist, &t, t_entry);
306ba614123Sdlg 	}
307ba614123Sdlg 
308ba614123Sdlg 	if (--tq->tq_barriers == 0) {
309ba614123Sdlg 		/* we're the last one out */
310ba614123Sdlg 		tq->tq_bgen++;
311ba614123Sdlg 		wakeup(&tq->tq_bgen);
312ba614123Sdlg 		tq->tq_bthreads = 0;
313ba614123Sdlg 	} else {
314ba614123Sdlg 		unsigned int gen = tq->tq_bgen;
315ba614123Sdlg 		do {
316ba614123Sdlg 			msleep_nsec(&tq->tq_bgen, &tq->tq_mtx,
317ba614123Sdlg 			    PWAIT, "tqbarwait", INFSLP);
318ba614123Sdlg 		} while (gen == tq->tq_bgen);
319ba614123Sdlg 	}
320ba614123Sdlg 	mtx_leave(&tq->tq_mtx);
321ba614123Sdlg }
322ba614123Sdlg 
323ba614123Sdlg void
324ba614123Sdlg taskq_barrier(struct taskq *tq)
325ba614123Sdlg {
326ba614123Sdlg 	WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
327ba614123Sdlg 
328ba614123Sdlg 	taskq_do_barrier(tq);
329ba614123Sdlg }
330ba614123Sdlg 
331ba614123Sdlg void
332ba614123Sdlg taskq_del_barrier(struct taskq *tq, struct task *t)
333ba614123Sdlg {
334ba614123Sdlg 	WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
335ba614123Sdlg 
336*2ee1bbb5Smvs 	task_del(tq, t);
337ba614123Sdlg 	taskq_do_barrier(tq);
3386f1a6781Sdlg }
3396f1a6781Sdlg 
3406f1a6781Sdlg void
341e4195480Sdlg task_set(struct task *t, void (*fn)(void *), void *arg)
342e72c42a1Sdlg {
343e72c42a1Sdlg 	t->t_func = fn;
344e4195480Sdlg 	t->t_arg = arg;
345e72c42a1Sdlg 	t->t_flags = 0;
346e72c42a1Sdlg }
347e72c42a1Sdlg 
348e72c42a1Sdlg int
349e72c42a1Sdlg task_add(struct taskq *tq, struct task *w)
350e72c42a1Sdlg {
351e72c42a1Sdlg 	int rv = 0;
352e72c42a1Sdlg 
353a75b147bSmvs 	if (ISSET(w->t_flags, TASK_ONQUEUE))
354a75b147bSmvs 		return (0);
355a75b147bSmvs 
356e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
357e72c42a1Sdlg 	if (!ISSET(w->t_flags, TASK_ONQUEUE)) {
358e72c42a1Sdlg 		rv = 1;
359e72c42a1Sdlg 		SET(w->t_flags, TASK_ONQUEUE);
360e72c42a1Sdlg 		TAILQ_INSERT_TAIL(&tq->tq_worklist, w, t_entry);
3618430bc4bSanton #if NKCOV > 0
362da19784aSanton 		if (!kcov_cold)
3638430bc4bSanton 			w->t_process = curproc->p_p;
3648430bc4bSanton #endif
365e72c42a1Sdlg 	}
366e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
367e72c42a1Sdlg 
368e72c42a1Sdlg 	if (rv)
369e72c42a1Sdlg 		wakeup_one(tq);
370e72c42a1Sdlg 
371e72c42a1Sdlg 	return (rv);
372e72c42a1Sdlg }
373e72c42a1Sdlg 
374e72c42a1Sdlg int
375e72c42a1Sdlg task_del(struct taskq *tq, struct task *w)
376e72c42a1Sdlg {
377e72c42a1Sdlg 	int rv = 0;
378e72c42a1Sdlg 
379a75b147bSmvs 	if (!ISSET(w->t_flags, TASK_ONQUEUE))
380a75b147bSmvs 		return (0);
381a75b147bSmvs 
382e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
383e72c42a1Sdlg 	if (ISSET(w->t_flags, TASK_ONQUEUE)) {
384e72c42a1Sdlg 		rv = 1;
385e72c42a1Sdlg 		CLR(w->t_flags, TASK_ONQUEUE);
386e72c42a1Sdlg 		TAILQ_REMOVE(&tq->tq_worklist, w, t_entry);
387e72c42a1Sdlg 	}
388e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
389e72c42a1Sdlg 
390e72c42a1Sdlg 	return (rv);
391e72c42a1Sdlg }
392e72c42a1Sdlg 
393e72c42a1Sdlg int
394f07ea34cSdlg taskq_next_work(struct taskq *tq, struct task *work)
395e72c42a1Sdlg {
396e72c42a1Sdlg 	struct task *next;
397e72c42a1Sdlg 
398e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
399e72c42a1Sdlg 	while ((next = TAILQ_FIRST(&tq->tq_worklist)) == NULL) {
400e72c42a1Sdlg 		if (tq->tq_state != TQ_S_RUNNING) {
401e72c42a1Sdlg 			mtx_leave(&tq->tq_mtx);
402e72c42a1Sdlg 			return (0);
403e72c42a1Sdlg 		}
404e72c42a1Sdlg 
405babb761dSmpi 		msleep_nsec(tq, &tq->tq_mtx, PWAIT, "bored", INFSLP);
406e72c42a1Sdlg 	}
407e72c42a1Sdlg 
408e72c42a1Sdlg 	TAILQ_REMOVE(&tq->tq_worklist, next, t_entry);
409e72c42a1Sdlg 	CLR(next->t_flags, TASK_ONQUEUE);
410e72c42a1Sdlg 
411e72c42a1Sdlg 	*work = *next; /* copy to caller to avoid races */
412e72c42a1Sdlg 
413e72c42a1Sdlg 	next = TAILQ_FIRST(&tq->tq_worklist);
414e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
415e72c42a1Sdlg 
41608dae4e5Sdlg 	if (next != NULL && tq->tq_nthreads > 1)
417e72c42a1Sdlg 		wakeup_one(tq);
418e72c42a1Sdlg 
419e72c42a1Sdlg 	return (1);
420e72c42a1Sdlg }
421e72c42a1Sdlg 
422e72c42a1Sdlg void
423e72c42a1Sdlg taskq_thread(void *xtq)
424e72c42a1Sdlg {
425ba614123Sdlg 	struct taskq_thread self = { .tt_thread = curproc };
426e72c42a1Sdlg 	struct taskq *tq = xtq;
427e72c42a1Sdlg 	struct task work;
428e72c42a1Sdlg 	int last;
429e72c42a1Sdlg 
43079ea9c08Sdlg 	if (ISSET(tq->tq_flags, TASKQ_MPSAFE))
4314e29e7adSkettenis 		KERNEL_UNLOCK();
4324e29e7adSkettenis 
433ba614123Sdlg 	mtx_enter(&tq->tq_mtx);
434ba614123Sdlg 	SLIST_INSERT_HEAD(&tq->tq_threads, &self, tt_entry);
435ba614123Sdlg 	mtx_leave(&tq->tq_mtx);
436ba614123Sdlg 
43760aa962eSdlg 	WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
43860aa962eSdlg 
439f07ea34cSdlg 	while (taskq_next_work(tq, &work)) {
44060aa962eSdlg 		WITNESS_LOCK(&tq->tq_lock_object, 0);
4418430bc4bSanton #if NKCOV > 0
4428430bc4bSanton 		kcov_remote_enter(KCOV_REMOTE_COMMON, work.t_process);
4438430bc4bSanton #endif
444e4195480Sdlg 		(*work.t_func)(work.t_arg);
4458430bc4bSanton #if NKCOV > 0
4468430bc4bSanton 		kcov_remote_leave(KCOV_REMOTE_COMMON, work.t_process);
4478430bc4bSanton #endif
44860aa962eSdlg 		WITNESS_UNLOCK(&tq->tq_lock_object, 0);
4499b1ed563Smpi 		sched_pause(yield);
450bb38800eSblambert 	}
451e72c42a1Sdlg 
452e72c42a1Sdlg 	mtx_enter(&tq->tq_mtx);
453ba614123Sdlg 	SLIST_REMOVE(&tq->tq_threads, &self, taskq_thread, tt_entry);
454e72c42a1Sdlg 	last = (--tq->tq_running == 0);
455e72c42a1Sdlg 	mtx_leave(&tq->tq_mtx);
456e72c42a1Sdlg 
4579e51102cSdlg 	if (ISSET(tq->tq_flags, TASKQ_MPSAFE))
4589e51102cSdlg 		KERNEL_LOCK();
4599e51102cSdlg 
460e72c42a1Sdlg 	if (last)
461e72c42a1Sdlg 		wakeup_one(&tq->tq_running);
462e72c42a1Sdlg 
463e72c42a1Sdlg 	kthread_exit(0);
464e72c42a1Sdlg }
465