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