// TODO: CHECK/REPLACE/UPDATE OLD CODE (this file is based on xv6) // Mutual exclusion spin locks. #include "types.h" #include "param.h" #include "memlayout.h" #include "sched.h" #include "riscv.h" #include "proc.h" #include "defs.h" #include "sched.h" #include "kprintf.h" void initlock(sched_spinlock_t *lk, char *name) { lk->debugstring = name; lk->lockvar = 0; lk->core = 0; } // Acquire the lock. // Loops (spins) until the lock is acquired. void acquire(sched_spinlock_t *lk) { push_off(); // disable interrupts to avoid deadlock. if(holding(lk)) panic("acquire"); // On RISC-V, sync_lock_test_and_set turns into an atomic swap: // a5 = 1 // s1 = &lk->lockvar // amoswap.w.aq a5, a5, (s1) while(__sync_lock_test_and_set(&(lk->lockvar), 1) != 0) ; // Tell the C compiler and the processor to not move loads or stores // past this point, to ensure that the critical section's memory // references happen strictly after the lock is acquired. // On RISC-V, this emits a fence instruction. __sync_synchronize(); // Record info about lock acquisition for holding() and debugging. lk->core = SCHED_CORE_THIS_NOINTERRUPTS(); } // Release the lock. void release(sched_spinlock_t *lk) { if(!holding(lk)) panic("release"); lk->core = 0; // Tell the C compiler and the CPU to not move loads or stores // past this point, to ensure that all the stores in the critical // section are visible to other CPUs before the lock is released, // and that loads in the critical section occur strictly before // the lock is released. // On RISC-V, this emits a fence instruction. __sync_synchronize(); // Release the lock, equivalent to lk->lockvar = 0. // This code doesn't use a C assignment, since the C standard // implies that an assignment might be implemented with // multiple store instructions. // On RISC-V, sync_lock_release turns into an atomic swap: // s1 = &lk->lockvar // amoswap.w zero, zero, (s1) __sync_lock_release(&lk->lockvar); pop_off(); } // Check whether this cpu is holding the lock. // Interrupts must be off. int holding(sched_spinlock_t *lk) { int r; r = (lk->lockvar && lk->core == SCHED_CORE_THIS_NOINTERRUPTS()); return r; } // push_off/pop_off are like intr_off()/intr_on() except that they are matched: // it takes two pop_off()s to undo two push_off()s. Also, if interrupts // are initially off, then push_off, pop_off leaves them off. void push_off(void) { int old = intr_get(); intr_off(); if(SCHED_CORE_THIS_NOINTERRUPTS()->interruptsoff_depth == 0) SCHED_CORE_THIS_NOINTERRUPTS()->interruptsoff_wereinterruptson = old; SCHED_CORE_THIS_NOINTERRUPTS()->interruptsoff_depth += 1; } void pop_off(void) { sched_core_t* c = SCHED_CORE_THIS_NOINTERRUPTS(); if(intr_get()) panic("pop_off - interruptible"); if(c->interruptsoff_depth < 1) panic("pop_off"); c->interruptsoff_depth -= 1; if(c->interruptsoff_depth == 0 && c->interruptsoff_wereinterruptson) intr_on(); }