114 lines
3.0 KiB
C
114 lines
3.0 KiB
C
|
// 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();
|
||
|
}
|