cpu_thread_new_kstack: Introduce new MD callback

Previously, the cpu_thread_alloc callback was invoked each time a
kernel stack was allocated for a thread.  This included thread
creation, but it was also invoked if a recycled thread had to allocate
a new kstack.  This means that cpu_thread_alloc could be called
multiple times for a single thread, but cpu_thread_free is only called
once.  Not only that, but the cpu_thread_alloc callback can't tell if
it is being invoked on a new thread object, or a recycled thread.
Calling *_alloc multiple times on an object is also atypical for
kernel APIs.

As a result of this confusion, amd64 was potentially leaking an XSAVE
buffer each time a new kstack was allocated for an existing thread,
since cpu_thread_alloc for amd64 always allocated a new XSAVE buffer.
In practice, this edge case is probably rare.  A process object needs
to be recycled where either the new or old process is a kernel process
with a non-default kernel stack size.

Nevertheless, to ease the confusion, redefine cpu_thread_alloc to only
be called once when a new thread is allocated.  The new callback,
cpu_thread_new_kstack is invoked each time a kstack is allocated for a
thread, including both at thread creation time and if a recycled
thread allocates a new kstack.  The new callback should set any fields
whose value is dependent on td_kstack (e.g. the user frame in
td_frame, or td_pcb if the PCB is allocated on the kstack).

Reviewed by:	kib, andrew (arm changes)
Sponsored by:	AFRL, DARPA
Pull Request:	https://ron-dev.freebsd.org/FreeBSD/src/pulls/23
This commit is contained in:
John Baldwin
2026-03-28 13:56:24 +00:00
parent 59d67f2868
commit 2893bb741b
10 changed files with 55 additions and 13 deletions
+1
View File
@@ -943,6 +943,7 @@ MLINKS+=cpu_machdep.9 cpu_copy_thread.9 \
cpu_machdep.9 cpu_thread_clean.9 \
cpu_machdep.9 cpu_thread_exit.9 \
cpu_machdep.9 cpu_thread_free.9 \
cpu_machdep.9 cpu_thread_new_kstack.9 \
cpu_machdep.9 cpu_throw.9 \
cpu_machdep.9 cpu_update_pcb.9
MLINKS+=cpuset.9 CPUSET_T_INITIALIZER.9 \
+22 -6
View File
@@ -8,7 +8,7 @@
.\" Technology), and Capabilities Limited under Defense Advanced Research
.\" Projects Agency (DARPA) Contract No. FA8750-24-C-B047 ("DEC").
.\"
.Dd January 31, 2025
.Dd May 27, 2026
.Dt cpu_machdep 9
.Os
.Sh NAME
@@ -31,6 +31,7 @@
.Nm cpu_thread_clean ,
.Nm cpu_thread_exit ,
.Nm cpu_thread_free ,
.Nm cpu_thread_new_kstack ,
.Nm cpu_throw ,
.Nm cpu_update_pcb
.Nd machine-dependent interfaces to handle CPU and thread state
@@ -84,6 +85,8 @@
.Ft void
.Fn cpu_thread_free "struct thread *td"
.Ft void
.Fn cpu_thread_new_kstack "struct thread *td"
.Ft void
.Fn cpu_throw "struct thread *old" "struct thread *new"
.Ft void
.Fn cpu_update_pcb "struct thread *td"
@@ -366,19 +369,25 @@ When the process object is later reused for a new process in
.Xr fork 2 ,
the kernel recycles that last thread object and uses it as the initial
thread in the new process.
When a thread is recycled, some of the steps in the thread allocation
and free cycle are skipped as an optimization.
When a thread is recycled, a new kernel stack may be allocated if
the existing kernel stack is not suitable for the new process.
.Pp
.Fn cpu_thread_alloc
initializes machine-dependent fields in
.Fa td
when allocating a new thread object.
.Pp
.Fn cpu_thread_new_kstack
initializes kernel stack-related machine-dependent fields in
.Fa td
after allocating a new kernel stack.
This function typically sets the
.Fa td_pcb
.Pq on architectures which store the pcb in the kernel stack
and initial
.Fa td_frame
pointers.
.Fn cpu_thread_alloc
.Fn cpu_thread_new_kstack
is called both when allocating a new thread object and
when a recycled thread allocates a new kernel stack.
Note that this function is
@@ -386,12 +395,19 @@ Note that this function is
called if a recycled thread reuses its existing kernel stack.
.Pp
.Fn cpu_thread_clean
releases any machine-dependent resources for the last thread in a
releases machine-dependent resources for the last thread in a
process during
.Xr wait 2 .
The thread is a candidate for recycling so should be reset to run as a
Since the thread is a candidate for recycling,
machine-dependent fields should be reset to run as a
new thread in case it is recycled by a future
.Xr fork 2 .
In particular,
if the thread reuses its existing kernel stack,
no other
.Fn cpu_thread_*
function will be invoked before the thread is reused as the main
thread of a new process.
.Pp
.Fn cpu_thread_exit
cleans any machine-dependent state in