Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-13 Thread Boqun Feng
On Thu, Apr 12, 2018 at 11:12:17AM +0200, Peter Zijlstra wrote:
> On Thu, Apr 12, 2018 at 10:12:33AM +0800, Boqun Feng wrote:
> > A trivial fix/hack would be adding local_irq_disable() and
> > local_irq_enable() around srcu_lock_sync() like:
> > 
> > static inline void srcu_lock_sync(struct lockdep_map *map)
> > {
> > local_irq_disable();
> > lock_map_acquire(map);
> > lock_map_release(map);
> > local_irq_enable();
> > }
> > 
> > However, it might be better, if lockdep could provide some annotation
> > API for such an empty critical section to say the grap-and-drop is
> > atomic. Something like:
> > 
> > /*
> >  * Annotate a wait point for all previous critical section to
> >  * go out.
> >  * 
> >  * This won't make @map a irq unsafe lock, no matter it's called
> >  * w/ or w/o irq disabled.
> >  */
> > lock_wait_unlock(struct lockdep_map *map, ..)
> > 
> > And in this primitive, we do something similar like
> > lock_acquire()+lock_release(). This primitive could be used elsewhere,
> > as I bebieve we have several empty grab-and-drop critical section for
> > lockdep annotations, e.g. in start_flush_work().
> > 
> > Thoughts?
> > 
> > This cerntainly requires a bit more work, in the meanwhile, I will add
> > another self testcase which has a srcu_read_lock() called in irq.
> 
> Yeah, I've never really bothered to clean those things up, but I don't
> see any reason to stop you from doing it ;-)
> 
> As to the initial pattern with disabling IRQs, I think I've seen code
> like that before, and in general performance isn't a top priority

Yeah, I saw we used that pattern in del_timer_sync()

> (within reason) when you're running lockdep kernels, so I've usually let
> it be.

Turns out it's not very hard to write a working version of
lock_wait_unlock() ;-) Just call __lock_acquire() and __lock_release()
back-to-back with the @hardirqoff for __lock_acquire() to be 1:

/*
 * lock_sync() - synchronize with all previous critical sections to 
finish.
 *
 * Simply a acquire+release annotation with hardirqoff is true, because 
no lock
 * is actually held, so this annotaion alone is safe to be interrupted 
as if
 * irqs are off
 */
void lock_sync(struct lockdep_map *lock, unsigned subclass, int read,
   int check, struct lockdep_map *nest_lock, unsigned long 
ip)
{
unsigned long flags;

if (unlikely(current->lockdep_recursion))
return;

raw_local_irq_save(flags);
check_flags(flags);

current->lockdep_recursion = 1;
__lock_acquire(lock, subclass, 0, read, check, 1, nest_lock, 
ip, 0, 0);
if (__lock_release(lock, 0, ip))
check_chain_key(current);

current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(lock_sync);

I rename as lock_sync(), because most of the time, we annotate with this
for a "sync point" with other critical sections. We can avoid some
overhead if we refactor __lock_acquire() and __lock_release() with some
helper functions, but I think this version is good enough for now, at
least better than disabling IRQs around lock_map_acquire() +
lock_map_release() ;-)

Thoughts?

Regards,
Boqun



signature.asc
Description: PGP signature


Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-13 Thread Boqun Feng
On Thu, Apr 12, 2018 at 11:12:17AM +0200, Peter Zijlstra wrote:
> On Thu, Apr 12, 2018 at 10:12:33AM +0800, Boqun Feng wrote:
> > A trivial fix/hack would be adding local_irq_disable() and
> > local_irq_enable() around srcu_lock_sync() like:
> > 
> > static inline void srcu_lock_sync(struct lockdep_map *map)
> > {
> > local_irq_disable();
> > lock_map_acquire(map);
> > lock_map_release(map);
> > local_irq_enable();
> > }
> > 
> > However, it might be better, if lockdep could provide some annotation
> > API for such an empty critical section to say the grap-and-drop is
> > atomic. Something like:
> > 
> > /*
> >  * Annotate a wait point for all previous critical section to
> >  * go out.
> >  * 
> >  * This won't make @map a irq unsafe lock, no matter it's called
> >  * w/ or w/o irq disabled.
> >  */
> > lock_wait_unlock(struct lockdep_map *map, ..)
> > 
> > And in this primitive, we do something similar like
> > lock_acquire()+lock_release(). This primitive could be used elsewhere,
> > as I bebieve we have several empty grab-and-drop critical section for
> > lockdep annotations, e.g. in start_flush_work().
> > 
> > Thoughts?
> > 
> > This cerntainly requires a bit more work, in the meanwhile, I will add
> > another self testcase which has a srcu_read_lock() called in irq.
> 
> Yeah, I've never really bothered to clean those things up, but I don't
> see any reason to stop you from doing it ;-)
> 
> As to the initial pattern with disabling IRQs, I think I've seen code
> like that before, and in general performance isn't a top priority

