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