1 // Mutual exclusion spin locks.
2
3 #include "types.h"
4 #include "param.h"
5 #include "memlayout.h"
6 #include "spinlock.h"
7 #include "riscv.h"
8 #include "proc.h"
9 #include "defs.h"
10
11 void
12 initlock(struct spinlock *lk, char *name)
13 {
14 lk->name = name;
15 lk->locked = 0;
16 lk->cpu = 0;
17 }
18
19 // Acquire the lock.
20 // Loops (spins) until the lock is acquired.
21 void
22 acquire(struct spinlock *lk)
23 {
24 push_off(); // disable interrupts to avoid deadlock.
25 if(holding(lk))
26 panic("acquire");
27
28 // On RISC-V, sync_lock_test_and_set turns into an atomic swap:
29 // a5 = 1
30 // s1 = &lk->locked
31 // amoswap.w.aq a5, a5, (s1)
32 while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
33 ;
34
35 // Tell the C compiler and the processor to not move loads or stores
36 // past this point, to ensure that the critical section's memory
37 // references happen strictly after the lock is acquired.
38 // On RISC-V, this emits a fence instruction.
39 __sync_synchronize();
40
41 // Record info about lock acquisition for holding() and debugging.
42 lk->cpu = mycpu();
43 }
44
45 // Release the lock.
46 void
47 release(struct spinlock *lk)
48 {
49 if(!holding(lk))
50 panic("release");
51
52 lk->cpu = 0;
53
54 // Tell the C compiler and the CPU to not move loads or stores
55 // past this point, to ensure that all the stores in the critical
56 // section are visible to other CPUs before the lock is released,
57 // and that loads in the critical section occur strictly before
58 // the lock is released.
59 // On RISC-V, this emits a fence instruction.
60 __sync_synchronize();
61
62 // Release the lock, equivalent to lk->locked = 0.
63 // This code doesn't use a C assignment, since the C standard
64 // implies that an assignment might be implemented with
65 // multiple store instructions.
66 // On RISC-V, sync_lock_release turns into an atomic swap:
67 // s1 = &lk->locked
68 // amoswap.w zero, zero, (s1)
69 __sync_lock_release(&lk->locked);
70
71 pop_off();
72 }
73
74 // Check whether this cpu is holding the lock.
75 // Interrupts must be off.
76 int
77 holding(struct spinlock *lk)
78 {
79 int r;
80 r = (lk->locked && lk->cpu == mycpu());
81 return r;
82 }
83
84 // push_off/pop_off are like intr_off()/intr_on() except that they are matched:
85 // it takes two pop_off()s to undo two push_off()s. Also, if interrupts
86 // are initially off, then push_off, pop_off leaves them off.
87
88 void
89 push_off(void)
90 {
91 int old = intr_get();
92
93 // disable interrupts to prevent an involuntary context
94 // switch while using mycpu().
95 intr_off();
96
97 if(mycpu()->noff == 0)
98 mycpu()->intena = old;
99 mycpu()->noff += 1;
100 }
101
102 void
103 pop_off(void)
104 {
105 struct cpu *c = mycpu();
106 if(intr_get())
107 panic("pop_off - interruptible");
108 if(c->noff < 1)
109 panic("pop_off");
110 c->noff -= 1;
111 if(c->noff == 0 && c->intena)
112 intr_on();
113 }