Skip to contentSkip to conversation

lockdep: Add nr_save_trace_invocations counter

PATCH In review John Kacur 5 participants 20 messages Apr 22, 2010 – May 7, 2010

lore
Share & tools
lore

John Kacur

author
#0 lore raw

PATCH 1/1 lockdep: Add nr_save_trace_invocations counter

NOT FOR INCLUSION

I created this patch as a result of Peter Zilstra's request to get more info from lockdep. This patch is not for inclusion, at least in its present form, because it adds some redunant info to /proc/lockdep_stats

However, some of the fields are new, and it is worth examining, and / or applying if you are looking at the MAX_STACK_TRACE_ENTRIES too big problem.

I generated this patch against a recent tip/master but it applies without conflicts to the latest rt kernel as well. Comments are welcome, in fact they are appreciated.

Quoted 1 line

From 5181c0296dd1549e4e706ff25a4cd81a1d90137d Mon Sep 17 00:00:00 2001

From: John Kacur <[email protected]>
Date: Thu, 22 Apr 2010 17:02:42 +0200
Subject: [PATCH] lockdep: Add nr_save_trace_invocations counter

Add the nr_save_trace_invocations counter which counts the number of time save_trace() is invoked when relevant for trace enteries.

This means, those invocations from mark_lock() and add_lock_to_list()

When called from mark_lock() we break it down into LOCKSTATE categories.

---
 kernel/lockdep.c           |   20 ++++++++++++++++++++
 kernel/lockdep_internals.h |    2 ++
 kernel/lockdep_proc.c      |   23 +++++++++++++++++++++++
 3 files changed, 45 insertions(+), 0 deletions(-)
kernel/lockdep.c +20 -0
index 78325f8..f921576 100644
@@ -371,6 +371,10 @@ static int verbose(struct lock_class *class)
 unsigned long nr_stack_trace_entries;
 static unsigned long stack_trace[MAX_STACK_TRACE_ENTRIES];
 
+/* Calls to save_trace() from mark_lock() and add_lock_to_list() only*/
+unsigned long nr_save_trace_invocations;
+unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];
+
 static int save_trace(struct stack_trace *trace)
 {
 	trace->nr_entries = 0;
@@ -410,6 +414,19 @@ static int save_trace(struct stack_trace *trace)
 	return 1;
 }
 
+/*
+ * This function is only called from mark_lock() and add_lock_to_list()
+ * which are only called when holding the graph_lock. This counter
+ * piggybacks off of that lock
+ */
+static void inc_save_trace_invocations(enum lock_usage_bit new_bit)
+{
+	nr_save_trace_invocations++;
+	if (WARN_ON(new_bit >= LOCK_USAGE_STATES))
+		return;
+	nr_save_trace_invocations_type[new_bit]++;
+}
+
 unsigned int nr_hardirq_chains;
 unsigned int nr_softirq_chains;
 unsigned int nr_process_chains;
@@ -449,6 +466,7 @@ static const char *usage_str[] =
 #define LOCKDEP_STATE(__STATE) __USAGE(__STATE)
 #include "lockdep_states.h"
 #undef LOCKDEP_STATE
+#undef __USAGE
 	[LOCK_USED] = "INITIAL USE",
 };
 
@@ -816,6 +834,7 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
+	nr_save_trace_invocations++;
 	if (!save_trace(&entry->trace))
 		return 0;
 
@@ -2615,6 +2634,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
 
 	hlock_class(this)->usage_mask |= new_mask;
 
+	inc_save_trace_invocations(new_bit);
 	if (!save_trace(hlock_class(this)->usage_traces + new_bit))
 		return 0;
 
kernel/lockdep_internals.h +2 -0
index 8d7d4b6..6149358 100644
@@ -84,6 +84,8 @@ extern unsigned long nr_list_entries;
 extern unsigned long nr_lock_chains;
 extern int nr_chain_hlocks;
 extern unsigned long nr_stack_trace_entries;
+extern unsigned long nr_save_trace_invocations;
+extern unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];
 
 extern unsigned int nr_hardirq_chains;
 extern unsigned int nr_softirq_chains;
kernel/lockdep_proc.c +23 -0
index 59b76c8..ef5f372 100644
@@ -215,8 +215,24 @@ static void lockdep_stats_debug_show(struct seq_file *m)
 #endif
 }
 