Yeah, I saw we used that pattern in del_timer_sync()

> (within reason) when you're running lockdep kernels, so I've usually let
> it be.

Turns out it's not very hard to write a working version of
lock_wait_unlock() ;-) Just call __lock_acquire() and __lock_release()
back-to-back with the @hardirqoff for __lock_acquire() to be 1:

/*
 * lock_sync() - synchronize with all previous critical sections to 
finish.
 *
 * Simply a acquire+release annotation with hardirqoff is true, because 
no lock
 * is actually held, so this annotaion alone is safe to be interrupted 
as if
 * irqs are off
 */
void lock_sync(struct lockdep_map *lock, unsigned subclass, int read,
   int check, struct lockdep_map *nest_lock, unsigned long 
ip)
{
unsigned long flags;

if (unlikely(current->lockdep_recursion))
return;

raw_local_irq_save(flags);
check_flags(flags);

current->lockdep_recursion = 1;
__lock_acquire(lock, subclass, 0, read, check, 1, nest_lock, 
ip, 0, 0);
if (__lock_release(lock, 0, ip))
check_chain_key(current);

current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(lock_sync);

I rename as lock_sync(), because most of the time, we annotate with this
for a "sync point" with other critical sections. We can avoid some
overhead if we refactor __lock_acquire() and __lock_release() with some
helper functions, but I think this version is good enough for now, at
least better than disabling IRQs around lock_map_acquire() +
lock_map_release() ;-)

Thoughts?

Regards,
Boqun



signature.asc
Description: PGP signature


Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-12 Thread Peter Zijlstra
On Thu, Apr 12, 2018 at 10:12:33AM +0800, Boqun Feng wrote:
> A trivial fix/hack would be adding local_irq_disable() and
> local_irq_enable() around srcu_lock_sync() like:
> 
>   static inline void srcu_lock_sync(struct lockdep_map *map)
>   {
>   local_irq_disable();
>   lock_map_acquire(map);
>   lock_map_release(map);
>   local_irq_enable();
>   }
> 
> However, it might be better, if lockdep could provide some annotation
> API for such an empty critical section to say the grap-and-drop is
> atomic. Something like:
> 
>   /*
>* Annotate a wait point for all previous critical section to
>* go out.
>* 
>* This won't make @map a irq unsafe lock, no matter it's called
>* w/ or w/o irq disabled.
>*/
>   lock_wait_unlock(struct lockdep_map *map, ..)
> 
> And in this primitive, we do something similar like
> lock_acquire()+lock_release(). This primitive could be used elsewhere,
> as I bebieve we have several empty grab-and-drop critical section for
> lockdep annotations, e.g. in start_flush_work().
> 
> Thoughts?
> 
> This cerntainly requires a bit more work, in the meanwhile, I will add
> another self testcase which has a srcu_read_lock() called in irq.

Yeah, I've never really bothered to clean those things up, but I don't
see any reason to stop you from doing it ;-)

