1 /* $NetBSD: sysmon_taskq.c,v 1.20 2018/02/08 09:05:20 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 2001, 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * General purpose task queue for sysmon back-ends. This can be 40 * used to run callbacks that require thread context. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: sysmon_taskq.c,v 1.20 2018/02/08 09:05:20 dholland Exp $"); 45 46 #include <sys/param.h> 47 #include <sys/malloc.h> 48 #include <sys/queue.h> 49 #include <sys/proc.h> 50 #include <sys/kthread.h> 51 #include <sys/systm.h> 52 #include <sys/module.h> 53 #include <sys/once.h> 54 55 #include <dev/sysmon/sysmon_taskq.h> 56 57 struct sysmon_task { 58 TAILQ_ENTRY(sysmon_task) st_list; 59 void (*st_func)(void *); 60 void *st_arg; 61 u_int st_pri; 62 }; 63 64 static TAILQ_HEAD(, sysmon_task) sysmon_task_queue = 65 TAILQ_HEAD_INITIALIZER(sysmon_task_queue); 66 67 static kmutex_t sysmon_task_queue_mtx; 68 static kmutex_t sysmon_task_queue_init_mtx; 69 static kcondvar_t sysmon_task_queue_cv; 70 71 static int sysmon_task_queue_initialized; 72 static int sysmon_task_queue_cleanup_sem; 73 static struct lwp *sysmon_task_queue_lwp; 74 static void sysmon_task_queue_thread(void *); 75 76 MODULE(MODULE_CLASS_MISC, sysmon_taskq, NULL); 77 78 /* 79 * XXX Normally, all initialization would be handled as part of 80 * the module(9) framework. However, there are a number of 81 * users of the sysmon_taskq facility that are not modular, 82 * and these can directly call sysmon_task_queue_init() 83 * directly. To accommodate these non-standard users, we 84 * make sure that sysmon_task_queue_init() handles multiple 85 * invocations. And we also ensure that, if any non-module 86 * user exists, we don't allow the module to be unloaded. 87 * (We can't use module_hold() for this, since the module(9) 88 * framework itself isn't necessarily initialized yet.) 89 */ 90 91 /* 92 * tq_preinit: 93 * 94 * Early one-time initialization of task-queue 95 */ 96 97 ONCE_DECL(once_tq); 98 99 static int 100 tq_preinit(void) 101 { 102 103 mutex_init(&sysmon_task_queue_mtx, MUTEX_DEFAULT, IPL_VM); 104 mutex_init(&sysmon_task_queue_init_mtx, MUTEX_DEFAULT, IPL_NONE); 105 cv_init(&sysmon_task_queue_cv, "smtaskq"); 106 sysmon_task_queue_initialized = 0; 107 108 return 0; 109 } 110 111 /* 112 * sysmon_task_queue_init: 113 * 114 * Initialize the sysmon task queue. 115 */ 116 void 117 sysmon_task_queue_init(void) 118 { 119 int error; 120 121 (void)RUN_ONCE(&once_tq, tq_preinit); 122 123 mutex_enter(&sysmon_task_queue_init_mtx); 124 if (sysmon_task_queue_initialized++) { 125 mutex_exit(&sysmon_task_queue_init_mtx); 126 return; 127 } 128 129 mutex_exit(&sysmon_task_queue_init_mtx); 130 131 error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, 132 sysmon_task_queue_thread, NULL, &sysmon_task_queue_lwp, "sysmon"); 133 if (error) { 134 printf("Unable to create sysmon task queue thread, " 135 "error = %d\n", error); 136 panic("sysmon_task_queue_init"); 137 } 138 } 139 140 /* 141 * sysmon_task_queue_fini: 142 * 143 * Tear town the sysmon task queue. 144 */ 145 int 146 sysmon_task_queue_fini(void) 147 { 148 149 if (sysmon_task_queue_initialized > 1) 150 return EBUSY; 151 152 mutex_enter(&sysmon_task_queue_mtx); 153 154 sysmon_task_queue_cleanup_sem = 1; 155 cv_signal(&sysmon_task_queue_cv); 156 157 while (sysmon_task_queue_cleanup_sem != 0) 158 cv_wait(&sysmon_task_queue_cv, 159 &sysmon_task_queue_mtx); 160 161 mutex_exit(&sysmon_task_queue_mtx); 162 163 return 0; 164 } 165 166 /* 167 * sysmon_task_queue_thread: 168 * 169 * The sysmon task queue execution thread. We execute callbacks that 170 * have been queued for us. 171 */ 172 static void 173 sysmon_task_queue_thread(void *arg) 174 { 175 struct sysmon_task *st; 176 177 /* 178 * Run through all the tasks before we check for the exit 179 * condition; it's probably more important to actually run 180 * all the tasks before we exit. 181 */ 182 mutex_enter(&sysmon_task_queue_mtx); 183 for (;;) { 184 st = TAILQ_FIRST(&sysmon_task_queue); 185 if (st != NULL) { 186 TAILQ_REMOVE(&sysmon_task_queue, st, st_list); 187 mutex_exit(&sysmon_task_queue_mtx); 188 (*st->st_func)(st->st_arg); 189 free(st, M_TEMP); 190 mutex_enter(&sysmon_task_queue_mtx); 191 } else { 192 /* Check for the exit condition. */ 193 if (sysmon_task_queue_cleanup_sem != 0) 194 break; 195 cv_wait(&sysmon_task_queue_cv, &sysmon_task_queue_mtx); 196 } 197 } 198 /* Time to die. */ 199 sysmon_task_queue_cleanup_sem = 0; 200 cv_broadcast(&sysmon_task_queue_cv); 201 mutex_exit(&sysmon_task_queue_mtx); 202 kthread_exit(0); 203 } 204 205 /* 206 * sysmon_task_queue_sched: 207 * 208 * Schedule a task for deferred execution. 209 */ 210 int 211 sysmon_task_queue_sched(u_int pri, void (*func)(void *), void *arg) 212 { 213 struct sysmon_task *st, *lst; 214 215 (void)RUN_ONCE(&once_tq, tq_preinit); 216 217 if (sysmon_task_queue_lwp == NULL) 218 aprint_debug("WARNING: Callback scheduled before sysmon " 219 "task queue thread present\n"); 220 221 if (func == NULL) 222 return EINVAL; 223 224 st = malloc(sizeof(*st), M_TEMP, M_NOWAIT); 225 if (st == NULL) 226 return ENOMEM; 227 228 st->st_func = func; 229 st->st_arg = arg; 230 st->st_pri = pri; 231 232 mutex_enter(&sysmon_task_queue_mtx); 233 TAILQ_FOREACH(lst, &sysmon_task_queue, st_list) { 234 if (st->st_pri > lst->st_pri) { 235 TAILQ_INSERT_BEFORE(lst, st, st_list); 236 break; 237 } 238 } 239 240 if (lst == NULL) 241 TAILQ_INSERT_TAIL(&sysmon_task_queue, st, st_list); 242 243 cv_broadcast(&sysmon_task_queue_cv); 244 mutex_exit(&sysmon_task_queue_mtx); 245 246 return 0; 247 } 248 249 static 250 int 251 sysmon_taskq_modcmd(modcmd_t cmd, void *arg) 252 { 253 int ret; 254 255 switch (cmd) { 256 case MODULE_CMD_INIT: 257 sysmon_task_queue_init(); 258 ret = 0; 259 break; 260 261 case MODULE_CMD_FINI: 262 ret = sysmon_task_queue_fini(); 263 break; 264 265 case MODULE_CMD_STAT: 266 default: 267 ret = ENOTTY; 268 } 269 270 return ret; 271 } 272