1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #pragma once
6
7 #include <atomic>
8 #include <fbl/algorithm.h>
9 #include <stdint.h>
10 #include <zircon/assert.h>
11 #include <zircon/compiler.h>
12
13 #include <lockdep/common.h>
14 #include <lockdep/lock_dependency_set.h>
15 #include <lockdep/lock_traits.h>
16
17 namespace lockdep {
18
19 // Type that holds the essential information and state for a lock class. This is
20 // used by ThreadLockState to uniformly operate on the variety of lock classes
21 // created by each template instantiation of LockClass. Each template
22 // instantiation of LockClass creates a unique static instance of LockClassState.
23 class LockClassState {
24 public:
25 // Constructs an instance of LockClassState.
LockClassState(const char * const name,LockDependencySet * dependency_set,LockFlags flags)26 LockClassState(const char* const name,
27 LockDependencySet* dependency_set,
28 LockFlags flags)
29 : name_{name},
30 dependency_set_{dependency_set},
31 flags_{flags} {}
32
33 // Disable copy construction / assignment.
34 LockClassState(const LockClassState&) = delete;
35 LockClassState& operator=(const LockClassState&) = delete;
36
37 // Returns the LockClassState instance for the given lock class id. The id must
38 // be a valid lock class id.
Get(LockClassId id)39 static LockClassState* Get(LockClassId id) {
40 return reinterpret_cast<LockClassState*>(id);
41 }
42
43 // Returns the type name of the lock class for the given lock class id.
GetName(LockClassId id)44 static const char* GetName(LockClassId id) { return Get(id)->name_; }
45
46 // Returns true if lock class given by |search_id| is in the dependency set of
47 // the lock class given by |id|, false otherwise.
HasLockClass(LockClassId id,LockClassId search_id)48 static bool HasLockClass(LockClassId id, LockClassId search_id) {
49 return Get(id)->dependency_set_->HasLockClass(search_id);
50 }
51
52 // Adds the lock class given by |add_id| to the dependency set of the lock
53 // class given by |id|.
AddLockClass(LockClassId id,LockClassId add_id)54 static LockResult AddLockClass(LockClassId id, LockClassId add_id) {
55 return Get(id)->dependency_set_->AddLockClass(add_id);
56 }
57
58 // Returns true if the given lock class is irq-safe, false otherwise.
IsIrqSafe(LockClassId id)59 static bool IsIrqSafe(LockClassId id) {
60 return !!(Get(id)->flags_ & LockFlagsIrqSafe);
61 }
62
63 // Returns true if the given lock class is nestable, false otherwise.
IsNestable(LockClassId id)64 static bool IsNestable(LockClassId id) {
65 return !!(Get(id)->flags_ & LockFlagsNestable);
66 }
67
68 // Returns true if reporting is disabled for the given lock class, false
69 // otherwise.
IsReportingDisabled(LockClassId id)70 static bool IsReportingDisabled(LockClassId id) {
71 return !!(Get(id)->flags_ & LockFlagsReportingDisabled);
72 }
73
74 // Returns true if the validator should abort the program if it detects an
75 // invalid re-acquire with this lock class.
IsReAcquireFatal(LockClassId id)76 static bool IsReAcquireFatal(LockClassId id) {
77 return !!(Get(id)->flags_ & LockFlagsReAcquireFatal);
78 }
79
80 // Returns true if the lock should not be added to the active lock list
81 // during an acquire.
IsActiveListDisabled(LockClassId id)82 static bool IsActiveListDisabled(LockClassId id) {
83 return !!(Get(id)->flags_ & LockFlagsActiveListDisabled);
84 }
85
86 // Returns true if the lock should not be tracked.
IsTrackingDisabled(LockClassId id)87 static bool IsTrackingDisabled(LockClassId id) {
88 return !!(Get(id)->flags_ & LockFlagsTrackingDisabled);
89 }
90
91 // Iterator type to traverse the set of LockClassState instances.
92 class Iterator {
93 public:
94 Iterator() = default;
95 Iterator(const Iterator&) = default;
96 Iterator& operator=(const Iterator&) = default;
97
98 LockClassState& operator*() { return *state_; }
99 Iterator operator++() {
100 state_ = state_->next_;
101 return *this;
102 }
103 bool operator!=(const Iterator& other) const {
104 return state_ != other.state_;
105 }
106
begin()107 Iterator begin() { return {*Head()}; }
end()108 Iterator end() { return {nullptr}; }
109
110 private:
Iterator(LockClassState * state)111 Iterator(LockClassState* state)
112 : state_{state} {}
113 LockClassState* state_{nullptr};
114 };
115
116 // Returns an iterator for the init-time linked list of state instances.
Iter()117 static Iterator Iter() { return {}; }
118
119 // Returns the lock class id for this instance. The id is the address of the
120 // instance.
id()121 LockClassId id() const { return reinterpret_cast<LockClassId>(this); }
122
123 // Returns the name of this lock class.
name()124 const char* name() const { return name_; }
125
126 // Return the flags of this lock class.
flags()127 LockFlags flags() const { return flags_; }
128
129 // Returns the dependency set for this lock class.
dependency_set()130 const LockDependencySet& dependency_set() const { return *dependency_set_; }
131
connected_set()132 LockClassState* connected_set() { return LoopDetector::FindSet(&loop_node_)->ToState(); }
133
134 // Runs a loop detection pass on the set of lock classes to find possible
135 // circular lock dependencies.
LoopDetectionPass()136 static void LoopDetectionPass() {
137 static LoopDetector detector;
138 detector.DetectionPass();
139 }
140
index()141 uint64_t index() const { return loop_node_.index; }
least()142 uint64_t least() const { return loop_node_.least; }
143
144 // Resets the dependency set and disjoint set of this object. This is
145 // primarily used to initialize the state between successive tests.
Reset()146 void Reset() {
147 dependency_set_->clear();
148 loop_node_.Reset();
149 }
150
151 private:
152 // The name of the lock class type.
153 const char* const name_;
154
155 // The set of out edges from this node in the lock class dependency graph.
156 // Out edges represent lock classes that have been held before this class.
157 LockDependencySet* const dependency_set_;
158
159 // Flags specifying which which rules to apply during lock validation.
160 const LockFlags flags_;
161
162 // Linked list pointer to the next state instance. This list is constructed
163 // by a global initializer and never modified again. The list is used by the
164 // loop detector and runtime lock inspection commands to access the complete
165 // list of lock classes.
166 LockClassState* next_{InitNext(this)};
167
168 // Returns a pointer to the head pointer of the state linked list.
Head()169 static LockClassState** Head() {
170 static LockClassState* head{nullptr};
171 return &head;
172 }
173
174 // Updates the linked list to include the given state node and returns the
175 // previous head. This is used by the global initializer to setup the
176 // next_ member.
InitNext(LockClassState * state)177 static LockClassState* InitNext(LockClassState* state) {
178 LockClassState* head = *Head();
179 *Head() = state;
180 return head;
181 }
182
183 // Per-lock class state used by the loop detection algorithm.
184 struct LoopNode {
185 // The parent of the disjoint sets this node belongs to. Nodes start out
186 // alone in their own set. Sets are joined by the loop detector when
187 // found within a cycle.
188 std::atomic<LoopNode *> parent{this};
189
190 // Linked list node for the loop detector's active node stack. The use
191 // of a linked list of statically allocated nodes avoids dynamic memory
192 // allocation during graph traversal.
193 LoopNode* next{nullptr};
194
195 // Index values used by the loop detector algorithm.
196 uint64_t index{0};
197 uint64_t least{0};
198
199 // Returns a pointer to the LockClassState instance that contains this
200 // LoopNode. This allows the loop detector to mostly operate in terms
201 // of LoopNode instances, simplifying expressions in the main algorithm.
ToStateLoopNode202 LockClassState* ToState() {
203 static_assert(fbl::is_standard_layout<LockClassState>::value,
204 "LockClassState must be standard layout!");
205 uint8_t* byte_pointer = reinterpret_cast<uint8_t*>(this);
206 uint8_t* state_pointer =
207 byte_pointer - __offsetof(LockClassState, loop_node_);
208 return reinterpret_cast<LockClassState*>(state_pointer);
209 }
210
211 // Performs a relaxed, weak compare exchange on the parent pointer of
212 // this loop node. Due to the loops in FindSet() and UnionSet() this
213 // may fail due to races, the result is not required and will be
214 // retried based on other conditions. Relaxed order is used because
215 // neither caller publishes any other stores, nor depends on any other
216 // loads.
CompareExchangeParentLoopNode217 void CompareExchangeParent(LoopNode** expected, LoopNode* desired) {
218 parent.compare_exchange_weak(*expected, desired,
219 std::memory_order_relaxed,
220 std::memory_order_relaxed);
221 }
222
223 // Removes this node from whatever disjoint set it belongs to and
224 // returns it to its own separate set.
ResetLoopNode225 void Reset() {
226 parent.store(this, std::memory_order_relaxed);
227 }
228 };
229
230 // Loop detector node.
231 LoopNode loop_node_;
232
233 // Loop detection using Tarjan's strongly connected components algorithm to
234 // efficiently identify loops and disjoint set structures to store and
235 // update the sets of nodes involved in loops.
236 // NOTE: The loop detector methods, except FindSet and UnionSets, must only
237 // be called from the loop detector thread.
238 struct LoopDetector {
239 // The maximum index of the last loop detection run. Node index values
240 // are compared with this value to determine whether to revisit the
241 // node. Using a generation count permits running subsequent passes
242 // without first clearing the state of every node.
243 uint64_t generation{0};
244
245 // Running counter marking the step at which a node has been visited or
246 // revisited.
247 uint64_t index{0};
248
249 // The head of the stack of active nodes in a path traversal. The bottom
250 // of the stack is marked with the sentinel value 1 instead of nullptr
251 // to simplify determining whether a node is on the stack. Every node on
252 // the stack has LoopNode::next != nulltpr.
253 LoopNode* stack{reinterpret_cast<LoopNode*>(1)};
254
255 // Performs a single traversal of the lock dependency graph and updates
256 // the disjoint set structures with any detected loops.
DetectionPassLoopDetector257 void DetectionPass() {
258 // The next generation starts at the end of the previous. All nodes
259 // with indices less than or equal to the generation are revisited.
260 generation = index;
261
262 for (auto& state : LockClassState::Iter()) {
263 if (state.loop_node_.index <= generation)
264 Connect(&state.loop_node_);
265 }
266 }
267
268 // Recursively traverses a node path and updates the disjoint set
269 // structures when loops are detected.
ConnectLoopDetector270 void Connect(LoopNode* node) {
271 index += 1;
272 node->index = index;
273 node->least = index;
274 Push(node);
275
276 // Evaluate each node along the out edges of the dependency graph.
277 const auto& out_edges = node->ToState()->dependency_set();
278 for (LockClassId id : out_edges) {
279 LoopNode* related_node = &Get(id)->loop_node_;
280 if (related_node->index <= generation) {
281 Connect(related_node);
282 node->least = fbl::min(node->least, related_node->least);
283 } else if (related_node->next != nullptr) {
284 node->least = fbl::min(node->least, related_node->index);
285 }
286 }
287
288 // Update the disjoint set structures. Other nodes above this one on
289 // the stack are merged into this set.
290 if (node->index == node->least) {
291 LoopNode* top = nullptr;
292 size_t set_size = 0;
293 while (top != node) {
294 top = Pop();
295 UnionSets(node, top);
296 set_size++;
297 }
298
299 // Report loops with more than two components. Basic inversions
300 // with only two locks are reported by ThreadLockState::Acquire.
301 if (set_size > 2) {
302 LoopNode* root = FindSet(node);
303 SystemCircularLockDependencyDetected(root->ToState());
304 }
305 }
306 }
307
308 // Pushes a node on the active nodes stack.
PushLoopDetector309 void Push(LoopNode* node) {
310 ZX_DEBUG_ASSERT(node->next == nullptr);
311 node->next = stack;
312 stack = node;
313 }
314
315 // Pops a node from the active nodes stack.
PopLoopDetector316 LoopNode* Pop() {
317 ZX_DEBUG_ASSERT(stack != reinterpret_cast<LoopNode*>(1));
318 LoopNode* node = stack;
319 stack = node->next;
320 node->next = nullptr;
321 return node;
322 }
323
324 // Finds the parent node of the disjoint set this node belongs to. This
325 // approach applies thread-safe path splitting to flatten the set as it
326 // traverses the path, using the two-try optimization suggested by
327 // Jayanti and Tarjan.
FindSetLoopDetector328 static LoopNode* FindSet(LoopNode* node) {
329 while (true) {
330 // First pass: either terminate or attempt path split.
331 LoopNode* parent = node->parent.load(std::memory_order_relaxed);
332 LoopNode* grandparent = parent->parent.load(std::memory_order_relaxed);
333 if (parent == grandparent)
334 return parent;
335 node->CompareExchangeParent(&parent, grandparent);
336
337 // Second pass: either terminate, retry if last pass failed, or
338 // advance and attempt path split.
339 parent = node->parent.load(std::memory_order_relaxed);
340 grandparent = parent->parent.load(std::memory_order_relaxed);
341 if (parent == grandparent)
342 return parent;
343 node->CompareExchangeParent(&parent, grandparent);
344
345 // Advance regardless of whether split succeeded or failed.
346 node = parent;
347 }
348 }
349
350 // Joins the disjoint sets for the given nodes. Performs linking based
351 // on address order, which approximates the randomized total order
352 // suggested by Jayanti and Tarjan.
UnionSetsLoopDetector353 static void UnionSets(LoopNode* a, LoopNode* b) {
354 while (true) {
355 LoopNode* root_a = FindSet(a);
356 LoopNode* root_b = FindSet(b);
357
358 a = root_a;
359 b = root_b;
360
361 if (root_a == root_b) {
362 return; // Nothing to do for nodes in the same set.
363 } else if (root_a < root_b) {
364 root_b->CompareExchangeParent(&root_b, root_a);
365 } else {
366 root_a->CompareExchangeParent(&root_a, root_b);
367 }
368 }
369 }
370 };
371 };
372
373 // Runs a loop detection pass to find circular lock dependencies. This must be
374 // invoked at some point in the future after the lock validator calls the
375 // system-defined SystemTriggerLoopDetection().
LoopDetectionPass()376 static inline void LoopDetectionPass() {
377 LockClassState::LoopDetectionPass();
378 }
379
380 } // namespace lockdep
381