As to the initial pattern with disabling IRQs, I think I've seen code
like that before, and in general performance isn't a top priority
(within reason) when you're running lockdep kernels, so I've usually let
it be.


Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-12 Thread Peter Zijlstra
On Thu, Apr 12, 2018 at 10:12:33AM +0800, Boqun Feng wrote:
> A trivial fix/hack would be adding local_irq_disable() and
> local_irq_enable() around srcu_lock_sync() like:
> 
>   static inline void srcu_lock_sync(struct lockdep_map *map)
>   {
>   local_irq_disable();
>   lock_map_acquire(map);
>   lock_map_release(map);
>   local_irq_enable();
>   }
> 
> However, it might be better, if lockdep could provide some annotation
> API for such an empty critical section to say the grap-and-drop is
> atomic. Something like:
> 
>   /*
>* Annotate a wait point for all previous critical section to
>* go out.
>* 
>* This won't make @map a irq unsafe lock, no matter it's called
>* w/ or w/o irq disabled.
>*/
>   lock_wait_unlock(struct lockdep_map *map, ..)
> 
> And in this primitive, we do something similar like
> lock_acquire()+lock_release(). This primitive could be used elsewhere,
> as I bebieve we have several empty grab-and-drop critical section for
> lockdep annotations, e.g. in start_flush_work().
> 
> Thoughts?
> 
> This cerntainly requires a bit more work, in the meanwhile, I will add
> another self testcase which has a srcu_read_lock() called in irq.

Yeah, I've never really bothered to clean those things up, but I don't
see any reason to stop you from doing it ;-)

As to the initial pattern with disabling IRQs, I think I've seen code
like that before, and in general performance isn't a top priority
(within reason) when you're running lockdep kernels, so I've usually let
it be.


Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-11 Thread Boqun Feng
On Wed, Apr 11, 2018 at 11:57:30AM -0700, Paul E. McKenney wrote:
> On Wed, Apr 11, 2018 at 09:56:44PM +0800, Boqun Feng wrote:
> > Although all flavors of RCU are annotated correctly with lockdep
> > annotations as recursive read locks, the 'check' parameter for their
> > calls to lock_acquire() is unset. Which means RCU read locks are not
> > added into the lockdep dependency graph. This is fine for all flavors
> > except sleepable RCU, because the deadlock scenarios for them are
> > simple: calling synchronize_rcu() and its friends inside their read-side
> > critical sections. But for sleepable RCU, as there may be multiple
> > instances with multiple classes, there are more deadlock cases.
> > Considering the following:
> > 
> > TASK 1  TASK 2
> > === 
> > i = srcu_read_lock();i = srcu_read_lock();
> > synchronize_srcu();  synchronize_srcu();
> > srcu_read_unlock();  srcu_read_unlock();
> > 
> > Neither TASK 1 or 2 could go out of the read-side critical sections,
> > because they are waiting for each other at synchronize_srcu().
> > 
> > With the new improvement for lockdep, which allows us to detect
> > deadlocks for recursive read locks, we can actually detect this. What we
> > need to do are simply: a) mark srcu_read_{,un}lock() as 'check'
> > lock_acquire() and b) annotate synchronize_srcu() as a empty
> > grab-and-drop for a write lock (because synchronize_srcu() will wait for
> > previous srcu_read_lock() to release, and won't block the next
> > srcu_read_lock(), just like a empty write lock section).
> > 
> > This patch adds those to allow we check deadlocks related to sleepable
> > RCU with lockdep.
> > 
> > Cc: Paul E. McKenney 
> > Signed-off-by: Boqun Feng 
> 
> Very cool!
> 
> One question though...  Won't this report a false-positive self-deadlock if
> srcu_read_lock() is invoked from an interrupt handler?
> 

Ah.. right. And the false-positive happens because synchronize_srcu() is
annotated as a irq-write-unsafe lock, which should be fixed because
synchronize_srcu() doesn't block a srcu_read_lock() and the empty
write lock critical section in srcu_lock_sync() should mean the
grab-and-drop is atomic (i.e. no one could interrupt), therefore no irq
inversion problem.

A trivial fix/hack would be adding local_irq_disable() and
local_irq_enable() around srcu_lock_sync() like:

static inline void srcu_lock_sync(struct lockdep_map *map)
{
local_irq_disable();
lock_map_acquire(map);
lock_map_release(map);
local_irq_enable();
}

However, it might be better, if lockdep could provide some annotation
API for such an empty critical section to say the grap-and-drop is
atomic. Something like:

/*
 * Annotate a wait point for all previous critical section to
 * go out.
 * 
 * This won't make @map a irq unsafe lock, no matter it's called
 * w/ or w/o irq disabled.
 */
lock_wait_unlock(struct lockdep_map *map, ..)

And in this primitive, we do something similar like
lock_acquire()+lock_release(). This primitive could be used elsewhere,
as I bebieve we have several empty grab-and-drop critical section for
lockdep annotations, e.g. in start_flush_work().

Thoughts?

