/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2018, Linaro Limited */ #ifndef __KERNEL_LOCKDEP_H #define __KERNEL_LOCKDEP_H #include #include #include #include #include #include /* * Lock graph. If node A has an edge to node B, then A was locked before B in * the same thread of execution. */ struct lockdep_edge { struct lockdep_node *to; uintptr_t thread_id; vaddr_t *call_stack_from; vaddr_t *call_stack_to; STAILQ_ENTRY(lockdep_edge) link; }; STAILQ_HEAD(lockdep_edge_head, lockdep_edge); struct lockdep_node { uintptr_t lock_id; /* For instance, address of actual lock object */ struct lockdep_edge_head edges; TAILQ_ENTRY(lockdep_node) link; uint8_t flags; /* Used temporarily when walking the graph */ }; TAILQ_HEAD(lockdep_node_head, lockdep_node); /* Per-thread queue of currently owned locks (point to nodes in the graph) */ struct lockdep_lock { struct lockdep_node *node; vaddr_t *call_stack; TAILQ_ENTRY(lockdep_lock) link; }; TAILQ_HEAD(lockdep_lock_head, lockdep_lock); #ifdef CFG_LOCKDEP /* * Functions used internally and for testing the algorithm. Actual locking code * should use the wrappers below (which panic in case of error). */ TEE_Result __lockdep_lock_acquire(struct lockdep_node_head *graph, struct lockdep_lock_head *owned, uintptr_t id); TEE_Result __lockdep_lock_tryacquire(struct lockdep_node_head *graph, struct lockdep_lock_head *owned, uintptr_t id); TEE_Result __lockdep_lock_release(struct lockdep_lock_head *owned, uintptr_t id); /* Delete all elements in @graph */ void lockdep_graph_delete(struct lockdep_node_head *graph); /* Delete all elements in @queue */ void lockdep_queue_delete(struct lockdep_lock_head *queue); /* * Acquire lock @id, while already holding the locks in @owned. * @owned represent the caller; there should be one instance per thread of * execution. @graph is the directed acyclic graph (DAG) to be used for * potential deadlock detection; use the same @graph for all the locks of the * same type as lock @id. * * This function will panic() if the acquire operation would result in a lock * hierarchy violation (potential deadlock). */ static inline void lockdep_lock_acquire(struct lockdep_node_head *graph, struct lockdep_lock_head *owned, uintptr_t id) { TEE_Result res = __lockdep_lock_acquire(graph, owned, id); if (res) { EMSG("lockdep: error %#" PRIx32, res); panic(); } } /* * Non-blocking acquire lock @id, while already holding the locks in @owned. * @owned represent the caller; there should be one instance per thread of * execution. @graph is the directed acyclic graph (DAG) to be used for * potential deadlock detection; use the same @graph for all the locks of the * same type as lock @id. */ static inline void lockdep_lock_tryacquire(struct lockdep_node_head *graph, struct lockdep_lock_head *owned, uintptr_t id) { TEE_Result res = __lockdep_lock_tryacquire(graph, owned, id); if (res) { EMSG("lockdep: error %#" PRIx32, res); panic(); } } /* * Release lock @id. The lock is removed from @owned. * * This function will panic() if the lock is not held by the caller. */ static inline void lockdep_lock_release(struct lockdep_lock_head *owned, uintptr_t id) { TEE_Result res = __lockdep_lock_release(owned, id); if (res) { EMSG("lockdep: error %#" PRIx32, res); panic(); } } /* * Destroy lock @id in @graph. The lock is freed. */ void lockdep_lock_destroy(struct lockdep_node_head *graph, uintptr_t id); /* Initialize lockdep for mutex objects (kernel/mutex.h) */ void mutex_lockdep_init(void); #else /* CFG_LOCKDEP */ static inline void lockdep_lock_acquire(struct lockdep_node_head *g __unused, struct lockdep_lock_head *o __unused, uintptr_t id __unused) {} static inline void lockdep_lock_release(struct lockdep_lock_head *o __unused, uintptr_t id __unused) {} static inline void lockdep_lock_destroy(struct lockdep_node_head *graph __unused, uintptr_t id __unused) {} static inline void mutex_lockdep_init(void) {} #endif /* !CFG_LOCKDEP */ #endif /* !__KERNEL_LOCKDEP_H */