The Pedigree Project 0.1
Spinlock.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/Metrics.h"
22#include "pedigree/kernel/Spinlock.h"
23#include "pedigree/kernel/panic.h"
24#include "pedigree/kernel/processor/Processor.h"
25#include "pedigree/kernel/processor/ProcessorInformation.h"
26
27#if TRACK_LOCKS
28#include "pedigree/kernel/debugger/commands/LocksCommand.h"
29#endif
30
31Spinlock::Spinlock() = default;
32
33Spinlock::Spinlock(bool bLocked, bool bAvoidTracking)
34 : m_Lock(bLocked), m_bAvoidTracking(bAvoidTracking) {}
35
36bool Spinlock::acquire(bool recurse, bool safe) {
37 // Keep this local until we own the lock: another CPU (or a preempting
38 // thread before CLI) must not overwrite the owner's restoration state.
39 const bool interrupts = Processor::getInterrupts();
40 if (interrupts)
42
43 uintptr_t ra = 0;
44#if SPINLOCK_DIAGNOSTICS
45 ra = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
46 if (UNLIKELY(m_Magic != 0xdeadbaba))
47 badMagic(ra);
48#endif
49
50#if TRACK_LOCKS
51 if (!m_bAvoidTracking) {
52 g_LocksCommand.clearFatal();
53 if (!g_LocksCommand.lockAttempted(this, Processor::index(), interrupts))
54 panic("Lock checker disallowed spinlock acquisition");
55 g_LocksCommand.setFatal();
56 }
57#endif
58
59 const bool reentered = UNLIKELY(!m_Lock.tryAcquire()) && acquireContended(recurse, safe, ra);
60 if (!reentered) {
61 m_bInterrupts = interrupts;
62 __atomic_store_n(&m_OwnedProcessor, Processor::index(), __ATOMIC_RELAXED);
63 if (recurse) {
64 m_pOwner = Processor::information().getCurrentThread();
65 m_Level = 1;
66 }
67#if SPINLOCK_DIAGNOSTICS
68 m_Ra = ra;
69#endif
70 }
71
72#if TRACK_LOCKS
73 if (!m_bAvoidTracking) {
74 g_LocksCommand.clearFatal();
75 if (!g_LocksCommand.lockAcquired(this, Processor::index(), interrupts))
76 panic("Lock checker disallowed acquired spinlock");
77 g_LocksCommand.setFatal();
78 }
79#endif
80 Metrics::increment(Metrics::SpinlockAcquire);
81 return true;
82}
83
84bool Spinlock::acquireContended(bool recurse, bool safe, uintptr_t ra) {
85 bool spun = false;
86 const size_t processorId = Processor::index();
87 do {
88 const size_t owner = __atomic_load_n(&m_OwnedProcessor, __ATOMIC_RELAXED);
89 // Test CPU identity first: other CPUs must not inspect recursion state
90 // which only its owner can access. Early boot threads may all be null.
91 if (owner == processorId && recurse && m_Level &&
92 m_pOwner == Processor::information().getCurrentThread()) {
93 ++m_Level;
94 return true;
95 }
96
97#if TRACK_LOCKS
98 if (!m_bAvoidTracking) {
99 g_LocksCommand.clearFatal();
100 if (!g_LocksCommand.checkState(this, processorId))
101 panic("Lock checker rejected contended spinlock");
102 g_LocksCommand.setFatal();
103 }
104#endif
105
106#if MULTIPROCESSOR
107 if (Processor::getCount() > 1 && (!safe || owner != processorId)) {
108 // Read while occupied instead of repeatedly issuing locked RMWs.
109 spun = true;
111 continue;
112 }
113#endif
114 deadlock(ra, false);
115 } while (m_Lock.acquired() || !m_Lock.tryAcquire());
116 if (spun) {
117 Metrics::increment(Metrics::SpinlockContended);
118 }
119 return false;
120}
121
122void Spinlock::trackRelease(uintptr_t ra) const {
123#if TRACK_LOCKS
124 if (!m_bAvoidTracking) {
125 g_LocksCommand.clearFatal();
126 const size_t processorId = m_OwnedProcessor == ~size_t(0) ? ~0U : m_OwnedProcessor;
127 if (!g_LocksCommand.lockReleased(this, processorId))
128 panic("Lock checker disallowed spinlock release");
129 g_LocksCommand.setFatal();
130 }
131#endif
132}
133
134inline void Spinlock::unlock(uintptr_t ra) {
135#if SPINLOCK_DIAGNOSTICS
137 badReleaseInterrupts();
138 if (UNLIKELY(m_Magic != 0xdeadbaba))
139 badMagic(ra);
140#endif
141
142#if TRACK_LOCKS
143 // Cross-CPU releases retire the acquisition CPU's entry before clearing it.
144 trackRelease(ra);
145#endif
146 if (m_Level && --m_Level)
147 return;
148
149 m_pOwner = nullptr;
150 __atomic_store_n(&m_OwnedProcessor, ~size_t(0), __ATOMIC_RELAXED);
151#if SPINLOCK_DIAGNOSTICS
152 // Save diagnostic data before publishing unlock: a new owner may immediately
153 // overwrite the lock's fields. Nothing may touch them after publication.
154 const uintptr_t acquiredAt = m_Ra;
155 m_Ra = 0;
156 if (UNLIKELY(!m_Lock.releaseChecked())) {
157 deadlock(ra, true, acquiredAt);
158 }
159#else
160 m_Lock.release();
161#endif
162}
163
164void Spinlock::exit(uintptr_t ra) {
165 unlock(ra);
166}
167
169 // Only the outermost recursive release restores IRQs. Capture before unlock,
170 // since another CPU can acquire and change the saved state immediately.
171 const bool interrupts = m_bInterrupts && m_Level <= 1;
172 uintptr_t ra = 0;
173#if SPINLOCK_DIAGNOSTICS
174 ra = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
175#endif
176 unlock(ra);
177 if (interrupts)
179}
180
181inline void Spinlock::unwind() {
182 m_Level = 0;
183 m_pOwner = nullptr;
184 __atomic_store_n(&m_OwnedProcessor, ~size_t(0), __ATOMIC_RELAXED);
185#if SPINLOCK_DIAGNOSTICS
186 m_Ra = 0;
187#endif
188}
189
190volatile processor_register_t* Spinlock::deferredReleaseWord() {
191 unwind();
192 return &m_Lock.m_Value;
193}
194
195void Spinlock::unlockForScheduler() {
196 unwind();
197 m_Lock.release();
198}
199
200bool Spinlock::acquired() {
201 return m_Lock.acquired();
202}
203
204bool Spinlock::interrupts() const {
205 return m_bInterrupts;
206}
207
208uintptr_t Spinlock::acquisitionAddress() const {
209#if SPINLOCK_DIAGNOSTICS
210 return m_Ra;
211#else
212 return 0;
213#endif
214}
215
216void Spinlock::badMagic(uintptr_t ra) const {
217#if SPINLOCK_DIAGNOSTICS
218 WARNING(" --> fail: sentinels: before=" << Hex << m_Sentinel << " after=" << m_MagicAlign);
219 FATAL_NOLOCK("Wrong magic in Spinlock [" << Hex << m_Magic << " should be 0xdeadbaba] [this="
220 << reinterpret_cast<uintptr_t>(this)
221 << "] return=" << ra);
222#endif
223 panic("Corrupt spinlock");
224}
225
226void Spinlock::badReleaseInterrupts() const {
227 FATAL_NOLOCK("Spinlock: release() called with interrupts enabled.");
228 panic("Spinlock released with interrupts enabled");
229}
230
231void Spinlock::deadlock(uintptr_t ra, bool releasing, uintptr_t acquiredAt) {
232 // Logging/debugger backtraces may themselves need the deadlocked lock.
233 const bool locked = m_Lock.acquired();
234 if (!releasing)
235 acquiredAt = acquisitionAddress();
236 m_Lock.release();
237 ERROR_NOLOCK("Spinlock deadlocked in " << (releasing ? "release" : "acquire"));
238 ERROR_NOLOCK(" -> my return address is " << Hex << ra);
239 ERROR_NOLOCK(" -> return address of other locker is " << Hex << acquiredAt);
240 FATAL_NOLOCK("Spinlock has deadlocked, spinlock is " << Hex << reinterpret_cast<uintptr_t>(this)
241 << ", locked=" << locked << ".");
242 panic("Spinlock has deadlocked");
243}
bool checkState(const Spinlock *pLock, size_t nCpu=~0U)
bool lockReleased(const Spinlock *pLock, size_t nCpu=~0U)
static bool getInterrupts()
static ProcessorInformation & information()
static size_t getCount()
static void pause()
static void setInterrupts(bool bEnable)
static size_t index()
volatile processor_register_t * deferredReleaseWord()
Definition Spinlock.cc:190
void release()
Definition Spinlock.cc:168
void unwind() ALWAYS_INLINE
Definition Spinlock.cc:181
void trackRelease(uintptr_t ra) const
Definition Spinlock.cc:122
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
bool acquireContended(bool recurse, bool safe, uintptr_t ra) NEVER_INLINE
Definition Spinlock.cc:84
void unlock(uintptr_t ra) ALWAYS_INLINE
Definition Spinlock.cc:134
void exit(uintptr_t ra=0)
Definition Spinlock.cc:164
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Hex
Definition Log.h:124