Home | History | Annotate | Line # | Download | only in rumpkern
intr.c revision 1.18
      1 /*	$NetBSD: intr.c,v 1.18 2009/09/19 14:18:01 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2008 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.18 2009/09/19 14:18:01 pooka Exp $");
     30 
     31 #include <sys/param.h>
     32 #include <sys/cpu.h>
     33 #include <sys/kmem.h>
     34 #include <sys/kthread.h>
     35 #include <sys/intr.h>
     36 
     37 #include <rump/rumpuser.h>
     38 
     39 #include "rump_private.h"
     40 
     41 /*
     42  * Interrupt simulator.  It executes hardclock() and softintrs.
     43  */
     44 
     45 time_t time_uptime = 0;
     46 
     47 #define SI_MPSAFE 0x01
     48 #define SI_ONLIST 0x02
     49 #define SI_KILLME 0x04
     50 struct softint {
     51 	void (*si_func)(void *);
     52 	void *si_arg;
     53 	int si_flags;
     54 
     55 	LIST_ENTRY(softint) si_entries;
     56 };
     57 static LIST_HEAD(, softint) si_pending = LIST_HEAD_INITIALIZER(si_pending);
     58 static kmutex_t si_mtx;
     59 static kcondvar_t si_cv;
     60 
     61 #define INTRTHREAD_DEFAULT	2
     62 #define INTRTHREAD_MAX		20
     63 static int wrkidle, wrktotal;
     64 
     65 static void sithread(void *);
     66 
     67 static void
     68 makeworker(bool bootstrap)
     69 {
     70 	int rv;
     71 
     72 	if (wrktotal > INTRTHREAD_MAX) {
     73 		/* XXX: ratecheck */
     74 		printf("maximum interrupt threads (%d) reached\n",
     75 		    INTRTHREAD_MAX);
     76 		return;
     77 	}
     78 	rv = kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_INTR, NULL,
     79 	    sithread, NULL, NULL, "rumpsi");
     80 	if (rv) {
     81 		if (bootstrap)
     82 			panic("intr thread creation failed %d", rv);
     83 		else
     84 			printf("intr thread creation failed %d\n", rv);
     85 	} else {
     86 		wrkidle++;
     87 		wrktotal++;
     88 	}
     89 }
     90 
     91 /* rumpuser structures since we call rumpuser interfaces directly */
     92 static struct rumpuser_cv *clockcv;
     93 static struct rumpuser_mtx *clockmtx;
     94 static struct timespec clockbase, clockup;
     95 static unsigned clkgen;
     96 
     97 void
     98 rump_getuptime(struct timespec *ts)
     99 {
    100 	int startgen, i = 0;
    101 
    102 	do {
    103 		startgen = clkgen;
    104 		if (__predict_false(i++ > 10)) {
    105 			yield();
    106 			i = 0;
    107 		}
    108 		*ts = clockup;
    109 	} while (startgen != clkgen || clkgen % 2 != 0);
    110 }
    111 
    112 void
    113 rump_gettime(struct timespec *ts)
    114 {
    115 	struct timespec ts_up;
    116 
    117 	rump_getuptime(&ts_up);
    118 	timespecadd(&clockbase, &ts_up, ts);
    119 }
    120 
    121 /*
    122  * clock "interrupt"
    123  */
    124 static void
    125 doclock(void *noarg)
    126 {
    127 	struct timespec tick, curtime;
    128 	uint64_t sec, nsec;
    129 	int ticks = 0, error;
    130 	extern int hz;
    131 
    132 	rumpuser_gettime(&sec, &nsec, &error);
    133 	clockbase.tv_sec = sec;
    134 	clockbase.tv_nsec = nsec;
    135 	curtime = clockbase;
    136 	tick.tv_sec = 0;
    137 	tick.tv_nsec = 1000000000/hz;
    138 
    139 	rumpuser_mutex_enter(clockmtx);
    140 	rumpuser_cv_signal(clockcv);
    141 
    142 	for (;;) {
    143 		callout_hardclock();
    144 
    145 		if (++ticks == hz) {
    146 			time_uptime++;
    147 			ticks = 0;
    148 		}
    149 
    150 		/* wait until the next tick. XXX: what if the clock changes? */
    151 		while (rumpuser_cv_timedwait(clockcv, clockmtx,
    152 		    &curtime) != EWOULDBLOCK)
    153 			continue;
    154 
    155 		clkgen++;
    156 		timespecadd(&clockup, &tick, &clockup);
    157 		clkgen++;
    158 		timespecadd(&clockup, &clockbase, &curtime);
    159 	}
    160 }
    161 
    162 /*
    163  * run a scheduled soft interrupt
    164  */
    165 static void
    166 sithread(void *arg)
    167 {
    168 	struct softint *si;
    169 	void (*func)(void *) = NULL;
    170 	void *funarg;
    171 	bool mpsafe;
    172 
    173 	mutex_enter(&si_mtx);
    174 	for (;;) {
    175 		if (!LIST_EMPTY(&si_pending)) {
    176 			si = LIST_FIRST(&si_pending);
    177 			func = si->si_func;
    178 			funarg = si->si_arg;
    179 			mpsafe = si->si_flags & SI_MPSAFE;
    180 
    181 			si->si_flags &= ~SI_ONLIST;
    182 			LIST_REMOVE(si, si_entries);
    183 			if (si->si_flags & SI_KILLME)
    184 				softint_disestablish(si);
    185 		} else {
    186 			cv_wait(&si_cv, &si_mtx);
    187 			continue;
    188 		}
    189 		wrkidle--;
    190 		if (__predict_false(wrkidle == 0))
    191 			makeworker(false);
    192 		mutex_exit(&si_mtx);
    193 
    194 		if (!mpsafe)
    195 			KERNEL_LOCK(1, curlwp);
    196 		func(funarg);
    197 		if (!mpsafe)
    198 			KERNEL_UNLOCK_ONE(curlwp);
    199 
    200 		mutex_enter(&si_mtx);
    201 		wrkidle++;
    202 	}
    203 }
    204 
    205 void
    206 softint_init(struct cpu_info *ci)
    207 {
    208 	int rv;
    209 
    210 	mutex_init(&si_mtx, MUTEX_DEFAULT, IPL_NONE);
    211 	cv_init(&si_cv, "intrw8"); /* cv of temporary w8ness */
    212 
    213 	rumpuser_cv_init(&clockcv);
    214 	rumpuser_mutex_init(&clockmtx);
    215 
    216 	/* XXX: should have separate "wanttimer" control */
    217 	if (rump_threads) {
    218 		rumpuser_mutex_enter(clockmtx);
    219 		rv = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, doclock,
    220 		    NULL, NULL, "rumpclk");
    221 		if (rv)
    222 			panic("clock thread creation failed: %d", rv);
    223 		mutex_enter(&si_mtx);
    224 		while (wrktotal < INTRTHREAD_DEFAULT) {
    225 			makeworker(true);
    226 		}
    227 		mutex_exit(&si_mtx);
    228 
    229 		/* make sure we have a clocktime before returning */
    230 		rumpuser_cv_wait(clockcv, clockmtx);
    231 		rumpuser_mutex_exit(clockmtx);
    232 	}
    233 }
    234 
    235 /*
    236  * Soft interrupts bring two choices.  If we are running with thread
    237  * support enabled, defer execution, otherwise execute in place.
    238  * See softint_schedule().
    239  *
    240  * As there is currently no clear concept of when a thread finishes
    241  * work (although rump_clear_curlwp() is close), simply execute all
    242  * softints in the timer thread.  This is probably not the most
    243  * efficient method, but good enough for now.
    244  */
    245 void *
    246 softint_establish(u_int flags, void (*func)(void *), void *arg)
    247 {
    248 	struct softint *si;
    249 
    250 	si = kmem_alloc(sizeof(*si), KM_SLEEP);
    251 	si->si_func = func;
    252 	si->si_arg = arg;
    253 	si->si_flags = flags & SOFTINT_MPSAFE ? SI_MPSAFE : 0;
    254 
    255 	return si;
    256 }
    257 
    258 void
    259 softint_schedule(void *arg)
    260 {
    261 	struct softint *si = arg;
    262 
    263 	if (!rump_threads) {
    264 		si->si_func(si->si_arg);
    265 	} else {
    266 		mutex_enter(&si_mtx);
    267 		if (!(si->si_flags & SI_ONLIST)) {
    268 			LIST_INSERT_HEAD(&si_pending, si, si_entries);
    269 			si->si_flags |= SI_ONLIST;
    270 		}
    271 		cv_signal(&si_cv);
    272 		mutex_exit(&si_mtx);
    273 	}
    274 }
    275 
    276 /* flimsy disestablish: should wait for softints to finish */
    277 void
    278 softint_disestablish(void *cook)
    279 {
    280 	struct softint *si = cook;
    281 
    282 	if (si->si_flags & SI_ONLIST) {
    283 		si->si_flags |= SI_KILLME;
    284 		return;
    285 	}
    286 	kmem_free(si, sizeof(*si));
    287 }
    288 
    289 bool
    290 cpu_intr_p(void)
    291 {
    292 
    293 	return false;
    294 }
    295