Re: [RFC 06/10] kmod: provide sanity check on kmod_concurrent access

From: Luis R. Rodriguez
Date: Tue Jan 10 2017 - 15:02:05 EST


On Thu, Dec 15, 2016 at 01:57:48PM +0100, Petr Mladek wrote:
> On Thu 2016-12-08 11:48:50, Luis R. Rodriguez wrote:
> > Only decrement *iff* we're possitive. Warn if we've hit
> > a situation where the counter is already 0 after we're done
> > with a modprobe call, this would tell us we have an unaccounted
> > counter access -- this in theory should not be possible as
> > only one routine controls the counter, however preemption is
> > one case that could trigger this situation. Avoid that situation
> > by disabling preemptiong while we access the counter.
> >
> > Signed-off-by: Luis R. Rodriguez <mcgrof@xxxxxxxxxx>
> > ---
> > kernel/kmod.c | 20 ++++++++++++++++----
> > 1 file changed, 16 insertions(+), 4 deletions(-)
> >
> > diff --git a/kernel/kmod.c b/kernel/kmod.c
> > index ab38539f7e91..09cf35a2075a 100644
> > --- a/kernel/kmod.c
> > +++ b/kernel/kmod.c
> > @@ -113,16 +113,28 @@ static int call_modprobe(char *module_name, int wait)
> >
> > static int kmod_umh_threads_get(void)
> > {
> > + int ret = 0;
> > +
> > + preempt_disable();
> > atomic_inc(&kmod_concurrent);
> > if (atomic_read(&kmod_concurrent) < max_modprobes)
> > - return 0;
> > - atomic_dec(&kmod_concurrent);
> > - return -EBUSY;
> > + goto out;
>
> I though more about it and the disabled preemtion might make
> sense here. It makes sure that we are not rescheduled here
> and that kmod_concurrent is not increased by mistake for too long.

I think its good to add a comment here about this.

> Well, it still would make sense to increment the value
> only when it is under the limit and set the incremented
> value using cmpxchg to avoid races.
>
> I mean to use similar trick that is used by refcount_inc(), see
> https://lkml.kernel.org/r/20161114174446.832175072@xxxxxxxxxxxxx

Right, I see now. Since we are converting this to kref though we would
immediately get the advantages of kref_get() using the new refcount_inc() once
that goes in, so I think its best we just sit tight to get that benefit given
as Jessica acknowledged the existing code has has this issue for ages, waiting
a bit longer should not hurt. The preemption should help in the meantime as
well.

The note I've made then is:

/*
* Disabling preemption makes sure that we are not rescheduled here.
*
* Also preemption helps kmod_concurrent is not increased by mistake
* for too long given in theory two concurrent threads could race on
* kref_get() before we kref_read().
*
* XXX: once Peter's refcount_t gets merged kref's kref_get() will use
* the new refcount_inc() and then each inc will be atomic with respect
* to each thread, as such when Peter's refcount_t gets merged
* the above comment "Also preemption ..." can be removed.
*/

Come to think of it, once Peter's changes go in at first glance it may seem
preemption would be pointless then but but I think that just mitigates a few
of the refcount_inc() instances where (old != val), that is -- when two threads
got the same bump, so think it can be kept even after Peter's refcount_t work.

> > + atomic_dec_if_positive(&kmod_concurrent);
> > + ret = -EBUSY;
> > +out:
> > + preempt_enable();
> > + return 0;
> > }
> >
> > static void kmod_umh_threads_put(void)
> > {
> > - atomic_dec(&kmod_concurrent);
> > + int ret;
> > +
> > + preempt_disable();
> > + ret = atomic_dec_if_positive(&kmod_concurrent);
> > + WARN_ON(ret < 0);
> > + preempt_enable();
>
> The disabled preemption does not make much sense here.
> We do not need to tie the atomic operation and the WARN
> together so tightly.

Makes sense, will add a note.

kref also lacks such a mnemonic as atomic_dec_if_positive()
and since I've now converted this to kref I've dropped this.

Luis