subr_workqueue.c revision 1.3.10.2 1 /* $NetBSD: subr_workqueue.c,v 1.3.10.2 2006/11/18 21:39:23 ad Exp $ */
2
3 /*-
4 * Copyright (c)2002, 2005 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: subr_workqueue.c,v 1.3.10.2 2006/11/18 21:39:23 ad Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kthread.h>
35 #include <sys/kmem.h>
36 #include <sys/proc.h>
37 #include <sys/workqueue.h>
38 #include <sys/mutex.h>
39
40 SIMPLEQ_HEAD(workqhead, work);
41
42 struct workqueue_queue {
43 kmutex_t q_mutex;
44 struct workqhead q_queue;
45 struct proc *q_worker;
46 };
47
48 struct workqueue {
49 struct workqueue_queue wq_queue; /* todo: make this per-cpu */
50
51 void (*wq_func)(struct work *, void *);
52 void *wq_arg;
53 const char *wq_name;
54 int wq_prio;
55 int wq_ipl;
56 };
57
58 #define POISON 0xaabbccdd
59
60 static void
61 workqueue_runlist(struct workqueue *wq, struct workqhead *list)
62 {
63 struct work *wk;
64 struct work *next;
65
66 /*
67 * note that "list" is not a complete SIMPLEQ.
68 */
69
70 for (wk = SIMPLEQ_FIRST(list); wk != NULL; wk = next) {
71 next = SIMPLEQ_NEXT(wk, wk_entry);
72 (*wq->wq_func)(wk, wq->wq_arg);
73 }
74 }
75
76 static void
77 workqueue_run(struct workqueue *wq)
78 {
79 struct workqueue_queue *q = &wq->wq_queue;
80
81 for (;;) {
82 struct workqhead tmp;
83 int error;
84
85 /*
86 * we violate abstraction of SIMPLEQ.
87 */
88
89 #if defined(DIAGNOSTIC)
90 tmp.sqh_last = (void *)POISON;
91 #endif /* defined(DIAGNOSTIC) */
92
93 mutex_enter(&q->q_mutex);
94 while (SIMPLEQ_EMPTY(&q->q_queue)) {
95 error = mtsleep(q, wq->wq_prio, wq->wq_name, 0,
96 &q->q_mutex);
97 if (error) {
98 panic("%s: %s error=%d",
99 __func__, wq->wq_name, error);
100 }
101 }
102 tmp.sqh_first = q->q_queue.sqh_first; /* XXX */
103 SIMPLEQ_INIT(&q->q_queue);
104 mutex_exit(&q->q_mutex);
105
106 workqueue_runlist(wq, &tmp);
107 }
108 }
109
110 static void
111 workqueue_worker(void *arg)
112 {
113 struct workqueue *wq = arg;
114
115 workqueue_run(wq);
116 }
117
118 static void
119 workqueue_init(struct workqueue *wq, const char *name,
120 void (*callback_func)(struct work *, void *), void *callback_arg,
121 int prio, int ipl)
122 {
123
124 wq->wq_ipl = ipl;
125 wq->wq_prio = prio;
126 wq->wq_name = name;
127 wq->wq_func = callback_func;
128 wq->wq_arg = callback_arg;
129 }
130
131 static int
132 workqueue_initqueue(struct workqueue *wq)
133 {
134 struct workqueue_queue *q = &wq->wq_queue;
135 int error;
136
137 mutex_init(&q->q_mutex, MUTEX_SPIN, wq->wq_ipl);
138 SIMPLEQ_INIT(&q->q_queue);
139 error = kthread_create1(workqueue_worker, wq, &q->q_worker,
140 wq->wq_name);
141
142 return error;
143 }
144
145 struct workqueue_exitargs {
146 struct work wqe_wk;
147 struct workqueue_queue *wqe_q;
148 };
149
150 static void
151 workqueue_exit(struct work *wk, void *arg)
152 {
153 struct workqueue_exitargs *wqe = (void *)wk;
154 struct workqueue_queue *q = wqe->wqe_q;
155
156 /*
157 * no need to raise ipl because only competition at this point
158 * is workqueue_finiqueue.
159 */
160
161 KASSERT(q->q_worker == curproc);
162 mutex_enter(&q->q_mutex);
163 q->q_worker = NULL;
164 mutex_exit(&q->q_mutex);
165 wakeup(q);
166 kthread_exit(0);
167 }
168
169 static void
170 workqueue_finiqueue(struct workqueue *wq)
171 {
172 struct workqueue_queue *q = &wq->wq_queue;
173 struct workqueue_exitargs wqe;
174
175 wq->wq_func = workqueue_exit;
176
177 wqe.wqe_q = q;
178 KASSERT(SIMPLEQ_EMPTY(&q->q_queue));
179 KASSERT(q->q_worker != NULL);
180 mutex_enter(&q->q_mutex);
181 SIMPLEQ_INSERT_TAIL(&q->q_queue, &wqe.wqe_wk, wk_entry);
182 wakeup(q);
183 while (q->q_worker != NULL) {
184 int error;
185
186 error = mtsleep(q, wq->wq_prio, "wqfini", 0, &q->q_mutex);
187 if (error) {
188 panic("%s: %s error=%d",
189 __func__, wq->wq_name, error);
190 }
191 }
192 mutex_exit(&q->q_mutex);
193 }
194
195 /* --- */
196
197 int
198 workqueue_create(struct workqueue **wqp, const char *name,
199 void (*callback_func)(struct work *, void *), void *callback_arg,
200 int prio, int ipl, int flags)
201 {
202 struct workqueue *wq;
203 int error;
204
205 wq = kmem_alloc(sizeof(*wq), KM_SLEEP);
206 if (wq == NULL) {
207 return ENOMEM;
208 }
209
210 workqueue_init(wq, name, callback_func, callback_arg, prio, ipl);
211
212 error = workqueue_initqueue(wq);
213 if (error) {
214 kmem_free(wq, sizeof(*wq));
215 return error;
216 }
217
218 *wqp = wq;
219 return 0;
220 }
221
222 void
223 workqueue_destroy(struct workqueue *wq)
224 {
225
226 workqueue_finiqueue(wq);
227 kmem_free(wq, sizeof(*wq));
228 }
229
230 void
231 workqueue_enqueue(struct workqueue *wq, struct work *wk)
232 {
233 struct workqueue_queue *q = &wq->wq_queue;
234 boolean_t wasempty;
235
236 mutex_enter(&q->q_mutex);
237 wasempty = SIMPLEQ_EMPTY(&q->q_queue);
238 SIMPLEQ_INSERT_TAIL(&q->q_queue, wk, wk_entry);
239 mutex_exit(&q->q_mutex);
240
241 if (wasempty) {
242 wakeup(q);
243 }
244 }
245