1d93a5628a
This implements proper intrdisable() support for all interrupt controllers. For enable, (*arch->intrassign)(Vctl*) fills in the Vctl.enable and Vctl.disable pointers with the appropriate routines and returns the assigned vector number. Once the Vctl struct has been linked to its vector chain, Vctl.enable(Vctl*, shared) gets called with a flag if the vector has been already enabled (shared). This order is important here as enabling the interrupt on the controller before we have linked the chain can cause spurious interrupts, expecially on mp system where the interrupt can target a different cpu than the caller of intrenable(). The intrdisable() case is the other way around. We first disable the interrupt on the controller and after that unlink the Vctl from the chain. On a multiprocessor, the xfree() of the Vctl struct is delayed to avoid freeing it while it is still in use by another cpu. The xen port now also uses pc/irq.c which has been made generic enougth to handle xen's irq scheme. Also, archgeneric is now a separate file to avoid pulling in dependencies from the 8259 interrupt controller code.
307 lines
5.7 KiB
C
307 lines
5.7 KiB
C
#include "u.h"
|
|
#include "tos.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "ureg.h"
|
|
#include "../port/error.h"
|
|
|
|
static Lock vctllock;
|
|
static Vctl *vclock, *vctl[256];
|
|
|
|
enum
|
|
{
|
|
Ntimevec = 20 /* number of time buckets for each intr */
|
|
};
|
|
ulong intrtimes[256][Ntimevec];
|
|
|
|
/*
|
|
* keep histogram of interrupt service times
|
|
*/
|
|
static void
|
|
intrtime(Mach*, int vno)
|
|
{
|
|
ulong diff;
|
|
ulong x;
|
|
|
|
x = perfticks();
|
|
diff = x - m->perf.intrts;
|
|
m->perf.intrts = x;
|
|
|
|
m->perf.inintr += diff;
|
|
if(up == nil && m->perf.inidle > diff)
|
|
m->perf.inidle -= diff;
|
|
|
|
diff /= m->cpumhz*100; /* quantum = 100µsec */
|
|
if(diff >= Ntimevec)
|
|
diff = Ntimevec-1;
|
|
intrtimes[vno][diff]++;
|
|
}
|
|
|
|
int
|
|
irqhandled(Ureg *ureg, int vno)
|
|
{
|
|
Vctl *ctl, *v;
|
|
int i;
|
|
|
|
ctl = vctl[vno];
|
|
if(ctl != nil){
|
|
if(vno < VectorPIC){
|
|
(*ctl->f)(ureg, ctl->a);
|
|
return 1;
|
|
}
|
|
|
|
m->perf.intrts = perfticks();
|
|
m->intr++;
|
|
m->lastintr = ctl->irq;
|
|
if(ctl->isr != nil)
|
|
(*ctl->isr)(vno);
|
|
for(v = ctl; v != nil; v = v->next)
|
|
(*v->f)(ureg, v->a);
|
|
if(ctl->eoi != nil)
|
|
(*ctl->eoi)(vno);
|
|
intrtime(m, vno);
|
|
|
|
if(up != nil){
|
|
if(ctl == vclock){
|
|
/* delaysched set because we held a lock or because our quantum ended */
|
|
if(up->delaysched)
|
|
sched();
|
|
} else {
|
|
preempted();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if(vno < VectorPIC || vno == VectorSYSCALL)
|
|
return 0;
|
|
|
|
m->spuriousintr++;
|
|
if(arch->intrspurious != nil && (*arch->intrspurious)(vno) == 0)
|
|
return 1; /* false alarm */
|
|
|
|
iprint("cpu%d: spurious interrupt %d, last %d\n",
|
|
m->machno, vno, m->lastintr);
|
|
|
|
/* call all non-local interrupt routines, just in case */
|
|
for(i = VectorPIC; i < nelem(vctl); i++){
|
|
ctl = vctl[i];
|
|
if(ctl == nil || ctl == vclock || ctl->local)
|
|
continue;
|
|
for(v = ctl; v != nil; v = v->next)
|
|
(*v->f)(ureg, v->a);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
|
{
|
|
Vctl *v;
|
|
|
|
if(f == nil){
|
|
print("trapenable: nil handler for %d, for %s\n",
|
|
vno, name);
|
|
return;
|
|
}
|
|
|
|
if(vno < 0 || vno >= VectorPIC){
|
|
print("trapenable: vno %d out of range", vno);
|
|
return;
|
|
}
|
|
|
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
|
panic("trapenable: out of memory");
|
|
|
|
v->f = f;
|
|
v->a = a;
|
|
|
|
v->tbdf = BUSUNKNOWN;
|
|
v->irq = -1;
|
|
v->vno = vno;
|
|
v->cpu = -1;
|
|
|
|
strncpy(v->name, name, KNAMELEN-1);
|
|
v->name[KNAMELEN-1] = 0;
|
|
|
|
ilock(&vctllock);
|
|
if(vctl[vno] != nil){
|
|
print("trapenable: vno %d assigned twice: %s %s\n",
|
|
vno, vctl[vno]->name, v->name);
|
|
iunlock(&vctllock);
|
|
xfree(v);
|
|
return;
|
|
}
|
|
vctl[vno] = v;
|
|
iunlock(&vctllock);
|
|
}
|
|
|
|
static Vctl*
|
|
delayfree(Vctl *v)
|
|
{
|
|
static Vctl *q[4];
|
|
static uint x;
|
|
Vctl *r;
|
|
|
|
r = q[x], q[x] = v;
|
|
x = (x+1) % nelem(q);
|
|
return r;
|
|
}
|
|
|
|
void
|
|
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
|
{
|
|
Vctl **pv, *v;
|
|
|
|
if(f == nil){
|
|
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
|
irq, tbdf, name);
|
|
return;
|
|
}
|
|
|
|
if(arch->intrirqno != nil)
|
|
irq = (*arch->intrirqno)(irq, tbdf);
|
|
|
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
|
panic("intrenable: out of memory");
|
|
|
|
v->f = f;
|
|
v->a = a;
|
|
|
|
v->tbdf = tbdf;
|
|
v->irq = irq;
|
|
v->vno = -1;
|
|
v->cpu = -1;
|
|
|
|
strncpy(v->name, name, KNAMELEN-1);
|
|
v->name[KNAMELEN-1] = 0;
|
|
|
|
ilock(&vctllock);
|
|
v->vno = (*arch->intrassign)(v);
|
|
if(v->vno < VectorPIC || v->vno >= nelem(vctl)){
|
|
print("intrenable: couldn't assign irq %d, tbdf 0x%uX for %s\n",
|
|
irq, tbdf, v->name);
|
|
Unlockandfree:
|
|
iunlock(&vctllock);
|
|
if(v != nil)
|
|
xfree(v);
|
|
return;
|
|
}
|
|
pv = &vctl[v->vno];
|
|
if(*pv != nil){
|
|
if((*pv)->isr != v->isr || (*pv)->eoi != v->eoi){
|
|
print("intrenable: incompatible handler: %s %s %#p %#p %#p %#p\n",
|
|
(*pv)->name, v->name,
|
|
(*pv)->isr, v->isr,
|
|
(*pv)->eoi, v->eoi);
|
|
goto Unlockandfree;
|
|
}
|
|
if(*pv == vclock)
|
|
pv = &vclock->next;
|
|
v->next = *pv;
|
|
}
|
|
if(strcmp(name, "clock") == 0)
|
|
vclock = v;
|
|
*pv = v;
|
|
if(v->enable != nil){
|
|
coherence();
|
|
if((*v->enable)(v, pv != &vctl[v->vno] || v->next != nil) < 0){
|
|
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
|
irq, tbdf, v->name);
|
|
*pv = v->next;
|
|
if(v == vclock)
|
|
vclock = nil;
|
|
if(conf.nmach > 1)
|
|
v = delayfree(v);
|
|
goto Unlockandfree;
|
|
}
|
|
}
|
|
iunlock(&vctllock);
|
|
}
|
|
|
|
void
|
|
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
|
|
{
|
|
Vctl **pv, *v;
|
|
int vno;
|
|
|
|
if(arch->intrirqno != nil)
|
|
irq = (*arch->intrirqno)(irq, tbdf);
|
|
|
|
if(irq != -1 && arch->intrvecno != nil) {
|
|
vno = (*arch->intrvecno)(irq);
|
|
if(vno < VectorPIC || vno >= nelem(vctl)){
|
|
irq = -1;
|
|
vno = VectorPIC;
|
|
}
|
|
} else {
|
|
irq = -1;
|
|
vno = VectorPIC;
|
|
}
|
|
|
|
ilock(&vctllock);
|
|
do {
|
|
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
|
if((v->irq == irq || irq == -1)
|
|
&& v->tbdf == tbdf && v->f == f && v->a == a
|
|
&& strcmp(v->name, name) == 0)
|
|
break;
|
|
}
|
|
if(v != nil){
|
|
if(v->disable != nil){
|
|
if((*v->disable)(v, pv != &vctl[vno] || v->next != nil) < 0){
|
|
print("intrdisable: couldn't disable irq %d, tbdf 0x%uX for %s\n",
|
|
irq, tbdf, name);
|
|
}
|
|
coherence();
|
|
}
|
|
*pv = v->next;
|
|
if(v == vclock)
|
|
vclock = nil;
|
|
if(conf.nmach > 1)
|
|
v = delayfree(v);
|
|
iunlock(&vctllock);
|
|
if(v != nil)
|
|
xfree(v);
|
|
return;
|
|
}
|
|
} while(irq == -1 && ++vno < nelem(vctl));
|
|
iunlock(&vctllock);
|
|
}
|
|
|
|
static long
|
|
irqallocread(Chan*, void *a, long n, vlong offset)
|
|
{
|
|
char buf[2*(11+1)+KNAMELEN+1+1];
|
|
int vno, m;
|
|
Vctl *v;
|
|
|
|
if(n < 0 || offset < 0)
|
|
error(Ebadarg);
|
|
|
|
for(vno = 0; vno < nelem(vctl); vno++){
|
|
for(v = vctl[vno]; v != nil; v = v->next){
|
|
m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
|
|
offset -= m;
|
|
if(offset >= 0)
|
|
continue;
|
|
if(n > -offset)
|
|
n = -offset;
|
|
offset += m;
|
|
memmove(a, buf+offset, n);
|
|
return n;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
irqinit(void)
|
|
{
|
|
addarchfile("irqalloc", 0444, irqallocread, nil);
|
|
}
|