xref: /netbsd-src/sys/dev/sysmon/sysmon_taskq.c (revision fedc51107dbdd5c3050f16850829431bab1d18be)
1*fedc5110Sriastradh /*	$NetBSD: sysmon_taskq.c,v 1.23 2021/12/31 14:29:14 riastradh Exp $	*/
2d14efd22Sthorpej 
3d14efd22Sthorpej /*
4d14efd22Sthorpej  * Copyright (c) 2001, 2003 Wasabi Systems, Inc.
5d14efd22Sthorpej  * All rights reserved.
6d14efd22Sthorpej  *
7d14efd22Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8d14efd22Sthorpej  *
9d14efd22Sthorpej  * Redistribution and use in source and binary forms, with or without
10d14efd22Sthorpej  * modification, are permitted provided that the following conditions
11d14efd22Sthorpej  * are met:
12d14efd22Sthorpej  * 1. Redistributions of source code must retain the above copyright
13d14efd22Sthorpej  *    notice, this list of conditions and the following disclaimer.
14d14efd22Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15d14efd22Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16d14efd22Sthorpej  *    documentation and/or other materials provided with the distribution.
17d14efd22Sthorpej  * 3. All advertising materials mentioning features or use of this software
18d14efd22Sthorpej  *    must display the following acknowledgement:
19d14efd22Sthorpej  *	This product includes software developed for the NetBSD Project by
20d14efd22Sthorpej  *	Wasabi Systems, Inc.
21d14efd22Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22d14efd22Sthorpej  *    or promote products derived from this software without specific prior
23d14efd22Sthorpej  *    written permission.
24d14efd22Sthorpej  *
25d14efd22Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26d14efd22Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27d14efd22Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28d14efd22Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29d14efd22Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30d14efd22Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31d14efd22Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32d14efd22Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33d14efd22Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34d14efd22Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35d14efd22Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
36d14efd22Sthorpej  */
37d14efd22Sthorpej 
38d14efd22Sthorpej /*
39d14efd22Sthorpej  * General purpose task queue for sysmon back-ends.  This can be
40d14efd22Sthorpej  * used to run callbacks that require thread context.
41d14efd22Sthorpej  */
42d14efd22Sthorpej 
43365cbd94Slukem #include <sys/cdefs.h>
44*fedc5110Sriastradh __KERNEL_RCSID(0, "$NetBSD: sysmon_taskq.c,v 1.23 2021/12/31 14:29:14 riastradh Exp $");
45365cbd94Slukem 
46d14efd22Sthorpej #include <sys/param.h>
47d14efd22Sthorpej #include <sys/malloc.h>
48d14efd22Sthorpej #include <sys/queue.h>
49d14efd22Sthorpej #include <sys/proc.h>
50d14efd22Sthorpej #include <sys/kthread.h>
51d14efd22Sthorpej #include <sys/systm.h>
528061eedeSpgoyette #include <sys/module.h>
531f3e12e9Spgoyette #include <sys/once.h>
54d14efd22Sthorpej 
55d14efd22Sthorpej #include <dev/sysmon/sysmon_taskq.h>
56d14efd22Sthorpej 
57d14efd22Sthorpej struct sysmon_task {
58d14efd22Sthorpej 	TAILQ_ENTRY(sysmon_task) st_list;
59d14efd22Sthorpej 	void (*st_func)(void *);
60d14efd22Sthorpej 	void *st_arg;
61d14efd22Sthorpej 	u_int st_pri;
62d14efd22Sthorpej };
63d14efd22Sthorpej 
64d14efd22Sthorpej static TAILQ_HEAD(, sysmon_task) sysmon_task_queue =
65d14efd22Sthorpej     TAILQ_HEAD_INITIALIZER(sysmon_task_queue);
66d14efd22Sthorpej 
675623c9a1Sxtraeme static kmutex_t sysmon_task_queue_mtx;
685623c9a1Sxtraeme static kmutex_t sysmon_task_queue_init_mtx;
695623c9a1Sxtraeme static kcondvar_t sysmon_task_queue_cv;
70d14efd22Sthorpej 
715623c9a1Sxtraeme static int sysmon_task_queue_initialized;
722c91ca8bSchristos static int sysmon_task_queue_cleanup_sem;
7388ab7da9Sad static struct lwp *sysmon_task_queue_lwp;
74d14efd22Sthorpej static void sysmon_task_queue_thread(void *);
75d14efd22Sthorpej 
768061eedeSpgoyette MODULE(MODULE_CLASS_MISC, sysmon_taskq, NULL);
778061eedeSpgoyette 
788061eedeSpgoyette /*
798061eedeSpgoyette  * XXX	Normally, all initialization would be handled as part of
808061eedeSpgoyette  *	the module(9) framework.  However, there are a number of
818061eedeSpgoyette  *	users of the sysmon_taskq facility that are not modular,
828061eedeSpgoyette  *	and these can directly call sysmon_task_queue_init()
8332cded6cSdholland  *	directly.  To accommodate these non-standard users, we
848061eedeSpgoyette  *	make sure that sysmon_task_queue_init() handles multiple
858061eedeSpgoyette  *	invocations.  And we also ensure that, if any non-module
868061eedeSpgoyette  *	user exists, we don't allow the module to be unloaded.
878061eedeSpgoyette  *	(We can't use module_hold() for this, since the module(9)
888061eedeSpgoyette  *	framework itself isn't necessarily initialized yet.)
898061eedeSpgoyette  */
908061eedeSpgoyette 
918061eedeSpgoyette /*
921f3e12e9Spgoyette  * tq_preinit:
938061eedeSpgoyette  *
948061eedeSpgoyette  *	Early one-time initialization of task-queue
958061eedeSpgoyette  */
961f3e12e9Spgoyette 
971f3e12e9Spgoyette ONCE_DECL(once_tq);
981f3e12e9Spgoyette 
991f3e12e9Spgoyette static int
tq_preinit(void)1001f3e12e9Spgoyette tq_preinit(void)
1015623c9a1Sxtraeme {
1028061eedeSpgoyette 
103598ab03aSad 	mutex_init(&sysmon_task_queue_mtx, MUTEX_DEFAULT, IPL_VM);
1045623c9a1Sxtraeme 	mutex_init(&sysmon_task_queue_init_mtx, MUTEX_DEFAULT, IPL_NONE);
1055623c9a1Sxtraeme 	cv_init(&sysmon_task_queue_cv, "smtaskq");
1068061eedeSpgoyette 	sysmon_task_queue_initialized = 0;
1071f3e12e9Spgoyette 
1081f3e12e9Spgoyette 	return 0;
1095623c9a1Sxtraeme }
1105623c9a1Sxtraeme 
111d14efd22Sthorpej /*
112d14efd22Sthorpej  * sysmon_task_queue_init:
113d14efd22Sthorpej  *
114d14efd22Sthorpej  *	Initialize the sysmon task queue.
115d14efd22Sthorpej  */
116d14efd22Sthorpej void
sysmon_task_queue_init(void)117d14efd22Sthorpej sysmon_task_queue_init(void)
118d14efd22Sthorpej {
11988ab7da9Sad 	int error;
120d14efd22Sthorpej 
1211f3e12e9Spgoyette 	(void)RUN_ONCE(&once_tq, tq_preinit);
1221f3e12e9Spgoyette 
1235623c9a1Sxtraeme 	mutex_enter(&sysmon_task_queue_init_mtx);
1248061eedeSpgoyette 	if (sysmon_task_queue_initialized++) {
1255623c9a1Sxtraeme 		mutex_exit(&sysmon_task_queue_init_mtx);
126d14efd22Sthorpej 		return;
127d14efd22Sthorpej 	}
128d14efd22Sthorpej 
1295623c9a1Sxtraeme 	mutex_exit(&sysmon_task_queue_init_mtx);
130d14efd22Sthorpej 
131c32766fdSxtraeme 	error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
132c32766fdSxtraeme 	    sysmon_task_queue_thread, NULL, &sysmon_task_queue_lwp, "sysmon");
13388ab7da9Sad 	if (error) {
13488ab7da9Sad 		printf("Unable to create sysmon task queue thread, "
13588ab7da9Sad 		    "error = %d\n", error);
13688ab7da9Sad 		panic("sysmon_task_queue_init");
13788ab7da9Sad 	}
138d14efd22Sthorpej }
139d14efd22Sthorpej 
140d14efd22Sthorpej /*
141d14efd22Sthorpej  * sysmon_task_queue_fini:
142d14efd22Sthorpej  *
143d14efd22Sthorpej  *	Tear town the sysmon task queue.
144d14efd22Sthorpej  */
1450d9dbfe9Spgoyette int
sysmon_task_queue_fini(void)146d14efd22Sthorpej sysmon_task_queue_fini(void)
147d14efd22Sthorpej {
148d14efd22Sthorpej 
1498061eedeSpgoyette 	if (sysmon_task_queue_initialized > 1)
1500d9dbfe9Spgoyette 		return EBUSY;
1518061eedeSpgoyette 
1525623c9a1Sxtraeme 	mutex_enter(&sysmon_task_queue_mtx);
153d14efd22Sthorpej 
154d14efd22Sthorpej 	sysmon_task_queue_cleanup_sem = 1;
1555623c9a1Sxtraeme 	cv_signal(&sysmon_task_queue_cv);
156d14efd22Sthorpej 
1575623c9a1Sxtraeme 	while (sysmon_task_queue_cleanup_sem != 0)
1585623c9a1Sxtraeme 		cv_wait(&sysmon_task_queue_cv,
1595623c9a1Sxtraeme 			&sysmon_task_queue_mtx);
160d14efd22Sthorpej 
1615623c9a1Sxtraeme 	mutex_exit(&sysmon_task_queue_mtx);
1620d9dbfe9Spgoyette 
1630d9dbfe9Spgoyette 	return 0;
164d14efd22Sthorpej }
165d14efd22Sthorpej 
166d14efd22Sthorpej /*
167d14efd22Sthorpej  * sysmon_task_queue_thread:
168d14efd22Sthorpej  *
169d14efd22Sthorpej  *	The sysmon task queue execution thread.  We execute callbacks that
170d14efd22Sthorpej  *	have been queued for us.
171d14efd22Sthorpej  */
172d14efd22Sthorpej static void
sysmon_task_queue_thread(void * arg)173168cd830Schristos sysmon_task_queue_thread(void *arg)
174d14efd22Sthorpej {
175d14efd22Sthorpej 	struct sysmon_task *st;
176d14efd22Sthorpej 
177d14efd22Sthorpej 	/*
178d14efd22Sthorpej 	 * Run through all the tasks before we check for the exit
179d14efd22Sthorpej 	 * condition; it's probably more important to actually run
180d14efd22Sthorpej 	 * all the tasks before we exit.
181d14efd22Sthorpej 	 */
1825623c9a1Sxtraeme 	mutex_enter(&sysmon_task_queue_mtx);
183c8ddbd2dSgmcgarry 	for (;;) {
184d14efd22Sthorpej 		st = TAILQ_FIRST(&sysmon_task_queue);
185c8ddbd2dSgmcgarry 		if (st != NULL) {
186c8ddbd2dSgmcgarry 			TAILQ_REMOVE(&sysmon_task_queue, st, st_list);
187c8ddbd2dSgmcgarry 			mutex_exit(&sysmon_task_queue_mtx);
188c8ddbd2dSgmcgarry 			(*st->st_func)(st->st_arg);
189c8ddbd2dSgmcgarry 			free(st, M_TEMP);
190c8ddbd2dSgmcgarry 			mutex_enter(&sysmon_task_queue_mtx);
191c8ddbd2dSgmcgarry 		} else {
192d14efd22Sthorpej 			/* Check for the exit condition. */
193c8ddbd2dSgmcgarry 			if (sysmon_task_queue_cleanup_sem != 0)
194c8ddbd2dSgmcgarry 				break;
195c8ddbd2dSgmcgarry 			cv_wait(&sysmon_task_queue_cv, &sysmon_task_queue_mtx);
196c8ddbd2dSgmcgarry 		}
197c8ddbd2dSgmcgarry 	}
198d14efd22Sthorpej 	/* Time to die. */
199d14efd22Sthorpej 	sysmon_task_queue_cleanup_sem = 0;
2005623c9a1Sxtraeme 	cv_broadcast(&sysmon_task_queue_cv);
2015623c9a1Sxtraeme 	mutex_exit(&sysmon_task_queue_mtx);
202d14efd22Sthorpej 	kthread_exit(0);
203d14efd22Sthorpej }
204d14efd22Sthorpej 
20588580404Sriastradh static void
sysmon_task_queue_sched_task(struct sysmon_task * st)20688580404Sriastradh sysmon_task_queue_sched_task(struct sysmon_task *st)
20788580404Sriastradh {
20888580404Sriastradh 	struct sysmon_task *lst;
20988580404Sriastradh 
21088580404Sriastradh 	mutex_enter(&sysmon_task_queue_mtx);
21188580404Sriastradh 	TAILQ_FOREACH(lst, &sysmon_task_queue, st_list) {
21288580404Sriastradh 		if (st->st_pri > lst->st_pri) {
21388580404Sriastradh 			TAILQ_INSERT_BEFORE(lst, st, st_list);
21488580404Sriastradh 			break;
21588580404Sriastradh 		}
21688580404Sriastradh 	}
21788580404Sriastradh 
21888580404Sriastradh 	if (lst == NULL)
21988580404Sriastradh 		TAILQ_INSERT_TAIL(&sysmon_task_queue, st, st_list);
22088580404Sriastradh 
22188580404Sriastradh 	cv_broadcast(&sysmon_task_queue_cv);
22288580404Sriastradh 	mutex_exit(&sysmon_task_queue_mtx);
22388580404Sriastradh }
22488580404Sriastradh 
225d14efd22Sthorpej /*
226d14efd22Sthorpej  * sysmon_task_queue_sched:
227d14efd22Sthorpej  *
228d14efd22Sthorpej  *	Schedule a task for deferred execution.
229d14efd22Sthorpej  */
230d14efd22Sthorpej int
sysmon_task_queue_sched(u_int pri,void (* func)(void *),void * arg)231d14efd22Sthorpej sysmon_task_queue_sched(u_int pri, void (*func)(void *), void *arg)
232d14efd22Sthorpej {
23388580404Sriastradh 	struct sysmon_task *st;
234d14efd22Sthorpej 
2357bd37d6aSmartin 	(void)RUN_ONCE(&once_tq, tq_preinit);
2367bd37d6aSmartin 
23788ab7da9Sad 	if (sysmon_task_queue_lwp == NULL)
238e31adb2dSxtraeme 		aprint_debug("WARNING: Callback scheduled before sysmon "
239e31adb2dSxtraeme 		    "task queue thread present\n");
240d14efd22Sthorpej 
241d14efd22Sthorpej 	if (func == NULL)
2425623c9a1Sxtraeme 		return EINVAL;
243d14efd22Sthorpej 
244d14efd22Sthorpej 	st = malloc(sizeof(*st), M_TEMP, M_NOWAIT);
245d14efd22Sthorpej 	if (st == NULL)
2465623c9a1Sxtraeme 		return ENOMEM;
247d14efd22Sthorpej 
248d14efd22Sthorpej 	st->st_func = func;
249d14efd22Sthorpej 	st->st_arg = arg;
250d14efd22Sthorpej 	st->st_pri = pri;
251d14efd22Sthorpej 
25288580404Sriastradh 	sysmon_task_queue_sched_task(st);
2535623c9a1Sxtraeme 
2545623c9a1Sxtraeme 	return 0;
255d14efd22Sthorpej }
2568061eedeSpgoyette 
25788580404Sriastradh struct tqbarrier {
25888580404Sriastradh 	kmutex_t	lock;
25988580404Sriastradh 	kcondvar_t	cv;
26088580404Sriastradh 	bool		done;
26188580404Sriastradh };
26288580404Sriastradh 
26388580404Sriastradh static void
tqbarrier_task(void * cookie)26488580404Sriastradh tqbarrier_task(void *cookie)
26588580404Sriastradh {
26688580404Sriastradh 	struct tqbarrier *bar = cookie;
26788580404Sriastradh 
26888580404Sriastradh 	mutex_enter(&bar->lock);
26988580404Sriastradh 	bar->done = true;
27088580404Sriastradh 	cv_broadcast(&bar->cv);
27188580404Sriastradh 	mutex_exit(&bar->lock);
27288580404Sriastradh }
27388580404Sriastradh 
27488580404Sriastradh /*
27588580404Sriastradh  * sysmon_task_queue_barrier:
27688580404Sriastradh  *
27788580404Sriastradh  *	Wait for the completion of all tasks at priority pri or lower
27888580404Sriastradh  *	currently queued at the time of the call.
27988580404Sriastradh  */
28088580404Sriastradh void
sysmon_task_queue_barrier(u_int pri)28188580404Sriastradh sysmon_task_queue_barrier(u_int pri)
28288580404Sriastradh {
283*fedc5110Sriastradh 	struct sysmon_task *st;
28488580404Sriastradh 	struct tqbarrier bar;
28588580404Sriastradh 
28688580404Sriastradh 	(void)RUN_ONCE(&once_tq, tq_preinit);
28788580404Sriastradh 
28888580404Sriastradh 	KASSERT(sysmon_task_queue_lwp);
28988580404Sriastradh 	KASSERT(curlwp != sysmon_task_queue_lwp);
29088580404Sriastradh 
29188580404Sriastradh 	mutex_init(&bar.lock, MUTEX_DEFAULT, IPL_NONE);
29288580404Sriastradh 	cv_init(&bar.cv, "sysmontq");
29388580404Sriastradh 	bar.done = false;
29488580404Sriastradh 
295*fedc5110Sriastradh 	st = malloc(sizeof(*st), M_TEMP, M_WAITOK);
296*fedc5110Sriastradh 	st->st_func = &tqbarrier_task;
297*fedc5110Sriastradh 	st->st_arg = &bar;
298*fedc5110Sriastradh 	st->st_pri = pri;
29988580404Sriastradh 
300*fedc5110Sriastradh 	sysmon_task_queue_sched_task(st);
30188580404Sriastradh 
30288580404Sriastradh 	mutex_enter(&bar.lock);
30388580404Sriastradh 	while (!bar.done)
30488580404Sriastradh 		cv_wait(&bar.cv, &bar.lock);
30588580404Sriastradh 	mutex_exit(&bar.lock);
30688580404Sriastradh 
30788580404Sriastradh 	cv_destroy(&bar.cv);
30888580404Sriastradh 	mutex_destroy(&bar.lock);
30988580404Sriastradh }
31088580404Sriastradh 
31154853d3dSriastradh static int
sysmon_taskq_modcmd(modcmd_t cmd,void * arg)3128061eedeSpgoyette sysmon_taskq_modcmd(modcmd_t cmd, void *arg)
3138061eedeSpgoyette {
3148061eedeSpgoyette 	int ret;
3158061eedeSpgoyette 
3168061eedeSpgoyette 	switch (cmd) {
3178061eedeSpgoyette 	case MODULE_CMD_INIT:
3188061eedeSpgoyette 		sysmon_task_queue_init();
3198061eedeSpgoyette 		ret = 0;
3208061eedeSpgoyette 		break;
3218061eedeSpgoyette 	case MODULE_CMD_FINI:
3220d9dbfe9Spgoyette 		ret = sysmon_task_queue_fini();
3238061eedeSpgoyette 		break;
3248061eedeSpgoyette 	case MODULE_CMD_STAT:
3258061eedeSpgoyette 	default:
3268061eedeSpgoyette 		ret = ENOTTY;
3278061eedeSpgoyette 	}
3288061eedeSpgoyette 
3298061eedeSpgoyette 	return ret;
3308061eedeSpgoyette }
331