#include "types.h" #include "defs.h" #include "param.h" #include "x86.h" #include "memlayout.h" #include "mmu.h" #include "proc.h" #include "spinlock.h" void initlock(struct spinlock *lk, char *name) { lk->name = name; lk->locked = 0; lk->cpu = 0; } void acquire(struct spinlock *lk) { pushcli(); if(holding(lk)) panic("acquire"); { uint64 spins = 0; while(xchg(&lk->locked, 1) != 0){ if(++spins == 200000000ULL){ cprintf("acquire: stuck on \"%s\" held by cpu=%p pcs0=%p pcs1=%p pcs2=%p, acquirer cpu=%p\n", lk->name, lk->cpu, lk->pcs[0], lk->pcs[1], lk->pcs[2], mycpu()); panic("acquire: deadlock"); } } } __sync_synchronize(); lk->cpu = mycpu(); getcallerpcs(&lk, lk->pcs); } void release(struct spinlock *lk) { if(!holding(lk)) panic("release"); lk->pcs[0] = 0; lk->cpu = 0; __sync_synchronize(); asm volatile("movq $0, %0" : "+m" (lk->locked) : ); popcli(); } void getcallerpcs(void *v, addr_t pcs[]) { addr_t *rbp; asm volatile("mov %%rbp, %0" : "=r" (rbp)); getstackpcs(rbp, pcs); } void getstackpcs(addr_t *rbp, addr_t pcs[]) { int i; for(i = 0; i < 10; i++){ // This walks a frame chain that may be garbage -- it runs from // getcallerpcs() on every lock acquisition, and again from the trap // handler, where the thing being diagnosed is often the reason the stack // is unwalkable. A fault here replaces the real diagnosis with a second, // more confusing one, so every dereference is checked first. // // The old test also compared against 0xffffffff, which on x86-64 is // unreachable: it is far below KERNBASE and the check above already // rejected it. The value that actually turns up is all-ones, // 0xffffffffffffffff, which is *above* KERNBASE and sailed straight // through -- rbp[1] then wraps to address 0x7 and page-faults. Bound rbp // from above rather than naming another magic constant, so any frame // pointer near the top of the address space is rejected whatever it is. if(rbp == 0 || rbp < (addr_t*)KERNBASE || (addr_t)rbp > (addr_t)-16) break; // A frame pointer is 8-byte aligned. An unaligned one is not a frame, // and following it walks off into arbitrary memory. if(((addr_t)rbp & 7) != 0) break; pcs[i] = rbp[1]; rbp = (addr_t*)rbp[0]; } for(; i < 10; i++) pcs[i] = 0; } int holding(struct spinlock *lock) { return lock->locked && lock->cpu == mycpu(); } void pushcli(void) { int eflags; eflags = readeflags(); cli(); if(mycpu() == 0){ extern uint64 kernel_fs_base; cprintf("pushcli: cpu==NULL cs=0x%p kernel_fs_base=0x%p actual_fs_base=0x%p\n", readcs(), kernel_fs_base, rdmsr(0xC0000100)); panic("pushcli: cpu is NULL"); } if(mycpu()->ncli == 0) mycpu()->intena = eflags & FL_IF; mycpu()->ncli += 1; } void popcli(void) { if(readeflags()&FL_IF) panic("popcli - interruptible"); if(--mycpu()->ncli < 0) panic("popcli"); if(mycpu()->ncli == 0 && mycpu()->intena) sti(); }