+#define __USAGE(__STATE)	\
+[LOCK_USED_IN_##__STATE] = "LOCK_USED_IN_"__stringify(__STATE),	\
+[LOCK_ENABLED_##__STATE] = "LOCK_ENABLED_"__stringify(__STATE), \
+[LOCK_USED_IN_##__STATE##_READ] = "LOCK_USED_IN_"__stringify(__STATE)"_READ", \
+[LOCK_ENABLED_##__STATE##_READ] = "LOCK_ENABLED_"__stringify(__STATE)"_READ",
+
+static const char *lockstate_tostr[] =
+{
+#define LOCKDEP_STATE(__STATE) __USAGE(__STATE)
+#include "lockdep_states.h"
+#undef LOCKDEP_STATE
+#undef __USAGE
+	[LOCK_USED] = "LOCK_USED",
+};
+
 static int lockdep_stats_show(struct seq_file *m, void *v)
 {
+	int bit;
 	struct lock_class *class;
 	unsigned long nr_unused = 0, nr_uncategorized = 0,
 		      nr_irq_safe = 0, nr_irq_unsafe = 0,
@@ -307,6 +323,13 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
 			nr_process_chains);
 	seq_printf(m, " stack-trace entries:           %11lu [max: %lu]\n",
 			nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
+	seq_printf(m, " stack-trace invocations: %lu\n",
+			nr_save_trace_invocations);
+
+	for (bit=0; bit < LOCK_USAGE_STATES; bit++)
+		seq_printf(m, "\t%s: %lu\n", lockstate_tostr[bit],
+			       nr_save_trace_invocations_type[bit]);	
+
 	seq_printf(m, " combined max dependencies:     %11u\n",
 			(nr_hardirq_chains + 1) *
 			(nr_softirq_chains + 1) *
Signature · 1 line
1.6.6.1
#1 lore raw
Quoted 19 lines · John Kacur

NOT FOR INCLUSION

I created this patch as a result of Peter Zilstra's request to get more info from lockdep. This patch is not for inclusion, at least in its present form, because it adds some redunant info to /proc/lockdep_stats

However, some of the fields are new, and it is worth examining, and / or applying if you are looking at the MAX_STACK_TRACE_ENTRIES too big problem.

I generated this patch against a recent tip/master but it applies without conflicts to the latest rt kernel as well. Comments are welcome, in fact they are appreciated.

Quoted 1 line

From 5181c0296dd1549e4e706ff25a4cd81a1d90137d Mon Sep 17 00:00:00 2001

From: John Kacur <[email protected]>
Date: Thu, 22 Apr 2010 17:02:42 +0200
Subject: [PATCH] lockdep: Add nr_save_trace_invocations counter

Quoted 8 lines

Add the nr_save_trace_invocations counter which counts the number of time save_trace() is invoked when relevant for trace enteries.

This means, those invocations from mark_lock() and add_lock_to_list()

When called from mark_lock() we break it down into LOCKSTATE categories.

Just take a rough look at it. I don't think this can give more info.

kernel/lockdep.c static int verbose(struct lock_class *class) · line 375

Quoted 2 lines
kernel/lockdep.c +2 -0
+/* Calls to save_trace() from mark_lock() and add_lock_to_list() only*/
+unsigned long nr_save_trace_invocations;

It will equal to nr_list_entries.

Quoted 1 line

+unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];

And each item in this array will equal to nr_hardirq_[un]safe, nr_softirq_[un]safe and such things under lockdep_stats_show(). Right?

Thanks,
Yong

Quoted 112 lines
kernel/lockdep.c +17 -0
+
 static int save_trace(struct stack_trace *trace)
 {
 	trace->nr_entries = 0;
@@ -410,6 +414,19 @@ static int save_trace(struct stack_trace *trace)
 	return 1;
 }
 
+/*
+ * This function is only called from mark_lock() and add_lock_to_list()
+ * which are only called when holding the graph_lock. This counter
+ * piggybacks off of that lock
+ */
+static void inc_save_trace_invocations(enum lock_usage_bit new_bit)
+{
+	nr_save_trace_invocations++;
+	if (WARN_ON(new_bit >= LOCK_USAGE_STATES))
+		return;
+	nr_save_trace_invocations_type[new_bit]++;
+}
+
 unsigned int nr_hardirq_chains;
 unsigned int nr_softirq_chains;
 unsigned int nr_process_chains;
@@ -449,6 +466,7 @@ static const char *usage_str[] =
 #define LOCKDEP_STATE(__STATE) __USAGE(__STATE)
 #include "lockdep_states.h"
 #undef LOCKDEP_STATE
+#undef __USAGE
 	[LOCK_USED] = "INITIAL USE",
 };
 
@@ -816,6 +834,7 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
+	nr_save_trace_invocations++;
 	if (!save_trace(&entry->trace))
 		return 0;
 
@@ -2615,6 +2634,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
 
 	hlock_class(this)->usage_mask |= new_mask;
 
+	inc_save_trace_invocations(new_bit);
 	if (!save_trace(hlock_class(this)->usage_traces + new_bit))
 		return 0;
 
kernel/lockdep_internals.h +2 -0
index 8d7d4b6..6149358 100644
@@ -84,6 +84,8 @@ extern unsigned long nr_list_entries;
 extern unsigned long nr_lock_chains;
 extern int nr_chain_hlocks;
 extern unsigned long nr_stack_trace_entries;
+extern unsigned long nr_save_trace_invocations;
+extern unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];
 
 extern unsigned int nr_hardirq_chains;
 extern unsigned int nr_softirq_chains;
kernel/lockdep_proc.c +23 -0
index 59b76c8..ef5f372 100644
@@ -215,8 +215,24 @@ static void lockdep_stats_debug_show(struct seq_file *m)
 #endif
 }
 
+#define __USAGE(__STATE)	\
+[LOCK_USED_IN_##__STATE] = "LOCK_USED_IN_"__stringify(__STATE),	\
+[LOCK_ENABLED_##__STATE] = "LOCK_ENABLED_"__stringify(__STATE), \
+[LOCK_USED_IN_##__STATE##_READ] = "LOCK_USED_IN_"__stringify(__STATE)"_READ", \
+[LOCK_ENABLED_##__STATE##_READ] = "LOCK_ENABLED_"__stringify(__STATE)"_READ",
+
+static const char *lockstate_tostr[] =
+{
+#define LOCKDEP_STATE(__STATE) __USAGE(__STATE)
+#include "lockdep_states.h"
+#undef LOCKDEP_STATE
+#undef __USAGE
+	[LOCK_USED] = "LOCK_USED",
+};
+
 static int lockdep_stats_show(struct seq_file *m, void *v)
 {
+	int bit;
 	struct lock_class *class;
 	unsigned long nr_unused = 0, nr_uncategorized = 0,
 		      nr_irq_safe = 0, nr_irq_unsafe = 0,
@@ -307,6 +323,13 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
 			nr_process_chains);
 	seq_printf(m, " stack-trace entries:           %11lu [max: %lu]\n",
 			nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
+	seq_printf(m, " stack-trace invocations: %lu\n",
+			nr_save_trace_invocations);
+
+	for (bit=0; bit < LOCK_USAGE_STATES; bit++)
+		seq_printf(m, "\t%s: %lu\n", lockstate_tostr[bit],
+			       nr_save_trace_invocations_type[bit]);	
+
 	seq_printf(m, " combined max dependencies:     %11u\n",
 			(nr_hardirq_chains + 1) *
 			(nr_softirq_chains + 1) *
Signature · 7 lines
1.6.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
#2 lore raw
Quoted 5 lines · Yong Zhang
Quoted 2 lines

+unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];

And each item in this array will equal to nr_hardirq_[un]safe, nr_softirq_[un]safe and such things under lockdep_stats_show(). Right?

Well, the stats for RECLAIM_FS as missing, so at the very least we need to re-write lockdep_stats_show() to use the lockdep_states.h magic.

#4 lore raw
In reply to Yong Zhang
Quoted 36 lines · Yong Zhang
Quoted 19 lines · John Kacur

NOT FOR INCLUSION

I created this patch as a result of Peter Zilstra's request to get more info from lockdep. This patch is not for inclusion, at least in its present form, because it adds some redunant info to /proc/lockdep_stats

However, some of the fields are new, and it is worth examining, and / or applying if you are looking at the MAX_STACK_TRACE_ENTRIES too big problem.

I generated this patch against a recent tip/master but it applies without conflicts to the latest rt kernel as well. Comments are welcome, in fact they are appreciated.

Quoted 1 line

From 5181c0296dd1549e4e706ff25a4cd81a1d90137d Mon Sep 17 00:00:00 2001

From: John Kacur <[email protected]>
Date: Thu, 22 Apr 2010 17:02:42 +0200
Subject: [PATCH] lockdep: Add nr_save_trace_invocations counter

Quoted 8 lines

Add the nr_save_trace_invocations counter which counts the number of time save_trace() is invoked when relevant for trace enteries.

This means, those invocations from mark_lock() and add_lock_to_list()

When called from mark_lock() we break it down into LOCKSTATE categories.

Just take a rough look at it. I don't think this can give more info.

kernel/lockdep.c static int verbose(struct lock_class *class) · line 375

Quoted 2 lines
kernel/lockdep.c +2 -0
+/* Calls to save_trace() from mark_lock() and add_lock_to_list() only*/
+unsigned long nr_save_trace_invocations;

It will equal to nr_list_entries.

Quoted 8 lines
Quoted 1 line

+unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];

And each item in this array will equal to nr_hardirq_[un]safe, nr_softirq_[un]safe and such things under lockdep_stats_show(). Right?

Thanks,
Yong

Hi Yong

Some context here - Peter asked me to see if we could get some more detailed stats on why some configurations reach the MAX_STACK_TRACE_ENTRIES limit - whether the limit was really too low for some circumstances, or whether we were counting somethings unnecessarily.

In any case, I stamped a big NOT FOR INCLUSION on my mail, because I noticed that somethings were redundant - albeit, obtained in a slightly different manner, however, not everything is redundant.

In particular, nr_save_trace_invocations is NOT equal to nr_list_entries. You will see that reported in /proc/lockdep_stats as
direct dependencies: 8752 [max: 16384]
I have
stack-trace invocations: 10888
from the same run.

Still trying to figure out what the meaning is of that though to be honest.

Here is a portion of the lockdep_stats, with all of the new fields and the redundant ones.

stack-trace invocations: 10888

	LOCK_USED_IN_HARDIRQ: 15
	LOCK_USED_IN_HARDIRQ_READ: 0
	LOCK_ENABLED_HARDIRQ: 543
	LOCK_ENABLED_HARDIRQ_READ: 28
	LOCK_USED_IN_SOFTIRQ: 0
	LOCK_USED_IN_SOFTIRQ_READ: 0
	LOCK_ENABLED_SOFTIRQ: 543
	LOCK_ENABLED_SOFTIRQ_READ: 28
	LOCK_USED_IN_RECLAIM_FS: 5
	LOCK_USED_IN_RECLAIM_FS_READ: 0
	LOCK_ENABLED_RECLAIM_FS: 95
	LOCK_ENABLED_RECLAIM_FS_READ: 8
	LOCK_USED: 871

combined max dependencies: 139841
hardirq-safe locks: 15
hardirq-unsafe locks: 543
softirq-safe locks: 0
softirq-unsafe locks: 543
irq-safe locks: 15
irq-unsafe locks: 543
hardirq-read-safe locks: 0
hardirq-read-unsafe locks: 28
softirq-read-safe locks: 0
softirq-read-unsafe locks: 28
irq-read-safe locks: 0
irq-read-unsafe locks: 28

So, you see that all of the reclaim fields are new,
LOCK_USED_IN_RECLAIM_FS: 5 LOCK_USED_IN_RECLAIM_FS_READ: 0
LOCK_ENABLED_RECLAIM_FS: 95 LOCK_ENABLED_RECLAIM_FS_READ: 8

I can create a patch for inclusion that adds the reclaim fields, the question is, is the nr_save_trace_invocations a useful stat for us or not?

Thanks

#5 lore raw
Quoted 9 lines · John Kacur

Some context here - Peter asked me to see if we could get some more detailed stats on why some configurations reach the MAX_STACK_TRACE_ENTRIES limit - whether the limit was really too low for some circumstances, or whether we were counting somethings unnecessarily.

In any case, I stamped a big NOT FOR INCLUSION on my mail, because I noticed that somethings were redundant - albeit, obtained in a slightly different manner, however, not everything is redundant.

Quoted 6 lines

In particular, nr_save_trace_invocations is NOT equal to nr_list_entries. You will see that reported in /proc/lockdep_stats as
direct dependencies: 8752 [max: 16384]
I have
stack-trace invocations: 10888
from the same run.

I missed that nr_save_trace_invocations is also increased in inc_save_trace_invocations().
So nr_save_trace_invocations = nr_list_entries + sum of nr_save_trace_invocations_type[].

Quoted 35 lines

Still trying to figure out what the meaning is of that though to be honest.

Here is a portion of the lockdep_stats, with all of the new fields and the redundant ones.

stack-trace invocations: 10888

	LOCK_USED_IN_HARDIRQ: 15
	LOCK_USED_IN_HARDIRQ_READ: 0
	LOCK_ENABLED_HARDIRQ: 543
	LOCK_ENABLED_HARDIRQ_READ: 28
	LOCK_USED_IN_SOFTIRQ: 0
	LOCK_USED_IN_SOFTIRQ_READ: 0
	LOCK_ENABLED_SOFTIRQ: 543
	LOCK_ENABLED_SOFTIRQ_READ: 28
	LOCK_USED_IN_RECLAIM_FS: 5
	LOCK_USED_IN_RECLAIM_FS_READ: 0
	LOCK_ENABLED_RECLAIM_FS: 95
	LOCK_ENABLED_RECLAIM_FS_READ: 8
	LOCK_USED: 871

combined max dependencies: 139841
hardirq-safe locks: 15
hardirq-unsafe locks: 543
softirq-safe locks: 0
softirq-unsafe locks: 543
irq-safe locks: 15
irq-unsafe locks: 543
hardirq-read-safe locks: 0
hardirq-read-unsafe locks: 28
softirq-read-safe locks: 0
softirq-read-unsafe locks: 28
irq-read-safe locks: 0
irq-read-unsafe locks: 28

Quoted 5 lines

So, you see that all of the reclaim fields are new,
LOCK_USED_IN_RECLAIM_FS: 5 LOCK_USED_IN_RECLAIM_FS_READ: 0
LOCK_ENABLED_RECLAIM_FS: 95 LOCK_ENABLED_RECLAIM_FS_READ: 8

Yes, indeed, data in lockdep_stats_show() is out of time.
So as Peter has said in another thread, we should add sample for RECLAIM_FS.

Quoted 3 lines

I can create a patch for inclusion that adds the reclaim fields, the question is, is the nr_save_trace_invocations a useful stat for us or not?

Actually it's just a summation of the samples.
I don't think it's necessary.

Thanks,
Yong

#3 lore raw
In reply to Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Yong Zhang
Quoted 2 lines

+unsigned long nr_save_trace_invocations_type[LOCK_USAGE_STATES];

And each item in this array will equal to nr_hardirq_[un]safe, nr_softirq_[un]safe and such things under lockdep_stats_show(). Right?

Well, the stats for RECLAIM_FS as missing, so at the very least we need to re-write lockdep_stats_show() to use the lockdep_states.h magic.

Yeah, it's a good start point and will keep thing unifying in the future.

Thanks,
Yong

#6 lore raw
In reply to John Kacur
Quoted 12 lines · John Kacur

direct dependencies: 8752 [max: 16384]
I have
stack-trace invocations: 10888
from the same run.

Still trying to figure out what the meaning is of that though to be honest.

Here is a portion of the lockdep_stats, with all of the new fields and the redundant ones.

stack-trace invocations: 10888

Quoted 13 lines
        LOCK_USED_IN_HARDIRQ: 15
        LOCK_USED_IN_HARDIRQ_READ: 0
        LOCK_ENABLED_HARDIRQ: 543
        LOCK_ENABLED_HARDIRQ_READ: 28
        LOCK_USED_IN_SOFTIRQ: 0
        LOCK_USED_IN_SOFTIRQ_READ: 0
        LOCK_ENABLED_SOFTIRQ: 543
        LOCK_ENABLED_SOFTIRQ_READ: 28
        LOCK_USED_IN_RECLAIM_FS: 5
        LOCK_USED_IN_RECLAIM_FS_READ: 0
        LOCK_ENABLED_RECLAIM_FS: 95
        LOCK_ENABLED_RECLAIM_FS_READ: 8
        LOCK_USED: 871 

8752+871+8+95+5+28+543+28+543+15=10888

So you get a stack-trace for each direct-dependency, and you get a stack-trace for each LOCK_state, the sum seems to match the total invocations.

Non of these numbers look strange..

#7 lore raw
Quoted 27 lines · Peter Zijlstra
Quoted 11 lines · John Kacur

direct dependencies:                  8752 [max: 16384] I have
stack-trace invocations: 10888
from the same run.

Still trying to figure out what the meaning is of that though to be honest.

Here is a portion of the lockdep_stats, with all of the new fields and the redundant ones.

Quoted 14 lines

stack-trace invocations: 10888
        LOCK_USED_IN_HARDIRQ: 15
        LOCK_USED_IN_HARDIRQ_READ: 0
        LOCK_ENABLED_HARDIRQ: 543
        LOCK_ENABLED_HARDIRQ_READ: 28
        LOCK_USED_IN_SOFTIRQ: 0
        LOCK_USED_IN_SOFTIRQ_READ: 0
        LOCK_ENABLED_SOFTIRQ: 543
        LOCK_ENABLED_SOFTIRQ_READ: 28
        LOCK_USED_IN_RECLAIM_FS: 5
        LOCK_USED_IN_RECLAIM_FS_READ: 0
        LOCK_ENABLED_RECLAIM_FS: 95
        LOCK_ENABLED_RECLAIM_FS_READ: 8
        LOCK_USED: 871

Quoted 8 lines

8752+871+8+95+5+28+543+28+543+15=10888

So you get a stack-trace for each direct-dependency, and you get a stack-trace for each LOCK_state, the sum seems to match the total invocations.

Non of these numbers look strange..

As I told Peter privately the laptop that triggered the MAX_STACK_TRACE_ENTRIES every time, has met an
unfortunate early demise. However, I think it was the config - not the hardware. On this machine where the above
numbers come from, I believe I have less debug options configured - but it is running the exact same kernel as
the laptop was. (2.6.33.2-rt13)
--
To unsubscribe from this list: send the line "unsubscribe linux-rt-users" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw
Quoted 9 lines · John Kacur
Quoted 8 lines

8752+871+8+95+5+28+543+28+543+15=10888

So you get a stack-trace for each direct-dependency, and you get a stack-trace for each LOCK_state, the sum seems to match the total invocations.

Non of these numbers look strange..

Quoted 7 lines

As I told Peter privately the laptop that triggered the MAX_STACK_TRACE_ENTRIES every time, has met an
unfortunate early demise. However, I think it was the config - not the hardware. On this machine where the above
numbers come from, I believe I have less debug options configured - but it is running the exact same kernel as
the laptop was. (2.6.33.2-rt13)

Through a rough computation:
MAX_STACK_TRACE_ENTRIES/10888 = 24
That means the average stack deepth is about 24.
So I'm thinking if we can take a check on the biggest deepth? Could this make sense?

Thanks,
Yong

#9 lore raw
Quoted 18 lines · Yong Zhang
Quoted 9 lines · John Kacur
Quoted 8 lines

8752+871+8+95+5+28+543+28+543+15=10888

So you get a stack-trace for each direct-dependency, and you get a stack-trace for each LOCK_state, the sum seems to match the total invocations.

Non of these numbers look strange..

Quoted 7 lines

As I told Peter privately the laptop that triggered the MAX_STACK_TRACE_ENTRIES every time, has met an
unfortunate early demise. However, I think it was the config - not the hardware. On this machine where the above
numbers come from, I believe I have less debug options configured - but it is running the exact same kernel as
the laptop was. (2.6.33.2-rt13)

Quoted 6 lines

Through a rough computation:
MAX_STACK_TRACE_ENTRIES/10888 = 24
That means the average stack deepth is about 24.
So I'm thinking if we can take a check on the biggest deepth? Could this make sense?

Hi Yong, yes that makes sense, I'll see if I can provide a patch for that too.

Thanks

#10 lore raw

PATCH 1/1 lockdep: reduce stack_trace usage

In reply to John Kacur
Quoted 10 lines · John Kacur
Quoted 2 lines

Non of these numbers look strange..

As I told Peter privately the laptop that triggered the MAX_STACK_TRACE_ENTRIES every time, has met an
unfortunate early demise. However, I think it was the config - not the hardware. On this machine where the above
numbers come from, I believe I have less debug options configured - but it is running the exact same kernel as
the laptop was. (2.6.33.2-rt13)

Hi John,

(checking mail at home).
I find some place which can be hacked. Below is the patch. But I don't even compile it. Can you test it to see if it can smooth your problem.

---cut here ---

Quoted 1 line

From 6b9d513b7c417c0805ef0acc1cb3227bddba0889 Mon Sep 17 00:00:00 2001

From: Yong Zhang <[email protected]>
Date: Fri, 23 Apr 2010 21:13:54 +0800
Subject: [PATCH] lockdep: reduce stack_trace usage

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry. But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.
Using one copy of stack_trace instead.

Cc Peter Zijlstra <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc David S. Miller <[email protected]>
---
 kernel/lockdep.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)
kernel/lockdep.c +12 -8
index 2594e1c..097d5fb 100644
@@ -818,7 +818,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -829,11 +830,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1635,7 +1634,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
@@ -1694,14 +1693,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1732,6 +1731,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
 	struct held_lock *hlock;
+	struct stack_trace trace;
 
 	/*
 	 * Debugging checks.
@@ -1748,6 +1748,9 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 			curr->held_locks[depth-1].irq_context)
 		goto out_bug;
 
+	if (!save_trace(&trace))
+		return 0;
+
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth-1;
@@ -1756,7 +1759,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
Signature · 1 line
1.6.3.3
#11 lore raw

Re: [PATCH] lockdep: reduce stack_trace usage

Quoted 13 lines · Yong Zhang

Hi John,

(checking mail at home).
I find some place which can be hacked. Below is the patch. But I don't even compile it. Can you test it to see if it can smooth your problem.

---cut here ---

Quoted 1 line

From 6b9d513b7c417c0805ef0acc1cb3227bddba0889 Mon Sep 17 00:00:00 2001

From: Yong Zhang <[email protected]>
Date: Fri, 23 Apr 2010 21:13:54 +0800
Subject: [PATCH] lockdep: reduce stack_trace usage

Quoted 8 lines

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry. But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.
Using one copy of stack_trace instead.

With the following test patch running on my machine:

Quoted 1 line

cat /proc/lockdep_stats

lock-classes: 564 [max: 8191]
direct dependencies: 2626 [max: 16384]
indirect dependencies: 5951
all direct dependencies: 48226
dependency chains: 2576 [max: 32768]
dependency chain hlocks: 6740 [max: 163840]
in-hardirq chains: 18
in-softirq chains: 163
in-process chains: 2395
stack-trace entries: 63076 [max: 262144]
duplicated stack-trace entries: 7592 <=========
combined max dependencies: 7465936
hardirq-safe locks: 30
hardirq-unsafe locks: 380
softirq-safe locks: 82
softirq-unsafe locks: 305
irq-safe locks: 90
irq-unsafe locks: 380
hardirq-read-safe locks: 0

 hardirq-read-unsafe locks:              51
 softirq-read-safe locks:                11
 softirq-read-unsafe locks:              40
 irq-read-safe locks:                    11
 irq-read-unsafe locks:                  51

uncategorized locks: 112
unused locks: 1
max locking depth: 15
max bfs queue depth: 83
debug_locks: 1

We can see that about 12% stack_trace usage can be reduced.

Thanks,
Yong

------lockdep-duplicated-stack_trace-detect.patch------

kernel/lockdep.c +23 -3
index 2594e1c..dfd37ea 100644
@@ -369,6 +369,7 @@ static int verbose(struct lock_class *class)
  * addresses. Protected by the graph_lock.
  */
 unsigned long nr_stack_trace_entries;
+unsigned long nr_duplicated_stack_trace_entries;
 static unsigned long stack_trace[MAX_STACK_TRACE_ENTRIES];
 
 static int save_trace(struct stack_trace *trace)
@@ -818,9 +819,14 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct held_lock *next)
 {
 	struct lock_list *entry;
+	static struct held_lock *hlock;
+	static struct stack_trace trace;
+	int i;
+
 	/*
 	 * Lock not present yet - get a new dependency struct and
 	 * add it to the list:
@@ -834,6 +840,20 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 
 	entry->class = this;
 	entry->distance = distance;
+
+	if (hlock != next) {
+		hlock = next;
+		trace = entry->trace;
+	} else if (trace.nr_entries == entry->trace.nr_entries &&
+		 trace.max_entries == entry->trace.max_entries) {
+		 /* start from 2 to skip the stack introduced by lockdep */
+		 for (i=2; i<trace.nr_entries; i++) {
+			if (trace.entries[i] != entry->trace.entries[i])
+				goto no_duplicated;
+		}
+		nr_duplicated_stack_trace_entries += trace.nr_entries;
+	}
+no_duplicated:
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1694,14 +1714,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, next);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, next);
 	if (!ret)
 		return 0;
 
kernel/lockdep_internals.h +1 -0
index a2ee95a..d00fe7b 100644
@@ -84,6 +84,7 @@ extern unsigned long nr_list_entries;
 extern unsigned long nr_lock_chains;
 extern int nr_chain_hlocks;
 extern unsigned long nr_stack_trace_entries;
+extern unsigned long nr_duplicated_stack_trace_entries;
 
 extern unsigned int nr_hardirq_chains;
 extern unsigned int nr_softirq_chains;
kernel/lockdep_proc.c +2 -0
index d4aba4f..32cb9a3 100644
@@ -307,6 +307,8 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
 			nr_process_chains);
 	seq_printf(m, " stack-trace entries:           %11lu [max: %lu]\n",
 			nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
+	seq_printf(m, " duplicated stack-trace entries:%11lu\n",
+			nr_duplicated_stack_trace_entries);
 	seq_printf(m, " combined max dependencies:     %11u\n",
 			(nr_hardirq_chains + 1) *
 			(nr_softirq_chains + 1) *
Quoted 92 lines
Cc Peter Zijlstra <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc David S. Miller <[email protected]>
---
 kernel/lockdep.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)
kernel/lockdep.c +12 -8
index 2594e1c..097d5fb 100644
@@ -818,7 +818,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -829,11 +830,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1635,7 +1634,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
@@ -1694,14 +1693,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1732,6 +1731,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
 	struct held_lock *hlock;
+	struct stack_trace trace;
 
 	/*
 	 * Debugging checks.
@@ -1748,6 +1748,9 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 			curr->held_locks[depth-1].irq_context)
 		goto out_bug;
 
+	if (!save_trace(&trace))
+		return 0;
+
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth-1;
@@ -1756,7 +1759,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
Signature · 1 line
1.6.3.3
#12 lore raw

Re: [PATCH] lockdep: reduce stack_trace usage

In reply to Yong Zhang
Quoted 5 lines · Yong Zhang

From 6b9d513b7c417c0805ef0acc1cb3227bddba0889 Mon Sep 17 00:00:00 2001
From: Yong Zhang <[email protected]>
Date: Fri, 23 Apr 2010 21:13:54 +0800
Subject: [PATCH] lockdep: reduce stack_trace usage

Quoted 8 lines

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry. But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.
Using one copy of stack_trace instead.

OK, I like the idea, but I'm a little confused as to why you pull save_trace() up two functions, from what I can see we can now end up saving a trace where we previously would not have done one (the whole recursive lock mess.

So please respin this with save_trace() in check_prev_add() right before the first add_lock_to_list().

Quoted 89 lines
Cc Peter Zijlstra <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc David S. Miller <[email protected]>
---
 kernel/lockdep.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)
kernel/lockdep.c +12 -8
index 2594e1c..097d5fb 100644
@@ -818,7 +818,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -829,11 +830,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1635,7 +1634,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
@@ -1694,14 +1693,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1732,6 +1731,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
 	struct held_lock *hlock;
+	struct stack_trace trace;
 
 	/*
 	 * Debugging checks.
@@ -1748,6 +1748,9 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 			curr->held_locks[depth-1].irq_context)
 		goto out_bug;
 
+	if (!save_trace(&trace))
+		return 0;
+
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth-1;
@@ -1756,7 +1759,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
#13 lore raw

Re: [PATCH] lockdep: reduce stack_trace usage

Quoted 2 lines · Peter Zijlstra

OK, I like the idea, but I'm a little confused as to why you pull save_trace() up two functions,

I also want to avoid adding redundant trace in case of trylock. On my machine, I catched about 150 trylock dependences.

Quoted 3 lines

from what I can see we can now end up
saving a trace where we previously would not have done one (the whole recursive lock mess.

Oh, Yes, this patch will not give result as expected. Thank you for pointing it. I will respin it in V2.

Quoted 3 lines

So please respin this with save_trace() in check_prev_add() right before the first add_lock_to_list().

No problem.
BTW, in case of trylock dependence, I will let check_prevs_add() carry a flag to check_prev_add(). Thus, we can save more redundant trace. How do you think about it?

Thanks,
Yong

Quoted 90 lines
Quoted 90 lines
Cc Peter Zijlstra <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc David S. Miller <[email protected]>
---
 kernel/lockdep.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)
kernel/lockdep.c +12 -8
index 2594e1c..097d5fb 100644
@@ -818,7 +818,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -829,11 +830,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1635,7 +1634,7 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
@@ -1694,14 +1693,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1732,6 +1731,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
 	struct held_lock *hlock;
+	struct stack_trace trace;
 
 	/*
 	 * Debugging checks.
@@ -1748,6 +1748,9 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 			curr->held_locks[depth-1].irq_context)
 		goto out_bug;
 
+	if (!save_trace(&trace))
+		return 0;
+
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth-1;
@@ -1756,7 +1759,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
#14 lore raw

PATCH v2 1/1 lockdep: reduce stack_trace usage

In reply to Yong Zhang
Quoted 1 line

From 04395389820e89c0bd4bb57b939ec1882e0bb5da Mon Sep 17 00:00:00 2001

From: Yong Zhang <[email protected]>
Date: Tue, 4 May 2010 14:16:48 +0800
Subject: [PATCH] lockdep: Reduce stack_trace usage

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry. But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.
Using one copy of stack_trace instead.

V2: As suggested by Peter Zijlstra, move save_trace() from
check_prevs_add() to check_prev_add().
Add tracking for trylock dependence which is also redundant.

Cc Peter Zijlstra <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc David S. Miller <[email protected]>
---
 kernel/lockdep.c |   22 ++++++++++++++--------
 1 files changed, 14 insertions(+), 8 deletions(-)
kernel/lockdep.c +14 -8
index 2594e1c..56e0ff9 100644
@@ -818,7 +818,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -829,11 +830,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1635,12 +1634,13 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, int trylock_loop)
 {
 	struct lock_list *entry;
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
+	static struct stack_trace trace;
 
 	/*
 	 * Prove that the new <prev> -> <next> dependency would not
@@ -1688,20 +1688,23 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 		}
 	}
 
+	if (!trylock_loop && !save_trace(&trace))
+		return 0;
+
 	/*
 	 * Ok, all validations passed, add the new lock
 	 * to the previous lock's dependency list:
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, &trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, &trace);
 	if (!ret)
 		return 0;
 
@@ -1731,6 +1734,7 @@ static int
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
+	int trylock_loop = 0;
 	struct held_lock *hlock;
 
 	/*
@@ -1756,7 +1760,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, trylock_loop))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
@@ -1779,6 +1784,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		if (curr->held_locks[depth].irq_context !=
 				curr->held_locks[depth-1].irq_context)
 			break;
+		trylock_loop = 1;
 	}
 	return 1;
 out_bug:
Signature · 1 line
1.6.3.3
#15 lore raw

Re: [PATCH V2] lockdep: reduce stack_trace usage

Quoted 5 lines · Yong Zhang

From 04395389820e89c0bd4bb57b939ec1882e0bb5da Mon Sep 17 00:00:00 2001
From: Yong Zhang <[email protected]>
Date: Tue, 4 May 2010 14:16:48 +0800
Subject: [PATCH] lockdep: Reduce stack_trace usage

Quoted 8 lines

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry. But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.
Using one copy of stack_trace instead.

Right, this looks like it ought to do as intended.

I added the below snipped because I know I'll fail to remember and that 'static' qualifier is fairly easy to miss.

Thanks!

---

kernel/lockdep.c +7 -0
Index: linux-2.6/kernel/lockdep.c
===================================================================
@@ -1627,6 +1627,13 @@ check_prev_add(struct task_struct *curr,
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
+	/*
+	 * Static variable, serialized by the graph_lock().
+	 *
+	 * We use this static variable to save the stack trace in case
+	 * we call into this function multiple times due to encountering
+	 * trylocks in the held lock stack.
+	 */
 	static struct stack_trace trace;
 
 	/*
#16 lore raw

Re: [PATCH V2] lockdep: reduce stack_trace usage

Quoted 4 lines · Peter Zijlstra

Right, this looks like it ought to do as intended.

I added the below snipped because I know I'll fail to remember and that 'static' qualifier is fairly easy to miss.

OK. Thanks for your comments.

BTW, do you need me resend the patch with your comments in?

Thanks,
Yong

#17 lore raw

Re: [PATCH V2] lockdep: reduce stack_trace usage

Quoted 9 lines · Yong Zhang
Quoted 4 lines · Peter Zijlstra

Right, this looks like it ought to do as intended.

I added the below snipped because I know I'll fail to remember and that 'static' qualifier is fairly easy to miss.

OK. Thanks for your comments.

BTW, do you need me resend the patch with your comments in?

No, I've got it.

#18 lore raw

Re: [PATCH V2] lockdep: reduce stack_trace usage

Quoted 3 lines · Peter Zijlstra
Quoted 1 line

BTW, do you need me resend the patch with your comments in?

No, I've got it.

That's great. Thank you. :)

#19 lore raw

[tip:core/locking] lockdep: Reduce stack_trace usage

In reply to Yong Zhang
Commit-ID:  4726f2a617ebd868a4fdeb5679613b897e5f1676
Gitweb:     http://git.kernel.org/tip/4726f2a617ebd868a4fdeb5679613b897e5f1676
Author:     Yong Zhang <[email protected]>
AuthorDate: Tue, 4 May 2010 14:16:48 +0800
Committer:  Ingo Molnar <[email protected]>
CommitDate: Fri, 7 May 2010 11:27:26 +0200

lockdep: Reduce stack_trace usage

When calling check_prevs_add(), if all validations passed add_lock_to_list() will add new lock to dependency tree and alloc stack_trace for each list_entry.

But at this time, we are always on the same stack, so stack_trace for each list_entry has the same value. This is redundant and eats up lots of memory which could lead to warning on low MAX_STACK_TRACE_ENTRIES.

Use one copy of stack_trace instead.

V2: As suggested by Peter Zijlstra, move save_trace() from
check_prevs_add() to check_prev_add().
Add tracking for trylock dependence which is also redundant.

LKML-Reference: <[email protected]>

---
 kernel/lockdep.c |   29 +++++++++++++++++++++--------
 1 files changed, 21 insertions(+), 8 deletions(-)
kernel/lockdep.c +21 -8
index 9cf7985..5108080 100644
@@ -805,7 +805,8 @@ static struct lock_list *alloc_list_entry(void)
  * Add a new dependency to the head of the list:
  */
 static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
-			    struct list_head *head, unsigned long ip, int distance)
+			    struct list_head *head, unsigned long ip,
+			    int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	/*
@@ -816,11 +817,9 @@ static int add_lock_to_list(struct lock_class *class, struct lock_class *this,
 	if (!entry)
 		return 0;
 
-	if (!save_trace(&entry->trace))
-		return 0;
-
 	entry->class = this;
 	entry->distance = distance;
+	entry->trace = *trace;
 	/*
 	 * Since we never remove from the dependency list, the list can
 	 * be walked lockless by other CPUs, it's only allocation
@@ -1622,12 +1621,20 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance)
+	       struct held_lock *next, int distance, int trylock_loop)
 {
 	struct lock_list *entry;
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
+	/*
+	 * Static variable, serialized by the graph_lock().
+	 *
+	 * We use this static variable to save the stack trace in case
+	 * we call into this function multiple times due to encountering
+	 * trylocks in the held lock stack.
+	 */
+	static struct stack_trace trace;
 
 	/*
 	 * Prove that the new <prev> -> <next> dependency would not
@@ -1675,20 +1682,23 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 		}
 	}
 
+	if (!trylock_loop && !save_trace(&trace))
+		return 0;
+
 	/*
 	 * Ok, all validations passed, add the new lock
 	 * to the previous lock's dependency list:
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, &trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance);
+			       next->acquire_ip, distance, &trace);
 	if (!ret)
 		return 0;
 
@@ -1718,6 +1728,7 @@ static int
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
+	int trylock_loop = 0;
 	struct held_lock *hlock;
 
 	/*
@@ -1743,7 +1754,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2) {
-			if (!check_prev_add(curr, hlock, next, distance))
+			if (!check_prev_add(curr, hlock, next,
+						distance, trylock_loop))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
@@ -1766,6 +1778,7 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		if (curr->held_locks[depth].irq_context !=
 				curr->held_locks[depth-1].irq_context)
 			break;
+		trylock_loop = 1;
 	}
 	return 1;
 out_bug: