[Skiboot] [PATCH] core/lock: Add deadlock detection
Matt Brown
matthew.brown.dev at gmail.com
Fri Jun 23 14:35:06 AEST 2017
This adds simple deadlock detection.
The detection looks for circular dependencies in the lock requests.
It will abort and display a stack trace when a deadlock occurs.
The detection will reduce performance so only use for debugging.
To use, enable DEBUG_DEADLOCKS in include/config.h (disabled by default).
Signed-off-by: Matt Brown <matthew.brown.dev at gmail.com>
---
core/lock.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
include/config.h | 3 ++
include/lock.h | 5 ++++
3 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/core/lock.c b/core/lock.c
index e82048b..0379fb0 100644
--- a/core/lock.c
+++ b/core/lock.c
@@ -1,4 +1,4 @@
-/* Copyright 2013-2014 IBM Corp.
+/* Copyright 2013-2017 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,6 +66,73 @@ static inline void lock_check(struct lock *l) { };
static inline void unlock_check(struct lock *l) { };
#endif /* DEBUG_LOCKS */
+#ifdef DEBUG_DEADLOCKS
+
+#define MAX_THREADS 2048
+static struct lock *lock_table[MAX_THREADS];
+
+/* Find circular dependencies in the lock requests. */
+static bool check_deadlock(void)
+{
+ int lock_owner, i, start;
+ struct lock *next;
+
+ start = this_cpu()->pir;
+ next = lock_table[start];
+ i = 0;
+
+ while (i < MAX_THREADS) {
+
+ if (!next)
+ return false;
+
+ if (!(next->lock_val & 1) || next->in_con_path)
+ return false;
+
+ lock_owner = next->lock_val >> 32;
+
+ if (lock_owner >= MAX_THREADS)
+ return false;
+
+ if (lock_owner == start && i > 0)
+ return true;
+
+ next = lock_table[lock_owner];
+ i++;
+ }
+
+ return false;
+}
+
+void add_lock_request(struct lock *l)
+{
+ if (this_cpu()->state == cpu_state_active ||
+ this_cpu()->state == cpu_state_os) {
+
+ if (this_cpu()->pir >= MAX_THREADS)
+ return;
+
+ lock_table[this_cpu()->pir] = l;
+
+ if (check_deadlock() && check_deadlock()) {
+#ifdef DEBUG_LOCKS
+ lock_error(l, "Deadlock detected", 0);
+#else
+ prlog(PR_EMERG, "LOCK: Deadlock detected\n");
+ backtrace();
+ abort();
+#endif /* DEBUG_LOCKS */
+ }
+ }
+}
+
+void remove_lock_request(void)
+{
+ if (this_cpu()->pir < MAX_THREADS)
+ lock_table[this_cpu()->pir] = NULL;
+}
+#endif /* DEBUG_DEADLOCKS */
+
bool lock_held_by_me(struct lock *l)
{
uint64_t pir64 = this_cpu()->pir;
@@ -86,15 +153,29 @@ bool try_lock(struct lock *l)
void lock(struct lock *l)
{
+
if (bust_locks)
return;
lock_check(l);
+
+#ifdef DEBUG_DEADLOCKS
+ if (try_lock(l))
+ return;
+
+ add_lock_request(l);
+#endif
+
for (;;) {
if (try_lock(l))
break;
cpu_relax();
}
+
+#ifdef DEBUG_DEADLOCKS
+ remove_lock_request();
+#endif
+
}
void unlock(struct lock *l)
diff --git a/include/config.h b/include/config.h
index cd8a0a6..c2f80e9 100644
--- a/include/config.h
+++ b/include/config.h
@@ -39,6 +39,9 @@
/* Enable lock debugging */
#define DEBUG_LOCKS 1
+/* Enable deadlock detection */
+//#define DEBUG_DEADLOCKS 1
+
/* Enable malloc debugging */
#define DEBUG_MALLOC 1
diff --git a/include/lock.h b/include/lock.h
index 0ac943d..27bedc7 100644
--- a/include/lock.h
+++ b/include/lock.h
@@ -81,4 +81,9 @@ extern bool lock_recursive(struct lock *l);
/* Called after per-cpu data structures are available */
extern void init_locks(void);
+#ifdef DEBUG_DEADLOCKS
+extern void add_lock_request(struct lock *l);
+extern void remove_lock_request(void);
+#endif /* DEBUG_DEADLOCKS*/
+
#endif /* __LOCK_H */
--
2.9.3
More information about the Skiboot
mailing list