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