Implementing KernelGuardIf
Relevant source files
This page provides a comprehensive guide for implementing the KernelGuardIf
trait to enable preemption control in the kernel_guard crate. This implementation is required when using guards that need to disable kernel preemption, such as NoPreempt
and NoPreemptIrqSave
. For information about feature configuration, see Feature Configuration. For details about the core guard types, see RAII Guards.
When KernelGuardIf Implementation is Required
The KernelGuardIf
trait must be implemented by users when the preempt
feature is enabled and they want to use preemption-aware guards. Without this implementation, preemption control operations become no-ops.
Guard Type | Requires KernelGuardIf | IRQ Control | Preemption Control |
---|---|---|---|
NoOp | No | No | No |
IrqSave | No | Yes | No |
NoPreempt | Yes (with preempt feature) | No | Yes |
NoPreemptIrqSave | Yes (with preempt feature) | Yes | Yes |
Sources: src/lib.rs(L80 - L111) src/lib.rs(L149 - L179)
Trait Definition and Interface
The KernelGuardIf
trait defines the interface for kernel preemption control:
flowchart TD subgraph subGraph2["Guard Usage"] ACQUIRE["BaseGuard::acquire()"] RELEASE["BaseGuard::release()"] CALL["crate_interface::call_interface!"] end subgraph subGraph1["User Implementation"] IMPL["User struct implementing KernelGuardIf#[crate_interface::impl_interface]"] ENABLE_IMPL["enable_preempt() implementation"] DISABLE_IMPL["disable_preempt() implementation"] end subgraph subGraph0["KernelGuardIf Trait Interface"] TRAIT["KernelGuardIf trait#[crate_interface::def_interface]"] ENABLE["fn enable_preempt()"] DISABLE["fn disable_preempt()"] end ACQUIRE --> CALL CALL --> DISABLE_IMPL CALL --> ENABLE_IMPL IMPL --> DISABLE_IMPL IMPL --> ENABLE_IMPL RELEASE --> CALL TRAIT --> DISABLE TRAIT --> ENABLE
The trait is defined using the crate_interface::def_interface
attribute, which creates a stable interface that can be implemented by external crates. The methods are called through the crate_interface::call_interface!
macro.
Sources: src/lib.rs(L58 - L66)
Implementation Steps
Step 1: Define Implementation Struct
Create a struct that will implement the KernelGuardIf
trait:
struct KernelGuardIfImpl;
The struct typically doesn't need any fields, as preemption control is usually a global system operation.
Step 2: Implement the Trait
Use the #[crate_interface::impl_interface]
attribute to implement the trait:
flowchart TD subgraph subGraph0["Implementation Pattern"] ATTR["#[crate_interface::impl_interface]"] IMPL_BLOCK["impl KernelGuardIf for UserStruct"] ENABLE_FN["fn enable_preempt()"] DISABLE_FN["fn disable_preempt()"] end ATTR --> IMPL_BLOCK IMPL_BLOCK --> DISABLE_FN IMPL_BLOCK --> ENABLE_FN
The #[crate_interface::impl_interface]
attribute registers the implementation with the crate_interface system, making it available for dynamic dispatch.
Sources: src/lib.rs(L35 - L43) README.md(L41 - L49)
Step 3: Implement Method Bodies
Provide platform-specific implementations for enabling and disabling preemption:
enable_preempt()
: Should re-enable kernel preemptiondisable_preempt()
: Should disable kernel preemption
These implementations are highly platform-specific and depend on the kernel or operating system being used.
Interface Mechanism and Call Flow
The crate_interface mechanism provides runtime dispatch to user implementations:
sequenceDiagram participant Guard as Guard participant call_interface as call_interface! participant UserImplementation as User Implementation Note over Guard: Guard creation (e.g., NoPreempt::new()) Guard ->> call_interface: BaseGuard::acquire() call_interface ->> UserImplementation: KernelGuardIf::disable_preempt() UserImplementation -->> call_interface: Platform-specific disable call_interface -->> Guard: Return Note over Guard: Critical section execution Note over Guard: Guard drop Guard ->> call_interface: BaseGuard::release() call_interface ->> UserImplementation: KernelGuardIf::enable_preempt() UserImplementation -->> call_interface: Platform-specific enable call_interface -->> Guard: Return
The guards call the interface methods conditionally based on feature flags:
NoPreempt
calls the interface in bothacquire()
andrelease()
methodsNoPreemptIrqSave
calls the interface alongside IRQ control operations- Calls are wrapped in
#[cfg(feature = "preempt")]
conditionals
Sources: src/lib.rs(L153 - L154) src/lib.rs(L158 - L159) src/lib.rs(L167 - L168) src/lib.rs(L176 - L177)
Complete Implementation Example
Here's a complete example showing the implementation pattern:
use kernel_guard::{KernelGuardIf, NoPreempt};
struct KernelGuardIfImpl;
#[crate_interface::impl_interface]
impl KernelGuardIf for KernelGuardIfImpl {
fn enable_preempt() {
// Platform-specific implementation
// Example: atomic decrement of preemption counter
// or direct scheduler manipulation
}
fn disable_preempt() {
// Platform-specific implementation
// Example: atomic increment of preemption counter
// or direct scheduler manipulation
}
}
// Usage after implementation
let guard = NoPreempt::new();
// Critical section with preemption disabled
drop(guard); // Preemption re-enabled
Sources: src/lib.rs(L30 - L52) README.md(L36 - L58)
Guard Behavior with KernelGuardIf
The following diagram shows how different guards utilize the KernelGuardIf implementation:
flowchart TD subgraph subGraph1["NoPreemptIrqSave Guard"] NPIS_IRQ_DISABLE["arch::local_irq_save_and_disable()"] NPIS_ENABLE["call_interface!(enable_preempt)"] subgraph subGraph0["NoPreempt Guard"] NPIS_NEW["NoPreemptIrqSave::new()"] NPIS_ACQUIRE["BaseGuard::acquire()"] NPIS_DISABLE["call_interface!(disable_preempt)"] NPIS_DROP["Drop::drop()"] NPIS_RELEASE["BaseGuard::release()"] NPIS_IRQ_RESTORE["arch::local_irq_restore()"] NP_NEW["NoPreempt::new()"] NP_ACQUIRE["BaseGuard::acquire()"] NP_DISABLE["call_interface!(disable_preempt)"] NP_DROP["Drop::drop()"] NP_RELEASE["BaseGuard::release()"] NP_ENABLE["call_interface!(enable_preempt)"] end end NPIS_ACQUIRE --> NPIS_DISABLE NPIS_DISABLE --> NPIS_IRQ_DISABLE NPIS_DROP --> NPIS_RELEASE NPIS_IRQ_RESTORE --> NPIS_ENABLE NPIS_NEW --> NPIS_ACQUIRE NPIS_RELEASE --> NPIS_IRQ_RESTORE NP_ACQUIRE --> NP_DISABLE NP_DROP --> NP_RELEASE NP_NEW --> NP_ACQUIRE NP_RELEASE --> NP_ENABLE
Note the ordering in NoPreemptIrqSave
: preemption is disabled first, then IRQs are disabled. On release, IRQs are restored first, then preemption is re-enabled.
Sources: src/lib.rs(L149 - L179)
Best Practices and Considerations
Implementation Guidelines
- Thread Safety: Ensure implementations are thread-safe, as they may be called from multiple contexts
- Performance: Keep implementations lightweight, as they're called frequently in critical sections
- Error Handling: Implementations should not panic, as this would break RAII guarantees
- Platform Specific: Implementations must match the target platform's preemption semantics
Common Implementation Patterns
Pattern | Use Case | Example |
---|---|---|
Reference Counting | Nested preemption disable | Atomic counter increment/decrement |
Direct Control | Simple scheduler toggle | Direct register manipulation |
System Call | User-space kernels | Syscall to kernel preemption control |
No-op Stub | Testing/debugging | Empty implementations for testing |
Feature Flag Considerations
When the preempt
feature is disabled, all calls to KernelGuardIf
methods are compiled out. This means:
- No runtime overhead when preemption control is not needed
- Guards still provide IRQ control functionality
- Implementation is not required when feature is disabled
Sources: src/lib.rs(L23 - L26) src/lib.rs(L153) src/lib.rs(L158) src/lib.rs(L167) src/lib.rs(L176)