Am Tue, Sep 07, 2021 at 02:43:22PM +0200 schrieb Patrick Wildt:
> Am Mon, Sep 06, 2021 at 09:43:29PM +0200 schrieb Patrick Wildt:
> > Am Fri, Jul 30, 2021 at 07:55:29PM +0200 schrieb Alexander Bluhm:
> > > On Mon, Jul 26, 2021 at 08:12:39AM -0500, Scott Cheloha wrote:
> > > > On Fri, Jun 25, 2021 at 06:09:27PM -0500, Scott Cheloha wrote:
> > > > 1 month bump.  I really appreciate the tests I've gotten so far, thank
> > > > you.
> > > 
> > > On my Xeon machine it works and all regress tests pass.
> > > 
> > > But it fails on my old Opteron machine.  It hangs after attaching
> > > cpu1.
> > 
> > This seems to be caused by contention on the mutex in i8254's gettick().
> > 
> > With Scott's diff, delay_func is i8254_delay() on that old AMD machine.
> > Its gettick() implementation uses a mutex to protect I/O access to the
> > i8254.
> > 
> > When secondary CPUs come up, they will wait for CPU0 to let them boot up
> > further by checking for a flag:
> > 
> >     /*
> >      * We need to wait until we can identify, otherwise dmesg
> >      * output will be messy.
> >      */
> >     while ((ci->ci_flags & CPUF_IDENTIFY) == 0)
> >             delay(10);
> > 
> > Now that machine has 3 secondary cores that are spinning like that.  At
> > the same time CPU0 waits for the core to come up:
> > 
> >     /* wait for it to identify */
> >     for (i = 2000000; (ci->ci_flags & CPUF_IDENTIFY) && i > 0; i--)
> >             delay(10);
> > 
> > That means we have 3-4 cores spinning just to be able to delay().  Our
> > mutex implementation isn't fair, which means whoever manages to claim
> > the free mutex wins.  Now if CPU2 and CPU3 are spinning all the time,
> > CPU1 identifies and needs delay() and CPU0 waits for CPU1, maybe the
> > one that needs to make progress never gets it.
> > 
> > I changed those delay(10) in cpu_hatch() to CPU_BUSY_CYCLE() and it went
> > ahead a bit better instead of hanging forever.
> > 
> > Then I remembered an idea something from years ago: fair kernel mutexes,
> > so basically mutexes implemented as ticket lock, like our kerne lock.
> > 
> > I did a quick diff, which probably contains a million bugs, but with
> > this bluhm's machine boots up well.
> > 
> > I'm not saying this is the solution, but it might be.
> > 
> > Patrick
> 
> Cleaned the diff up a little, changes since last time:
> 
> * Rename the struct members to be the same as mplock.
> * Change the code to use ticket/user numbers like mplock.  This
>   has one obvious downside: If a mutex is not initialized, trying
>   to get this mutex will result in a hang.  At least that just let
>   me find some uninitialized mutexes.
> * More consistent use of the 'ci' variable.
> * Definitely compiles with/without DIAGNOSTIC.
> * Made sure mtx_enter() still has the membar.
> * No need for READ_ONCE() when members are volatile.
> 
> Apart from being fair, this diff also changes the behaviour while
> spinning for a lock.  Previously mtx_enter called mtx_enter_try
> in a loop until it got the lock.  mtx_enter_try does splraise,
> try lock, splx.  This diff currently spins with the SPL raised,
> so that's a change in behaviour.  I'm sure I can change the diff
> to splraise/splx while looping, if we prefer that behaviour.
> 
> Patrick

make -j17 seems to have used less system time, so that seemed to have
made the machine slightly faster:

old: make -j17  1160.01s user 3244.58s system 1288% cpu 5:41.96 total
new: make -j17  1171.80s user 3059.67s system 1295% cpu 5:26.65 total

I'll change the diff to do splraise/splx while looping, to make the
behaviour more similar to before, and then re-do my testing.

Patrick

Reply via email to