spinlocks using gcc intrinsics

push_off() / pop_off()
set up per-hart plic stuff so all harts get device interrupts
This commit is contained in:
Robert Morris 2019-06-05 14:05:46 -04:00
parent f1a727b971
commit 3113643768
11 changed files with 96 additions and 105 deletions

View file

@ -182,7 +182,7 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \
else echo "-s -p $(GDBPORT)"; fi)
ifndef CPUS
CPUS := 1
CPUS := 2
endif
QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic
QEMUOPTS += -initrd fs.img

8
defs.h
View file

@ -130,12 +130,11 @@ void swtch(struct context*, struct context*);
// spinlock.c
void acquire(struct spinlock*);
void getcallerpcs(void*, uint64*);
int holding(struct spinlock*);
void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void pushcli(void);
void popcli(void);
void push_off(void);
void pop_off(void);
// sleeplock.c
void acquiresleep(struct sleeplock*);
@ -168,6 +167,7 @@ void timerinit(void);
// trap.c
extern uint ticks;
void trapinit(void);
void trapinithart(void);
extern struct spinlock tickslock;
void usertrapret(void);
@ -179,6 +179,7 @@ int uartgetc(void);
// vm.c
void kvminit(void);
void kvminithart(void);
pagetable_t uvmcreate(void);
void uvminit(pagetable_t, char *, uint);
uint64 uvmalloc(pagetable_t, uint64, uint64);
@ -194,6 +195,7 @@ int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64
// plic.c
void plicinit(void);
void plicinithart(void);
uint64 plic_pending(void);
int plic_claim(void);
void plic_complete(int);

22
main.c
View file

@ -4,25 +4,39 @@
#include "riscv.h"
#include "defs.h"
volatile static int started = 0;
// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
void
main(int hartid)
main()
{
w_tp(hartid); // save hartid where cpuid() can find it
if(cpuid() == 0){
uartinit(); // serial port
consoleinit();
printf("entering main() on hart %d\n", hartid);
printf("hart %d starting\n", cpuid());
kinit(); // physical page allocator
kvminit(); // kernel page table
kvminit(); // create kernel page table
kvminithart(); // turn on paging
procinit(); // process table
trapinit(); // trap vectors
trapinithart(); // install kernel trap vector
plicinit(); // set up interrupt controller
plicinithart(); // ask PLIC for device interrupts
binit(); // buffer cache
fileinit(); // file table
ramdiskinit(); // disk
userinit(); // first user process
started = 1;
} else {
while(started == 0)
;
printf("hart %d starting\n", cpuid());
kvminithart(); // turn on paging
trapinithart(); // install kernel trap vector
plicinithart(); // ask PLIC for device interrupts
}
scheduler();
}

View file

@ -28,6 +28,14 @@
// qemu puts programmable interrupt controller here.
#define PLIC 0x0c000000L
#define PLIC_PRIORITY (PLIC + 0x0)
#define PLIC_PENDING (PLIC + 0x1000)
#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100)
#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)
#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000)
#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)
#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000)
#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000)
#define RAMDISK 0x88000000L

3
proc.c
View file

@ -385,7 +385,6 @@ scheduler(void)
c->proc = 0;
}
release(&ptable.lock);
}
}
@ -393,7 +392,7 @@ scheduler(void)
// and have changed proc->state. Saves and restores
// intena because intena is a property of this
// kernel thread, not this CPU. It should
// be proc->intena and proc->ncli, but that would
// be proc->intena and proc->noff, but that would
// break in the few places where a lock is held but
// there's no process.
void

5
proc.h
View file

@ -22,9 +22,8 @@ struct context {
struct cpu {
struct proc *proc; // The process running on this cpu or null
struct context scheduler; // swtch() here to enter scheduler
volatile uint started; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
int noff; // Depth of push_off() nesting.
int intena; // Were interrupts enabled before push_off()?
};
extern struct cpu cpus[NCPU];

View file

@ -5,6 +5,7 @@
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "proc.h"
#include "defs.h"
void
@ -15,27 +16,6 @@ initlock(struct spinlock *lk, char *name)
lk->cpu = 0;
}
void
acquire(struct spinlock *lk)
{
lk->locked = 1;
lk->cpu = mycpu();
}
void
release(struct spinlock *lk)
{
lk->locked = 0;
lk->cpu = 0;
}
int
holding(struct spinlock *lk)
{
return lk->locked && lk->cpu == mycpu();
}
#if 0
// Acquire the lock.
// Loops (spins) until the lock is acquired.
// Holding a lock for a long time may cause
@ -43,12 +23,14 @@ holding(struct spinlock *lk)
void
acquire(struct spinlock *lk)
{
pushcli(); // disable interrupts to avoid deadlock.
push_off(); // disable interrupts to avoid deadlock.
if(holding(lk))
panic("acquire");
// The xchg is atomic.
while(xchg(&lk->locked, 1) != 0)
//while(xchg(&lk->locked, 1) != 0)
// ;
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
;
// Tell the C compiler and the processor to not move loads or stores
@ -58,7 +40,6 @@ acquire(struct spinlock *lk)
// Record info about lock acquisition for holding() and debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
// Release the lock.
@ -68,7 +49,6 @@ release(struct spinlock *lk)
if(!holding(lk))
panic("release");
lk->pcs[0] = 0;
lk->cpu = 0;
// Tell the C compiler and the processor to not move loads or stores
@ -81,27 +61,10 @@ release(struct spinlock *lk)
// Release the lock, equivalent to lk->locked = 0.
// This code can't use a C assignment, since it might
// not be atomic. A real OS would use C atomics here.
asm volatile("movl $0, %0" : "+m" (lk->locked) : );
//asm volatile("movl $0, %0" : "+m" (lk->locked) : );
__sync_lock_release(&lk->locked);
popcli();
}
// Record the current call stack in pcs[] by following the %ebp chain.
void
getcallerpcs(void *v, uint64 pcs[])
{
uint64 *ebp;
int i;
asm volatile("mov %%rbp, %0" : "=r" (ebp));
for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint64*)KERNBASE || ebp == (uint64*)0xffffffff)
break;
pcs[i] = ebp[1]; // saved %eip
ebp = (uint64*)ebp[0]; // saved %ebp
}
for(; i < 10; i++)
pcs[i] = 0;
pop_off();
}
// Check whether this cpu is holding the lock.
@ -109,37 +72,37 @@ int
holding(struct spinlock *lk)
{
int r;
pushcli();
push_off();
r = lk->locked && lk->cpu == mycpu();
popcli();
pop_off();
return r;
}
// Pushcli/popcli are like cli/sti except that they are matched:
// it takes two popcli to undo two pushcli. Also, if interrupts
// are off, then pushcli, popcli leaves them off.
// push_off/pop_off are like intr_off()/intr_on() except that they are matched:
// it takes two pop_off to undo two push_off. Also, if interrupts
// are initially off, then push_off, pop_off leaves them off.
void
pushcli(void)
push_off(void)
{
int eflags;
struct cpu *c = mycpu();
int old = intr_get();
eflags = readeflags();
cli();
if(mycpu()->ncli == 0)
mycpu()->intena = eflags & FL_IF;
mycpu()->ncli += 1;
intr_off();
if(c->noff == 0)
c->intena = old;
c->noff += 1;
}
void
popcli(void)
pop_off(void)
{
if(readeflags()&FL_IF)
panic("popcli - interruptible");
if(--mycpu()->ncli < 0)
panic("popcli");
if(mycpu()->ncli == 0 && mycpu()->intena)
sti();
struct cpu *c = mycpu();
if(intr_get())
panic("pop_off - interruptible");
c->noff -= 1;
if(c->noff < 0)
panic("pop_off");
if(c->noff == 0 && c->intena)
intr_on();
}
#endif

View file

@ -5,7 +5,5 @@ struct spinlock {
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
uint64 pcs[10]; // The call stack (an array of program counters)
// that locked the lock.
};

14
start.c
View file

@ -9,12 +9,12 @@ void main();
// entry.S needs one stack per CPU.
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
// assembly code in kernelvec for machine-mode timer interrupt.
extern void machinevec();
// scratch area for timer interrupt, one per CPU.
uint64 mscratch0[NCPU * 32];
// assembly code in kernelvec for machine-mode timer interrupt.
extern void machinevec();
// entry.S jumps here in machine mode on stack0.
void
mstart()
@ -48,7 +48,9 @@ mstart()
w_mstatus(r_mstatus() | MSTATUS_MIE);
w_mie(r_mie() | MIE_MTIE);
// call main(hartid) in supervisor mode.
asm("csrr a0, mhartid ; \
mret");
// keep each CPU's hartid in its tp register, for cpuid().
w_tp(id);
// call main() in supervisor mode.
asm("mret");
}

8
trap.c
View file

@ -19,12 +19,14 @@ extern int devintr();
void
trapinit(void)
{
int i;
initlock(&tickslock, "time");
}
// set up to take exceptions and traps while in the kernel.
void
trapinithart(void)
{
w_stvec((uint64)kernelvec);
initlock(&tickslock, "time");
}
//

4
vm.c
View file

@ -54,9 +54,13 @@ kvminit()
// the highest virtual address in the kernel.
mappages(kernel_pagetable, TRAMPOLINE, PGSIZE,
(uint64)trampout, PTE_R | PTE_X);
}
// Switch h/w page table register to the kernel's page table,
// and enable paging.
void
kvminithart()
{
w_satp(MAKE_SATP(kernel_pagetable));
}