cpu_machdep,
cpu_copy_thread,
cpu_exec_vmspace_reuse,
cpu_exit,
cpu_fetch_syscall_args,
cpu_fork,
cpu_fork_kthread_handler,
cpu_idle, cpu_idle_wakeup,
cpu_procctl,
cpu_set_syscall_retval,
cpu_set_upcall,
cpu_set_user_tls,
cpu_switch, cpu_sync_core,
cpu_thread_alloc,
cpu_thread_clean,
cpu_thread_exit,
cpu_thread_free, cpu_throw
— machine-dependent interfaces to handle CPU and
thread state
#include
<sys/proc.h>
#include <sys/ptrace.h>
void
cpu_copy_thread(struct
thread *td, struct thread
*td0);
bool
cpu_exec_vmspace_reuse(struct
proc *p, struct vm_map
*map);
void
cpu_exit(struct
thread *td);
int
cpu_fetch_syscall_args(struct
thread *td);
void
cpu_fork(struct thread *td1,
struct proc *p2, struct thread
*td2, int flags);
void
cpu_fork_kthread_handler(struct thread
*td, void (*func)(void *), void
*arg);
void
cpu_idle(int
busy);
int
cpu_idle_wakeup(int
cpu);
int
cpu_procctl(struct thread *td,
int idtype, id_t id,
int com, void *data);
int
cpu_ptrace(struct
thread *_td, int
req, void *addr,
int data);
void
cpu_set_syscall_retval(struct
thread *td, int
error);
int
cpu_set_upcall(struct thread
*td, void (*entry)(void *), void
*arg, stack_t *stack);
int
cpu_set_user_tls(struct
thread *td, void
*tls_base);
void
cpu_switch(struct
thread *old, struct
thread *new, struct mtx
*mtx);
void
cpu_sync_core(void);
void
cpu_thread_alloc(struct
thread *td);
void
cpu_thread_clean(struct
thread *td);
void
cpu_thread_exit(struct
thread *td);
void
cpu_thread_free(struct
thread *td);
void
cpu_throw(struct
thread *old, struct
thread *new);
These functions provide architecture-specific implementations of
machine-independent abstractions.
cpu_exec_vmspace_reuse()
returns true if
exec_new_vmspace()
can reuse an existing struct vmspace
(map) for the process p during
execve(2).
This is only invoked if map is not shared with any
other consumers. If this returns false,
exec_new_vmspace() will create a new
struct vmspace.
cpu_exit()
releases machine-dependent resources other than the address space for the
process containing td during process exit.
cpu_fork()
copies and updates machine-dependent state (for example, the pcb and user
registers) from the forking thread td1 in an existing
process to the new thread td2 in the new process
p2. This function must set up the new thread's kernel
stack and pcb so that td2 calls
fork_exit()
when it begins execution passing a pointer to
fork_return()
as the callout argument and td2
as the arg argument.
cpu_fork_kthread_handler()
adjusts a new thread's initial pcb and/or kernel stack to pass
func and arg as the
callout and arg arguments to
fork_exit().
This must be called before a new thread is scheduled to run and is used to
set the “main” function for kernel threads.
cpu_copy_thread()
copies machine-dependent state (for example, the pcb and user registers)
from td to td0 when creating a
new thread in the same process. This function must set up the new thread's
kernel stack and pcb so that td0 calls
fork_exit()
when it begins execution passing a pointer to
fork_return()
as the callout argument and td0
as the arg argument.
cpu_set_upcall()
updates a new thread's initial user register state to call
entry with arg as the sole
argument using the user stack described in stack.
cpu_set_user_tls()
sets a new thread's initial user thread pointer register to reference the
user TLS base pointer tls_base.
cpu_fetch_syscall_args()
fetches the current system call arguments for the native FreeBSD ABI from
the current thread's user register state and/or user stack. The arguments
are saved in the td_sa member of
td.
cpu_set_syscall_retval()
updates the user register state for td to store system
call error and return values. If error is 0, indicate
success and return the two values in td_retval. If
error is ERESTART, adjust the
user PC to re-invoke the current system call after returning to user mode.
If error is EJUSTRETURN, leave
the current user register state unchanged. For any other value of
error, indicate error and return
error as the error code.
cpu_idle()
waits for the next interrupt to occur on the current CPU. If an architecture
supports low power idling, this function should place the CPU into a low
power state while waiting. busy is a hint from the
scheduler. If busy is non-zero, the scheduler expects
a short sleep, so the CPU should prefer low-latency over maximum power
savings. If busy is zero, the CPU should maximumize
power savings including deferring unnecessary clock interrupts via
cpu_idleclock().
cpu_idle_wakeup()
awakens the idle CPU with the ID cpu from a low-power
state.
cpu_procctl()
handles any machine-dependent
procctl(2)
requests.
cpu_ptrace()
handles any machine-dependent
ptrace(2)
requests.
cpu_switch()
switches the current CPU between threads by swapping register state. This
function saves the current CPU register state in the pcb of
old and loads register values from the pcb of
new before returning. While the pcb generally contains
caller-save kernel register state, it can also contain user registers that
are not saved in the trapframe.
After saving the current CPU register state of
old,
cpu_switch()
stores mtx in the td_lock member
of old transferring ownership of the old thread. No
data belonging to old can be accessed after that
store. Specifically, the old thread's kernel stack must not be accessed
after this point.
When SCHED_ULE is being used, this
function must wait (via spinning) for the td_lock
member of new to change to a value not equal to
&blocked_lock before loading register values from
new or accessing its kernel stack.
From the caller's perspective,
cpu_switch()
returns when old is rescheduled in the future,
possibly on a different CPU. However, the implementation of
cpu_switch() returns immediately on the same CPU
into the previously-saved context of new.
cpu_throw()
is similar to cpu_switch() but does not save any
state for old or write to the old thread's
td_lock member.
cpu_sync_core()
ensures that all possible speculation and out-of-order execution is
serialized on the current CPU. Note that this is called from an IPI handler
so only has to handle additional serialization beyond that provided by
handling an IPI.
These functions support the management of machine-dependent thread
state in conjunction with a thread object's lifecycle.
The general model is that a thread object is allocated each time a
new kernel thread is created either by system calls like
fork(2)
or
thr_new(2)
or when kernel-only threads are created via
kproc_create(9),
kproc_kthread_add(9),
or
kthread_add(9).
When a kernel thread exits, the thread object is freed. However, there is
one special case to support an optimization where each free process object
caches a thread object. When a process exits, the last thread object is not
freed but remains attached to the process. When the process object is later
reused for a new process in
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.
cpu_thread_alloc()
initializes machine-dependent fields in td after
allocating a new kernel stack. This function typically sets the
td_pcb and initial td_frame
pointers. cpu_thread_alloc() is called both when
allocating a new thread object and when a recycled thread allocates a new
kernel stack. Note that this function is
not called
if a recycled thread reuses its existing kernel stack.
cpu_thread_clean()
releases any machine-dependent resources for the last thread in a process
during
wait(2).
The thread is a candidate for recycling so should be reset to run as a new
thread in case it is recycled by a future
fork(2).
cpu_thread_exit()
cleans any machine-dependent state in td while it is
exiting. This is called by the exiting thread so cannot free state needed
during in-kernel execution.
cpu_thread_free()
releases any machine-dependent state in td when it is
being freed. This is called for any thread that was not the last thread in a
process once it has finished execution.
This manual page was developed by SRI International, the
University of Cambridge Computer Laboratory (Department of Computer Science
and Technology), and Capabilities Limited under contract (FA8750-24-C-B047)
(“DEC”).