freebsd-dev/sys/kern/kern_intr.c
Jake Burkholder fa2fbc3dac - Protect the callout wheel with a separate spin mutex, callout_lock.
- Use the mutex in hardclock to ensure no races between it and
  softclock.
- Make softclock be INTR_MPSAFE and provide a flag,
  CALLOUT_MPSAFE, which specifies that a callout handler does not
  need giant.  There is still no way to set this flag when
  regstering a callout.

Reviewed by:	-smp@, jlemon
2000-11-19 06:02:32 +00:00

315 lines
8.4 KiB
C

/*
* Copyright (c) 1997, Stefan Esser <se@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*
*/
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/rtprio.h>
#include <sys/systm.h>
#include <sys/ipl.h>
#include <sys/interrupt.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/ktr.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/unistd.h>
#include <sys/vmmeter.h>
#include <machine/atomic.h>
#include <machine/cpu.h>
#include <machine/md_var.h>
#include <net/netisr.h> /* prototype for legacy_setsoftnet */
struct intrhand *net_ih;
struct intrhand *vm_ih;
struct intrhand *softclock_ih;
struct ithd *clk_ithd;
struct ithd *tty_ithd;
static void start_softintr(void *);
static void swi_net(void *);
int
ithread_priority(flags)
int flags;
{
int pri;
flags &= ~INTR_MPSAFE;
switch (flags) {
case INTR_TYPE_TTY: /* keyboard or parallel port */
pri = PI_TTYLOW;
break;
case (INTR_TYPE_TTY | INTR_FAST): /* sio */
pri = PI_TTYHIGH;
break;
case INTR_TYPE_BIO:
/*
* XXX We need to refine this. BSD/OS distinguishes
* between tape and disk priorities.
*/
pri = PI_DISK;
break;
case INTR_TYPE_NET:
pri = PI_NET;
break;
case INTR_TYPE_CAM:
pri = PI_DISK; /* XXX or PI_CAM? */
break;
case INTR_TYPE_MISC:
pri = PI_DULL; /* don't care */
break;
/* We didn't specify an interrupt level. */
default:
panic("ithread_priority: no interrupt type in flags");
}
return pri;
}
void sithd_loop(void *);
struct intrhand *
sinthand_add(const char *name, struct ithd **ithdp, driver_intr_t handler,
void *arg, int pri, int flags)
{
struct proc *p;
struct ithd *ithd;
struct intrhand *ih;
struct intrhand *this_ih;
ithd = (ithdp != NULL) ? *ithdp : NULL;
if (ithd == NULL) {
int error;
ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK | M_ZERO);
error = kthread_create(sithd_loop, NULL, &p,
RFSTOPPED | RFHIGHPID, "swi%d: %s", pri, name);
if (error)
panic("inthand_add: Can't create interrupt thread");
ithd->it_proc = p;
p->p_ithd = ithd;
p->p_rtprio.type = RTP_PRIO_ITHREAD;
p->p_rtprio.prio = pri + PI_SOFT; /* soft interrupt */
p->p_stat = SWAIT; /* we're idle */
/* XXX - some hacks are _really_ gross */
if (pri == SWI_CLOCK)
p->p_flag |= P_NOLOAD;
if (ithdp != NULL)
*ithdp = ithd;
}
this_ih = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO);
this_ih->ih_handler = handler;
this_ih->ih_argument = arg;
this_ih->ih_flags = flags;
this_ih->ih_ithd = ithd;
this_ih->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
if ((ih = ithd->it_ih)) {
while (ih->ih_next != NULL)
ih = ih->ih_next;
ih->ih_next = this_ih;
} else
ithd->it_ih = this_ih;
strcpy(this_ih->ih_name, name);
return (this_ih);
}
/*
* Schedule a heavyweight software interrupt process.
*/
void
sched_swi(struct intrhand *ih, int flag)
{
struct ithd *it = ih->ih_ithd; /* and the process that does it */
struct proc *p = it->it_proc;
atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
CTR3(KTR_INTR, "sched_sihand pid %d(%s) need=%d",
p->p_pid, p->p_comm, it->it_need);
/*
* Set it_need so that if the thread is already running but close
* to done, it will do another go-round. Then get the sched lock
* and see if the thread is on whichkqs yet. If not, put it on
* there. In any case, kick everyone so that if the new thread
* is higher priority than their current thread, it gets run now.
*/
ih->ih_need = 1;
if (!(flag & SWI_DELAY)) {
it->it_need = 1;
mtx_enter(&sched_lock, MTX_SPIN);
if (p->p_stat == SWAIT) { /* not on run queue */
CTR1(KTR_INTR, "sched_swi: setrunqueue %d", p->p_pid);
/* membar_lock(); */
p->p_stat = SRUN;
setrunqueue(p);
aston();
}
else {
CTR3(KTR_INTR, "sched_swi %d: it_need %d, state %d",
p->p_pid, it->it_need, p->p_stat );
}
mtx_exit(&sched_lock, MTX_SPIN);
need_resched();
}
}
/*
* This is the main code for soft interrupt threads.
*/
void
sithd_loop(void *dummy)
{
struct ithd *it; /* our thread context */
struct intrhand *ih; /* and our interrupt handler chain */
struct proc *p = curproc;
it = p->p_ithd; /* point to myself */
/*
* As long as we have interrupts outstanding, go through the
* list of handlers, giving each one a go at it.
*/
for (;;) {
CTR3(KTR_INTR, "sithd_loop pid %d(%s) need=%d",
p->p_pid, p->p_comm, it->it_need);
while (it->it_need) {
/*
* Service interrupts. If another interrupt
* arrives while we are running, they will set
* it_need to denote that we should make
* another pass.
*/
it->it_need = 0;
for (ih = it->it_ih; ih != NULL; ih = ih->ih_next) {
if (!ih->ih_need)
continue;
ih->ih_need = 0;
CTR5(KTR_INTR,
"sithd_loop pid %d ih=%p: %p(%p) flg=%x",
p->p_pid, (void *)ih,
(void *)ih->ih_handler, ih->ih_argument,
ih->ih_flags);
if ((ih->ih_flags & INTR_MPSAFE) == 0)
mtx_enter(&Giant, MTX_DEF);
ih->ih_handler(ih->ih_argument);
if ((ih->ih_flags & INTR_MPSAFE) == 0)
mtx_exit(&Giant, MTX_DEF);
}
}
/*
* Processed all our interrupts. Now get the sched
* lock. This may take a while and it_need may get
* set again, so we have to check it again.
*/
mtx_assert(&Giant, MA_NOTOWNED);
mtx_enter(&sched_lock, MTX_SPIN);
if (!it->it_need) {
p->p_stat = SWAIT; /* we're idle */
CTR1(KTR_INTR, "sithd_loop pid %d: done", p->p_pid);
mi_switch();
CTR1(KTR_INTR, "sithd_loop pid %d: resumed", p->p_pid);
}
mtx_exit(&sched_lock, MTX_SPIN);
}
}
SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
/*
* Start standard software interrupt threads
*/
static void
start_softintr(dummy)
void *dummy;
{
net_ih = sinthand_add("net", NULL, swi_net, NULL, SWI_NET, 0);
softclock_ih =
sinthand_add("clock", &clk_ithd, softclock, NULL, SWI_CLOCK, INTR_MPSAFE);
vm_ih = sinthand_add("vm", NULL, swi_vm, NULL, SWI_VM, 0);
}
void
legacy_setsoftnet()
{
sched_swi(net_ih, SWI_NOSWITCH);
}
/*
* XXX: This should really be in the network code somewhere and installed
* via a SI_SUB_SOFINTR, SI_ORDER_MIDDLE sysinit.
*/
void (*netisrs[32]) __P((void));
u_int netisr;
static void
swi_net(void *dummy)
{
u_int bits;
int i;
bits = atomic_readandclear_int(&netisr);
while ((i = ffs(bits)) != 0) {
i--;
netisrs[i]();
bits &= ~(1 << i);
}
}
/*
* Dummy spl calls. The only reason for these is to not break
* all the code which expects to call them.
*/
void spl0 (void) {}
void splx (intrmask_t x) {}
intrmask_t splq(intrmask_t mask) { return 0; }
intrmask_t splbio(void) { return 0; }
intrmask_t splcam(void) { return 0; }
intrmask_t splclock(void) { return 0; }
intrmask_t splhigh(void) { return 0; }
intrmask_t splimp(void) { return 0; }
intrmask_t splnet(void) { return 0; }
intrmask_t splsoftcam(void) { return 0; }
intrmask_t splsoftcambio(void) { return 0; }
intrmask_t splsoftcamnet(void) { return 0; }
intrmask_t splsoftclock(void) { return 0; }
intrmask_t splsofttty(void) { return 0; }
intrmask_t splsoftvm(void) { return 0; }
intrmask_t splsofttq(void) { return 0; }
intrmask_t splstatclock(void) { return 0; }
intrmask_t spltty(void) { return 0; }
intrmask_t splvm(void) { return 0; }