Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1560812 > unrolled thread

Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace

Started byPeter Zijlstra <peterz@infradead.org>
First post2017-01-17 18:00 +0100
Last post2017-01-19 04:20 +0100
Articles 4 — 2 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate  stack_trace Peter Zijlstra <peterz@infradead.org> - 2017-01-17 18:00 +0100
    Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate  stack_trace Byungchul Park <byungchul.park@lge.com> - 2017-01-18 03:20 +0100
      Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate  stack_trace Peter Zijlstra <peterz@infradead.org> - 2017-01-18 17:00 +0100
        Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate  stack_trace Byungchul Park <byungchul.park@lge.com> - 2017-01-19 04:20 +0100

#1560812 — Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace

FromPeter Zijlstra <peterz@infradead.org>
Date2017-01-17 18:00 +0100
SubjectRe: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
Message-ID<t0Fh0-12w-7@gated-at.bofh.it>
On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> What do you think about the following patches doing it?

I was more thinking about something like so...

Also, I think I want to muck with struct stack_trace; the members:
max_nr_entries and skip are input arguments to save_stack_trace() and
bloat the structure for no reason.

---
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 7c38f8f3d97b..f2df300a96ee 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -430,6 +430,21 @@ static int save_trace(struct stack_trace *trace)
 	return 1;
 }
 
+static bool return_trace(struct stack_trace *trace)
+{
+	/*
+	 * If @trace is the last trace generated by save_trace(), then we can
+	 * return the entries by simply subtracting @nr_stack_trace_entries
+	 * again.
+	 */
+	if (trace->entries != stack_trace + nr_stack_trace_entries - trace->nr_entres)
+		return false;
+
+	nr_stack_trace_entries -= trace->nr_entries;
+	trace->entries = NULL;
+	return true;
+}
+
 unsigned int nr_hardirq_chains;
 unsigned int nr_softirq_chains;
 unsigned int nr_process_chains;
@@ -1797,20 +1812,12 @@ static inline void inc_chains(void)
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance, int *stack_saved)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	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
@@ -1858,11 +1865,7 @@ static inline void inc_chains(void)
 		}
 	}
 
-	if (!*stack_saved) {
-		if (!save_trace(&trace))
-			return 0;
-		*stack_saved = 1;
-	}
+	trace->skip = 1; /* mark used */
 
 	/*
 	 * Ok, all validations passed, add the new lock
@@ -1870,14 +1873,14 @@ static inline void inc_chains(void)
 	 */
 	ret = add_lock_to_list(hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1885,8 +1888,6 @@ static inline void inc_chains(void)
 	 * Debugging printouts:
 	 */
 	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
-		/* We drop graph lock, so another thread can overwrite trace. */
-		*stack_saved = 0;
 		graph_unlock();
 		printk("\n new dependency: ");
 		print_lock_name(hlock_class(prev));
@@ -1908,10 +1909,15 @@ static inline void inc_chains(void)
 static int
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
+	struct stack_trace trace = { .nr_entries = 0, .skip = 0, };
 	int depth = curr->lockdep_depth;
-	int stack_saved = 0;
 	struct held_lock *hlock;
 
+	if (!save_trace(&trace))
+		goto out_bug;
+
+	trace.skip = 0; /* abuse to mark usage */
+
 	/*
 	 * Debugging checks.
 	 *
@@ -1936,7 +1942,7 @@ static inline void inc_chains(void)
 		 */
 		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
-						distance, &stack_saved))
+					    distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
@@ -1962,6 +1968,9 @@ static inline void inc_chains(void)
 	}
 	return 1;
 out_bug:
+	if (trace.nr_entries && !trace.skip)
+		return_trace(&trace);
+
 	if (!debug_locks_off_graph_unlock())
 		return 0;
 

[toc] | [next] | [standalone]


#1561223

FromByungchul Park <byungchul.park@lge.com>
Date2017-01-18 03:20 +0100
Message-ID<t0O0W-6wc-7@gated-at.bofh.it>
In reply to#1560812
On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > What do you think about the following patches doing it?
> 
> I was more thinking about something like so...
> 
> Also, I think I want to muck with struct stack_trace; the members:
> max_nr_entries and skip are input arguments to save_stack_trace() and
> bloat the structure for no reason.

