1b3fd78c4SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2b3fd78c4SJerome Forissier /*
3b3fd78c4SJerome Forissier * Copyright (c) 2018, Linaro Limited
4b3fd78c4SJerome Forissier */
5b3fd78c4SJerome Forissier
6b3fd78c4SJerome Forissier #include <assert.h>
72288b071SJerome Forissier #include <config.h>
8b3fd78c4SJerome Forissier #include <kernel/lockdep.h>
9b3fd78c4SJerome Forissier #include <kernel/unwind.h>
10b3fd78c4SJerome Forissier #include <stdlib.h>
11b3fd78c4SJerome Forissier #include <string.h>
12b3fd78c4SJerome Forissier #include <sys/queue.h>
13b3fd78c4SJerome Forissier #include <util.h>
14b3fd78c4SJerome Forissier
15b3fd78c4SJerome Forissier /* lockdep_node::flags values */
16b3fd78c4SJerome Forissier /* Flags used for depth-first topological sorting */
17b3fd78c4SJerome Forissier #define LOCKDEP_NODE_TEMP_MARK BIT(0)
18b3fd78c4SJerome Forissier #define LOCKDEP_NODE_PERM_MARK BIT(1)
19b3fd78c4SJerome Forissier /* Flag used during breadth-first search (print shortest cycle) */
20b3fd78c4SJerome Forissier #define LOCKDEP_NODE_BFS_VISITED BIT(2)
21b3fd78c4SJerome Forissier
22b3fd78c4SJerome Forissier /* Find node in graph or add it */
lockdep_add_to_graph(struct lockdep_node_head * graph,uintptr_t lock_id)23b3fd78c4SJerome Forissier static struct lockdep_node *lockdep_add_to_graph(
24b3fd78c4SJerome Forissier struct lockdep_node_head *graph,
25b3fd78c4SJerome Forissier uintptr_t lock_id)
26b3fd78c4SJerome Forissier {
27b3fd78c4SJerome Forissier struct lockdep_node *node = NULL;
28b3fd78c4SJerome Forissier
29b3fd78c4SJerome Forissier assert(graph);
30b3fd78c4SJerome Forissier TAILQ_FOREACH(node, graph, link)
31b3fd78c4SJerome Forissier if (node->lock_id == lock_id)
32b3fd78c4SJerome Forissier return node;
33b3fd78c4SJerome Forissier
34b3fd78c4SJerome Forissier node = calloc(1, sizeof(*node));
35b3fd78c4SJerome Forissier if (!node)
36b3fd78c4SJerome Forissier return NULL;
37b3fd78c4SJerome Forissier
38b3fd78c4SJerome Forissier node->lock_id = lock_id;
39b3fd78c4SJerome Forissier STAILQ_INIT(&node->edges);
40b3fd78c4SJerome Forissier TAILQ_INSERT_TAIL(graph, node, link);
41b3fd78c4SJerome Forissier
42b3fd78c4SJerome Forissier return node;
43b3fd78c4SJerome Forissier }
44b3fd78c4SJerome Forissier
dup_call_stack(vaddr_t * stack)45b3fd78c4SJerome Forissier static vaddr_t *dup_call_stack(vaddr_t *stack)
46b3fd78c4SJerome Forissier {
475ee85d76SJerome Forissier vaddr_t *nstack = NULL;
485ee85d76SJerome Forissier int n = 0;
495ee85d76SJerome Forissier
50b3fd78c4SJerome Forissier if (!stack)
51b3fd78c4SJerome Forissier return NULL;
52b3fd78c4SJerome Forissier
53b3fd78c4SJerome Forissier while (stack[n])
54b3fd78c4SJerome Forissier n++;
55b3fd78c4SJerome Forissier
565ee85d76SJerome Forissier nstack = malloc((n + 1) * sizeof(vaddr_t));
57b3fd78c4SJerome Forissier if (!nstack)
58b3fd78c4SJerome Forissier return NULL;
595ee85d76SJerome Forissier
605ee85d76SJerome Forissier memcpy(nstack, stack, (n + 1) * sizeof(vaddr_t));
615ee85d76SJerome Forissier
62b3fd78c4SJerome Forissier return nstack;
63b3fd78c4SJerome Forissier }
64b3fd78c4SJerome Forissier
lockdep_print_call_stack(vaddr_t * stack)65b3fd78c4SJerome Forissier static void lockdep_print_call_stack(vaddr_t *stack)
66b3fd78c4SJerome Forissier {
67b3fd78c4SJerome Forissier vaddr_t *p = NULL;
68b3fd78c4SJerome Forissier
692288b071SJerome Forissier if (!IS_ENABLED(CFG_LOCKDEP_RECORD_STACK))
702288b071SJerome Forissier return;
712288b071SJerome Forissier
72b3fd78c4SJerome Forissier EMSG_RAW("Call stack:");
73b3fd78c4SJerome Forissier for (p = stack; p && *p; p++)
74b3fd78c4SJerome Forissier EMSG_RAW(" %#" PRIxPTR, *p);
75b3fd78c4SJerome Forissier }
76b3fd78c4SJerome Forissier
lockdep_add_edge(struct lockdep_node * from,struct lockdep_node * to,vaddr_t * call_stack_from,vaddr_t * call_stack_to,uintptr_t thread_id)77b3fd78c4SJerome Forissier static TEE_Result lockdep_add_edge(struct lockdep_node *from,
78b3fd78c4SJerome Forissier struct lockdep_node *to,
79b3fd78c4SJerome Forissier vaddr_t *call_stack_from,
80b3fd78c4SJerome Forissier vaddr_t *call_stack_to,
81b3fd78c4SJerome Forissier uintptr_t thread_id)
82b3fd78c4SJerome Forissier {
83b3fd78c4SJerome Forissier struct lockdep_edge *edge = NULL;
84b3fd78c4SJerome Forissier
85b3fd78c4SJerome Forissier STAILQ_FOREACH(edge, &from->edges, link)
86b3fd78c4SJerome Forissier if (edge->to == to)
87b3fd78c4SJerome Forissier return TEE_SUCCESS;
88b3fd78c4SJerome Forissier
89b3fd78c4SJerome Forissier edge = calloc(1, sizeof(*edge));
90b3fd78c4SJerome Forissier if (!edge)
91b3fd78c4SJerome Forissier return TEE_ERROR_OUT_OF_MEMORY;
92b3fd78c4SJerome Forissier edge->to = to;
93b3fd78c4SJerome Forissier edge->call_stack_from = dup_call_stack(call_stack_from);
94b3fd78c4SJerome Forissier edge->call_stack_to = dup_call_stack(call_stack_to);
95b3fd78c4SJerome Forissier edge->thread_id = thread_id;
96b3fd78c4SJerome Forissier STAILQ_INSERT_TAIL(&from->edges, edge, link);
97b3fd78c4SJerome Forissier
98b3fd78c4SJerome Forissier return TEE_SUCCESS;
99b3fd78c4SJerome Forissier }
100b3fd78c4SJerome Forissier
101b3fd78c4SJerome Forissier struct lockdep_bfs {
102b3fd78c4SJerome Forissier struct lockdep_node *node;
103b3fd78c4SJerome Forissier uintptr_t *path;
104b3fd78c4SJerome Forissier int pathlen;
105b3fd78c4SJerome Forissier TAILQ_ENTRY(lockdep_bfs) link;
106b3fd78c4SJerome Forissier };
107b3fd78c4SJerome Forissier
108b3fd78c4SJerome Forissier TAILQ_HEAD(lockdep_bfs_head, lockdep_bfs);
109b3fd78c4SJerome Forissier
lockdep_bfs_queue_delete(struct lockdep_bfs_head * queue)110b3fd78c4SJerome Forissier static void lockdep_bfs_queue_delete(struct lockdep_bfs_head *queue)
111b3fd78c4SJerome Forissier {
112b3fd78c4SJerome Forissier struct lockdep_bfs *cur = NULL;
113b3fd78c4SJerome Forissier struct lockdep_bfs *next = NULL;
114b3fd78c4SJerome Forissier
115b3fd78c4SJerome Forissier TAILQ_FOREACH_SAFE(cur, queue, link, next) {
116b3fd78c4SJerome Forissier TAILQ_REMOVE(queue, cur, link);
117b3fd78c4SJerome Forissier free(cur->path);
118b3fd78c4SJerome Forissier free(cur);
119b3fd78c4SJerome Forissier }
120b3fd78c4SJerome Forissier }
121b3fd78c4SJerome Forissier
122b3fd78c4SJerome Forissier /*
123b3fd78c4SJerome Forissier * Print shortest cycle in @graph that contains @node.
124b3fd78c4SJerome Forissier * This function performs an iterative breadth-first search starting from @node,
125b3fd78c4SJerome Forissier * and stops when it reaches @node again. In each node we're tracking the path
126b3fd78c4SJerome Forissier * from the start node.
127b3fd78c4SJerome Forissier */
lockdep_graph_get_shortest_cycle(struct lockdep_node * node)128b3fd78c4SJerome Forissier static uintptr_t *lockdep_graph_get_shortest_cycle(struct lockdep_node *node)
129b3fd78c4SJerome Forissier {
130b3fd78c4SJerome Forissier struct lockdep_bfs_head queue;
131b3fd78c4SJerome Forissier struct lockdep_bfs *qe = NULL;
132b3fd78c4SJerome Forissier uintptr_t *ret = NULL;
133b3fd78c4SJerome Forissier
134b3fd78c4SJerome Forissier TAILQ_INIT(&queue);
135b3fd78c4SJerome Forissier node->flags |= LOCKDEP_NODE_BFS_VISITED;
136b3fd78c4SJerome Forissier
137b3fd78c4SJerome Forissier qe = calloc(1, sizeof(*qe));
138b3fd78c4SJerome Forissier if (!qe)
139b3fd78c4SJerome Forissier goto out;
140b3fd78c4SJerome Forissier qe->node = node;
141b3fd78c4SJerome Forissier qe->path = malloc(sizeof(uintptr_t));
142b3fd78c4SJerome Forissier if (!qe->path)
143b3fd78c4SJerome Forissier goto out;
144b3fd78c4SJerome Forissier qe->path[0] = node->lock_id;
145b3fd78c4SJerome Forissier qe->pathlen = 1;
146b3fd78c4SJerome Forissier TAILQ_INSERT_TAIL(&queue, qe, link);
147b3fd78c4SJerome Forissier
148b3fd78c4SJerome Forissier while (!TAILQ_EMPTY(&queue)) {
149*6e2de0d7SJens Wiklander struct lockdep_node *n = NULL;
150b3fd78c4SJerome Forissier struct lockdep_edge *e = NULL;
151b3fd78c4SJerome Forissier
152*6e2de0d7SJens Wiklander qe = TAILQ_FIRST(&queue);
153*6e2de0d7SJens Wiklander n = qe->node;
154*6e2de0d7SJens Wiklander TAILQ_REMOVE(&queue, qe, link);
155*6e2de0d7SJens Wiklander
156b3fd78c4SJerome Forissier STAILQ_FOREACH(e, &n->edges, link) {
157b3fd78c4SJerome Forissier if (e->to->lock_id == node->lock_id) {
158b3fd78c4SJerome Forissier uintptr_t *tmp = NULL;
159b3fd78c4SJerome Forissier size_t nlen = qe->pathlen + 1;
160b3fd78c4SJerome Forissier
161b3fd78c4SJerome Forissier /*
162b3fd78c4SJerome Forissier * Cycle found. Terminate cycle path with NULL
163b3fd78c4SJerome Forissier * and return it.
164b3fd78c4SJerome Forissier */
165b3fd78c4SJerome Forissier tmp = realloc(qe->path,
166b3fd78c4SJerome Forissier nlen * sizeof(uintptr_t));
167b3fd78c4SJerome Forissier if (!tmp) {
168b3fd78c4SJerome Forissier EMSG("Out of memory");
169b3fd78c4SJerome Forissier free(qe->path);
170b3fd78c4SJerome Forissier ret = NULL;
171b3fd78c4SJerome Forissier goto out;
172b3fd78c4SJerome Forissier }
173b3fd78c4SJerome Forissier qe->path = tmp;
174b3fd78c4SJerome Forissier qe->path[nlen - 1] = 0;
175b3fd78c4SJerome Forissier ret = qe->path;
176b3fd78c4SJerome Forissier goto out;
177b3fd78c4SJerome Forissier }
178b3fd78c4SJerome Forissier
179b3fd78c4SJerome Forissier if (!(e->to->flags & LOCKDEP_NODE_BFS_VISITED)) {
180*6e2de0d7SJens Wiklander size_t nlen = 0;
181*6e2de0d7SJens Wiklander struct lockdep_bfs *nqe = NULL;
182*6e2de0d7SJens Wiklander
183b3fd78c4SJerome Forissier e->to->flags |= LOCKDEP_NODE_BFS_VISITED;
184b3fd78c4SJerome Forissier
185*6e2de0d7SJens Wiklander nlen = qe->pathlen + 1;
186*6e2de0d7SJens Wiklander nqe = calloc(1, sizeof(*nqe));
187b3fd78c4SJerome Forissier if (!nqe)
188b3fd78c4SJerome Forissier goto out;
189b3fd78c4SJerome Forissier nqe->node = e->to;
190b3fd78c4SJerome Forissier nqe->path = malloc(nlen * sizeof(uintptr_t));
191b3fd78c4SJerome Forissier if (!nqe->path)
192b3fd78c4SJerome Forissier goto out;
193b3fd78c4SJerome Forissier nqe->pathlen = nlen;
194b3fd78c4SJerome Forissier memcpy(nqe->path, qe->path,
195b3fd78c4SJerome Forissier qe->pathlen * sizeof(uintptr_t));
196b3fd78c4SJerome Forissier nqe->path[nlen - 1] = e->to->lock_id;
197b3fd78c4SJerome Forissier TAILQ_INSERT_TAIL(&queue, nqe, link);
198b3fd78c4SJerome Forissier }
199b3fd78c4SJerome Forissier }
200b3fd78c4SJerome Forissier free(qe->path);
201b3fd78c4SJerome Forissier free(qe);
202b3fd78c4SJerome Forissier qe = NULL;
203b3fd78c4SJerome Forissier }
204b3fd78c4SJerome Forissier
205b3fd78c4SJerome Forissier out:
206b3fd78c4SJerome Forissier free(qe);
207b3fd78c4SJerome Forissier lockdep_bfs_queue_delete(&queue);
208b3fd78c4SJerome Forissier return ret;
209b3fd78c4SJerome Forissier }
210b3fd78c4SJerome Forissier
lockdep_visit(struct lockdep_node * node)211b3fd78c4SJerome Forissier static TEE_Result lockdep_visit(struct lockdep_node *node)
212b3fd78c4SJerome Forissier {
213*6e2de0d7SJens Wiklander struct lockdep_edge *e = NULL;
214*6e2de0d7SJens Wiklander
215b3fd78c4SJerome Forissier if (node->flags & LOCKDEP_NODE_PERM_MARK)
216b3fd78c4SJerome Forissier return TEE_SUCCESS;
217b3fd78c4SJerome Forissier
218b3fd78c4SJerome Forissier if (node->flags & LOCKDEP_NODE_TEMP_MARK)
219b3fd78c4SJerome Forissier return TEE_ERROR_BAD_STATE; /* Not a DAG! */
220b3fd78c4SJerome Forissier
221b3fd78c4SJerome Forissier node->flags |= LOCKDEP_NODE_TEMP_MARK;
222b3fd78c4SJerome Forissier
223b3fd78c4SJerome Forissier STAILQ_FOREACH(e, &node->edges, link) {
224b3fd78c4SJerome Forissier TEE_Result res = lockdep_visit(e->to);
225b3fd78c4SJerome Forissier
226b3fd78c4SJerome Forissier if (res)
227b3fd78c4SJerome Forissier return res;
228b3fd78c4SJerome Forissier }
229b3fd78c4SJerome Forissier
230b3fd78c4SJerome Forissier node->flags |= LOCKDEP_NODE_PERM_MARK;
231b3fd78c4SJerome Forissier return TEE_SUCCESS;
232b3fd78c4SJerome Forissier }
233b3fd78c4SJerome Forissier
lockdep_graph_sort(struct lockdep_node_head * graph)234b3fd78c4SJerome Forissier static TEE_Result lockdep_graph_sort(struct lockdep_node_head *graph)
235b3fd78c4SJerome Forissier {
236b3fd78c4SJerome Forissier struct lockdep_node *node = NULL;
237b3fd78c4SJerome Forissier
238b3fd78c4SJerome Forissier TAILQ_FOREACH(node, graph, link) {
239b3fd78c4SJerome Forissier if (!node->flags) {
240b3fd78c4SJerome Forissier /* Unmarked node */
241b3fd78c4SJerome Forissier TEE_Result res = lockdep_visit(node);
242b3fd78c4SJerome Forissier
243b3fd78c4SJerome Forissier if (res)
244b3fd78c4SJerome Forissier return res;
245b3fd78c4SJerome Forissier }
246b3fd78c4SJerome Forissier }
247b3fd78c4SJerome Forissier
248b3fd78c4SJerome Forissier TAILQ_FOREACH(node, graph, link)
249b3fd78c4SJerome Forissier node->flags = 0;
250b3fd78c4SJerome Forissier
251b3fd78c4SJerome Forissier return TEE_SUCCESS;
252b3fd78c4SJerome Forissier }
253b3fd78c4SJerome Forissier
lockdep_find_edge(struct lockdep_node_head * graph,uintptr_t from,uintptr_t to)254b3fd78c4SJerome Forissier static struct lockdep_edge *lockdep_find_edge(struct lockdep_node_head *graph,
255b3fd78c4SJerome Forissier uintptr_t from, uintptr_t to)
256b3fd78c4SJerome Forissier {
257b3fd78c4SJerome Forissier struct lockdep_node *node = NULL;
258b3fd78c4SJerome Forissier struct lockdep_edge *edge = NULL;
259b3fd78c4SJerome Forissier
260b3fd78c4SJerome Forissier TAILQ_FOREACH(node, graph, link)
261b3fd78c4SJerome Forissier if (node->lock_id == from)
262b3fd78c4SJerome Forissier STAILQ_FOREACH(edge, &node->edges, link)
263b3fd78c4SJerome Forissier if (edge->to->lock_id == to)
264b3fd78c4SJerome Forissier return edge;
265b3fd78c4SJerome Forissier return NULL;
266b3fd78c4SJerome Forissier }
267b3fd78c4SJerome Forissier
lockdep_print_edge_info(uintptr_t from __maybe_unused,struct lockdep_edge * edge)268b3fd78c4SJerome Forissier static void lockdep_print_edge_info(uintptr_t from __maybe_unused,
269b3fd78c4SJerome Forissier struct lockdep_edge *edge)
270b3fd78c4SJerome Forissier {
271b3fd78c4SJerome Forissier uintptr_t __maybe_unused to = edge->to->lock_id;
2722288b071SJerome Forissier const char __maybe_unused *at_msg = "";
2732288b071SJerome Forissier const char __maybe_unused *acq_msg = "";
274b3fd78c4SJerome Forissier
2752288b071SJerome Forissier if (IS_ENABLED(CFG_LOCKDEP_RECORD_STACK)) {
2762288b071SJerome Forissier at_msg = " at:";
2772288b071SJerome Forissier acq_msg = " acquired at:";
2782288b071SJerome Forissier }
2792288b071SJerome Forissier
2802288b071SJerome Forissier EMSG_RAW("-> Thread %#" PRIxPTR " acquired lock %#" PRIxPTR "%s",
2812288b071SJerome Forissier edge->thread_id, to, at_msg);
282b3fd78c4SJerome Forissier lockdep_print_call_stack(edge->call_stack_to);
2832288b071SJerome Forissier EMSG_RAW("...while holding lock %#" PRIxPTR "%s",
2842288b071SJerome Forissier from, acq_msg);
285b3fd78c4SJerome Forissier lockdep_print_call_stack(edge->call_stack_from);
286b3fd78c4SJerome Forissier }
287b3fd78c4SJerome Forissier
288b3fd78c4SJerome Forissier /*
289b3fd78c4SJerome Forissier * Find cycle containing @node in the lock graph, then print full debug
290b3fd78c4SJerome Forissier * information about each edge (thread that acquired the locks and call stacks)
291b3fd78c4SJerome Forissier */
lockdep_print_cycle_info(struct lockdep_node_head * graph,struct lockdep_node * node)292b3fd78c4SJerome Forissier static void lockdep_print_cycle_info(struct lockdep_node_head *graph,
293b3fd78c4SJerome Forissier struct lockdep_node *node)
294b3fd78c4SJerome Forissier {
295b3fd78c4SJerome Forissier struct lockdep_edge *edge = NULL;
296b3fd78c4SJerome Forissier uintptr_t *cycle = NULL;
297b3fd78c4SJerome Forissier uintptr_t *p = NULL;
298b3fd78c4SJerome Forissier uintptr_t from = 0;
299b3fd78c4SJerome Forissier uintptr_t to = 0;
300b3fd78c4SJerome Forissier
301b3fd78c4SJerome Forissier cycle = lockdep_graph_get_shortest_cycle(node);
302b3fd78c4SJerome Forissier assert(cycle && cycle[0]);
303b3fd78c4SJerome Forissier EMSG_RAW("-> Shortest cycle:");
304b3fd78c4SJerome Forissier for (p = cycle; *p; p++)
305b3fd78c4SJerome Forissier EMSG_RAW(" Lock %#" PRIxPTR, *p);
306b3fd78c4SJerome Forissier for (p = cycle; ; p++) {
307b3fd78c4SJerome Forissier if (!*p) {
308b3fd78c4SJerome Forissier assert(p != cycle);
309b3fd78c4SJerome Forissier from = to;
310b3fd78c4SJerome Forissier to = cycle[0];
311b3fd78c4SJerome Forissier edge = lockdep_find_edge(graph, from, to);
312b3fd78c4SJerome Forissier lockdep_print_edge_info(from, edge);
313b3fd78c4SJerome Forissier break;
314b3fd78c4SJerome Forissier }
315b3fd78c4SJerome Forissier if (p != cycle)
316b3fd78c4SJerome Forissier from = to;
317b3fd78c4SJerome Forissier to = *p;
318b3fd78c4SJerome Forissier if (p != cycle) {
319b3fd78c4SJerome Forissier edge = lockdep_find_edge(graph, from, to);
320b3fd78c4SJerome Forissier lockdep_print_edge_info(from, edge);
321b3fd78c4SJerome Forissier }
322b3fd78c4SJerome Forissier }
323b3fd78c4SJerome Forissier free(cycle);
324b3fd78c4SJerome Forissier }
325b3fd78c4SJerome Forissier
lockdep_get_kernel_stack(void)3262288b071SJerome Forissier static vaddr_t *lockdep_get_kernel_stack(void)
3272288b071SJerome Forissier {
3282288b071SJerome Forissier if (IS_ENABLED(CFG_LOCKDEP_RECORD_STACK))
3292288b071SJerome Forissier return unw_get_kernel_stack();
3302288b071SJerome Forissier
3312288b071SJerome Forissier return NULL;
3322288b071SJerome Forissier }
3332288b071SJerome Forissier
__lockdep_lock_acquire(struct lockdep_node_head * graph,struct lockdep_lock_head * owned,uintptr_t id)334b3fd78c4SJerome Forissier TEE_Result __lockdep_lock_acquire(struct lockdep_node_head *graph,
335b3fd78c4SJerome Forissier struct lockdep_lock_head *owned,
336b3fd78c4SJerome Forissier uintptr_t id)
337b3fd78c4SJerome Forissier {
338b3fd78c4SJerome Forissier struct lockdep_node *node = lockdep_add_to_graph(graph, id);
339*6e2de0d7SJens Wiklander struct lockdep_lock *lock = NULL;
340*6e2de0d7SJens Wiklander TEE_Result res = TEE_SUCCESS;
341*6e2de0d7SJens Wiklander vaddr_t *acq_stack = NULL;
342b3fd78c4SJerome Forissier
343b3fd78c4SJerome Forissier if (!node)
344b3fd78c4SJerome Forissier return TEE_ERROR_OUT_OF_MEMORY;
345b3fd78c4SJerome Forissier
346*6e2de0d7SJens Wiklander acq_stack = lockdep_get_kernel_stack();
347b3fd78c4SJerome Forissier
348b3fd78c4SJerome Forissier TAILQ_FOREACH(lock, owned, link) {
349*6e2de0d7SJens Wiklander res = lockdep_add_edge(lock->node, node, lock->call_stack,
350*6e2de0d7SJens Wiklander acq_stack, (uintptr_t)owned);
351b3fd78c4SJerome Forissier if (res)
352b3fd78c4SJerome Forissier return res;
353b3fd78c4SJerome Forissier }
354b3fd78c4SJerome Forissier
355*6e2de0d7SJens Wiklander res = lockdep_graph_sort(graph);
356b3fd78c4SJerome Forissier if (res) {
357b3fd78c4SJerome Forissier EMSG_RAW("Potential deadlock detected!");
358b3fd78c4SJerome Forissier EMSG_RAW("When trying to acquire lock %#" PRIxPTR, id);
359b3fd78c4SJerome Forissier lockdep_print_cycle_info(graph, node);
360b3fd78c4SJerome Forissier return res;
361b3fd78c4SJerome Forissier }
362b3fd78c4SJerome Forissier
363b3fd78c4SJerome Forissier lock = calloc(1, sizeof(*lock));
364b3fd78c4SJerome Forissier if (!lock)
365b3fd78c4SJerome Forissier return TEE_ERROR_OUT_OF_MEMORY;
366b3fd78c4SJerome Forissier
367b3fd78c4SJerome Forissier lock->node = node;
368b3fd78c4SJerome Forissier lock->call_stack = acq_stack;
369b3fd78c4SJerome Forissier TAILQ_INSERT_TAIL(owned, lock, link);
370b3fd78c4SJerome Forissier
371b3fd78c4SJerome Forissier return TEE_SUCCESS;
372b3fd78c4SJerome Forissier }
373b3fd78c4SJerome Forissier
37402fbb41aSJerome Forissier /*
37502fbb41aSJerome Forissier * Call this when it is known that the thread has been able to acquire the lock.
37602fbb41aSJerome Forissier * Similar to __lockdep_lock_acquire(), but since the operation is non-blocking,
37702fbb41aSJerome Forissier * no dependency to currently owned locks are created.
37802fbb41aSJerome Forissier */
__lockdep_lock_tryacquire(struct lockdep_node_head * graph,struct lockdep_lock_head * owned,uintptr_t id)37902fbb41aSJerome Forissier TEE_Result __lockdep_lock_tryacquire(struct lockdep_node_head *graph,
38002fbb41aSJerome Forissier struct lockdep_lock_head *owned,
38102fbb41aSJerome Forissier uintptr_t id)
38202fbb41aSJerome Forissier {
38302fbb41aSJerome Forissier struct lockdep_node *node = lockdep_add_to_graph(graph, id);
384*6e2de0d7SJens Wiklander struct lockdep_lock *lock = NULL;
385*6e2de0d7SJens Wiklander vaddr_t *acq_stack = NULL;
38602fbb41aSJerome Forissier
38702fbb41aSJerome Forissier if (!node)
38802fbb41aSJerome Forissier return TEE_ERROR_OUT_OF_MEMORY;
38902fbb41aSJerome Forissier
390*6e2de0d7SJens Wiklander acq_stack = lockdep_get_kernel_stack();
39102fbb41aSJerome Forissier
39202fbb41aSJerome Forissier lock = calloc(1, sizeof(*lock));
39302fbb41aSJerome Forissier if (!lock)
39402fbb41aSJerome Forissier return TEE_ERROR_OUT_OF_MEMORY;
39502fbb41aSJerome Forissier
39602fbb41aSJerome Forissier lock->node = node;
39702fbb41aSJerome Forissier lock->call_stack = acq_stack;
39802fbb41aSJerome Forissier TAILQ_INSERT_TAIL(owned, lock, link);
39902fbb41aSJerome Forissier
40002fbb41aSJerome Forissier return TEE_SUCCESS;
40102fbb41aSJerome Forissier }
40202fbb41aSJerome Forissier
__lockdep_lock_release(struct lockdep_lock_head * owned,uintptr_t id)403b3fd78c4SJerome Forissier TEE_Result __lockdep_lock_release(struct lockdep_lock_head *owned, uintptr_t id)
404b3fd78c4SJerome Forissier {
405b3fd78c4SJerome Forissier struct lockdep_lock *lock = NULL;
406b3fd78c4SJerome Forissier
407b3fd78c4SJerome Forissier TAILQ_FOREACH_REVERSE(lock, owned, lockdep_lock_head, link) {
408b3fd78c4SJerome Forissier if (lock->node->lock_id == id) {
409b3fd78c4SJerome Forissier TAILQ_REMOVE(owned, lock, link);
410b3fd78c4SJerome Forissier free(lock->call_stack);
411b3fd78c4SJerome Forissier free(lock);
412b3fd78c4SJerome Forissier return TEE_SUCCESS;
413b3fd78c4SJerome Forissier }
414b3fd78c4SJerome Forissier }
415b3fd78c4SJerome Forissier
416b3fd78c4SJerome Forissier EMSG_RAW("Thread %p does not own lock %#" PRIxPTR, (void *)owned, id);
417b3fd78c4SJerome Forissier return TEE_ERROR_ITEM_NOT_FOUND;
418b3fd78c4SJerome Forissier }
419b3fd78c4SJerome Forissier
lockdep_free_edge(struct lockdep_edge * edge)420ccbc05e1SJens Wiklander static void lockdep_free_edge(struct lockdep_edge *edge)
421ccbc05e1SJens Wiklander {
422ccbc05e1SJens Wiklander free(edge->call_stack_from);
423ccbc05e1SJens Wiklander free(edge->call_stack_to);
424ccbc05e1SJens Wiklander free(edge);
425ccbc05e1SJens Wiklander }
426ccbc05e1SJens Wiklander
lockdep_node_delete(struct lockdep_node * node)427b3fd78c4SJerome Forissier static void lockdep_node_delete(struct lockdep_node *node)
428b3fd78c4SJerome Forissier {
429b3fd78c4SJerome Forissier struct lockdep_edge *edge = NULL;
430b3fd78c4SJerome Forissier struct lockdep_edge *next = NULL;
431b3fd78c4SJerome Forissier
432ccbc05e1SJens Wiklander STAILQ_FOREACH_SAFE(edge, &node->edges, link, next)
433ccbc05e1SJens Wiklander lockdep_free_edge(edge);
434ccbc05e1SJens Wiklander
435b3fd78c4SJerome Forissier free(node);
436b3fd78c4SJerome Forissier }
437b3fd78c4SJerome Forissier
lockdep_graph_delete(struct lockdep_node_head * graph)438b3fd78c4SJerome Forissier void lockdep_graph_delete(struct lockdep_node_head *graph)
439b3fd78c4SJerome Forissier {
440b3fd78c4SJerome Forissier struct lockdep_node *node = NULL;
441b3fd78c4SJerome Forissier struct lockdep_node *next = NULL;
442b3fd78c4SJerome Forissier
443b3fd78c4SJerome Forissier TAILQ_FOREACH_SAFE(node, graph, link, next) {
444b3fd78c4SJerome Forissier TAILQ_REMOVE(graph, node, link);
445b3fd78c4SJerome Forissier lockdep_node_delete(node);
446b3fd78c4SJerome Forissier }
447b3fd78c4SJerome Forissier }
448b3fd78c4SJerome Forissier
lockdep_queue_delete(struct lockdep_lock_head * owned)449b3fd78c4SJerome Forissier void lockdep_queue_delete(struct lockdep_lock_head *owned)
450b3fd78c4SJerome Forissier {
451b3fd78c4SJerome Forissier struct lockdep_lock *lock = NULL;
452b3fd78c4SJerome Forissier struct lockdep_lock *next = NULL;
453b3fd78c4SJerome Forissier
454b3fd78c4SJerome Forissier TAILQ_FOREACH_SAFE(lock, owned, link, next) {
455b3fd78c4SJerome Forissier TAILQ_REMOVE(owned, lock, link);
456b3fd78c4SJerome Forissier free(lock);
457b3fd78c4SJerome Forissier }
458b3fd78c4SJerome Forissier }
459ccbc05e1SJens Wiklander
lockdep_node_destroy(struct lockdep_node_head * graph,struct lockdep_node * node)460ccbc05e1SJens Wiklander static void lockdep_node_destroy(struct lockdep_node_head *graph,
461ccbc05e1SJens Wiklander struct lockdep_node *node)
462ccbc05e1SJens Wiklander {
463ccbc05e1SJens Wiklander struct lockdep_edge *edge = NULL;
464ccbc05e1SJens Wiklander struct lockdep_edge *next = NULL;
465ccbc05e1SJens Wiklander struct lockdep_node *from = NULL;
466ccbc05e1SJens Wiklander
467ccbc05e1SJens Wiklander TAILQ_REMOVE(graph, node, link);
468ccbc05e1SJens Wiklander
469ccbc05e1SJens Wiklander /*
470ccbc05e1SJens Wiklander * Loop over all nodes in the graph to remove all edges with the
471ccbc05e1SJens Wiklander * node to remove in the "to" field.
472ccbc05e1SJens Wiklander */
473ccbc05e1SJens Wiklander TAILQ_FOREACH(from, graph, link) {
474ccbc05e1SJens Wiklander edge = STAILQ_FIRST(&from->edges);
475ccbc05e1SJens Wiklander while (edge && edge->to == node) {
476ccbc05e1SJens Wiklander STAILQ_REMOVE_HEAD(&from->edges, link);
477ccbc05e1SJens Wiklander lockdep_free_edge(edge);
478ccbc05e1SJens Wiklander edge = STAILQ_FIRST(&from->edges);
479ccbc05e1SJens Wiklander }
480ccbc05e1SJens Wiklander
481ccbc05e1SJens Wiklander if (!edge)
482ccbc05e1SJens Wiklander continue;
483ccbc05e1SJens Wiklander
484ccbc05e1SJens Wiklander next = STAILQ_NEXT(edge, link);
485ccbc05e1SJens Wiklander while (next) {
486ccbc05e1SJens Wiklander if (next->to == node) {
487ccbc05e1SJens Wiklander STAILQ_REMOVE_AFTER(&from->edges, edge, link);
488ccbc05e1SJens Wiklander lockdep_free_edge(next);
489ccbc05e1SJens Wiklander } else {
490ccbc05e1SJens Wiklander edge = next;
491ccbc05e1SJens Wiklander }
492ccbc05e1SJens Wiklander next = STAILQ_NEXT(edge, link);
493ccbc05e1SJens Wiklander }
494ccbc05e1SJens Wiklander }
495ccbc05e1SJens Wiklander
496ccbc05e1SJens Wiklander STAILQ_FOREACH_SAFE(edge, &node->edges, link, next)
497ccbc05e1SJens Wiklander lockdep_free_edge(edge);
498ccbc05e1SJens Wiklander
499ccbc05e1SJens Wiklander free(node);
500ccbc05e1SJens Wiklander }
501ccbc05e1SJens Wiklander
lockdep_lock_destroy(struct lockdep_node_head * graph,uintptr_t lock_id)502ccbc05e1SJens Wiklander void lockdep_lock_destroy(struct lockdep_node_head *graph, uintptr_t lock_id)
503ccbc05e1SJens Wiklander {
504ccbc05e1SJens Wiklander struct lockdep_node *node = NULL;
505ccbc05e1SJens Wiklander
506ccbc05e1SJens Wiklander assert(graph);
507ccbc05e1SJens Wiklander TAILQ_FOREACH(node, graph, link) {
508ccbc05e1SJens Wiklander if (node->lock_id == lock_id) {
509ccbc05e1SJens Wiklander lockdep_node_destroy(graph, node);
510ccbc05e1SJens Wiklander break;
511ccbc05e1SJens Wiklander }
512ccbc05e1SJens Wiklander }
513ccbc05e1SJens Wiklander }
514