svn commit: r315136 - head/sys/netpfil/pf

John Baldwin jhb at freebsd.org
Wed Mar 15 19:18:44 UTC 2017


On Wednesday, March 15, 2017 10:26:39 AM Kristof Provost wrote:
> On 15 Mar 2017, at 6:57, Gleb Smirnoff wrote:
> > On Sun, Mar 12, 2017 at 05:42:57AM +0000, Kristof Provost wrote:
> > K> Log:
> > K>   pf: Fix incorrect rw_sleep() in pf_unload()
> > K>
> > K>   When we unload we don't hold the pf_rules_lock, so we cannot call 
> > rw_sleep()
> > K>   with it, because it would release a lock we do not hold. There's 
> > no need for the
> > K>   lock either, so we can just tsleep().
> > K>
> > K>   While here also make the same change in pf_purge_thread(), 
> > because it explicitly
> > K>   takes the lock before rw_sleep() and then immediately releases it 
> > afterwards.
> >
> > The correct change would to be grab lock in pf_unload(), exactly as 
> > pf_purge_thread()
> > does. With your change you introduces a possible infinite sleep due to 
> > race, since
> > there is no timeout and no lock.
> >
> I must be missing something, because I don’t see the race, and don’t 
> see how we
> could end up with an infinite sleep.

You are ignoring interrupts and preemption.  Suppose you get an interrupt
after 'wakeup_one(pf_purge_thread)' and before 'tsleep(..., 0)' in
pf_unload().  If the interrupt preempts and results in the purge thread
running and issuing its wakeup before the thread executing pf_unload()
resumes, then eventually when pf_unload() resumes it will do a tsleep() with
no timeout that will never be awoken.

You obviously didn't test this in a debug kernel since there is a KASSERT
explicitly to catch obvious tsleep races in _sleep():

        KASSERT(sbt != 0 || mtx_owned(&Giant) || lock != NULL,
            ("sleeping without a lock"));

You should fix this in the way that Gleb suggested.

Also, all kthreads/kprocs do a wakeup() inside of exit1() or kthread_exit()
to allow you to wait for a kthread to exit when unloading a module.  The
general structure should be something like:

struct thread *my_thread;

void
thread_main(void *arg)
{

    LOCK(&mylock);
    while (!thread_quit) {
       UNLOCK(&mylock);
       /* do work */
       LOCK(&mylock);
       if (!thread_quit && no_work_to_do)
           lock_sleep(&some_wchan, &mylock, ...);
    }
    UNLOCK(&mylock);
    kthread_exit();
}

void
unload_handler(...)
{
    ...

    LOCK(&mylock);
    thread_quit = true;
    wakeup(&some_wchan);
    lock_sleep(my_thread, &mylock, ...);
    UNLOCK(&mylock);
}

void
load_handler(...)
{
    ...

    kthread_add(thread_main, arg, NULL, &my_thread, ...);
}

If you want to create a proc then you can use 'struct proc *my_proc'
and sleep on 'my_proc' instead (along with using kproc_exit(), though
kthread_exit() from the last thread in a kproc should call kproc_exit()
for you).

-- 
John Baldwin


More information about the svn-src-head mailing list