1 | /* $NetBSD: sysmon_taskq.c,v 1.19 2015/04/28 11:58:49 martin 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.19 2015/04/28 11:58:49 martin 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 accomodate 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 | |