This cerntainly requires a bit more work, in the meanwhile, I will add
another self testcase which has a srcu_read_lock() called in irq.
Thanks!

Regards,
Boqun

>   Thanx, Paul
> 
> > ---
> >  include/linux/srcu.h  | 51 
> > +--
> >  kernel/rcu/srcutiny.c |  2 ++
> >  kernel/rcu/srcutree.c |  2 ++
> >  3 files changed, 53 insertions(+), 2 deletions(-)
> > 
> > diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> > index 33c1c698df09..23f397bd192c 100644
> > --- a/include/linux/srcu.h
> > +++ b/include/linux/srcu.h
> > @@ -99,6 +99,49 @@ static inline int srcu_read_lock_held(const struct 
> > srcu_struct *sp)
> > return lock_is_held(>dep_map);
> >  }
> > 
> > +/**
> > + * lockdep annotations for srcu_read_{un,}lock, and synchronize_srcu():
> > + *
> > + * srcu_read_lock() and srcu_read_unlock() are similar to rcu_read_lock() 
> > and
> > + * rcu_read_unlock(), they are recursive read locks. But we mark them as
> > + * "check", they will be added into lockdep dependency graph for deadlock
> > + * detection. And we also annotate synchronize_srcu() as a
> > + * write_lock()+write_unlock(), because synchronize_srcu() will wait for 
> > any
> > + * corresponding previous srcu_read_lock() to release, and that acts like a
> > + * empty grab-and-drop write lock.
> > + *
> > + * We do so because multiple sleepable rcu instances may cause deadlock as
> > + * follow:
> > + *
> > + *   Task 1:
> > + * ia = srcu_read_lock(_A);
> > + * 

Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-11 Thread Boqun Feng
On Wed, Apr 11, 2018 at 11:57:30AM -0700, Paul E. McKenney wrote:
> On Wed, Apr 11, 2018 at 09:56:44PM +0800, Boqun Feng wrote:
> > Although all flavors of RCU are annotated correctly with lockdep
> > annotations as recursive read locks, the 'check' parameter for their
> > calls to lock_acquire() is unset. Which means RCU read locks are not
> > added into the lockdep dependency graph. This is fine for all flavors
> > except sleepable RCU, because the deadlock scenarios for them are
> > simple: calling synchronize_rcu() and its friends inside their read-side
> > critical sections. But for sleepable RCU, as there may be multiple
> > instances with multiple classes, there are more deadlock cases.
> > Considering the following:
> > 
> > TASK 1  TASK 2
> > === 
> > i = srcu_read_lock();i = srcu_read_lock();
> > synchronize_srcu();  synchronize_srcu();
> > srcu_read_unlock();  srcu_read_unlock();
> > 
> > Neither TASK 1 or 2 could go out of the read-side critical sections,
> > because they are waiting for each other at synchronize_srcu().
> > 
> > With the new improvement for lockdep, which allows us to detect
> > deadlocks for recursive read locks, we can actually detect this. What we
> > need to do are simply: a) mark srcu_read_{,un}lock() as 'check'
> > lock_acquire() and b) annotate synchronize_srcu() as a empty
> > grab-and-drop for a write lock (because synchronize_srcu() will wait for
> > previous srcu_read_lock() to release, and won't block the next
> > srcu_read_lock(), just like a empty write lock section).
> > 
> > This patch adds those to allow we check deadlocks related to sleepable
> > RCU with lockdep.
> > 
> > Cc: Paul E. McKenney 
> > Signed-off-by: Boqun Feng 
> 
> Very cool!
> 
> One question though...  Won't this report a false-positive self-deadlock if
> srcu_read_lock() is invoked from an interrupt handler?
> 

Ah.. right. And the false-positive happens because synchronize_srcu() is
annotated as a irq-write-unsafe lock, which should be fixed because
synchronize_srcu() doesn't block a srcu_read_lock() and the empty
write lock critical section in srcu_lock_sync() should mean the
grab-and-drop is atomic (i.e. no one could interrupt), therefore no irq
inversion problem.

A trivial fix/hack would be adding local_irq_disable() and
local_irq_enable() around srcu_lock_sync() like:

static inline void srcu_lock_sync(struct lockdep_map *map)
{
local_irq_disable();
lock_map_acquire(map);
lock_map_release(map);
local_irq_enable();
}

However, it might be better, if lockdep could provide some annotation
API for such an empty critical section to say the grap-and-drop is
atomic. Something like:

/*
 * Annotate a wait point for all previous critical section to
 * go out.
 * 
 * This won't make @map a irq unsafe lock, no matter it's called
 * w/ or w/o irq disabled.
 */
