lockdep: check that no locks held at freeze time

We shouldn't try_to_freeze if locks are held.  Holding a lock can cause a
deadlock if the lock is later acquired in the suspend or hibernate path
(e.g.  by dpm).  Holding a lock can also cause a deadlock in the case of
cgroup_freezer if a lock is held inside a frozen cgroup that is later
acquired by a process outside that group.

[akpm@linux-foundation.org: export debug_check_no_locks_held]
Signed-off-by: Mandeep Singh Baines <msb@chromium.org>
Cc: Ben Chan <benchan@chromium.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Mandeep Singh Baines 2013-02-27 17:03:18 -08:00 committed by Linus Torvalds
parent c2c1b089b4
commit 6aa9707099
4 changed files with 14 additions and 12 deletions

View File

@ -51,7 +51,7 @@ struct task_struct;
extern void debug_show_all_locks(void); extern void debug_show_all_locks(void);
extern void debug_show_held_locks(struct task_struct *task); extern void debug_show_held_locks(struct task_struct *task);
extern void debug_check_no_locks_freed(const void *from, unsigned long len); extern void debug_check_no_locks_freed(const void *from, unsigned long len);
extern void debug_check_no_locks_held(struct task_struct *task); extern void debug_check_no_locks_held(void);
#else #else
static inline void debug_show_all_locks(void) static inline void debug_show_all_locks(void)
{ {
@ -67,7 +67,7 @@ debug_check_no_locks_freed(const void *from, unsigned long len)
} }
static inline void static inline void
debug_check_no_locks_held(struct task_struct *task) debug_check_no_locks_held(void)
{ {
} }
#endif #endif

View File

@ -3,6 +3,7 @@
#ifndef FREEZER_H_INCLUDED #ifndef FREEZER_H_INCLUDED
#define FREEZER_H_INCLUDED #define FREEZER_H_INCLUDED
#include <linux/debug_locks.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/atomic.h> #include <linux/atomic.h>
@ -48,6 +49,8 @@ extern void thaw_kernel_threads(void);
static inline bool try_to_freeze(void) static inline bool try_to_freeze(void)
{ {
if (!(current->flags & PF_NOFREEZE))
debug_check_no_locks_held();
might_sleep(); might_sleep();
if (likely(!freezing(current))) if (likely(!freezing(current)))
return false; return false;

View File

@ -835,7 +835,7 @@ void do_exit(long code)
/* /*
* Make sure we are holding no locks: * Make sure we are holding no locks:
*/ */
debug_check_no_locks_held(tsk); debug_check_no_locks_held();
/* /*
* We can do this unlocked here. The futex code uses this flag * We can do this unlocked here. The futex code uses this flag
* just to verify whether the pi state cleanup has been done * just to verify whether the pi state cleanup has been done

View File

@ -4088,7 +4088,7 @@ void debug_check_no_locks_freed(const void *mem_from, unsigned long mem_len)
} }
EXPORT_SYMBOL_GPL(debug_check_no_locks_freed); EXPORT_SYMBOL_GPL(debug_check_no_locks_freed);
static void print_held_locks_bug(struct task_struct *curr) static void print_held_locks_bug(void)
{ {
if (!debug_locks_off()) if (!debug_locks_off())
return; return;
@ -4097,22 +4097,21 @@ static void print_held_locks_bug(struct task_struct *curr)
printk("\n"); printk("\n");
printk("=====================================\n"); printk("=====================================\n");
printk("[ BUG: lock held at task exit time! ]\n"); printk("[ BUG: %s/%d still has locks held! ]\n",
current->comm, task_pid_nr(current));
print_kernel_ident(); print_kernel_ident();
printk("-------------------------------------\n"); printk("-------------------------------------\n");
printk("%s/%d is exiting with locks still held!\n", lockdep_print_held_locks(current);
curr->comm, task_pid_nr(curr));
lockdep_print_held_locks(curr);
printk("\nstack backtrace:\n"); printk("\nstack backtrace:\n");
dump_stack(); dump_stack();
} }
void debug_check_no_locks_held(struct task_struct *task) void debug_check_no_locks_held(void)
{ {
if (unlikely(task->lockdep_depth > 0)) if (unlikely(current->lockdep_depth > 0))
print_held_locks_bug(task); print_held_locks_bug();
} }
EXPORT_SYMBOL_GPL(debug_check_no_locks_held);
void debug_show_all_locks(void) void debug_show_all_locks(void)
{ {