All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] locking/lockdep: add debug_show_all_lock_holders()
@ 2022-08-11 11:32 Tetsuo Handa
  2022-08-11 19:00 ` Waiman Long
  0 siblings, 1 reply; 4+ messages in thread
From: Tetsuo Handa @ 2022-08-11 11:32 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Will Deacon, Waiman Long, Boqun Feng
  Cc: Thomas Gleixner, Shaokun Zhang, Sebastian Andrzej Siewior,
	Petr Mladek, Andrew Morton, Ben Dooks, Rasmus Villemoes,
	Luis Chamberlain, Xiaoming Ni, John Ogness, LKML, syzkaller-bugs

Currently, check_hung_uninterruptible_tasks() reports details of locks
held in the system. Also, lockdep_print_held_locks() does not report
details of locks held by a thread if that thread is in TASK_RUNNING state.
Several years of experience of debugging without vmcore tells me that
these limitations have been a barrier for understanding what went wrong
in syzbot's "INFO: task hung in" reports.

I initially thought that the cause of "INFO: task hung in" reports is
due to over-stressing. But I understood that over-stressing is unlikely.
I now consider that there likely is a deadlock/livelock bug where lockdep
cannot report as a deadlock when "INFO: task hung in" is reported.

A typical case is that thread-1 is waiting for something to happen (e.g.
wait_event_*()) with a lock held. When thread-2 tries to hold that lock
using e.g. mutex_lock(), check_hung_uninterruptible_tasks() reports that
thread-2 is hung and thread-1 is holding a lock which thread-2 is trying
to hold. But currently check_hung_uninterruptible_tasks() cannot report
the exact location of thread-1 which gives us an important hint for
understanding why thread-1 is holding that lock for so long period.

When check_hung_uninterruptible_tasks() reports a thread waiting for a
lock, it is important to report backtrace of threads which already held
that lock. Therefore, allow check_hung_uninterruptible_tasks() to report
the exact location of threads which is holding any lock.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
I wish that lockdep continues tracking locks even after something went
wrong (i.e. debug_locks remains 1), for recently I sometimes encounter
problems that disable lockdep during boot stage.

It would be noisy to report possibility of e.g. circular locking dependency
every time due to keeping debug_locks enabled. But tracking locks even
after something went wrong will help debug_show_all_lock_holders() to
survive problems during boot stage.

Changing debug_locks behavior is a future patch. For now, this patch alone
will help debugging Greg's usb.git#usb-testing tree which is generating
many "INFO: task hung in" reports.

 include/linux/debug_locks.h |  5 +++++
 kernel/hung_task.c          |  2 +-
 kernel/locking/lockdep.c    | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h
index dbb409d77d4f..0567d5ce5b4a 100644
--- a/include/linux/debug_locks.h
+++ b/include/linux/debug_locks.h
@@ -50,6 +50,7 @@ extern int debug_locks_off(void);
 #ifdef CONFIG_LOCKDEP
 extern void debug_show_all_locks(void);
 extern void debug_show_held_locks(struct task_struct *task);
+extern void debug_show_all_lock_holders(void);
 extern void debug_check_no_locks_freed(const void *from, unsigned long len);
 extern void debug_check_no_locks_held(void);
 #else
@@ -61,6 +62,10 @@ static inline void debug_show_held_locks(struct task_struct *task)
 {
 }
 
+static inline void debug_show_all_lock_holders(void)
+{
+}
+
 static inline void
 debug_check_no_locks_freed(const void *from, unsigned long len)
 {
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index bb2354f73ded..18e22bbb714f 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -205,7 +205,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
  unlock:
 	rcu_read_unlock();
 	if (hung_task_show_lock)
-		debug_show_all_locks();
+		debug_show_all_lock_holders();
 
 	if (hung_task_show_all_bt) {
 		hung_task_show_all_bt = false;
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 64a13eb56078..d06254108eb7 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -55,6 +55,7 @@
 #include <linux/rcupdate.h>
 #include <linux/kprobes.h>
 #include <linux/lockdep.h>
+#include <linux/sched/debug.h>
 
 #include <asm/sections.h>
 
@@ -6509,6 +6510,37 @@ void debug_show_all_locks(void)
 	pr_warn("=============================================\n\n");
 }
 EXPORT_SYMBOL_GPL(debug_show_all_locks);
+
+void debug_show_all_lock_holders(void)
+{
+	struct task_struct *g, *p;
+
+	if (unlikely(!debug_locks)) {
+		pr_warn("INFO: lockdep is turned off.\n");
+		return;
+	}
+	pr_warn("\nShowing all threads with locks held in the system:\n");
+
+	rcu_read_lock();
+	for_each_process_thread(g, p) {
+		if (!p->lockdep_depth)
+			continue;
+		/*
+		 * Assuming that the caller of this function is in a process
+		 * context without any locks held, skip current thread which is
+		 * holding only RCU read lock.
+		 */
+		if (p == current)
+			continue;
+		sched_show_task(p);
+		lockdep_print_held_locks(p);
+		touch_nmi_watchdog();
+		touch_all_softlockup_watchdogs();
+	}
+	rcu_read_unlock();
+	pr_warn("\n");
+	pr_warn("=============================================\n\n");
+}
 #endif
 
 /*
-- 
2.18.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] locking/lockdep: add debug_show_all_lock_holders()
  2022-08-11 11:32 [PATCH] locking/lockdep: add debug_show_all_lock_holders() Tetsuo Handa
@ 2022-08-11 19:00 ` Waiman Long
  2022-08-12 11:50   ` Tetsuo Handa
  0 siblings, 1 reply; 4+ messages in thread
From: Waiman Long @ 2022-08-11 19:00 UTC (permalink / raw)
  To: Tetsuo Handa, Peter Zijlstra, Ingo Molnar, Will Deacon,
	Boqun Feng
  Cc: Thomas Gleixner, Shaokun Zhang, Sebastian Andrzej Siewior,
	Petr Mladek, Andrew Morton, Ben Dooks, Rasmus Villemoes,
	Luis Chamberlain, Xiaoming Ni, John Ogness, LKML, syzkaller-bugs

On 8/11/22 07:32, Tetsuo Handa wrote:
> Currently, check_hung_uninterruptible_tasks() reports details of locks
> held in the system. Also, lockdep_print_held_locks() does not report
> details of locks held by a thread if that thread is in TASK_RUNNING state.
> Several years of experience of debugging without vmcore tells me that
> these limitations have been a barrier for understanding what went wrong
> in syzbot's "INFO: task hung in" reports.
>
> I initially thought that the cause of "INFO: task hung in" reports is
> due to over-stressing. But I understood that over-stressing is unlikely.
> I now consider that there likely is a deadlock/livelock bug where lockdep
> cannot report as a deadlock when "INFO: task hung in" is reported.
>
> A typical case is that thread-1 is waiting for something to happen (e.g.
> wait_event_*()) with a lock held. When thread-2 tries to hold that lock
> using e.g. mutex_lock(), check_hung_uninterruptible_tasks() reports that
> thread-2 is hung and thread-1 is holding a lock which thread-2 is trying
> to hold. But currently check_hung_uninterruptible_tasks() cannot report
> the exact location of thread-1 which gives us an important hint for
> understanding why thread-1 is holding that lock for so long period.
>
> When check_hung_uninterruptible_tasks() reports a thread waiting for a
> lock, it is important to report backtrace of threads which already held
> that lock. Therefore, allow check_hung_uninterruptible_tasks() to report
> the exact location of threads which is holding any lock.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> I wish that lockdep continues tracking locks even after something went
> wrong (i.e. debug_locks remains 1), for recently I sometimes encounter
> problems that disable lockdep during boot stage.
>
> It would be noisy to report possibility of e.g. circular locking dependency
> every time due to keeping debug_locks enabled. But tracking locks even
> after something went wrong will help debug_show_all_lock_holders() to
> survive problems during boot stage.
>
> Changing debug_locks behavior is a future patch. For now, this patch alone
> will help debugging Greg's usb.git#usb-testing tree which is generating
> many "INFO: task hung in" reports.
>
>   include/linux/debug_locks.h |  5 +++++
>   kernel/hung_task.c          |  2 +-
>   kernel/locking/lockdep.c    | 32 ++++++++++++++++++++++++++++++++
>   3 files changed, 38 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h
> index dbb409d77d4f..0567d5ce5b4a 100644
> --- a/include/linux/debug_locks.h
> +++ b/include/linux/debug_locks.h
> @@ -50,6 +50,7 @@ extern int debug_locks_off(void);
>   #ifdef CONFIG_LOCKDEP
>   extern void debug_show_all_locks(void);
>   extern void debug_show_held_locks(struct task_struct *task);
> +extern void debug_show_all_lock_holders(void);
>   extern void debug_check_no_locks_freed(const void *from, unsigned long len);
>   extern void debug_check_no_locks_held(void);
>   #else
> @@ -61,6 +62,10 @@ static inline void debug_show_held_locks(struct task_struct *task)
>   {
>   }
>   
> +static inline void debug_show_all_lock_holders(void)
> +{
> +}
> +
>   static inline void
>   debug_check_no_locks_freed(const void *from, unsigned long len)
>   {
> diff --git a/kernel/hung_task.c b/kernel/hung_task.c
> index bb2354f73ded..18e22bbb714f 100644
> --- a/kernel/hung_task.c
> +++ b/kernel/hung_task.c
> @@ -205,7 +205,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
>    unlock:
>   	rcu_read_unlock();
>   	if (hung_task_show_lock)
> -		debug_show_all_locks();
> +		debug_show_all_lock_holders();
>   
>   	if (hung_task_show_all_bt) {
>   		hung_task_show_all_bt = false;
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 64a13eb56078..d06254108eb7 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -55,6 +55,7 @@
>   #include <linux/rcupdate.h>
>   #include <linux/kprobes.h>
>   #include <linux/lockdep.h>
> +#include <linux/sched/debug.h>
>   
>   #include <asm/sections.h>
>   
> @@ -6509,6 +6510,37 @@ void debug_show_all_locks(void)
>   	pr_warn("=============================================\n\n");
>   }
>   EXPORT_SYMBOL_GPL(debug_show_all_locks);
> +
> +void debug_show_all_lock_holders(void)
> +{
> +	struct task_struct *g, *p;
> +
> +	if (unlikely(!debug_locks)) {
> +		pr_warn("INFO: lockdep is turned off.\n");
> +		return;
> +	}
> +	pr_warn("\nShowing all threads with locks held in the system:\n");
> +
> +	rcu_read_lock();
> +	for_each_process_thread(g, p) {
> +		if (!p->lockdep_depth)
> +			continue;
> +		/*
> +		 * Assuming that the caller of this function is in a process
> +		 * context without any locks held, skip current thread which is
> +		 * holding only RCU read lock.
> +		 */
> +		if (p == current)
> +			continue;
> +		sched_show_task(p);
> +		lockdep_print_held_locks(p);
> +		touch_nmi_watchdog();
> +		touch_all_softlockup_watchdogs();
> +	}
> +	rcu_read_unlock();
> +	pr_warn("\n");
> +	pr_warn("=============================================\n\n");
> +}
>   #endif

Your debug_show_all_lock_holders() is very similar to 
debug_show_all_locks(). Maybe you can combine the 2 into a common helper 
instead duplicating the code.

BTW, this function will produce much more verbose output. Typically how 
many line of debug outputs are going to be dumped to the console by 
calling this function?

Cheers,
Longman


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] locking/lockdep: add debug_show_all_lock_holders()
  2022-08-11 19:00 ` Waiman Long
