> -----Original Message-----
> From: linux-kernel-ow...@vger.kernel.org [mailto:linux-kernel-
> ow...@vger.kernel.org] On Behalf Of Sebastian Andrzej Siewior
> Sent: Saturday, March 16, 2019 00:43
> To: Liu, Yongxin
> Cc: linux-ker...@vger.kernel.org; linux-rt-us...@vger.kernel.org;
> t...@linutronix.de; rost...@goodmis.org; dan.j.willi...@intel.com;
> pagu...@redhat.com; Gortmaker, Paul; linux-nvdimm@lists.01.org
> Subject: Re: [PATCH RT] nvdimm: make lane acquirement RT aware
> 
> On 2019-03-11 00:44:58 [+0000], Liu, Yongxin wrote:
> > > but you still have the ndl_lock->lock which protects the resource. So
> in
> > > the unlikely (but possible event) that you switch CPUs after
> obtaining
> > > the CPU number you block on the lock. No harm is done, right?
> >
> > The resource "lane" can be acquired recursively, so "ndl_lock->lock" is
> a conditional lock.
> >
> > ndl_count->count is per CPU.
> > ndl_lock->lock is per lane.
> >
> > Here is an example:
> > Thread A  on CPU 5 --> nd_region_acquire_lane --> lane# 5 --> get
> "ndl_lock->lock"
> > --> nd_region_acquire_lane --> lane# 5 --> bypass "ndl_lock->lock" due
> to "ndl_count->count++".
> >
> > Thread B on CPU 5 --> nd_region_acquire_lane --> lane# 5 --> bypass
> "ndl_lock->lock" ("ndl_count->count"
> > was changed by Thread A)
> >
> > If we use raw_smp_processor_id(), no matter which CPU the thread was
> migrated to,
> > if there is another thread running on the old CPU, there will be race
> condition
> > due to per CPU variable "ndl_count->count".
> 
> so I've been looking at it again. The recursive locking could have been
> solved better. Like the local_lock() on -RT is doing it.
> Given that you lock with preempt_disable() there should be no in-IRQ
> usage.
> But in the "nd_region->num_lanes >= nr_cpu_ids" case you don't take any
> locks. That would be a problem with raw_smp_processor_id() approach.
> 
> So what about the completely untested patch here:
> 
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index 379bf4305e615..98c2e9df4b2e4 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -109,7 +109,8 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata
> *ndd);
>                       res; res = next, next = next ? next->sibling : NULL)
> 
>  struct nd_percpu_lane {
> -     int count;
> +     struct task_struct *owner;
> +     int nestcnt;
>       spinlock_t lock;
>  };
> 
> diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
> index e2818f94f2928..8a62f9833513f 100644
> --- a/drivers/nvdimm/region_devs.c
> +++ b/drivers/nvdimm/region_devs.c
> @@ -946,19 +946,17 @@ int nd_blk_region_init(struct nd_region *nd_region)
>   */
>  unsigned int nd_region_acquire_lane(struct nd_region *nd_region)
>  {
> +     struct nd_percpu_lane *ndl_lock;
>       unsigned int cpu, lane;
> 
> -     cpu = get_cpu();
> -     if (nd_region->num_lanes < nr_cpu_ids) {
> -             struct nd_percpu_lane *ndl_lock, *ndl_count;
> -
> -             lane = cpu % nd_region->num_lanes;
> -             ndl_count = per_cpu_ptr(nd_region->lane, cpu);
> -             ndl_lock = per_cpu_ptr(nd_region->lane, lane);
> -             if (ndl_count->count++ == 0)
> -                     spin_lock(&ndl_lock->lock);
> -     } else
> -             lane = cpu;
> +     cpu = raw_smp_processor_id();
> +     lane = cpu % nd_region->num_lanes;
> +     ndl_lock  = per_cpu_ptr(nd_region->lane, lane);
> +     if (ndl_lock->owner != current) {
> +             spin_lock(&ndl_lock->lock);
> +             ndl_lock->owner = current;
> +     }
> +     ndl_lock->nestcnt++;
> 
>       return lane;
>  }
> @@ -966,17 +964,16 @@ EXPORT_SYMBOL(nd_region_acquire_lane);
> 
>  void nd_region_release_lane(struct nd_region *nd_region, unsigned int
> lane)
>  {
> -     if (nd_region->num_lanes < nr_cpu_ids) {
> -             unsigned int cpu = get_cpu();
> -             struct nd_percpu_lane *ndl_lock, *ndl_count;
> +     struct nd_percpu_lane *ndl_lock;
> 
> -             ndl_count = per_cpu_ptr(nd_region->lane, cpu);
> -             ndl_lock = per_cpu_ptr(nd_region->lane, lane);
> -             if (--ndl_count->count == 0)
> -                     spin_unlock(&ndl_lock->lock);
> -             put_cpu();
> -     }
> -     put_cpu();
> +     ndl_lock = per_cpu_ptr(nd_region->lane, lane);
> +     WARN_ON(ndl_lock->nestcnt == 0);
> +     WARN_ON(ndl_lock->owner != current);
> +     if (--ndl_lock->nestcnt)
> +             return;
> +
> +     ndl_lock->owner = NULL;
> +     spin_unlock(&ndl_lock->lock);
>  }
>  EXPORT_SYMBOL(nd_region_release_lane);
> 
> @@ -1042,7 +1039,8 @@ static struct nd_region *nd_region_create(struct
> nvdimm_bus *nvdimm_bus,
> 
>               ndl = per_cpu_ptr(nd_region->lane, i);
>               spin_lock_init(&ndl->lock);
> -             ndl->count = 0;
> +             ndl->owner = NULL;
> +             ndl->nestcnt = 0;
>       }
> 
>       for (i = 0; i < ndr_desc->num_mappings; i++) {
> 
> > Thanks,
> > Yongxin
> 

Consider the recursive call to nd_region_acquire_lane() in the following 
situation.
Will there be a dead lock?


    Thread A                    Thread B
       |                           |
       |                           |
     CPU 1                       CPU 2
       |                           |
       |                           |
 get lock for Lane 1         get lock for Lane 2
       |                           |
       |                           |
 migrate to CPU 2            migrate to CPU 1
       |                           |
       |                           |
 wait lock for Lane 2        wait lock for Lane 1 
       |                           |
       |                           |
       _____________________________
                   |
                dead lock ?


Thanks,
Yognxin


> Sebastian
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

Reply via email to