lock_wait_unlock(struct lockdep_map *map, ..)

And in this primitive, we do something similar like
lock_acquire()+lock_release(). This primitive could be used elsewhere,
as I bebieve we have several empty grab-and-drop critical section for
lockdep annotations, e.g. in start_flush_work().

Thoughts?

This cerntainly requires a bit more work, in the meanwhile, I will add
another self testcase which has a srcu_read_lock() called in irq.
Thanks!

Regards,
Boqun

>   Thanx, Paul
> 
> > ---
> >  include/linux/srcu.h  | 51 
> > +--
> >  kernel/rcu/srcutiny.c |  2 ++
> >  kernel/rcu/srcutree.c |  2 ++
> >  3 files changed, 53 insertions(+), 2 deletions(-)
> > 
> > diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> > index 33c1c698df09..23f397bd192c 100644
> > --- a/include/linux/srcu.h
> > +++ b/include/linux/srcu.h
> > @@ -99,6 +99,49 @@ static inline int srcu_read_lock_held(const struct 
> > srcu_struct *sp)
> > return lock_is_held(>dep_map);
> >  }
> > 
> > +/**
> > + * lockdep annotations for srcu_read_{un,}lock, and synchronize_srcu():
> > + *
> > + * srcu_read_lock() and srcu_read_unlock() are similar to rcu_read_lock() 
> > and
> > + * rcu_read_unlock(), they are recursive read locks. But we mark them as
> > + * "check", they will be added into lockdep dependency graph for deadlock
> > + * detection. And we also annotate synchronize_srcu() as a
> > + * write_lock()+write_unlock(), because synchronize_srcu() will wait for 
> > any
> > + * corresponding previous srcu_read_lock() to release, and that acts like a
> > + * empty grab-and-drop write lock.
> > + *
> > + * We do so because multiple sleepable rcu instances may cause deadlock as
> > + * follow:
> > + *
> > + *   Task 1:
> > + * ia = srcu_read_lock(_A);
> > + * synchronize_srcu(_B);
> > + * srcu_read_unlock(_A, 

Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-11 Thread Paul E. McKenney
On Wed, Apr 11, 2018 at 09:56:44PM +0800, Boqun Feng wrote:
> Although all flavors of RCU are annotated correctly with lockdep
> annotations as recursive read locks, the 'check' parameter for their
> calls to lock_acquire() is unset. Which means RCU read locks are not
> added into the lockdep dependency graph. This is fine for all flavors
> except sleepable RCU, because the deadlock scenarios for them are
> simple: calling synchronize_rcu() and its friends inside their read-side
> critical sections. But for sleepable RCU, as there may be multiple
> instances with multiple classes, there are more deadlock cases.
> Considering the following:
> 
>   TASK 1  TASK 2
>   === 
>   i = srcu_read_lock();i = srcu_read_lock();
>   synchronize_srcu();  synchronize_srcu();
>   srcu_read_unlock();  srcu_read_unlock();
> 
> Neither TASK 1 or 2 could go out of the read-side critical sections,
> because they are waiting for each other at synchronize_srcu().
> 
> With the new improvement for lockdep, which allows us to detect
> deadlocks for recursive read locks, we can actually detect this. What we
> need to do are simply: a) mark srcu_read_{,un}lock() as 'check'
> lock_acquire() and b) annotate synchronize_srcu() as a empty
> grab-and-drop for a write lock (because synchronize_srcu() will wait for
> previous srcu_read_lock() to release, and won't block the next
> srcu_read_lock(), just like a empty write lock section).
> 
> This patch adds those to allow we check deadlocks related to sleepable
> RCU with lockdep.
> 
> Cc: Paul E. McKenney 
> Signed-off-by: Boqun Feng 

Very cool!

One question though...  Won't this report a false-positive self-deadlock if
srcu_read_lock() is invoked from an interrupt handler?

Thanx, Paul

> ---
>  include/linux/srcu.h  | 51 
> +--
>  kernel/rcu/srcutiny.c |  2 ++
>  kernel/rcu/srcutree.c |  2 ++
>  3 files changed, 53 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> index 33c1c698df09..23f397bd192c 100644
> --- a/include/linux/srcu.h
> +++ b/include/linux/srcu.h
> @@ -99,6 +99,49 @@ static inline int srcu_read_lock_held(const struct 
> srcu_struct *sp)
>   return lock_is_held(>dep_map);
>  }
> 
> +/**
> + * lockdep annotations for srcu_read_{un,}lock, and synchronize_srcu():
> + *
> + * srcu_read_lock() and srcu_read_unlock() are similar to rcu_read_lock() and
> + * rcu_read_unlock(), they are recursive read locks. But we mark them as
> + * "check", they will be added into lockdep dependency graph for deadlock
> + * detection. And we also annotate synchronize_srcu() as a
> + * write_lock()+write_unlock(), because synchronize_srcu() will wait for any
> + * corresponding previous srcu_read_lock() to release, and that acts like a
> + * empty grab-and-drop write lock.
> + *
> + * We do so because multiple sleepable rcu instances may cause deadlock as
> + * follow:
> + *
> + *   Task 1:
> + * ia = srcu_read_lock(_A);
> + * synchronize_srcu(_B);
> + * srcu_read_unlock(_A, ia);
> + *
> + *   Task 2:
> + * ib = srcu_read_lock(_B);
> + * synchronize_srcu(_A);
> + * srcu_read_unlock(_B, ib);
> + *
> + * And we want lockdep to detect this or more complicated deadlock with SRCU
> + * involved.
> + */
> +static inline void srcu_lock_acquire(struct lockdep_map *map)
> +{
> + lock_map_acquire_read(map);
> +}
> +
> +static inline void srcu_lock_release(struct lockdep_map *map)
> +{
> + lock_map_release(map);
> +}
> +
> +static inline void srcu_lock_sync(struct lockdep_map *map)
> +{
> + lock_map_acquire(map);
> + lock_map_release(map);
> +}
> +
>  #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> 
>  static inline int srcu_read_lock_held(const struct srcu_struct *sp)
> @@ -106,6 +149,10 @@ static inline int srcu_read_lock_held(const struct 
> srcu_struct *sp)
>   return 1;
>  }
> 
> +#define srcu_lock_acquire(m) do { } while (0)
> +#define srcu_lock_release(m) do { } while (0)
> +#define srcu_lock_sync(m)do { } while (0)
> +
>  #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> 
>  /**
> @@ -157,7 +204,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) 
> __acquires(sp)
>   int retval;
> 
>   retval = __srcu_read_lock(sp);
> - rcu_lock_acquire(&(sp)->dep_map);
> + srcu_lock_acquire(&(sp)->dep_map);
>   return retval;
>  }
> 
> @@ -171,7 +218,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) 
> __acquires(sp)
>  static inline void srcu_read_unlock(struct srcu_struct *sp, int idx)
>   __releases(sp)
>  {
> - rcu_lock_release(&(sp)->dep_map);
> + srcu_lock_release(&(sp)->dep_map);
>   __srcu_read_unlock(sp, idx);
>  }
> 
> diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
> index 

Re: [RFC tip/locking/lockdep v6 19/20] rcu: Equip sleepable RCU with lockdep dependency graph checks

2018-04-11 Thread Paul E. McKenney
On Wed, Apr 11, 2018 at 09:56:44PM +0800, Boqun Feng wrote:
> Although all flavors of RCU are annotated correctly with lockdep
> annotations as recursive read locks, the 'check' parameter for their
> calls to lock_acquire() is unset. Which means RCU read locks are not
> added into the lockdep dependency graph. This is fine for all flavors
> except sleepable RCU, because the deadlock scenarios for them are
> simple: calling synchronize_rcu() and its friends inside their read-side
> critical sections. But for sleepable RCU, as there may be multiple
> instances with multiple classes, there are more deadlock cases.
> Considering the following:
> 
>   TASK 1  TASK 2
>   === 
>   i = srcu_read_lock();i = srcu_read_lock();
>   synchronize_srcu();  synchronize_srcu();
>   srcu_read_unlock();  srcu_read_unlock();
> 
> Neither TASK 1 or 2 could go out of the read-side critical sections,
> because they are waiting for each other at synchronize_srcu().
> 
> With the new improvement for lockdep, which allows us to detect
> deadlocks for recursive read locks, we can actually detect this. What we
> need to do are simply: a) mark srcu_read_{,un}lock() as 'check'
> lock_acquire() and b) annotate synchronize_srcu() as a empty
> grab-and-drop for a write lock (because synchronize_srcu() will wait for
> previous srcu_read_lock() to release, and won't block the next
> srcu_read_lock(), just like a empty write lock section).
> 
> This patch adds those to allow we check deadlocks related to sleepable
> RCU with lockdep.
> 
> Cc: Paul E. McKenney 
> Signed-off-by: Boqun Feng 

Very cool!

One question though...  Won't this report a false-positive self-deadlock if
srcu_read_lock() is invoked from an interrupt handler?

Thanx, Paul

> ---
>  include/linux/srcu.h  | 51 
> +--
>  kernel/rcu/srcutiny.c |  2 ++
>  kernel/rcu/srcutree.c |  2 ++
>  3 files changed, 53 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> index 33c1c698df09..23f397bd192c 100644
> --- a/include/linux/srcu.h
> +++ b/include/linux/srcu.h
> @@ -99,6 +99,49 @@ static inline int srcu_read_lock_held(const struct 
> srcu_struct *sp)
>   return lock_is_held(>dep_map);
>  }
> 
> +/**
> + * lockdep annotations for srcu_read_{un,}lock, and synchronize_srcu():
> + *
> + * srcu_read_lock() and srcu_read_unlock() are similar to rcu_read_lock() and
> + * rcu_read_unlock(), they are recursive read locks. But we mark them as
> + * "check", they will be added into lockdep dependency graph for deadlock
> + * detection. And we also annotate synchronize_srcu() as a
> + * write_lock()+write_unlock(), because synchronize_srcu() will wait for any
> + * corresponding previous srcu_read_lock() to release, and that acts like a
> + * empty grab-and-drop write lock.
> + *
> + * We do so because multiple sleepable rcu instances may cause deadlock as
> + * follow:
> + *
> + *   Task 1:
> + * ia = srcu_read_lock(_A);
> + * synchronize_srcu(_B);
> + * srcu_read_unlock(_A, ia);
> + *
> + *   Task 2:
> + * ib = srcu_read_lock(_B);
> + * synchronize_srcu(_A);
> + * srcu_read_unlock(_B, ib);
> + *
> + * And we want lockdep to detect this or more complicated deadlock with SRCU
> + * involved.
> + */
> +static inline void srcu_lock_acquire(struct lockdep_map *map)
> +{
> + lock_map_acquire_read(map);
> +}
> +
> +static inline void srcu_lock_release(struct lockdep_map *map)
> +{
> + lock_map_release(map);
> +}
> +
> +static inline void srcu_lock_sync(struct lockdep_map *map)
> +{
> + lock_map_acquire(map);
> + lock_map_release(map);
> +}
> +
>  #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> 
>  static inline int srcu_read_lock_held(const struct srcu_struct *sp)
> @@ -106,6 +149,10 @@ static inline int srcu_read_lock_held(const struct 
> srcu_struct *sp)
>   return 1;
>  }
> 
> +#define srcu_lock_acquire(m) do { } while (0)
> +#define srcu_lock_release(m) do { } while (0)
> +#define srcu_lock_sync(m)do { } while (0)
> +
>  #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> 
>  /**
> @@ -157,7 +204,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) 
> __acquires(sp)
>   int retval;
> 
>   retval = __srcu_read_lock(sp);
> - rcu_lock_acquire(&(sp)->dep_map);
> + srcu_lock_acquire(&(sp)->dep_map);
>   return retval;
>  }
> 
> @@ -171,7 +218,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) 
> __acquires(sp)
>  static inline void srcu_read_unlock(struct srcu_struct *sp, int idx)
>   __releases(sp)
>  {
> - rcu_lock_release(&(sp)->dep_map);
> + srcu_lock_release(&(sp)->dep_map);
>   __srcu_read_unlock(sp, idx);
>  }
> 
> diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
> index 76ac5f50b2c7..bc89cb48d800 100644
> --- a/kernel/rcu/srcutiny.c