@ 2022-08-12 11:50   ` Tetsuo Handa
  2022-08-30  9:57     ` Tetsuo Handa
  0 siblings, 1 reply; 4+ messages in thread
From: Tetsuo Handa @ 2022-08-12 11:50 UTC (permalink / raw)
  To: Waiman Long, Peter Zijlstra, Ingo Molnar, Will Deacon, Boqun Feng
  Cc: Thomas Gleixner, Shaokun Zhang, Sebastian Andrzej Siewior,
	Petr Mladek, Andrew Morton, Ben Dooks, Rasmus Villemoes,
	Luis Chamberlain, Xiaoming Ni, John Ogness, LKML, syzkaller-bugs

On 2022/08/11 20:32, Tetsuo Handa wrote:
> I wish that lockdep continues tracking locks even after something went
> wrong (i.e. debug_locks remains 1), for recently I sometimes encounter
> problems that disable lockdep during boot stage.
>
> It would be noisy to report possibility of e.g. circular locking dependency
> every time due to keeping debug_locks enabled. But tracking locks even
> after something went wrong will help debug_show_all_lock_holders() to
> survive problems during boot stage.

I forgot to write. I'm not expecting lockdep to report the same problem forever.
Reporting possibility of each problem pattern (e.g. circular locking dependency)
up to once, by using cmpxchg() inside reporting functions that call printk(),
would be enough.

