Feature Flags Configuration

Relevant source files

This document explains how to configure the percpu crate's feature flags to optimize per-CPU data management for different system architectures and use cases. It covers the three main feature flags (sp-naive, preempt, arm-el2) and their interaction with dependencies and code generation.

For basic usage examples and integration steps, see Basic Usage Examples. For detailed implementation specifics of each feature, see Naive Implementation.

Overview of Feature Flags

The percpu crate provides three optional features that adapt the per-CPU data system to different operational environments and architectural requirements.

Feature Flag Architecture

flowchart TD
subgraph subGraph2["Dependencies Activated"]
    KERNEL_GUARD["kernel_guard cratePreemption control"]
    MACRO_FLAGS["percpu_macros featuresCode generation control"]
end
subgraph subGraph1["Generated Code Variants"]
    MULTI_CPU["Multi-CPU ImplementationArchitecture-specific registers"]
    GLOBAL_VAR["Global Variable ImplementationNo per-CPU isolation"]
    GUARDED["Preemption-Safe ImplementationNoPreemptGuard wrapper"]
    EL2_IMPL["EL2 Register ImplementationTPIDR_EL2 usage"]
end
subgraph subGraph0["Feature Configuration"]
    DEFAULT["default = []Standard multi-CPU mode"]
    SP_NAIVE["sp-naiveSingle-core fallback"]
    PREEMPT["preemptPreemption safety"]
    ARM_EL2["arm-el2Hypervisor mode"]
end

ARM_EL2 --> EL2_IMPL
ARM_EL2 --> MACRO_FLAGS
DEFAULT --> MULTI_CPU
PREEMPT --> GUARDED
PREEMPT --> KERNEL_GUARD
PREEMPT --> MACRO_FLAGS
SP_NAIVE --> GLOBAL_VAR
SP_NAIVE --> MACRO_FLAGS

Sources: percpu/Cargo.toml(L15 - L25)  percpu_macros/Cargo.toml(L15 - L25)  README.md(L69 - L79) 

Feature Flag Descriptions

sp-naive Feature

The sp-naive feature transforms per-CPU variables into standard global variables, eliminating per-CPU isolation for single-core systems.

AspectStandard Modesp-naive Mode
Variable StoragePer-CPU memory areasGlobal variables
Register UsageArchitecture-specific (GS_BASE, TPIDR_EL1, etc.)None
Memory Layout.percpu section with CPU multiplicationStandard .data/.bss sections
Access MethodCPU register + offset calculationDirect global access
Initializationpercpu::init()andpercpu::init_percpu_reg()Standard global initialization

Configuration:

[dependencies]
percpu = { version = "0.2", features = ["sp-naive"] }

Sources: percpu/Cargo.toml(L18 - L19)  percpu_macros/Cargo.toml(L18 - L19)  README.md(L71 - L73) 

preempt Feature

The preempt feature enables preemption safety by integrating with the kernel_guard crate to prevent data corruption during per-CPU access.

Feature Dependencies Chain

flowchart TD
subgraph subGraph3["Generated Access Methods"]
    READ_CURRENT["read_current()Preemption-safe read"]
    WRITE_CURRENT["write_current()Preemption-safe write"]
    NO_PREEMPT_GUARD["NoPreemptGuardRAII guard"]
end
subgraph subGraph2["percpu_macros Crate"]
    MACROS_PREEMPT["preempt featurepercpu_macros/Cargo.toml:22"]
    CODE_GEN["Code GenerationNoPreemptGuard integration"]
end
subgraph subGraph1["percpu Crate"]
    PERCPU_PREEMPT["preempt featurepercpu/Cargo.toml:22"]
    KERNEL_GUARD_DEP["kernel_guard dependencyConditionally enabled"]
end
subgraph subGraph0["User Configuration"]
    USER_TOML["Cargo.tomlfeatures = ['preempt']"]
end

CODE_GEN --> NO_PREEMPT_GUARD
CODE_GEN --> READ_CURRENT
CODE_GEN --> WRITE_CURRENT
KERNEL_GUARD_DEP --> NO_PREEMPT_GUARD
MACROS_PREEMPT --> CODE_GEN
PERCPU_PREEMPT --> KERNEL_GUARD_DEP
PERCPU_PREEMPT --> MACROS_PREEMPT
USER_TOML --> PERCPU_PREEMPT

Configuration:

[dependencies]
percpu = { version = "0.2", features = ["preempt"] }

Sources: percpu/Cargo.toml(L21 - L22)  percpu_macros/Cargo.toml(L21 - L22)  README.md(L74 - L76) 

arm-el2 Feature

The arm-el2 feature configures AArch64 systems to use the EL2 (Exception Level 2) thread pointer register for hypervisor environments.

Register UsageDefault (arm-el2 disabled)arm-el2 enabled
AArch64 RegisterTPIDR_EL1TPIDR_EL2
Privilege LevelEL1 (kernel mode)EL2 (hypervisor mode)
Use CaseStandard kernelHypervisor/VMM
Assembly Instructionsmrs/msrwithTPIDR_EL1mrs/msrwithTPIDR_EL2

Configuration:

[dependencies]
percpu = { version = "0.2", features = ["arm-el2"] }

Sources: percpu/Cargo.toml(L24 - L25)  percpu_macros/Cargo.toml(L24 - L25)  README.md(L33 - L35)  README.md(L77 - L79) 

Feature Combination Matrix

Compatible Feature Combinations

