1 /* $NetBSD: sysmon_taskq.c,v 1.14 2008/09/05 22:06:52 gmcgarry 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.14 2008/09/05 22:06:52 gmcgarry 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 53 #include <dev/sysmon/sysmon_taskq.h> 54 55 struct sysmon_task { 56 TAILQ_ENTRY(sysmon_task) st_list; 57 void (*st_func)(void *); 58 void *st_arg; 59 u_int st_pri; 60 }; 61 62 static TAILQ_HEAD(, sysmon_task) sysmon_task_queue = 63 TAILQ_HEAD_INITIALIZER(sysmon_task_queue); 64 65 static kmutex_t sysmon_task_queue_mtx; 66 static kmutex_t sysmon_task_queue_init_mtx; 67 static kcondvar_t sysmon_task_queue_cv; 68 69 static int sysmon_task_queue_initialized; 70 static int sysmon_task_queue_cleanup_sem; 71 static struct lwp *sysmon_task_queue_lwp; 72 static void sysmon_task_queue_thread(void *); 73 74 void 75 sysmon_task_queue_preinit(void) 76 { 77 mutex_init(&sysmon_task_queue_mtx, MUTEX_DEFAULT, IPL_VM); 78 mutex_init(&sysmon_task_queue_init_mtx, MUTEX_DEFAULT, IPL_NONE); 79 cv_init(&sysmon_task_queue_cv, "smtaskq"); 80 } 81 82 83 /* 84 * sysmon_task_queue_init: 85 * 86 * Initialize the sysmon task queue. 87 */ 88 void 89 sysmon_task_queue_init(void) 90 { 91 int error; 92 93 mutex_enter(&sysmon_task_queue_init_mtx); 94 if (sysmon_task_queue_initialized) { 95 mutex_exit(&sysmon_task_queue_init_mtx); 96 return; 97 } 98 99 sysmon_task_queue_initialized = 1; 100 mutex_exit(&sysmon_task_queue_init_mtx); 101 102 error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, 103 sysmon_task_queue_thread, NULL, &sysmon_task_queue_lwp, "sysmon"); 104 if (error) { 105 printf("Unable to create sysmon task queue thread, " 106 "error = %d\n", error); 107 panic("sysmon_task_queue_init"); 108 } 109 } 110 111 /* 112 * sysmon_task_queue_fini: 113 * 114 * Tear town the sysmon task queue. 115 */ 116 void 117 sysmon_task_queue_fini(void) 118 { 119 120 mutex_enter(&sysmon_task_queue_mtx); 121 122 sysmon_task_queue_cleanup_sem = 1; 123 cv_signal(&sysmon_task_queue_cv); 124 125 while (sysmon_task_queue_cleanup_sem != 0) 126 cv_wait(&sysmon_task_queue_cv, 127 &sysmon_task_queue_mtx); 128 129 mutex_exit(&sysmon_task_queue_mtx); 130 } 131 132 /* 133 * sysmon_task_queue_thread: 134 * 135 * The sysmon task queue execution thread. We execute callbacks that 136 * have been queued for us. 137 */ 138 static void 139 sysmon_task_queue_thread(void *arg) 140 { 141 struct sysmon_task *st; 142 143 /* 144 * Run through all the tasks before we check for the exit 145 * condition; it's probably more important to actually run 146 * all the tasks before we exit. 147 */ 148 mutex_enter(&sysmon_task_queue_mtx); 149 for (;;) { 150 st = TAILQ_FIRST(&sysmon_task_queue); 151 if (st != NULL) { 152 TAILQ_REMOVE(&sysmon_task_queue, st, st_list); 153 mutex_exit(&sysmon_task_queue_mtx); 154 (*st->st_func)(st->st_arg); 155 free(st, M_TEMP); 156 mutex_enter(&sysmon_task_queue_mtx); 157 } else { 158 /* Check for the exit condition. */ 159 if (sysmon_task_queue_cleanup_sem != 0) 160 break; 161 cv_wait(&sysmon_task_queue_cv, &sysmon_task_queue_mtx); 162 } 163 } 164 /* Time to die. */ 165 sysmon_task_queue_cleanup_sem = 0; 166 cv_broadcast(&sysmon_task_queue_cv); 167 mutex_exit(&sysmon_task_queue_mtx); 168 kthread_exit(0); 169 } 170 171 /* 172 * sysmon_task_queue_sched: 173 * 174 * Schedule a task for deferred execution. 175 */ 176 int 177 sysmon_task_queue_sched(u_int pri, void (*func)(void *), void *arg) 178 { 179 struct sysmon_task *st, *lst; 180 181 if (sysmon_task_queue_lwp == NULL) 182 aprint_debug("WARNING: Callback scheduled before sysmon " 183 "task queue thread present\n"); 184 185 if (func == NULL) 186 return EINVAL; 187 188 st = malloc(sizeof(*st), M_TEMP, M_NOWAIT); 189 if (st == NULL) 190 return ENOMEM; 191 192 st->st_func = func; 193 st->st_arg = arg; 194 st->st_pri = pri; 195 196 mutex_enter(&sysmon_task_queue_mtx); 197 TAILQ_FOREACH(lst, &sysmon_task_queue, st_list) { 198 if (st->st_pri > lst->st_pri) { 199 TAILQ_INSERT_BEFORE(lst, st, st_list); 200 break; 201 } 202 } 203 204 if (lst == NULL) 205 TAILQ_INSERT_TAIL(&sysmon_task_queue, st, st_list); 206 207 cv_broadcast(&sysmon_task_queue_cv); 208 mutex_exit(&sysmon_task_queue_mtx); 209 210 return 0; 211 } 212