I'm expecting lockdep to continue working without calling printk() even after
one of problem patterns (e.g. circular locking dependency) was printk()ed, so that
debug_show_all_locks()/debug_show_all_lock_holders() can call printk() when needed.

>
> Changing debug_locks behavior is a future patch. For now, this patch alone
> will help debugging Greg's usb.git#usb-testing tree which is generating
> many "INFO: task hung in" reports.
>



On 2022/08/12 4:00, Waiman Long wrote:
> 
> BTW, this function will produce much more verbose output. Typically how many line
> of debug outputs are going to be dumped to the console by calling this function?

Regarding amount of output, I can't estimate lines printed because it depends on how
many threads are holding locks. Thus, here are some examples obtained by this patch.

  https://syzkaller.appspot.com/text?tag=CrashReport&x=17c78aba080000
  https://syzkaller.appspot.com/text?tag=CrashReport&x=13219e6b080000
  https://syzkaller.appspot.com/text?tag=CrashReport&x=166c402d080000
  https://syzkaller.appspot.com/text?tag=CrashReport&x=15c6ff73080000

We can introduce flags like panic_printk variable in kernel/panic.c if amount of
output (or latency by output) turns out to be a problem.

I guess that the most significant switch would be console_verbose() in
check_hung_task() which forces synchronous output (causing latency problem
if there are many lines to output). If printk() messages are not printed to
consoles, latency by debug_show_all_lock_holders() will be negligible.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] locking/lockdep: add debug_show_all_lock_holders()
  2022-08-12 11:50   ` Tetsuo Handa
@ 2022-08-30  9:57     ` Tetsuo Handa
  0 siblings, 0 replies; 4+ messages in thread
From: Tetsuo Handa @ 2022-08-30  9:57 UTC (permalink / raw)
  To: Waiman Long, Peter Zijlstra, Ingo Molnar, Will Deacon, Boqun Feng
  Cc: Thomas Gleixner, Shaokun Zhang, Sebastian Andrzej Siewior,
	Petr Mladek, Andrew Morton, Ben Dooks, Rasmus Villemoes,
	Luis Chamberlain, Xiaoming Ni, John Ogness, LKML, syzkaller-bugs

Peter and Waiman, can we apply this patch?

On 2022/08/12 20:50, Tetsuo Handa wrote:
> On 2022/08/11 20:32, Tetsuo Handa wrote:
>> Changing debug_locks behavior is a future patch. For now, this patch alone
>> will help debugging Greg's usb.git#usb-testing tree which is generating
>> many "INFO: task hung in" reports.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-08-30  9:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-11 11:32 [PATCH] locking/lockdep: add debug_show_all_lock_holders() Tetsuo Handa
2022-08-11 19:00 ` Waiman Long
2022-08-12 11:50   ` Tetsuo Handa
2022-08-30  9:57     ` Tetsuo Handa

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.