Skip to content

Commit

Permalink
core/lock: Add deadlock detection
Browse files Browse the repository at this point in the history
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 is enabled by DEBUG_LOCKS (enabled by default).
While the detection may have a slight performance overhead, as there are
not a huge number of locks in skiboot this overhead isn't significant.

Signed-off-by: Matt Brown <matthew.brown.dev@gmail.com>
[stewart: fix build with DEBUG_LOCKS off]
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
m-brow authored and stewartsmith committed Mar 7, 2018
1 parent 217f74b commit 2d4c774
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
3 changes: 3 additions & 0 deletions core/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,9 @@ void init_all_cpus(void)
t->node = cpu;
t->chip_id = chip_id;
t->icp_regs = NULL; /* Will be set later */
#ifdef DEBUG_LOCKS
t->requested_lock = NULL;
#endif
t->core_hmi_state = 0;
t->core_hmi_state_ptr = &t->core_hmi_state;
t->thread_mask = 1;
Expand Down
82 changes: 81 additions & 1 deletion core/lock.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright 2013-2014 IBM Corp.
/* Copyright 2013-2018 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
bool bust_locks = true;

#ifdef DEBUG_LOCKS
static struct lock dl_lock = LOCK_UNLOCKED;

static void lock_error(struct lock *l, const char *reason, uint16_t err)
{
Expand Down Expand Up @@ -61,9 +62,81 @@ static void unlock_check(struct lock *l)
lock_error(l, "Releasing lock we don't hold depth", 4);
}

/* Find circular dependencies in the lock requests. */
static bool check_deadlock(void)
{
uint32_t lock_owner, start, i;
struct cpu_thread *next_cpu;
struct lock *next;

next = this_cpu()->requested_lock;
start = this_cpu()->pir;
i = 0;

while (i < cpu_max_pir) {

if (!next)
return false;

if (!(next->lock_val & 1) || next->in_con_path)
return false;

lock_owner = next->lock_val >> 32;

if (lock_owner == start)
return true;

next_cpu = find_cpu_by_pir(lock_owner);

if (!next_cpu)
return false;

next = next_cpu->requested_lock;
i++;
}

return false;
}

static void add_lock_request(struct lock *l)
{
struct cpu_thread *curr = this_cpu();

if (curr->state != cpu_state_active &&
curr->state != cpu_state_os)
return;

/*
* For deadlock detection we must keep the lock states constant
* while doing the deadlock check.
*/
for (;;) {
if (try_lock(&dl_lock))
break;
smt_lowest();
while (dl_lock.lock_val)
barrier();
smt_medium();
}

curr->requested_lock = l;

if (check_deadlock())
lock_error(l, "Deadlock detected", 0);

unlock(&dl_lock);
}

static void remove_lock_request(void)
{
this_cpu()->requested_lock = NULL;
}

#else
static inline void lock_check(struct lock *l) { };
static inline void unlock_check(struct lock *l) { };
static inline void add_lock_request(struct lock *l) { };
static inline void remove_lock_request(void) { };
#endif /* DEBUG_LOCKS */

bool lock_held_by_me(struct lock *l)
Expand Down Expand Up @@ -112,6 +185,11 @@ void lock_caller(struct lock *l, const char *owner)
return;

lock_check(l);

if (try_lock(l))
return;
add_lock_request(l);

for (;;) {
if (try_lock_caller(l, owner))
break;
Expand All @@ -120,6 +198,8 @@ void lock_caller(struct lock *l, const char *owner)
barrier();
smt_medium();
}

remove_lock_request();
}

void unlock(struct lock *l)
Expand Down
5 changes: 5 additions & 0 deletions include/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ struct cpu_thread {
u32 sensor_attr;
u32 token;
bool dts_read_in_progress;

#ifdef DEBUG_LOCKS
/* The lock requested by this cpu, used for deadlock detection */
struct lock *requested_lock;
#endif
};

/* This global is set to 1 to allow secondaries to callin,
Expand Down

0 comments on commit 2d4c774

Please sign in to comment.