// proc.c
struct proc* copyproc(struct proc*);
+struct proc* curproc();
void exit(void);
int growproc(int);
int kill(int);
int holding(struct spinlock*);
void initlock(struct spinlock*, char*);
void release(struct spinlock*);
+void splhi();
+void spllo();
// string.c
int memcmp(const void*, const void*, uint);
# 650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66 2 to 2.5 Mips
# 400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3 1 to 1.8 Mips
#=======================================================================
-cpu: count=2, ips=10000000, quantum=1
+cpu: count=2, ips=10000000, quantum=1, reset_on_triple_fault=0
#=======================================================================
# MEGS
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
#include "types.h"
+#include "defs.h"
#include "traps.h"
+#include "mmu.h"
+#include "x86.h"
// Local APIC registers, divided by 4 for use as uint[] indices.
#define ID (0x0020/4) // ID
int
cpu(void)
{
+ // Cannot call cpu when interrupts are enabled:
+ // result not guaranteed to last long enough to be used!
+ // Would prefer to panic but even printing is chancy here:
+ // everything, including cprintf, calls cpu, at least indirectly
+ // through acquire and release.
+ if(read_eflags()&FL_IF){
+ static int n;
+ int i;
+ uint pcs[10];
+
+ if(n++%999 == 0){
+ getcallerpcs((uint*)read_ebp() + 2, pcs);
+ cprintf("cpu called from %x with interrupts enabled: stk");
+ for(i=0; i<10 && pcs[i] && pcs[i] != -1; i++)
+ cprintf(" %x", pcs[i]);
+ cprintf("\n");
+ }
+ }
+
if(lapic)
return lapic[ID]>>24;
return 0;
// clear BSS
memset(edata, 0, end - edata);
- // Prevent release() from enabling interrupts.
+ // splhi() every processor during bootstrap.
for(i=0; i<NCPU; i++)
- cpus[i].nlock = 1;
+ cpus[i].nsplhi = 1;
mp_init(); // collect info about this machine
bcpu = mp_bcpu();
bootothers(); // boot other CPUs
if(!ismp)
timer_init(); // uniprocessor timer
+ cprintf("ismp %d\n", ismp);
+ cprintf("userinit\n");
userinit(); // first user process
// enable interrupts on this processor.
- cpus[cpu()].nlock--;
- sti();
+ spllo();
+ cprintf("scheduler\n");
scheduler();
}
setupsegs(0);
cpuid(0, 0, 0, 0, 0); // memory barrier
cpus[cpu()].booted = 1;
-
- // Enable interrupts on this processor.
- cpus[cpu()].nlock--;
- sti();
+ spllo();
scheduler();
}
struct spinlock proc_table_lock;
struct proc proc[NPROC];
-struct proc *curproc[NCPU];
static struct proc *initproc;
int nextpid = 1;
cp->mem = newmem;
kfree(oldmem, cp->sz);
cp->sz += n;
+ setupsegs(cp);
return cp->sz - n;
}
{
struct cpu *c;
+ splhi();
c = &cpus[cpu()];
c->ts.ss0 = SEG_KDATA << 3;
if(p)
lgdt(c->gdt, sizeof(c->gdt));
ltr(SEG_TSS << 3);
+ spllo();
}
// Create a new process copying p as the parent.
initproc = p;
}
+// Return currently running process.
+// XXX comment better
+struct proc*
+curproc(void)
+{
+ struct proc *p;
+
+ splhi();
+ p = cpus[cpu()].curproc;
+ spllo();
+ return p;
+}
+
//PAGEBREAK: 42
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
scheduler(void)
{
struct proc *p;
+ struct cpu *c;
int i;
for(;;){
// Loop over process table looking for process to run.
acquire(&proc_table_lock);
-
+
+ c = &cpus[cpu()];
for(i = 0; i < NPROC; i++){
p = &proc[i];
if(p->state != RUNNABLE)
// Switch to chosen process. It is the process's job
// to release proc_table_lock and then reacquire it
// before jumping back to us.
- cp = p;
+ c->curproc = p;
setupsegs(p);
p->state = RUNNING;
- swtch(&cpus[cpu()].context, &p->context);
+ swtch(&c->context, &p->context);
// Process is done running for now.
// It should have changed its p->state before coming back.
- cp = 0;
+ c->curproc = 0;
setupsegs(0);
}
void
sched(void)
{
+ if(read_eflags()&FL_IF)
+ panic("sched interruptible");
if(cp->state == RUNNING)
panic("sched running");
if(!holding(&proc_table_lock))
panic("sched proc_table_lock");
- if(cpus[cpu()].nlock != 1)
+ if(cpus[cpu()].nsplhi != 1)
panic("sched locks");
swtch(&cp->context, &cpus[cpu()].context);
// fixed-size stack
// expandable heap
-// Arrange that cp point to the struct proc that this
-// CPU is currently running. Such preprocessor
-// subterfuge can be confusing, but saves a lot of typing.
-extern struct proc *curproc[NCPU]; // Current (running) process per CPU
-#define cp (curproc[cpu()]) // Current process on this CPU
-
-
#define MPSTACK 512
// Per-CPU state
struct cpu {
uchar apicid; // Local APIC ID
+ struct proc *curproc; // Process currently running.
struct context context; // Switch here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table
char mpstack[MPSTACK]; // Per-CPU startup stack
volatile int booted; // Has the CPU started?
- int nlock; // Number of locks currently held
+ int nsplhi; // Depth of splhi nesting.
};
extern struct cpu cpus[NCPU];
extern int ncpu;
+
+// "cp" is a short alias for curproc().
+// It gets used enough to make this worthwhile.
+#define cp curproc()
void
acquire(struct spinlock *lock)
{
+ splhi();
if(holding(lock))
panic("acquire");
- if(cpus[cpu()].nlock == 0)
- cli();
- cpus[cpu()].nlock++;
-
while(cmpxchg(0, 1, &lock->locked) == 1)
;
cpuid(0, 0, 0, 0, 0); // memory barrier (see Ch 7, IA-32 manual vol 3)
lock->locked = 0;
- if(--cpus[cpu()].nlock == 0)
- sti();
+ spllo();
}
// Record the current call stack in pcs[] by following the %ebp chain.
return lock->locked && lock->cpu == cpu() + 10;
}
+
+
+// XXX!
+// Better names? Better functions?
+
+void
+splhi(void)
+{
+ cli();
+ cpus[cpu()].nsplhi++;
+}
+
+void
+spllo(void)
+{
+ if(read_eflags()&FL_IF)
+ panic("spllo - interruptible");
+ if(--cpus[cpu()].nsplhi < 0)
+ panic("spllo");
+ if(cpus[cpu()].nsplhi == 0)
+ sti();
+}
+
return -1;
if((addr = growproc(n)) < 0)
return -1;
- setupsegs(cp);
return addr;
}
return;
}
- // Make sure interrupts stay off during handler.
- cpus[cpu()].nlock++;
+ // No interrupts during interrupt handling.
+ splhi();
switch(tf->trapno){
case IRQ_OFFSET + IRQ_TIMER:
cp->killed = 1;
}
- cpus[cpu()].nlock--;
+ // Undo splhi but do not enable interrupts.
+ // If you change this to spllo() you can get a
+ // triple fault by just typing too fast at the prompt.
+ // An interrupt stops us right here, and when that
+ // interrupt tries to return, somehow the segment
+ // registers are all invalid.
+ --cpus[cpu()].nsplhi;
// Force process exit if it has been killed and is in user space.
// (If it is still executing in the kernel, let it keep running
"cc");
}
+static inline uint
+read_ebp(void)
+{
+ uint ebp;
+
+ asm volatile("movl %%ebp, %0" : "=a" (ebp));
+ return ebp;
+}
+
struct segdesc;
static inline void