xref: /xv6-public/spinlock.c (revision b121486c)
131085bb4Srsc // Mutual exclusion spin locks.
231085bb4Srsc 
321a88fd4Skaashoek #include "types.h"
421a88fd4Skaashoek #include "defs.h"
5558ab49fSrsc #include "param.h"
621a88fd4Skaashoek #include "x86.h"
77837c71bSkaashoek #include "mmu.h"
84e8f237bSrtm #include "proc.h"
94e8f237bSrtm #include "spinlock.h"
1021a88fd4Skaashoek 
110e84a0ecSrtm void
12*b121486cSRuss Cox initlock(struct spinlock *lk, char *name)
135be0039cSrtm {
14*b121486cSRuss Cox   lk->name = name;
15*b121486cSRuss Cox   lk->locked = 0;
16*b121486cSRuss Cox   lk->cpu = 0xffffffff;
175be0039cSrtm }
185be0039cSrtm 
1931085bb4Srsc // Acquire the lock.
2031085bb4Srsc // Loops (spins) until the lock is acquired.
21ab4cedb5Srtm // Holding a lock for a long time may cause
22ab4cedb5Srtm // other CPUs to waste time spinning to acquire it.
2321a88fd4Skaashoek void
24*b121486cSRuss Cox acquire(struct spinlock *lk)
2521a88fd4Skaashoek {
263807c1f2Srsc   pushcli();
27*b121486cSRuss Cox   if(holding(lk))
280dd42537Srsc     panic("acquire");
290dd42537Srsc 
30943fd378Srsc   // The xchg is atomic.
31943fd378Srsc   // It also serializes, so that reads after acquire are not
32943fd378Srsc   // reordered before it.
33*b121486cSRuss Cox   while(xchg(&lk->locked, 1) != 0)
3465bd8e13Srsc     ;
35e7a5b3c5Srsc 
3631085bb4Srsc   // Record info about lock acquisition for debugging.
3731085bb4Srsc   // The +10 is only so that we can tell the difference
3831085bb4Srsc   // between forgetting to initialize lock->cpu
3931085bb4Srsc   // and holding a lock on cpu 0.
40*b121486cSRuss Cox   lk->cpu = cpu() + 10;
41*b121486cSRuss Cox   getcallerpcs(&lk, lk->pcs);
4221a88fd4Skaashoek }
4321a88fd4Skaashoek 
4431085bb4Srsc // Release the lock.
4521a88fd4Skaashoek void
46*b121486cSRuss Cox release(struct spinlock *lk)
4721a88fd4Skaashoek {
48*b121486cSRuss Cox   if(!holding(lk))
490dd42537Srsc     panic("release");
500dd42537Srsc 
51*b121486cSRuss Cox   lk->pcs[0] = 0;
52*b121486cSRuss Cox   lk->cpu = 0xffffffff;
53e7a5b3c5Srsc 
54943fd378Srsc   // The xchg serializes, so that reads before release are
55be38c841Srtm   // not reordered after it.  The 1996 PentiumPro manual (Volume 3,
56be38c841Srtm   // 7.2) says reads can be carried out speculatively and in
57be38c841Srtm   // any order, which implies we need to serialize here.
58be38c841Srtm   // But the 2007 Intel 64 Architecture Memory Ordering White
59be38c841Srtm   // Paper says that Intel 64 and IA-32 will not move a load
60be38c841Srtm   // after a store. So lock->locked = 0 would work here.
61be38c841Srtm   // The xchg being asm volatile ensures gcc emits it after
62be38c841Srtm   // the above assignments (and after the critical section).
63*b121486cSRuss Cox   xchg(&lk->locked, 0);
649fd9f804Srsc 
653807c1f2Srsc   popcli();
6621a88fd4Skaashoek }
675af5f6aaSrsc 
685af5f6aaSrsc // Record the current call stack in pcs[] by following the %ebp chain.
695af5f6aaSrsc void
705af5f6aaSrsc getcallerpcs(void *v, uint pcs[])
715af5f6aaSrsc {
725af5f6aaSrsc   uint *ebp;
735af5f6aaSrsc   int i;
745af5f6aaSrsc 
755af5f6aaSrsc   ebp = (uint*)v - 2;
765af5f6aaSrsc   for(i = 0; i < 10; i++){
775af5f6aaSrsc     if(ebp == 0 || ebp == (uint*)0xffffffff)
785af5f6aaSrsc       break;
795af5f6aaSrsc     pcs[i] = ebp[1];     // saved %eip
805af5f6aaSrsc     ebp = (uint*)ebp[0]; // saved %ebp
815af5f6aaSrsc   }
825af5f6aaSrsc   for(; i < 10; i++)
835af5f6aaSrsc     pcs[i] = 0;
845af5f6aaSrsc }
855af5f6aaSrsc 
865af5f6aaSrsc // Check whether this cpu is holding the lock.
875af5f6aaSrsc int
885af5f6aaSrsc holding(struct spinlock *lock)
895af5f6aaSrsc {
905af5f6aaSrsc   return lock->locked && lock->cpu == cpu() + 10;
915af5f6aaSrsc }
925af5f6aaSrsc 
93c8919e65Srsc 
94ab08960fSrsc // Pushcli/popcli are like cli/sti except that they are matched:
95ab08960fSrsc // it takes two popcli to undo two pushcli.  Also, if interrupts
96ab08960fSrsc // are off, then pushcli, popcli leaves them off.
97c8919e65Srsc 
98c8919e65Srsc void
993807c1f2Srsc pushcli(void)
100c8919e65Srsc {
101ab08960fSrsc   int eflags;
102ab08960fSrsc 
10321575761Srsc   eflags = readeflags();
104c8919e65Srsc   cli();
10519333efbSrsc   if(c->ncli++ == 0)
10619333efbSrsc     c->intena = eflags & FL_IF;
107c8919e65Srsc }
108c8919e65Srsc 
109c8919e65Srsc void
1103807c1f2Srsc popcli(void)
111c8919e65Srsc {
11221575761Srsc   if(readeflags()&FL_IF)
1133807c1f2Srsc     panic("popcli - interruptible");
11419333efbSrsc   if(--c->ncli < 0)
1153807c1f2Srsc     panic("popcli");
11619333efbSrsc   if(c->ncli == 0 && c->intena)
117c8919e65Srsc     sti();
118c8919e65Srsc }
119c8919e65Srsc 
120