interrupts could be recursive since lapic_eoi() called before rti
so fast interrupts overflow the kernel stack fix: cli() before lapic_eoi()
This commit is contained in:
parent
8a8be1b8c3
commit
5be0039ce9
16 changed files with 194 additions and 28 deletions
75
Notes
75
Notes
|
@ -279,3 +279,78 @@ BUT now userfs doesn't do the final cat README
|
|||
|
||||
AND w/ cprintf("kbd overflow"), panic holding locks in scheduler
|
||||
maybe also simulataneous panic("interrupt while holding a lock")
|
||||
|
||||
again (holding down x key):
|
||||
kbd overflow
|
||||
kbd oaaniicloowh
|
||||
olding locks in scheduler
|
||||
trap v 33 eip 100F5F c^CNext at t=32166285
|
||||
(0) [0x0010033e] 0008:0010033e (unk. ctxt): jmp .+0xfffffffe (0x0010033e) ; ebfe
|
||||
(1) [0x0010005c] 0008:0010005c (unk. ctxt): jmp .+0xfffffffe (0x0010005c) ; ebfe
|
||||
cpu0 paniced due to holding locks in scheduler
|
||||
cpu1 got panic("interrupt while holding a lock")
|
||||
again in lapic_write.
|
||||
while re-enabling an IRQ?
|
||||
|
||||
again:
|
||||
cpu 0 panic("holding locks in scheduler")
|
||||
but didn't trigger related panics earlier in scheduler or sched()
|
||||
of course the panic is right after release() and thus sti()
|
||||
so we may be seeing an interrupt that left locks held
|
||||
cpu 1 unknown panic
|
||||
why does it happen to both cpus at the same time?
|
||||
|
||||
again:
|
||||
cpu 0 panic("holding locks in scheduler")
|
||||
but trap() didn't see any held locks on return
|
||||
cpu 1 no apparent panic
|
||||
|
||||
again:
|
||||
cpu 0 panic: holding too many locks in scheduler
|
||||
cpu 1 panic: kbd_intr returned while holding a lock
|
||||
|
||||
again:
|
||||
cpu 0 panic: holding too man
|
||||
la 10d70c lr 10027b
|
||||
those don't seem to be locks...
|
||||
only place non-constant lock is used is sleep()'s 2nd arg
|
||||
maybe register not preserved across context switch?
|
||||
it's in %esi...
|
||||
sched() doesn't touch %esi
|
||||
%esi is evidently callee-saved
|
||||
something to do with interrupts? since ordinarily it works
|
||||
cpu 1 panic: kbd_int returned while holding a lock
|
||||
la 107340 lr 107300
|
||||
console_lock and kbd_lock
|
||||
|
||||
maybe console_lock is often not released due to change
|
||||
in use_console_lock (panic on other cpu)
|
||||
|
||||
again:
|
||||
cpu 0: panic: h...
|
||||
la 10D78C lr 102CA0
|
||||
cpu 1: panic: acquire FL_IF (later than cpu 0)
|
||||
|
||||
but if sleep() were acquiring random locks, we'd see panics
|
||||
in release, after sleep() returned.
|
||||
actually when system is idle, maybe no-one sleeps at all.
|
||||
just scheduler() and interrupts
|
||||
|
||||
questions:
|
||||
does userfs use pipes? or fork?
|
||||
no
|
||||
does anything bad happen if process 1 exits? eg exit() in cat.c
|
||||
looks ok
|
||||
are there really no processes left?
|
||||
lock_init() so we can have a magic number?
|
||||
|
||||
HMM maybe the variables at the end of struct cpu are being overwritten
|
||||
nlocks, lastacquire, lastrelease
|
||||
by cpu->stack?
|
||||
adding junk buffers maybe causes crash to take longer...
|
||||
when do we run on cpu stack?
|
||||
just in scheduler()?
|
||||
and interrupts from scheduler()
|
||||
|
||||
OH! recursive interrupts will use up any amount of cpu[].stack!
|
||||
underflow and wrecks *previous* cpu's struct
|
||||
|
|
8
bio.c
8
bio.c
|
@ -8,7 +8,13 @@
|
|||
#include "buf.h"
|
||||
|
||||
struct buf buf[NBUF];
|
||||
struct spinlock buf_table_lock = { "buf_table" };
|
||||
struct spinlock buf_table_lock;
|
||||
|
||||
void
|
||||
binit(void)
|
||||
{
|
||||
initlock(&buf_table_lock, "buf_table");
|
||||
}
|
||||
|
||||
struct buf *
|
||||
getblk()
|
||||
|
|
31
console.c
31
console.c
|
@ -3,11 +3,16 @@
|
|||
#include "defs.h"
|
||||
#include "spinlock.h"
|
||||
#include "dev.h"
|
||||
#include "param.h"
|
||||
|
||||
struct spinlock console_lock = { "console" };
|
||||
struct spinlock console_lock;
|
||||
int panicked = 0;
|
||||
int use_console_lock = 0;
|
||||
|
||||
// per-cpu copy of output to help panic/lock debugging
|
||||
char obuf[NCPU][1024];
|
||||
uint obufi[NCPU];
|
||||
|
||||
/*
|
||||
* copy console output to parallel port, which you can tell
|
||||
* .bochsrc to copy to the stdout:
|
||||
|
@ -32,6 +37,10 @@ cons_putc(int c)
|
|||
ushort *crt = (ushort *) 0xB8000; // base of CGA memory
|
||||
int ind;
|
||||
|
||||
obuf[rcr4()][obufi[rcr4()]++] = c;
|
||||
if(obufi[rcr4()] >= 1024)
|
||||
obufi[rcr4()] = 0;
|
||||
|
||||
if(panicked){
|
||||
cli();
|
||||
for(;;)
|
||||
|
@ -101,11 +110,13 @@ printint(int xx, int base, int sgn)
|
|||
void
|
||||
cprintf(char *fmt, ...)
|
||||
{
|
||||
int i, state = 0, c;
|
||||
int i, state = 0, c, locking = 0;
|
||||
uint *ap = (uint *)(void*)&fmt + 1;
|
||||
|
||||
if(use_console_lock)
|
||||
if(use_console_lock){
|
||||
locking = 1;
|
||||
acquire(&console_lock);
|
||||
}
|
||||
|
||||
for(i = 0; fmt[i]; i++){
|
||||
c = fmt[i] & 0xff;
|
||||
|
@ -140,7 +151,7 @@ cprintf(char *fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
if(use_console_lock)
|
||||
if(locking)
|
||||
release(&console_lock);
|
||||
}
|
||||
|
||||
|
@ -293,7 +304,7 @@ static uchar *charcode[4] = {
|
|||
char kbd_buf[KBD_BUF];
|
||||
int kbd_r;
|
||||
int kbd_w;
|
||||
struct spinlock kbd_lock = { "kbd_lock" };
|
||||
struct spinlock kbd_lock;
|
||||
|
||||
void
|
||||
kbd_intr()
|
||||
|
@ -303,20 +314,17 @@ kbd_intr()
|
|||
|
||||
st = inb(KBSTATP);
|
||||
if ((st & KBS_DIB) == 0){
|
||||
lapic_eoi();
|
||||
return;
|
||||
}
|
||||
data = inb(KBDATAP);
|
||||
|
||||
if (data == 0xE0) {
|
||||
shift |= E0ESC;
|
||||
lapic_eoi();
|
||||
return;
|
||||
} else if (data & 0x80) {
|
||||
// Key released
|
||||
data = (shift & E0ESC ? data : data & 0x7F);
|
||||
shift &= ~(shiftcode[data] | E0ESC);
|
||||
lapic_eoi();
|
||||
return;
|
||||
} else if (shift & E0ESC) {
|
||||
// Last character was an E0 escape; or with 0x80
|
||||
|
@ -346,14 +354,17 @@ kbd_intr()
|
|||
}
|
||||
|
||||
release(&kbd_lock);
|
||||
|
||||
lapic_eoi();
|
||||
}
|
||||
|
||||
void
|
||||
console_init()
|
||||
{
|
||||
initlock(&console_lock, "console");
|
||||
initlock(&kbd_lock, "kbd");
|
||||
|
||||
devsw[CONSOLE].d_write = console_write;
|
||||
|
||||
ioapic_enable (IRQ_KBD, 1);
|
||||
|
||||
use_console_lock = 1;
|
||||
}
|
||||
|
|
5
defs.h
5
defs.h
|
@ -10,6 +10,7 @@ void panic(char *s);
|
|||
void kbd_intr(void);
|
||||
|
||||
// proc.c
|
||||
void pinit(void);
|
||||
struct proc;
|
||||
struct jmpbuf;
|
||||
void setupsegs(struct proc *);
|
||||
|
@ -67,6 +68,7 @@ void ioapic_enable (int irq, int cpu);
|
|||
|
||||
// spinlock.c
|
||||
struct spinlock;
|
||||
void initlock(struct spinlock *, char *);
|
||||
void acquire(struct spinlock*);
|
||||
void release(struct spinlock*);
|
||||
int holding(struct spinlock*);
|
||||
|
@ -83,6 +85,7 @@ int pipe_write(struct pipe *p, char *addr, int n);
|
|||
int pipe_read(struct pipe *p, char *addr, int n);
|
||||
|
||||
// fd.c
|
||||
void fd_init(void);
|
||||
int fd_ualloc(void);
|
||||
struct fd * fd_alloc(void);
|
||||
void fd_close(struct fd *);
|
||||
|
@ -97,6 +100,7 @@ void* ide_start_rw(int diskno, uint secno, void *dst, uint nsecs, int read);
|
|||
int ide_finish(void *);
|
||||
|
||||
// bio.c
|
||||
void binit(void);
|
||||
struct buf;
|
||||
struct buf *getblk(void);
|
||||
struct buf *bread(uint, uint);
|
||||
|
@ -104,6 +108,7 @@ void bwrite(uint, struct buf *, uint);
|
|||
void brelse(struct buf *);
|
||||
|
||||
// fs.c
|
||||
void iinit(void);
|
||||
struct inode * iget(uint dev, uint inum);
|
||||
void ilock(struct inode *ip);
|
||||
void iunlock(struct inode *ip);
|
||||
|
|
6
fd.c
6
fd.c
|
@ -13,6 +13,12 @@ struct devsw devsw[NDEV];
|
|||
|
||||
struct fd fds[NFD];
|
||||
|
||||
void
|
||||
fd_init(void)
|
||||
{
|
||||
initlock(&fd_table_lock, "fd_table");
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a file descriptor number for curproc.
|
||||
*/
|
||||
|
|
8
fs.c
8
fs.c
|
@ -13,10 +13,16 @@
|
|||
// these are inodes currently in use
|
||||
// an entry is free if count == 0
|
||||
struct inode inode[NINODE];
|
||||
struct spinlock inode_table_lock = { "inode_table" };
|
||||
struct spinlock inode_table_lock;
|
||||
|
||||
uint rootdev = 1;
|
||||
|
||||
void
|
||||
iinit(void)
|
||||
{
|
||||
initlock(&inode_table_lock, "inode_table");
|
||||
}
|
||||
|
||||
static uint
|
||||
balloc(uint dev)
|
||||
{
|
||||
|
|
4
ide.c
4
ide.c
|
@ -26,7 +26,7 @@ struct ide_request {
|
|||
};
|
||||
struct ide_request request[NREQUEST];
|
||||
int head, tail;
|
||||
struct spinlock ide_lock = { "ide" };
|
||||
struct spinlock ide_lock;
|
||||
|
||||
int disk_channel;
|
||||
|
||||
|
@ -46,6 +46,7 @@ ide_wait_ready(int check_error)
|
|||
void
|
||||
ide_init(void)
|
||||
{
|
||||
initlock(&ide_lock, "ide");
|
||||
if (ncpu < 2) {
|
||||
panic ("ide_init: disk interrupt is going to the second cpu\n");
|
||||
}
|
||||
|
@ -61,7 +62,6 @@ ide_intr(void)
|
|||
// cprintf("cpu%d: ide_intr\n", cpu());
|
||||
wakeup(&request[tail]);
|
||||
release(&ide_lock);
|
||||
lapic_eoi();
|
||||
}
|
||||
|
||||
int
|
||||
|
|
3
kalloc.c
3
kalloc.c
|
@ -15,7 +15,7 @@
|
|||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
struct spinlock kalloc_lock = { "kalloc" };
|
||||
struct spinlock kalloc_lock;
|
||||
|
||||
struct run {
|
||||
struct run *next;
|
||||
|
@ -37,6 +37,7 @@ kinit(void)
|
|||
uint mem;
|
||||
char *start;
|
||||
|
||||
initlock(&kalloc_lock, "kalloc");
|
||||
start = (char *) &end;
|
||||
start = (char *) (((uint)start + PAGE) & ~(PAGE-1));
|
||||
mem = 256; // XXX
|
||||
|
|
18
main.c
18
main.c
|
@ -15,8 +15,6 @@ extern uchar _binary_user1_start[], _binary_user1_size[];
|
|||
extern uchar _binary_usertests_start[], _binary_usertests_size[];
|
||||
extern uchar _binary_userfs_start[], _binary_userfs_size[];
|
||||
|
||||
extern int use_console_lock;
|
||||
|
||||
// CPU 0 starts running C code here.
|
||||
// This is called main0 not main so that it can have
|
||||
// a void return type. Gcc can't handle functions named
|
||||
|
@ -27,28 +25,36 @@ main0(void)
|
|||
int i;
|
||||
struct proc *p;
|
||||
|
||||
lcr4(0); // xxx copy of cpu #
|
||||
|
||||
// clear BSS
|
||||
memset(edata, 0, end - edata);
|
||||
|
||||
// Make sure interrupts stay disabled on all processors
|
||||
// until each signals it is ready, by pretending to hold
|
||||
// an extra lock.
|
||||
for(i=0; i<NCPU; i++)
|
||||
// xxx maybe replace w/ acquire remembering if FL_IF
|
||||
for(i=0; i<NCPU; i++){
|
||||
cpus[i].nlock++;
|
||||
cpus[i].guard1 = 0xdeadbeef;
|
||||
cpus[i].guard2 = 0xdeadbeef;
|
||||
}
|
||||
|
||||
mp_init(); // collect info about this machine
|
||||
|
||||
use_console_lock = 1;
|
||||
|
||||
lapic_init(mp_bcpu());
|
||||
|
||||
cprintf("\n\ncpu%d: booting xv6\n\n", cpu());
|
||||
|
||||
pinit();
|
||||
binit();
|
||||
pic_init(); // initialize PIC
|
||||
ioapic_init();
|
||||
kinit(); // physical memory allocator
|
||||
tvinit(); // trap vectors
|
||||
idtinit(); // this CPU's idt register
|
||||
fd_init();
|
||||
iinit();
|
||||
|
||||
// create a fake process per CPU
|
||||
// so each CPU always has a tss and a gdt
|
||||
|
@ -101,6 +107,8 @@ main0(void)
|
|||
void
|
||||
mpmain(void)
|
||||
{
|
||||
lcr4(1); // xxx copy of cpu #
|
||||
|
||||
cprintf("cpu%d: starting\n", cpu());
|
||||
idtinit(); // CPU's idt
|
||||
if(cpu() == 0)
|
||||
|
|
1
mmu.h
1
mmu.h
|
@ -178,6 +178,7 @@ struct gatedesc {
|
|||
|
||||
// Set up a normal interrupt/trap gate descriptor.
|
||||
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
|
||||
// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
|
||||
// - sel: Code segment selector for interrupt/trap handler
|
||||
// - off: Offset in code segment for interrupt/trap handler
|
||||
// - dpl: Descriptor Privilege Level -
|
||||
|
|
2
pipe.c
2
pipe.c
|
@ -34,7 +34,7 @@ pipe_alloc(struct fd **fd1, struct fd **fd2)
|
|||
p->writeopen = 1;
|
||||
p->writep = 0;
|
||||
p->readp = 0;
|
||||
memset(&p->lock, 0, sizeof(p->lock));
|
||||
initlock(&p->lock, "pipe");
|
||||
(*fd1)->type = FD_PIPE;
|
||||
(*fd1)->readable = 1;
|
||||
(*fd1)->writeable = 0;
|
||||
|
|
16
proc.c
16
proc.c
|
@ -7,7 +7,7 @@
|
|||
#include "defs.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
struct spinlock proc_table_lock = { "proc_table" };
|
||||
struct spinlock proc_table_lock;
|
||||
|
||||
struct proc proc[NPROC];
|
||||
struct proc *curproc[NCPU];
|
||||
|
@ -15,6 +15,12 @@ int next_pid = NCPU;
|
|||
extern void forkret(void);
|
||||
extern void forkret1(struct trapframe*);
|
||||
|
||||
void
|
||||
pinit(void)
|
||||
{
|
||||
initlock(&proc_table_lock, "proc_table");
|
||||
}
|
||||
|
||||
/*
|
||||
* set up a process's task state and segment descriptors
|
||||
* correctly, given its current size and address in memory.
|
||||
|
@ -146,6 +152,9 @@ scheduler(void)
|
|||
// Loop over process table looking for process to run.
|
||||
acquire(&proc_table_lock);
|
||||
for(i = 0; i < NPROC; i++){
|
||||
if(cpus[cpu()].guard1 != 0xdeadbeef ||
|
||||
cpus[cpu()].guard2 != 0xdeadbeef)
|
||||
panic("cpu guard");
|
||||
p = &proc[i];
|
||||
if(p->state != RUNNABLE)
|
||||
continue;
|
||||
|
@ -194,6 +203,7 @@ scheduler(void)
|
|||
|
||||
// XXX if not holding proc_table_lock panic.
|
||||
}
|
||||
|
||||
release(&proc_table_lock);
|
||||
|
||||
if(cpus[cpu()].nlock != 0)
|
||||
|
@ -212,7 +222,9 @@ scheduler(void)
|
|||
void
|
||||
sched(void)
|
||||
{
|
||||
if(setjmp(&curproc[cpu()]->jmpbuf) == 0)
|
||||
struct proc *p = curproc[cpu()];
|
||||
|
||||
if(setjmp(&p->jmpbuf) == 0)
|
||||
longjmp(&cpus[cpu()].jmpbuf);
|
||||
}
|
||||
|
||||
|
|
4
proc.h
4
proc.h
|
@ -41,8 +41,6 @@ struct proc{
|
|||
uint sz; // total size of mem, including kernel stack
|
||||
char *kstack; // kernel stack, separate from mem so it doesn't move
|
||||
enum proc_state state;
|
||||
enum proc_state newstate; // desired state after swtch()
|
||||
struct spinlock *mtx; // mutex for condition variable
|
||||
int pid;
|
||||
int ppid;
|
||||
void *chan; // sleep
|
||||
|
@ -68,7 +66,9 @@ extern struct proc *curproc[NCPU]; // can be NULL if no proc running.
|
|||
struct cpu {
|
||||
uchar apicid; // Local APIC ID
|
||||
struct jmpbuf jmpbuf;
|
||||
int guard1;
|
||||
char mpstack[MPSTACK]; // per-cpu start-up stack
|
||||
int guard2;
|
||||
volatile int booted;
|
||||
int nlock; // # of locks currently held
|
||||
struct spinlock *lastacquire; // xxx debug
|
||||
|
|
18
spinlock.c
18
spinlock.c
|
@ -10,8 +10,19 @@
|
|||
// because cprintf uses them itself.
|
||||
//#define cprintf dont_use_cprintf
|
||||
|
||||
#define LOCKMAGIC 0x6673ffea
|
||||
|
||||
extern int use_console_lock;
|
||||
|
||||
void
|
||||
initlock(struct spinlock *lock, char *name)
|
||||
{
|
||||
lock->magic = LOCKMAGIC;
|
||||
lock->name = name;
|
||||
lock->locked = 0;
|
||||
lock->cpu = 0xffffffff;
|
||||
}
|
||||
|
||||
void
|
||||
getcallerpcs(void *v, uint pcs[])
|
||||
{
|
||||
|
@ -27,6 +38,8 @@ getcallerpcs(void *v, uint pcs[])
|
|||
void
|
||||
acquire(struct spinlock * lock)
|
||||
{
|
||||
if(lock->magic != LOCKMAGIC)
|
||||
panic("weird lock magic");
|
||||
if(holding(lock))
|
||||
panic("acquire");
|
||||
|
||||
|
@ -45,6 +58,9 @@ acquire(struct spinlock * lock)
|
|||
void
|
||||
release(struct spinlock * lock)
|
||||
{
|
||||
if(lock->magic != LOCKMAGIC)
|
||||
panic("weird lock magic");
|
||||
|
||||
if(!holding(lock))
|
||||
panic("release");
|
||||
|
||||
|
@ -55,8 +71,6 @@ release(struct spinlock * lock)
|
|||
lock->locked = 0;
|
||||
if(--cpus[cpu()].nlock == 0)
|
||||
sti();
|
||||
// xxx we may have just turned interrupts on during
|
||||
// an interrupt, is that ok?
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct spinlock {
|
||||
uint magic;
|
||||
char *name;
|
||||
uint locked;
|
||||
uint pcs[10];
|
||||
int cpu;
|
||||
uint pcs[10];
|
||||
};
|
||||
|
|
20
trap.c
20
trap.c
|
@ -41,6 +41,17 @@ trap(struct trapframe *tf)
|
|||
panic("interrupt while holding a lock");
|
||||
}
|
||||
|
||||
if(cpu() == 1 && curproc[cpu()] == 0){
|
||||
if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + 512){
|
||||
cprintf("&tf %x mpstack %x\n", &tf, cpus[cpu()].mpstack);
|
||||
panic("trap cpu stack");
|
||||
}
|
||||
} else if(curproc[cpu()]){
|
||||
if(&tf < curproc[cpu()]->kstack){
|
||||
panic("trap kstack");
|
||||
}
|
||||
}
|
||||
|
||||
if(v == T_SYSCALL){
|
||||
struct proc *cp = curproc[cpu()];
|
||||
int num = cp->tf->eax;
|
||||
|
@ -97,11 +108,20 @@ trap(struct trapframe *tf)
|
|||
|
||||
if(v == (IRQ_OFFSET + IRQ_IDE)){
|
||||
ide_intr();
|
||||
if(cpus[cpu()].nlock)
|
||||
panic("ide_intr returned while holding a lock");
|
||||
cli(); // prevent a waiting interrupt from overflowing stack
|
||||
lapic_eoi();
|
||||
return;
|
||||
}
|
||||
|
||||
if(v == (IRQ_OFFSET + IRQ_KBD)){
|
||||
kbd_intr();
|
||||
if(cpus[cpu()].nlock){
|
||||
panic("kbd_intr returned while holding a lock");
|
||||
}
|
||||
cli(); // prevent a waiting interrupt from overflowing stack
|
||||
lapic_eoi();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue