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, __atomic_exchange_n turns into an atomic swap:
29 // a5 = 1
30 // s1 = &lk->locked
31 // amoswap.w.aq a5, a5, (s1)
32 //
33 // Passing __ATOMIC_ACQUIRE to __atomic_exchange_n tells
34 // the C compiler and the processor to not move loads or stores
35 // past this point, to ensure that the critical section's memory
36 // references happen strictly after the lock is acquired.
37 while (__atomic_exchange_n(&lk->locked, 1, __ATOMIC_ACQUIRE) != 0)
38 ;
39
40 // Record info about lock acquisition for holding() and debugging.
41 lk->cpu = mycpu();
42 }
43
44 // Release the lock.
45 void
46 release(struct spinlock *lk)
47 {
48 if (!holding(lk))
49 panic("release");
50
51 lk->cpu = 0;
52
53 // Release the lock, equivalent to lk->locked = 0.
54 //
55 // This code doesn't use a C assignment, since the C standard
56 // implies that an assignment might be implemented with
57 // multiple store instructions.
58 //
59 // On RISC-V, __atomic_store_n turns into a single atomic store:
60 // s1 = &lk->locked
61 // fence rw,w
62 // sw zero,0(s1)
63 //
64 // The __ATOMIC_RELEASE argument to __atomic_store_n tells the
65 // the C compiler and the CPU to not move loads or stores past
66 // this point, to ensure that all the stores in the critical
67 // section are visible to other CPUs before the lock is released,
68 // and that loads in the critical section occur strictly before
69 // the lock is released.
70 //
71 // On RISC-V, this generates a fence instruction before the store:
72 // fence rw,w
73 __atomic_store_n(&lk->locked, 0, __ATOMIC_RELEASE);
74
75 pop_off();
76 }
77
78 // Check whether this cpu is holding the lock.
79 // Interrupts must be off.
80 int
81 holding(struct spinlock *lk)
82 {
83 int r;
84 r = (lk->locked && lk->cpu == mycpu());
85 return r;
86 }
87
88 // push_off/pop_off are like intr_off()/intr_on() except that they are matched:
89 // it takes two pop_off()s to undo two push_off()s. Also, if interrupts
90 // are initially off, then push_off, pop_off leaves them off.
91
92 void
93 push_off(void)
94 {
95 // disable interrupts to prevent an involuntary context
96 // switch while using mycpu().
97 uint64 flags = rc_sstatus(SSTATUS_SIE);
98 int old = !!(flags & SSTATUS_SIE);
99
100 if (mycpu()->noff == 0)
101 mycpu()->intena = old;
102 mycpu()->noff += 1;
103 }
104
105 void
106 pop_off(void)
107 {
108 struct cpu *c = mycpu();
109 if (intr_get())
110 panic("pop_off - interruptible");
111 if (c->noff < 1)
112 panic("pop_off");
113 c->noff -= 1;
114 if (c->noff == 0 && c->intena)
115 intr_on();
116 }