flowchart TD
subgraph subGraph1["Architecture Applicability"]
    X86_COMPAT["x86_64 CompatibleGS_BASE register"]
    ARM_COMPAT["AArch64 CompatibleTPIDR_ELx registers"]
    RISCV_COMPAT["RISC-V Compatiblegp register"]
    LOONG_COMPAT["LoongArch Compatible$r21 register"]
end
subgraph subGraph0["Valid Configurations"]
    DEFAULT_ONLY["DefaultMulti-CPU, no preemption"]
    SP_NAIVE_ONLY["sp-naiveSingle-core system"]
    PREEMPT_ONLY["preemptMulti-CPU with preemption"]
    ARM_EL2_ONLY["arm-el2AArch64 hypervisor"]
    PREEMPT_ARM["preempt + arm-el2Hypervisor with preemption"]
    SP_NAIVE_PREEMPT["sp-naive + preemptSingle-core with preemption"]
    SP_NAIVE_ARM["sp-naive + arm-el2Single-core hypervisor"]
    ALL_FEATURES["sp-naive + preempt + arm-el2Single-core hypervisor with preemption"]
end

ARM_EL2_ONLY --> ARM_COMPAT
DEFAULT_ONLY --> ARM_COMPAT
DEFAULT_ONLY --> LOONG_COMPAT
DEFAULT_ONLY --> RISCV_COMPAT
DEFAULT_ONLY --> X86_COMPAT
PREEMPT_ARM --> ARM_COMPAT
PREEMPT_ONLY --> ARM_COMPAT
PREEMPT_ONLY --> LOONG_COMPAT
PREEMPT_ONLY --> RISCV_COMPAT
PREEMPT_ONLY --> X86_COMPAT
SP_NAIVE_ONLY --> ARM_COMPAT
SP_NAIVE_ONLY --> LOONG_COMPAT
SP_NAIVE_ONLY --> RISCV_COMPAT
SP_NAIVE_ONLY --> X86_COMPAT

Sources: percpu/Cargo.toml(L15 - L25)  percpu_macros/Cargo.toml(L15 - L25) 

Configuration Examples

Bare Metal Single-Core System

For embedded systems or single-core environments:

[dependencies]
percpu = { version = "0.2", features = ["sp-naive"] }

This configuration eliminates per-CPU overhead and uses simple global variables.

Multi-Core Preemptible Kernel

For operating system kernels with preemptive scheduling:

[dependencies]
percpu = { version = "0.2", features = ["preempt"] }

This enables preemption safety with NoPreemptGuard wrappers around access operations.

AArch64 Hypervisor

For hypervisors running at EL2 privilege level:

[dependencies]
percpu = { version = "0.2", features = ["arm-el2"] }

This uses TPIDR_EL2 instead of TPIDR_EL1 for per-CPU base address storage.

Multi-Core Hypervisor with Preemption

For complex hypervisor environments:

[dependencies]
percpu = { version = "0.2", features = ["preempt", "arm-el2"] }

This combines EL2 register usage with preemption safety mechanisms.

Conditional Compilation Impact

Feature-Dependent Code Paths

flowchart TD
subgraph subGraph2["Generated Code Variants"]
    GLOBAL_IMPL["Global Variable ImplementationDirect access"]
    PERCPU_IMPL["Per-CPU ImplementationRegister-based access"]
    GUARDED_IMPL["Guarded ImplementationNoPreemptGuard wrapper"]
    EL2_IMPL["EL2 ImplementationTPIDR_EL2 usage"]
end
subgraph subGraph1["Feature Detection"]
    CFG_SP_NAIVE["#[cfg(feature = 'sp-naive')]"]
    CFG_PREEMPT["#[cfg(feature = 'preempt')]"]
    CFG_ARM_EL2["#[cfg(feature = 'arm-el2')]"]
end
subgraph subGraph0["Macro Expansion"]
    DEF_PERCPU["#[def_percpu]static VAR: T = init;"]
end

CFG_ARM_EL2 --> EL2_IMPL
CFG_PREEMPT --> GUARDED_IMPL
CFG_SP_NAIVE --> GLOBAL_IMPL
CFG_SP_NAIVE --> PERCPU_IMPL
DEF_PERCPU --> CFG_ARM_EL2
DEF_PERCPU --> CFG_PREEMPT
DEF_PERCPU --> CFG_SP_NAIVE
PERCPU_IMPL --> EL2_IMPL
PERCPU_IMPL --> GUARDED_IMPL

Sources: percpu_macros/Cargo.toml(L15 - L25) 

Architecture-Specific Considerations

x86_64 Systems

All feature combinations are supported. The arm-el2 feature has no effect on x86_64 targets.

Register Used: GS_BASE (unless sp-naive is enabled)

AArch64 Systems

The arm-el2 feature specifically affects AArch64 compilation:

  • Default: Uses TPIDR_EL1 register
  • With arm-el2: Uses TPIDR_EL2 register

RISC-V Systems

The arm-el2 feature has no effect. Uses gp register for per-CPU base address.

LoongArch Systems

The arm-el2 feature has no effect. Uses $r21 register for per-CPU base address.

Sources: README.md(L19 - L36) 

Dependencies and Build Impact

Conditional Dependencies

The feature flags control which dependencies are included in the build:

FeatureAdditional DependenciesPurpose
sp-naiveNoneSimplifies to global variables
preemptkernel_guard = "0.1"ProvidesNoPreemptGuard
arm-el2NoneOnly affects macro code generation

Build Optimization

When sp-naive is enabled, the generated code is significantly simpler and may result in better optimization for single-core systems since it eliminates the per-CPU indirection overhead.

Sources: percpu/Cargo.toml(L27 - L36)