intr.c revision 1.36.14.1 1 /* $NetBSD: intr.c,v 1.36.14.1 2013/06/23 06:20:28 tls Exp $ */
2
3 /*
4 * Copyright (c) 2008-2010 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.36.14.1 2013/06/23 06:20:28 tls Exp $");
30
31 #include <sys/param.h>
32 #include <sys/atomic.h>
33 #include <sys/cpu.h>
34 #include <sys/kernel.h>
35 #include <sys/kmem.h>
36 #include <sys/kthread.h>
37 #include <sys/malloc.h>
38 #include <sys/intr.h>
39 #include <sys/timetc.h>
40
41 #include <rump/rumpuser.h>
42
43 #include "rump_private.h"
44
45 /*
46 * Interrupt simulator. It executes hardclock() and softintrs.
47 */
48
49 #define SI_MPSAFE 0x01
50 #define SI_KILLME 0x02
51
52 struct softint_percpu;
53 struct softint {
54 void (*si_func)(void *);
55 void *si_arg;
56 int si_flags;
57 int si_level;
58
59 struct softint_percpu *si_entry; /* [0,ncpu-1] */
60 };
61
62 struct softint_percpu {
63 struct softint *sip_parent;
64 bool sip_onlist;
65
66 LIST_ENTRY(softint_percpu) sip_entries;
67 };
68
69 struct softint_lev {
70 struct rumpuser_cv *si_cv;
71 LIST_HEAD(, softint_percpu) si_pending;
72 };
73
74 kcondvar_t lbolt; /* Oh Kath Ra */
75
76 static u_int ticks;
77 static int ncpu_final;
78
79 static u_int
80 rumptc_get(struct timecounter *tc)
81 {
82
83 KASSERT(rump_threads);
84 return ticks;
85 }
86
87 static struct timecounter rumptc = {
88 .tc_get_timecount = rumptc_get,
89 .tc_poll_pps = NULL,
90 .tc_counter_mask = ~0,
91 .tc_frequency = 0,
92 .tc_name = "rumpclk",
93 .tc_quality = 0,
94 };
95
96 /*
97 * clock "interrupt"
98 */
99 static void
100 doclock(void *noarg)
101 {
102 struct timespec thetick, curclock;
103 int64_t sec;
104 long nsec;
105 int error;
106 extern int hz;
107
108 error = rumpuser_clock_gettime(RUMPUSER_CLOCK_ABSMONO, &sec, &nsec);
109 if (error)
110 panic("clock: cannot get monotonic time");
111
112 curclock.tv_sec = sec;
113 curclock.tv_nsec = nsec;
114 thetick.tv_sec = 0;
115 thetick.tv_nsec = 1000000000/hz;
116
117 for (;;) {
118 callout_hardclock();
119
120 error = rumpuser_clock_sleep(RUMPUSER_CLOCK_ABSMONO,
121 curclock.tv_sec, curclock.tv_nsec);
122 KASSERT(!error);
123 timespecadd(&curclock, &thetick, &curclock);
124
125 #if 0
126 /* CPU_IS_PRIMARY is MD and hence unreliably correct here */
127 if (!CPU_IS_PRIMARY(curcpu()))
128 continue;
129 #else
130 if (curcpu()->ci_index != 0)
131 continue;
132 #endif
133
134 if ((++ticks % hz) == 0) {
135 cv_broadcast(&lbolt);
136 }
137 tc_ticktock();
138 }
139 }
140
141 /*
142 * Soft interrupt execution thread. This thread is pinned to the
143 * same CPU that scheduled the interrupt, so we don't need to do
144 * lock against si_lvl.
145 */
146 static void
147 sithread(void *arg)
148 {
149 struct softint_percpu *sip;
150 struct softint *si;
151 void (*func)(void *) = NULL;
152 void *funarg;
153 bool mpsafe;
154 int mylevel = (uintptr_t)arg;
155 struct softint_lev *si_lvlp, *si_lvl;
156 struct cpu_data *cd = &curcpu()->ci_data;
157
158 si_lvlp = cd->cpu_softcpu;
159 si_lvl = &si_lvlp[mylevel];
160
161 for (;;) {
162 if (!LIST_EMPTY(&si_lvl->si_pending)) {
163 sip = LIST_FIRST(&si_lvl->si_pending);
164 si = sip->sip_parent;
165
166 func = si->si_func;
167 funarg = si->si_arg;
168 mpsafe = si->si_flags & SI_MPSAFE;
169
170 sip->sip_onlist = false;
171 LIST_REMOVE(sip, sip_entries);
172 if (si->si_flags & SI_KILLME) {
173 softint_disestablish(si);
174 continue;
175 }
176 } else {
177 rump_schedlock_cv_wait(si_lvl->si_cv);
178 continue;
179 }
180
181 if (!mpsafe)
182 KERNEL_LOCK(1, curlwp);
183 func(funarg);
184 if (!mpsafe)
185 KERNEL_UNLOCK_ONE(curlwp);
186 }
187
188 panic("sithread unreachable");
189 }
190
191 void
192 rump_intr_init(int numcpu)
193 {
194
195 cv_init(&lbolt, "oh kath ra");
196 ncpu_final = numcpu;
197 }
198
199 void
200 softint_init(struct cpu_info *ci)
201 {
202 struct cpu_data *cd = &ci->ci_data;
203 struct softint_lev *slev;
204 int rv, i;
205
206 if (!rump_threads)
207 return;
208
209 /* XXX */
210 if (ci->ci_index == 0) {
211 rumptc.tc_frequency = hz;
212 tc_init(&rumptc);
213 }
214
215 slev = kmem_alloc(sizeof(struct softint_lev) * SOFTINT_COUNT, KM_SLEEP);
216 for (i = 0; i < SOFTINT_COUNT; i++) {
217 rumpuser_cv_init(&slev[i].si_cv);
218 LIST_INIT(&slev[i].si_pending);
219 }
220 cd->cpu_softcpu = slev;
221
222 /* softint might run on different physical CPU */
223 membar_sync();
224
225 for (i = 0; i < SOFTINT_COUNT; i++) {
226 rv = kthread_create(PRI_NONE,
227 KTHREAD_MPSAFE | KTHREAD_INTR, ci,
228 sithread, (void *)(uintptr_t)i,
229 NULL, "rsi%d/%d", ci->ci_index, i);
230 }
231
232 rv = kthread_create(PRI_NONE, KTHREAD_MPSAFE,
233 ci, doclock, NULL, NULL, "rumpclk%d", ci->ci_index);
234 if (rv)
235 panic("clock thread creation failed: %d", rv);
236 }
237
238 void *
239 softint_establish(u_int flags, void (*func)(void *), void *arg)
240 {
241 struct softint *si;
242 struct softint_percpu *sip;
243 int i;
244
245 si = malloc(sizeof(*si), M_TEMP, M_WAITOK);
246 si->si_func = func;
247 si->si_arg = arg;
248 si->si_flags = flags & SOFTINT_MPSAFE ? SI_MPSAFE : 0;
249 si->si_level = flags & SOFTINT_LVLMASK;
250 KASSERT(si->si_level < SOFTINT_COUNT);
251 si->si_entry = malloc(sizeof(*si->si_entry) * ncpu_final,
252 M_TEMP, M_WAITOK | M_ZERO);
253 for (i = 0; i < ncpu_final; i++) {
254 sip = &si->si_entry[i];
255 sip->sip_parent = si;
256 }
257
258 return si;
259 }
260
261 /*
262 * Soft interrupts bring two choices. If we are running with thread
263 * support enabled, defer execution, otherwise execute in place.
264 */
265
266 void
267 softint_schedule(void *arg)
268 {
269 struct softint *si = arg;
270 struct softint_percpu *sip = &si->si_entry[curcpu()->ci_index];
271 struct cpu_data *cd = &curcpu()->ci_data;
272 struct softint_lev *si_lvl = cd->cpu_softcpu;
273
274 if (!rump_threads) {
275 si->si_func(si->si_arg);
276 } else {
277 if (!sip->sip_onlist) {
278 LIST_INSERT_HEAD(&si_lvl[si->si_level].si_pending,
279 sip, sip_entries);
280 sip->sip_onlist = true;
281 }
282 }
283 }
284
285 /*
286 * flimsy disestablish: should wait for softints to finish.
287 */
288 void
289 softint_disestablish(void *cook)
290 {
291 struct softint *si = cook;
292 int i;
293
294 for (i = 0; i < ncpu_final; i++) {
295 struct softint_percpu *sip;
296
297 sip = &si->si_entry[i];
298 if (sip->sip_onlist) {
299 si->si_flags |= SI_KILLME;
300 return;
301 }
302 }
303 free(si->si_entry, M_TEMP);
304 free(si, M_TEMP);
305 }
306
307 void
308 rump_softint_run(struct cpu_info *ci)
309 {
310 struct cpu_data *cd = &ci->ci_data;
311 struct softint_lev *si_lvl = cd->cpu_softcpu;
312 int i;
313
314 if (!rump_threads)
315 return;
316
317 for (i = 0; i < SOFTINT_COUNT; i++) {
318 if (!LIST_EMPTY(&si_lvl[i].si_pending))
319 rumpuser_cv_signal(si_lvl[i].si_cv);
320 }
321 }
322
323 bool
324 cpu_intr_p(void)
325 {
326
327 return false;
328 }
329
330 bool
331 cpu_softintr_p(void)
332 {
333
334 return curlwp->l_pflag & LP_INTR;
335 }
336