Expand description
§ax-percpu
Typed per-CPU layout, initialization, and access for no_std kernels.
ax-percpu is dynamic-only. The final ELF contains one layout template; the
platform allocates one writable runtime area per CPU and initializes the
complete layout before any CPU is bound. Architecture register ownership is
provided by the separate cpu-local crate.
§Runtime contract
The initialization sequence is fixed:
- The linker retains exactly one
.percpu.templateplus the.percpu.initand.percpu.aligndescriptor tables. - The platform allocates shutdown-lifetime storage for every CPU area.
initialize_layout(PerCpuRegion)validates the complete geometry and descriptor tables before the first destination write.- Each
CpuAreaPrefixand each typed value is constructed once at its final runtime address. - The layout is frozen; the platform may then install
area(cpu).cpu_area()through its offline-CPU boundary.
There is no linked runtime layout or static per-CPU replication. Changing the runtime CPU count therefore does not change the ELF template size.
The linker contract uses only these output sections:
.percpu.template.percpu.init.percpu.align
Generated storage is placed in .percpu.template.storage; the fixed prefix
and end sentinel use .percpu.template.header and .percpu.template.end.
Linker boundaries use __PERCPU_* and __CPU_LOCAL_* names.
§Typed access
#[ax_percpu::def_percpu]
static CPU_ID: usize = 0;
fn set_cpu_id(pin: &ax_percpu::CpuPin<'_>, cpu_id: usize) {
ax_percpu::current_area(pin).expect("CPU area must match the frozen layout");
CPU_ID.write_current(pin, cpu_id);
assert_eq!(CPU_ID.read_current(pin), cpu_id);
}Primitive values use the matching atomic storage type. Object initializers are retained as typed descriptor thunks and construct one independent value in each final runtime area; arbitrary Rust object bytes are never duplicated from the ELF template.
Current access requires a scoped CpuPin, which validates the live register,
area self pointer, and CPU index when it is created. A mutable object borrow
additionally requires ExclusiveCpu; only the unsafe guard integration can
create that stronger capability after excluding IRQ/re-entry and conflicting
remote access.
Low-level execution-context owners and offline-bootstrap code can use the hidden
with_current_cpu_area and with_current_cpu_area_mut callbacks before a
CpuPin exists. These callbacks select the architecture-owned CPU area directly
instead of routing CPU-owned state through current execution-context
publication. The caller retains the complete migration, context-switch,
IRQ/re-entry, and remote aliasing contract. No runtime path uses these callbacks
yet; they are reserved for future owner-guard and offline CPU bootstrap
integration.
| Operation | Required protection |
|---|---|
| Atomic scalar | Migration disabled; local IRQs may remain enabled |
Shared T: Sync object | Migration disabled; the object synchronizes itself |
| Local mutable object | Migration, IRQ/re-entry, and conflicting remote access excluded |
| Pre-pin CPU-owner object | Migration and context switches excluded; mutable access also excludes IRQ/re-entry and remote conflicts |
| Context switch | IRQs and migration disabled; transactional tokens consumed |
| Preemption safe point | IRQs disabled; runtime baton claimed before pending release |
| vCPU run | Migration disabled; exit assembly restores host registers before Rust |
| CPU-area initialization | CPU offline and raw area exclusively owned |
§Host tests
The crate exposes one feature, host-test. It provides:
let layout = ax_percpu::host_test::initialize(
core::num::NonZeroU32::new(4).unwrap(),
)?;
let area = ax_percpu::area(ax_percpu::CpuIndex::try_from(0)?)?;
unsafe { cpu_local::install_cpu_area(area.cpu_area()?)? };
unsafe {
ax_percpu::with_cpu_pin(|pin| {
assert_eq!(ax_percpu::current_area(pin), Ok(area));
})?;
}The helper owns process-lifetime dynamic storage and initializes it once. Each modeled CPU thread must explicitly install its own binding.
§Validation
cargo test -p ax-percpu --features host-test
cargo test -p ax-percpu-macros
cargo xtask clippy --package ax-percpuLicensed under Apache-2.0.
Structs§
- CpuArea
Ref - Permanent typed reference to one fully initialized runtime CPU area.
- CpuIndex
- Dense logical index assigned to one CPU-local area.
- CpuPin
- Scoped proof that execution cannot migrate away from one validated CPU.
- Exclusive
Cpu - Scoped proof of exclusive local access to CPU-owned mutable state.
- PerCpu
- Typed descriptor for one symbol replicated in every runtime CPU area.
- PerCpu
Area - Descriptor for one initialized runtime per-CPU area.
- PerCpu
Layout - Frozen process-wide layout of initialized runtime per-CPU areas.
- PerCpu
Region - Platform-owned raw storage geometry for runtime per-CPU areas.
Enums§
- PerCpu
Error - Failure to initialize, locate, or access a runtime per-CPU area.
Statics§
- EXAMPLE_
PERCPU_ DATA doc - Example per-CPU data for documentation only.
Functions§
- area
- Returns one remote or current CPU area in O(1).
- current_
area - Verifies that
pinselects the area installed for its logical CPU. - current_
cpu_ index - Returns the logical CPU index carried by a validated pin.
- initialize_
layout ⚠ - Validates and constructs all values in raw runtime areas exactly once.
- layout
- Returns the immutable process-wide per-CPU layout.
- with_
cpu_ ⚠pin - Runs
operationwith a validated, non-escaping CPU pin. - with_
exclusive_ ⚠cpu - Runs
operationwith exclusive access to mutable state on the pinned CPU.
Type Aliases§
- EXAMPLE_
PERCPU_ DATA_ WRAPPER - Wrapper type for the per-CPU data
EXAMPLE_PERCPU_DATA
Attribute Macros§
- def_
percpu - Defines a per-CPU static variable.