1 /* $NetBSD: sysmon_taskq.c,v 1.3 2003/09/06 23:28:30 christos 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.3 2003/09/06 23:28:30 christos 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 struct simplelock sysmon_task_queue_slock = SIMPLELOCK_INITIALIZER; 66 67 #define SYSMON_TASK_QUEUE_LOCK(s) \ 68 do { \ 69 s = splsched(); \ 70 simple_lock(&sysmon_task_queue_slock); \ 71 } while (/*CONSTCOND*/0) 72 73 #define SYSMON_TASK_QUEUE_UNLOCK(s) \ 74 do { \ 75 simple_unlock(&sysmon_task_queue_slock); \ 76 splx((s)); \ 77 } while (/*CONSTCOND*/0) 78 79 static __volatile int sysmon_task_queue_cleanup_sem; 80 81 static struct proc *sysmon_task_queue_proc; 82 83 static void sysmon_task_queue_create_thread(void *); 84 static void sysmon_task_queue_thread(void *); 85 86 static struct simplelock sysmon_task_queue_initialized_slock = 87 SIMPLELOCK_INITIALIZER; 88 static int sysmon_task_queue_initialized; 89 90 /* 91 * sysmon_task_queue_init: 92 * 93 * Initialize the sysmon task queue. 94 */ 95 void 96 sysmon_task_queue_init(void) 97 { 98 99 simple_lock(&sysmon_task_queue_initialized_slock); 100 if (sysmon_task_queue_initialized) { 101 simple_unlock(&sysmon_task_queue_initialized_slock); 102 return; 103 } 104 105 sysmon_task_queue_initialized = 1; 106 simple_unlock(&sysmon_task_queue_initialized_slock); 107 108 kthread_create(sysmon_task_queue_create_thread, NULL); 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 int s; 120 121 SYSMON_TASK_QUEUE_LOCK(s); 122 123 sysmon_task_queue_cleanup_sem = 1; 124 wakeup(&sysmon_task_queue); 125 126 while (sysmon_task_queue_cleanup_sem != 0) { 127 (void) ltsleep((void *) &sysmon_task_queue_cleanup_sem, 128 PVM, "stfini", 0, &sysmon_task_queue_slock); 129 } 130 131 SYSMON_TASK_QUEUE_UNLOCK(s); 132 } 133 134 /* 135 * sysmon_task_queue_create_thread: 136 * 137 * Create the sysmon task queue execution thread. 138 */ 139 static void 140 sysmon_task_queue_create_thread(void *arg) 141 { 142 int error; 143 144 error = kthread_create1(sysmon_task_queue_thread, NULL, 145 &sysmon_task_queue_proc, "sysmon"); 146 if (error) { 147 printf("Unable to create sysmon task queue thread, " 148 "error = %d\n", error); 149 panic("sysmon_task_queue_create_thread"); 150 } 151 } 152 153 /* 154 * sysmon_task_queue_thread: 155 * 156 * The sysmon task queue execution thread. We execute callbacks that 157 * have been queued for us. 158 */ 159 static void 160 sysmon_task_queue_thread(void *arg) 161 { 162 struct sysmon_task *st; 163 int s; 164 165 /* 166 * Run through all the tasks before we check for the exit 167 * condition; it's probably more important to actually run 168 * all the tasks before we exit. 169 */ 170 for (;;) { 171 SYSMON_TASK_QUEUE_LOCK(s); 172 st = TAILQ_FIRST(&sysmon_task_queue); 173 if (st == NULL) { 174 /* Check for the exit condition. */ 175 if (sysmon_task_queue_cleanup_sem != 0) { 176 /* Time to die. */ 177 sysmon_task_queue_cleanup_sem = 0; 178 wakeup((void *) &sysmon_task_queue_cleanup_sem); 179 SYSMON_TASK_QUEUE_UNLOCK(s); 180 kthread_exit(0); 181 } 182 (void) ltsleep(&sysmon_task_queue, PVM, 183 "smtaskq", 0, &sysmon_task_queue_slock); 184 SYSMON_TASK_QUEUE_UNLOCK(s); 185 continue; 186 } 187 TAILQ_REMOVE(&sysmon_task_queue, st, st_list); 188 SYSMON_TASK_QUEUE_UNLOCK(s); 189 190 (*st->st_func)(st->st_arg); 191 free(st, M_TEMP); 192 } 193 panic("sysmon_task_queue_thread: impossible"); 194 } 195 196 /* 197 * sysmon_task_queue_sched: 198 * 199 * Schedule a task for deferred execution. 200 */ 201 int 202 sysmon_task_queue_sched(u_int pri, void (*func)(void *), void *arg) 203 { 204 struct sysmon_task *st, *lst; 205 int s; 206 207 if (sysmon_task_queue_proc == NULL) 208 printf("WARNING: Callback scheduled before sysmon task queue " 209 "thread present.\n"); 210 211 if (func == NULL) 212 return (EINVAL); 213 214 st = malloc(sizeof(*st), M_TEMP, M_NOWAIT); 215 if (st == NULL) 216 return (ENOMEM); 217 218 st->st_func = func; 219 st->st_arg = arg; 220 st->st_pri = pri; 221 222 SYSMON_TASK_QUEUE_LOCK(s); 223 TAILQ_FOREACH(lst, &sysmon_task_queue, st_list) { 224 if (st->st_pri > lst->st_pri) { 225 TAILQ_INSERT_BEFORE(lst, st, st_list); 226 break; 227 } 228 } 229 if (lst == NULL) 230 TAILQ_INSERT_TAIL(&sysmon_task_queue, st, st_list); 231 wakeup(&sysmon_task_queue); 232 SYSMON_TASK_QUEUE_UNLOCK(s); 233 234 return (0); 235 } 236