With your approach, save_trace() must be called whenever check_prevs_add()
is called, which might be unnecessary.

Frankly speaking, I think what I proposed resolved it neatly. Don't you
think so?

> 
> ---
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 7c38f8f3d97b..f2df300a96ee 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -430,6 +430,21 @@ static int save_trace(struct stack_trace *trace)
>  	return 1;
>  }
>  
> +static bool return_trace(struct stack_trace *trace)
> +{
> +	/*
> +	 * If @trace is the last trace generated by save_trace(), then we can
> +	 * return the entries by simply subtracting @nr_stack_trace_entries
> +	 * again.
> +	 */
> +	if (trace->entries != stack_trace + nr_stack_trace_entries - trace->nr_entres)
> +		return false;
> +
> +	nr_stack_trace_entries -= trace->nr_entries;
> +	trace->entries = NULL;
> +	return true;
> +}
> +
>  unsigned int nr_hardirq_chains;
>  unsigned int nr_softirq_chains;
>  unsigned int nr_process_chains;
> @@ -1797,20 +1812,12 @@ static inline void inc_chains(void)
>   */
>  static int
>  check_prev_add(struct task_struct *curr, struct held_lock *prev,
> -	       struct held_lock *next, int distance, int *stack_saved)
> +	       struct held_lock *next, int distance, struct stack_trace *trace)
>  {
>  	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
> @@ -1858,11 +1865,7 @@ static inline void inc_chains(void)
>  		}
>  	}
>  
> -	if (!*stack_saved) {
> -		if (!save_trace(&trace))
> -			return 0;
> -		*stack_saved = 1;
> -	}
> +	trace->skip = 1; /* mark used */
>  
>  	/*
>  	 * Ok, all validations passed, add the new lock
> @@ -1870,14 +1873,14 @@ static inline void inc_chains(void)
>  	 */
>  	ret = add_lock_to_list(hlock_class(next),
>  			       &hlock_class(prev)->locks_after,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  
>  	if (!ret)
>  		return 0;
>  
>  	ret = add_lock_to_list(hlock_class(prev),
>  			       &hlock_class(next)->locks_before,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  	if (!ret)
>  		return 0;
>  
> @@ -1885,8 +1888,6 @@ static inline void inc_chains(void)
>  	 * Debugging printouts:
>  	 */
>  	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
> -		/* We drop graph lock, so another thread can overwrite trace. */
> -		*stack_saved = 0;
>  		graph_unlock();
>  		printk("\n new dependency: ");
>  		print_lock_name(hlock_class(prev));
> @@ -1908,10 +1909,15 @@ static inline void inc_chains(void)
>  static int
>  check_prevs_add(struct task_struct *curr, struct held_lock *next)
>  {
> +	struct stack_trace trace = { .nr_entries = 0, .skip = 0, };
>  	int depth = curr->lockdep_depth;
> -	int stack_saved = 0;
>  	struct held_lock *hlock;
>  
> +	if (!save_trace(&trace))
> +		goto out_bug;
> +
> +	trace.skip = 0; /* abuse to mark usage */
> +
>  	/*
>  	 * Debugging checks.
>  	 *
> @@ -1936,7 +1942,7 @@ static inline void inc_chains(void)
>  		 */
>  		if (hlock->read != 2 && hlock->check) {
>  			if (!check_prev_add(curr, hlock, next,
> -						distance, &stack_saved))
> +					    distance, &trace))
>  				return 0;
>  			/*
>  			 * Stop after the first non-trylock entry,
> @@ -1962,6 +1968,9 @@ static inline void inc_chains(void)
>  	}
>  	return 1;
>  out_bug:
> +	if (trace.nr_entries && !trace.skip)
> +		return_trace(&trace);
> +
>  	if (!debug_locks_off_graph_unlock())
>  		return 0;
>  

[toc] | [prev] | [next] | [standalone]


#1561916

FromPeter Zijlstra <peterz@infradead.org>
Date2017-01-18 17:00 +0100
Message-ID<t10Oz-5VY-19@gated-at.bofh.it>
In reply to#1561223
On Wed, Jan 18, 2017 at 11:04:32AM +0900, Byungchul Park wrote:
> On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> > On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > > What do you think about the following patches doing it?
> > 
> > I was more thinking about something like so...
> > 
> > Also, I think I want to muck with struct stack_trace; the members:
> > max_nr_entries and skip are input arguments to save_stack_trace() and
> > bloat the structure for no reason.
> 
> With your approach, save_trace() must be called whenever check_prevs_add()
> is called, which might be unnecessary.

True.. but since we hold the graph_lock this is a slow path anyway, so I
didn't care much.

Then again, I forgot to clean up in a bunch of paths.

> Frankly speaking, I think what I proposed resolved it neatly. Don't you
> think so?

My initial reaction was to your patches being radically different to
what I had proposed. But after fixing mine I don't particularly like
either one of them.

Also, I think yours has a hole in, you check nr_stack_trace_entries
against an older copy to check we did save_stack(), this is not accurate
as check_prev_add() can drop graph_lock in the verbose case and then
someone else could have done save_stack().


Let me see if I can find something simpler..

[toc] | [prev] | [next] | [standalone]


#1562399

FromByungchul Park <byungchul.park@lge.com>
Date2017-01-19 04:20 +0100
Message-ID<t1bqy-4pZ-9@gated-at.bofh.it>
In reply to#1561916
On Wed, Jan 18, 2017 at 04:10:53PM +0100, Peter Zijlstra wrote:
> On Wed, Jan 18, 2017 at 11:04:32AM +0900, Byungchul Park wrote:
> > On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> > > On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > > > What do you think about the following patches doing it?
> > > 
> > > I was more thinking about something like so...
> > > 
> > > Also, I think I want to muck with struct stack_trace; the members:
> > > max_nr_entries and skip are input arguments to save_stack_trace() and
> > > bloat the structure for no reason.
> > 
> > With your approach, save_trace() must be called whenever check_prevs_add()
> > is called, which might be unnecessary.
> 
> True.. but since we hold the graph_lock this is a slow path anyway, so I
> didn't care much.

If we don't need to care it, the problem becomes easy to solve. But IMHO,
it'd be better to care it as original lockdep code did, because
save_trace() might have bigger overhead than we expect and
check_prevs_add() can be called frequently, so it'd be better to avoid it
when possible.

> Then again, I forgot to clean up in a bunch of paths.
> 
> > Frankly speaking, I think what I proposed resolved it neatly. Don't you
> > think so?
> 
> My initial reaction was to your patches being radically different to
> what I had proposed. But after fixing mine I don't particularly like
> either one of them.
> 
> Also, I think yours has a hole in, you check nr_stack_trace_entries
> against an older copy to check we did save_stack(), this is not accurate
> as check_prev_add() can drop graph_lock in the verbose case and then
> someone else could have done save_stack().

Right. My mistake..

Then.. The following patch on top of my patch 2/2 can solve it. Right?

---

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 49b9386..0f5bded 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1892,7 +1892,7 @@ static inline void inc_chains(void)
 		if (entry->class == hlock_class(next)) {
 			if (distance == 1)
 				entry->distance = 1;
-			return 2;
+			return 1;
 		}
 	}
 
@@ -1927,9 +1927,10 @@ static inline void inc_chains(void)
 		print_lock_name(hlock_class(next));
 		printk(KERN_CONT "\n");
 		dump_stack();
-		return graph_lock();
+		if (!graph_lock())
+			return 0;
 	}
-	return 1;
+	return 2;
 }
 
 /*
@@ -1975,15 +1976,16 @@ static inline void inc_chains(void)
 			 * added:
 			 */
 			if (hlock->read != 2 && hlock->check) {
-				if (!check_prev_add(curr, hlock, next,
-							distance, &trace, save))
+				int ret = check_prev_add(curr, hlock, next,
+							distance, &trace, save);
+				if (!ret)
 					return 0;
 
 				/*
 				 * Stop saving stack_trace if save_trace() was
 				 * called at least once:
 				 */
-				if (save && start_nr != nr_stack_trace_entries)
+				if (save && ret == 2)
 					save = NULL;
 
 				/*

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web