diff options
| author | Robert Morris <rtm@csail.mit.edu> | 2019-06-05 14:05:46 -0400 | 
|---|---|---|
| committer | Robert Morris <rtm@csail.mit.edu> | 2019-06-05 14:05:46 -0400 | 
| commit | 31136437684b5987ef46f4c6947940cf96de75b3 (patch) | |
| tree | 3c89b1f0e12bbddbf307259326ecb5f763ba6e09 | |
| parent | f1a727b971a59bab6025b4c4111342c27356ca40 (diff) | |
| download | xv6-labs-31136437684b5987ef46f4c6947940cf96de75b3.tar.gz xv6-labs-31136437684b5987ef46f4c6947940cf96de75b3.tar.bz2 xv6-labs-31136437684b5987ef46f4c6947940cf96de75b3.zip | |
spinlocks using gcc intrinsics
push_off() / pop_off()
set up per-hart plic stuff so all harts get device interrupts
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | defs.h | 8 | ||||
| -rw-r--r-- | main.c | 42 | ||||
| -rw-r--r-- | memlayout.h | 8 | ||||
| -rw-r--r-- | proc.c | 5 | ||||
| -rw-r--r-- | proc.h | 5 | ||||
| -rw-r--r-- | spinlock.c | 95 | ||||
| -rw-r--r-- | spinlock.h | 2 | ||||
| -rw-r--r-- | start.c | 14 | ||||
| -rw-r--r-- | trap.c | 10 | ||||
| -rw-r--r-- | vm.c | 8 | 
11 files changed, 95 insertions, 104 deletions
| @@ -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 @@ -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); @@ -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 -  uartinit();      // serial port -  consoleinit(); -  printf("entering main() on hart %d\n", hartid); -  kinit();         // physical page allocator -  kvminit();       // kernel page table -  procinit();      // process table -  trapinit();      // trap vectors -  plicinit();      // set up interrupt controller -  binit();         // buffer cache -  fileinit();      // file table -  ramdiskinit();   // disk -  userinit();      // first user process +  if(cpuid() == 0){ +    uartinit();      // serial port +    consoleinit(); +    printf("hart %d starting\n", cpuid()); +    kinit();         // physical page allocator +    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();          } diff --git a/memlayout.h b/memlayout.h index 9c9cfdb..462986c 100644 --- a/memlayout.h +++ b/memlayout.h @@ -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 @@ -360,7 +360,7 @@ scheduler(void)  {    struct proc *p;    struct cpu *c = mycpu(); -    +    c->proc = 0;    for(;;){      // Enable interrupts on this processor. @@ -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 @@ -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]; @@ -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 @@ -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.  }; @@ -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");  } @@ -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. +// set up to take exceptions and traps while in the kernel. +void +trapinithart(void) +{    w_stvec((uint64)kernelvec); - -  initlock(&tickslock, "time");  }  // @@ -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. +// Switch h/w page table register to the kernel's page table, +// and enable paging. +void +kvminithart() +{    w_satp(MAKE_SATP(kernel_pagetable));  } | 
