Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Mar 2019 21:01:34 -0400
From:      Mark Johnston <markj@freebsd.org>
To:        =?utf-8?Q?Mi=C5=82osz?= Kaniewski <m.kaniewski@fudosecurity.com>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: sched_pickcpu() and a pinned thread.
Message-ID:  <20190320010134.GD74753@raichu>
In-Reply-To: <CABQUGZzxrnS%2B9xkrbufc9cx1jyG%2B1V553AurYGeSV-WtJX0A2w@mail.gmail.com>
References:  <CABQUGZzxrnS%2B9xkrbufc9cx1jyG%2B1V553AurYGeSV-WtJX0A2w@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, Mar 19, 2019 at 10:10:21PM +0100, MiƂosz Kaniewski wrote:
> Hi,
> 
> I have an application which exchanges packets between two NIC ports.
> Most of a CPU time is therefore consumed by the userspace process and
> two IRQ threads. All of them are pinned to the same CPU using
> cpu_setaffinity() function so I expect that the scheduler usage should
> be minimal even on a high network load when a lot of interrupts are
> generated.
> I recently find out that my application spends a lot of time in
> cpu_search_lowest() function.  A backtrace shown me that
> cpu_search_lowest() is called by sched_pickcpu(). I tried to analyze
> this second function but I don't know what exactly should I expect
> from it if my thread is pinned to a specific CPU. I assume that the
> function should get to a point where ts->ts_cpu is returned. However
> it doesn't do it and sched_pickcpu() always ends looking for the
> lowest busy CPU using cpu_search_lowest(). In result it finds the CPU
> that the thread is pinned to but it is very time consuming process.
> Therefore I suspect that sched_pickcpu() should return before that
> happens.
> 
> For interrupt threads I see that there is a special case at the
> beginning of sched_pickcpu():
> /*
>      * Prefer to run interrupt threads on the processors that generate
>      * the interrupt.
>      */
>     pri = td->td_priority;
>     if (td->td_priority <= PRI_MAX_ITHD && THREAD_CAN_SCHED(td, self) &&
>         curthread->td_intr_nesting_level && ts->ts_cpu != self) {
>         SCHED_STAT_INC(pickcpu_intrbind);
>         ts->ts_cpu = self;
>         if (TDQ_CPU(self)->tdq_lowpri > pri) {
>             SCHED_STAT_INC(pickcpu_affinity);
>             return (ts->ts_cpu);
>         }
>     }
> However IRQ threads from the NIC doesn't fall into this case because
> ts->ts_cpu == self (these variables are equal to the CPU to which my
> threads are pinned). Is this check required?
> 
> For non-interrupt threads there are some other possible options where
> ts->ts_cpu is returned however I am not sure which should handle a
> pinned thread.
> 
> I would be grateful if someone would give me some clue how should I
> understand the sched_pickcpu() function and when it should return in
> case of a pinned thread.

sched_pickcpu() fetches the current cpuset's mask immediately before
calling sched_lowest().  In sched_search(), we skip over CPUs that
aren't in the mask with the check CPU_ISSET(cpu, &lgroup.cs_mask).  So
eventually it finds the CPU to which the threads are pinned, but the
current algorithm does seem rather inefficient for your case.  

As an experiment you can try the hack below to see how much better you
can do if sched_search() is avoided entirely.  I did not test it at all,
so be careful if you try it.

diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
index b20604d11274..0f105480a871 100644
--- a/sys/kern/sched_ule.c
+++ b/sys/kern/sched_ule.c
@@ -1314,7 +1314,9 @@ sched_pickcpu(struct thread *td, int flags)
 	cpu = -1;
 	/* Search the group for the less loaded idle CPU we can run now. */
 	mask = td->td_cpuset->cs_mask;
-	if (cg != NULL && cg != cpu_top &&
+	if (CPU_COUNT(&mask) == 1)
+		cpu = CPU_FFS(&mask) - 1;
+	if (cpu == -1 && cg != NULL && cg != cpu_top &&
 	    CPU_CMP(&cg->cg_mask, &cpu_top->cg_mask) != 0)
 		cpu = sched_lowest(cg, mask, max(pri, PRI_MAX_TIMESHARE),
 		    INT_MAX, ts->ts_cpu);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20190320010134.GD74753>