Date: Mon, 2 Aug 2021 05:43:57 +0200 From: Mateusz Guzik <mjguzik@gmail.com> To: Alexander Motin <mav@freebsd.org> Cc: src-committers@freebsd.org, dev-commits-src-all@freebsd.org, dev-commits-src-main@freebsd.org Subject: Re: git: 8bb173fb5bc3 - main - sched_ule(4): Use trylock when stealing load. Message-ID: <CAGudoHEeOZ8aSijNnyU3UU4Cb2BfTXoYiM%2BBHD-A1JgnU_OD0A@mail.gmail.com> In-Reply-To: <202108020258.1722walG094152@gitrepo.freebsd.org> References: <202108020258.1722walG094152@gitrepo.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Mostly related, I just had a look at the code and noticed:
static uint32_t
sched_random(void)
{
uint32_t *rndptr;
rndptr = DPCPU_PTR(randomval);
*rndptr = *rndptr * 69069 + 5;
return (*rndptr >> 16);
}
Except randomval starts at 0 for all CPUs. Should be easy to pre-init
with an actual random value. Is that something you played with?
On 8/2/21, Alexander Motin <mav@freebsd.org> wrote:
> The branch main has been updated by mav:
>
> URL:
> https://cgit.FreeBSD.org/src/commit/?id=8bb173fb5bc33a02d5a4670c9a60bba0ece07bac
>
> commit 8bb173fb5bc33a02d5a4670c9a60bba0ece07bac
> Author: Alexander Motin <mav@FreeBSD.org>
> AuthorDate: 2021-08-02 02:42:01 +0000
> Commit: Alexander Motin <mav@FreeBSD.org>
> CommitDate: 2021-08-02 02:42:01 +0000
>
> sched_ule(4): Use trylock when stealing load.
>
> On some load patterns it is possible for several CPUs to try steal
> thread from the same CPU despite randomization introduced. It may
> cause significant lock contention when holding one queue lock idle
> thread tries to acquire another one. Use of trylock on the remote
> queue allows both reduce the contention and handle lock ordering
> easier. If we can't get lock inside tdq_trysteal() we just return,
> allowing tdq_idled() handle it. If it happens in tdq_idled(), then
> we repeat search for load skipping this CPU.
>
> On 2-socket 80-thread Xeon system I am observing dramatic reduction
> of the lock spinning time when doing random uncached 4KB reads from
> 12 ZVOLs, while IOPS increase from 327K to 403K.
>
> MFC after: 1 month
> ---
> sys/kern/sched_ule.c | 39 +++++++++++++++++++++++++--------------
> 1 file changed, 25 insertions(+), 14 deletions(-)
>
> diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
> index 028e07efa889..1bdcfb1f793d 100644
> --- a/sys/kern/sched_ule.c
> +++ b/sys/kern/sched_ule.c
> @@ -300,6 +300,8 @@ static struct tdq tdq_cpu;
> #define TDQ_LOCK_ASSERT(t, type) mtx_assert(TDQ_LOCKPTR((t)), (type))
> #define TDQ_LOCK(t) mtx_lock_spin(TDQ_LOCKPTR((t)))
> #define TDQ_LOCK_FLAGS(t, f) mtx_lock_spin_flags(TDQ_LOCKPTR((t)), (f))
> +#define TDQ_TRYLOCK(t) mtx_trylock_spin(TDQ_LOCKPTR((t)))
> +#define TDQ_TRYLOCK_FLAGS(t, f) mtx_trylock_spin_flags(TDQ_LOCKPTR((t)),
> (f))
> #define TDQ_UNLOCK(t) mtx_unlock_spin(TDQ_LOCKPTR((t)))
> #define TDQ_LOCKPTR(t) ((struct mtx *)(&(t)->tdq_lock))
>
> @@ -989,13 +991,22 @@ tdq_idled(struct tdq *tdq)
> if (steal->tdq_load < steal_thresh ||
> steal->tdq_transferable == 0)
> goto restart;
> - tdq_lock_pair(tdq, steal);
> /*
> - * We were assigned a thread while waiting for the locks.
> - * Switch to it now instead of stealing a thread.
> + * Try to lock both queues. If we are assigned a thread while
> + * waited for the lock, switch to it now instead of stealing.
> + * If we can't get the lock, then somebody likely got there
> + * first so continue searching.
> */
> - if (tdq->tdq_load)
> - break;
> + TDQ_LOCK(tdq);
> + if (tdq->tdq_load > 0) {
> + mi_switch(SW_VOL | SWT_IDLE);
> + return (0);
> + }
> + if (TDQ_TRYLOCK_FLAGS(steal, MTX_DUPOK) == 0) {
> + TDQ_UNLOCK(tdq);
> + CPU_CLR(cpu, &mask);
> + continue;
> + }
> /*
> * The data returned by sched_highest() is stale and
> * the chosen CPU no longer has an eligible thread, or
> @@ -1948,18 +1959,18 @@ tdq_trysteal(struct tdq *tdq)
> if (steal->tdq_load < steal_thresh ||
> steal->tdq_transferable == 0)
> continue;
> - tdq_lock_pair(tdq, steal);
> /*
> - * If we get to this point, unconditonally exit the loop
> - * to bound the time spent in the critcal section.
> - *
> - * If a thread was added while interrupts were disabled don't
> - * steal one here.
> + * Try to lock both queues. If we are assigned a thread while
> + * waited for the lock, switch to it now instead of stealing.
> + * If we can't get the lock, then somebody likely got there
> + * first. At this point unconditonally exit the loop to
> + * bound the time spent in the critcal section.
> */
> - if (tdq->tdq_load > 0) {
> - TDQ_UNLOCK(steal);
> + TDQ_LOCK(tdq);
> + if (tdq->tdq_load > 0)
> + break;
> + if (TDQ_TRYLOCK_FLAGS(steal, MTX_DUPOK) == 0)
> break;
> - }
> /*
> * The data returned by sched_highest() is stale and
> * the chosen CPU no longer has an eligible thread.
>
--
Mateusz Guzik <mjguzik gmail.com>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAGudoHEeOZ8aSijNnyU3UU4Cb2BfTXoYiM%2BBHD-A1JgnU_OD0A>
