Message ID | 20190716132345.ujj2v3kqra2lbi75@gondor.apana.org.au (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Herbert Xu |
Headers | show |
Series | [v2] padata: Use RCU when fetching pd from do_serial | expand |
On Tue, Jul 16, 2019 at 09:23:45PM +0800, Herbert Xu wrote: > On Tue, Jul 16, 2019 at 09:09:28PM +0800, Herbert Xu wrote: > > > > Hmm, it doesn't work because the refcnt is attached to the old > > pd. That shouldn't be a problem though as we could simply ignore > > the refcnt in padata_flush_queue. > > This should fix it: > > ---8<--- > The function padata_do_serial uses parallel_data without obeying > the RCU rules around its life-cycle. This means that a concurrent > padata_replace call can result in a crash. > > This patch fixes it by using RCU just as we do in padata_do_parallel. RCU alone won't help because if some object is queued for async crypto, we left the RCU protected aera. I think padata_do_serial needs to do RCU and should free 'parallel_data' if the flag PADATA_RESET is set and the refcount goes to zero. padata_replace should do the same then. > > As the refcnt may now span two parallel_data structures, this patch > moves it to padata_instance instead. FWIW the refcnt is used to > limit the number of outstanding requests (albeit a soft limit as > we don't do a proper atomic inc and test). > > Fixes: 16295bec6398 ("padata: Generic parallelization/...") > Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> > > diff --git a/include/linux/padata.h b/include/linux/padata.h ... > index 5d13d25da2c8..ce51555cb86c 100644 > @@ -367,7 +367,7 @@ void padata_do_serial(struct padata_priv *padata) > struct parallel_data *pd; > int reorder_via_wq = 0; > > - pd = padata->pd; > + pd = rcu_dereference_bh(padata->inst->pd); Why not just pd = rcu_dereference_bh(padata->pd);
On Wed, Jul 17, 2019 at 10:36:41AM +0200, Steffen Klassert wrote: > > > This patch fixes it by using RCU just as we do in padata_do_parallel. > > RCU alone won't help because if some object is queued for async > crypto, we left the RCU protected aera. I think padata_do_serial > needs to do RCU and should free 'parallel_data' if the flag > PADATA_RESET is set and the refcount goes to zero. padata_replace > should do the same then. Yes this patch doesn't work because you can't just switch over to the new pd as the old pd will then get stuck due to the missing entry. > > index 5d13d25da2c8..ce51555cb86c 100644 > > @@ -367,7 +367,7 @@ void padata_do_serial(struct padata_priv *padata) > > struct parallel_data *pd; > > int reorder_via_wq = 0; > > > > - pd = padata->pd; > > + pd = rcu_dereference_bh(padata->inst->pd); > > Why not just > > pd = rcu_dereference_bh(padata->pd); I was trying to get the new pd which could only come from the inst. In any case the whole RCU idea doesn't work so we'll probably do the refcount idea that you suggested. Cheers,
diff --git a/include/linux/padata.h b/include/linux/padata.h index 5d13d25da2c8..ce51555cb86c 100644 --- a/include/linux/padata.h +++ b/include/linux/padata.h @@ -35,7 +35,7 @@ * struct padata_priv - Embedded to the users data structure. * * @list: List entry, to attach to the padata lists. - * @pd: Pointer to the internal control structure. + * @inst: Pointer to the overall control structure. * @cb_cpu: Callback cpu for serializatioon. * @cpu: Cpu for parallelization. * @seq_nr: Sequence number of the parallelized data object. @@ -45,7 +45,7 @@ */ struct padata_priv { struct list_head list; - struct parallel_data *pd; + struct padata_instance *inst; int cb_cpu; int cpu; int info; @@ -120,7 +120,6 @@ struct padata_cpumask { * @pqueue: percpu padata queues used for parallelization. * @squeue: percpu padata queues used for serialuzation. * @reorder_objects: Number of objects waiting in the reorder queues. - * @refcnt: Number of objects holding a reference on this parallel_data. * @max_seq_nr: Maximal used sequence number. * @cpumask: The cpumasks in use for parallel and serial workers. * @lock: Reorder lock. @@ -132,7 +131,6 @@ struct parallel_data { struct padata_parallel_queue __percpu *pqueue; struct padata_serial_queue __percpu *squeue; atomic_t reorder_objects; - atomic_t refcnt; atomic_t seq_nr; struct padata_cpumask cpumask; spinlock_t lock ____cacheline_aligned; @@ -152,6 +150,7 @@ struct parallel_data { * or both cpumasks change. * @kobj: padata instance kernel object. * @lock: padata instance lock. + * @refcnt: Number of objects holding a reference on this padata_instance. * @flags: padata flags. */ struct padata_instance { @@ -162,6 +161,7 @@ struct padata_instance { struct blocking_notifier_head cpumask_change_notifier; struct kobject kobj; struct mutex lock; + atomic_t refcnt; u8 flags; #define PADATA_INIT 1 #define PADATA_RESET 2 diff --git a/kernel/padata.c b/kernel/padata.c index 2d2fddbb7a4c..c86333fa3cec 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -123,12 +123,12 @@ int padata_do_parallel(struct padata_instance *pinst, if ((pinst->flags & PADATA_RESET)) goto out; - if (atomic_read(&pd->refcnt) >= MAX_OBJ_NUM) + if (atomic_read(&pinst->refcnt) >= MAX_OBJ_NUM) goto out; err = 0; - atomic_inc(&pd->refcnt); - padata->pd = pd; + atomic_inc(&pinst->refcnt); + padata->inst = pinst; padata->cb_cpu = cb_cpu; target_cpu = padata_cpu_hash(pd); @@ -347,7 +347,7 @@ static void padata_serial_worker(struct work_struct *serial_work) list_del_init(&padata->list); padata->serial(padata); - atomic_dec(&pd->refcnt); + atomic_dec(&pd->pinst->refcnt); } local_bh_enable(); } @@ -367,7 +367,7 @@ void padata_do_serial(struct padata_priv *padata) struct parallel_data *pd; int reorder_via_wq = 0; - pd = padata->pd; + pd = rcu_dereference_bh(padata->inst->pd); cpu = get_cpu(); @@ -489,7 +489,6 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst, timer_setup(&pd->timer, padata_reorder_timer, 0); atomic_set(&pd->seq_nr, -1); atomic_set(&pd->reorder_objects, 0); - atomic_set(&pd->refcnt, 0); pd->pinst = pinst; spin_lock_init(&pd->lock); @@ -535,8 +534,6 @@ static void padata_flush_queues(struct parallel_data *pd) squeue = per_cpu_ptr(pd->squeue, cpu); flush_work(&squeue->work); } - - BUG_ON(atomic_read(&pd->refcnt) != 0); } static void __padata_start(struct padata_instance *pinst)