Page MenuHomeFreeBSD

Add an EARLY_AP_STARTUP option to start APs earlier during boot.
ClosedPublic

Authored by jhb on Apr 22 2016, 7:31 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Mar 30, 10:12 PM
Unknown Object (File)
Mar 2 2025, 5:02 PM
Unknown Object (File)
Feb 9 2025, 4:06 AM
Unknown Object (File)
Dec 23 2024, 7:17 PM
Unknown Object (File)
Oct 2 2024, 4:56 AM
Unknown Object (File)
Oct 2 2024, 4:01 AM
Unknown Object (File)
Oct 1 2024, 5:14 PM
Unknown Object (File)
Oct 1 2024, 2:59 PM
Subscribers

Details

Summary

Add an EARLY_AP_STARTUP option to start APs earlier during boot.

Currently, Application Processors (non-boot CPUs) are started by
MD code at SI_SUB_CPU, but they are kept waiting in a "pen" until
SI_SUB_SMP at which point they are released to run kernel threads.
SI_SUB_SMP is one of the last SYSINIT levels, so APs don't enter
the scheduler and start running threads until fairly late in the
boot.

This change moves SI_SUB_SMP up to just before software interrupt
threads are created allowing the APs to start executing kernel
threads much sooner (before any devices are probed). This allows
several initialization routines that need to perform initialization
on all CPUs to now perform that initialization in one step rather
than having to defer the AP initialization to a second SYSINIT run
at SI_SUB_SMP. It also permits all CPUs to be available for
handling interrupts before any devices are probed.

This last feature fixes a problem on with interrupt vector exhaustion.
Specifically, in the old model all device interrupts were routed
onto the boot CPU during boot. Later after the APs were released at
SI_SUB_SMP, interrupts were redistributed across all CPUs.

However, several drivers for multiqueue hardware allocate N interrupts
per CPU in the system. In a system with many CPUs, just a few drivers
doing this could exhaust the available pool of interrupt vectors on
the boot CPU as each driver was allocating N * mp_ncpu vectors on the
boot CPU. Now, drivers will allocate interrupts on their desired CPUs
during boot meaning that only N interrupts are allocated from the boot
CPU instead of N * mp_ncpu.

Some other bits of code can also be simplified as smp_started is
now true much earlier and will now always be true for these bits of
code. This removes the need to treat the single-CPU boot environment
as a special case.

As a transition aid, the new behavior is moved under a new kernel
option (EARLY_AP_STARTUP). This will allow the option to be turned off
if need be during initial testing. I hope to enable this on x86 by
default in a followup commit and to have all platforms moved over before
11.0. Once the transition is complete, the option will be removed along
with the !EARLY_AP_STARTUP code.

These changes have only been tested on x86. Other platform maintainers
are encouraged to port their architectures over as well. The main
things to check for are any uses of smp_started in MD code that can be
simplified and SI_SUB_SMP SYSINITs in MD code that can be removed in
the EARLY_AP_STARTUP case (e.g. the interrupt shuffling).

PR: kern/199321
Sponsored by: Netflix

Test Plan
  • boot both GENERIC and GENERIC + EARLY kernels on amd64 (bhyve VM and fox2 in the netperf lab) and i386 (bhyve VM)

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 3415
Build 3453: arc lint + arc unit

Event Timeline

jhb retitled this revision from to Add an EARLY_AP_STARTUP option to start APs earlier during boot..
jhb updated this object.
jhb edited the test plan for this revision. (Show Details)
jhb added reviewers: kib, scottl, andrew, imp.
ngie added inline comments.
sys/cddl/dev/dtrace/amd64/dtrace_subr.c
249–259

Out of curiosity... can this be consolidated under x86/ ?

sys/dev/xen/control/control.c
202–207

This seems to duplicate code in acpi.c... should this be consolidated?

sys/kern/kern_clocksource.c
496

This pattern is used in a number of places. Can it be turned into a simple macro?

jhb added inline comments.
sys/cddl/dev/dtrace/amd64/dtrace_subr.c
249–259

That's orthogonal to this change and more a question for @markj .

sys/dev/xen/control/control.c
202–207

It's only somewhat duplicated, there are some differences.

sys/kern/kern_clocksource.c
496

I'm not sure a wrapper macro for this would really be clearer? (ASSERT_THAT_ALL_CPUS_ARE_RUNNING) I will probably also end up removing many of these once the initial testing phase is over and the !EARLY code is removed.

markj added a reviewer: markj.
markj added inline comments.
sys/cddl/dev/dtrace/amd64/dtrace_subr.c
249–259

I don't see any reason it couldn't be, but that's only because we've chosen to use the TSC for DTrace's high-res timer on both i386 and amd64. It might be useful to keep the ability to change amd64's timer without affecting i386.

This revision is now accepted and ready to land.Apr 22 2016, 8:42 PM
gnn edited edge metadata.
kib edited edge metadata.
kib added inline comments.
sys/kern/kern_clock.c
431

I am somewhat uneasy about this skip of PRS_NEW processes. We could miss a thread which is already blocked or sleeping. Then, either deadlkres would fire unduly, or worse, scheduler update of priority etc for the omited process is off by huge mark dur to INT_MAX hack.

I suppose it is safe to yield and retry if a PRS_NEW process was catched.

sys/kern/kern_clock.c
431

There are probably no PRS_NEW processes in practice (the other kthreads that have been created at this point don't create more kthreads and thread0 is running this). However, it might be simpler to move the 'ticks' fixup earlier before any kthreads are started in which case this won't be needed at all.

sys/kern/kern_clock.c
431

Moving ticks earlier isn't feasible as 'hz' is set by cpu_initclocks() a few lines before in this function. I think I will just assert that PRS_NEW is not true for now. If it becomes an issue in the future we can make the loop restart.

Actually, if the process is PRS_NEW, then the thread that is creating it hasn't yet started any of the threads in the process. (The state is changed to PRS_NORMAL under the PROC_LOCK before the thread is placed on the runqueue.) That means that the thread for that process can't possibly be asleep or blocked on a lock (the only conditions we need to update). I think the code is fine as-is.

scottl edited edge metadata.