xv6-cs450/spinlock.c
rsc ab08960f64 Final word on the locking fiasco?
Change pushcli / popcli so that they can never turn on
interrupts unexpectedly.  That is, if interrupts are on,
then pushcli(); popcli(); turns them off and back on, but
if they are off to begin with, then pushcli(); popcli(); is
a no-op.

I think our fundamental mistake was having a primitive
(release and then popcli nee spllo) that could turn
interrupts on at unexpected moments instead of being
explicit about when we want to start allowing interrupts.

With the new semantics, all the manual fiddling of ncli
to force interrupts off in certain sections goes away.
In return, we must explicitly mark the places where
we want to enable interrupts unconditionally, by calling sti().
There is only one: inside the scheduler loop.
2007-09-27 21:25:37 +00:00

117 lines
2.4 KiB
C

// Mutual exclusion spin locks.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"
extern int use_console_lock;
void
initlock(struct spinlock *lock, char *name)
{
lock->name = name;
lock->locked = 0;
lock->cpu = 0xffffffff;
}
// Acquire the lock.
// Loops (spins) until the lock is acquired.
// Holding a lock for a long time may cause
// other CPUs to waste time spinning to acquire it.
void
acquire(struct spinlock *lock)
{
pushcli();
if(holding(lock))
panic("acquire");
while(cmpxchg(0, 1, &lock->locked) == 1)
;
// Serialize instructions: now that lock is acquired, make sure
// we wait for all pending writes from other processors.
cpuid(0, 0, 0, 0, 0); // memory barrier (see Ch 7, IA-32 manual vol 3)
// Record info about lock acquisition for debugging.
// The +10 is only so that we can tell the difference
// between forgetting to initialize lock->cpu
// and holding a lock on cpu 0.
lock->cpu = cpu() + 10;
getcallerpcs(&lock, lock->pcs);
}
// Release the lock.
void
release(struct spinlock *lock)
{
if(!holding(lock))
panic("release");
lock->pcs[0] = 0;
lock->cpu = 0xffffffff;
// Serialize instructions: before unlocking the lock, make sure
// to flush any pending memory writes from this processor.
cpuid(0, 0, 0, 0, 0); // memory barrier (see Ch 7, IA-32 manual vol 3)
lock->locked = 0;
popcli();
}
// Record the current call stack in pcs[] by following the %ebp chain.
void
getcallerpcs(void *v, uint pcs[])
{
uint *ebp;
int i;
ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){
if(ebp == 0 || ebp == (uint*)0xffffffff)
break;
pcs[i] = ebp[1]; // saved %eip
ebp = (uint*)ebp[0]; // saved %ebp
}
for(; i < 10; i++)
pcs[i] = 0;
}
// Check whether this cpu is holding the lock.
int
holding(struct spinlock *lock)
{
return lock->locked && lock->cpu == cpu() + 10;
}
// 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.
void
pushcli(void)
{
int eflags;
eflags = read_eflags();
cli();
if(cpus[cpu()].ncli++ == 0)
cpus[cpu()].intena = eflags & FL_IF;
}
void
popcli(void)
{
if(read_eflags()&FL_IF)
panic("popcli - interruptible");
if(--cpus[cpu()].ncli < 0)
panic("popcli");
if(cpus[cpu()].ncli == 0 && cpus[cpu()].intena)
sti();
}