xref: /netbsd-src/sys/dev/sysmon/sysmon_taskq.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
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