PREEMPT_RT BSP Migration: Revisit Interrupt and Locking Assumptions

Review the synchronization property each driver path relies on before treating PREEMPT_RT as a configuration-only migration. On the RT kernel, a familiar spinlock or interrupt suffix can have different execution-context behavior. Code that accidentally relied on non-preemptibility may need a deliberate locking or per-CPU ownership change.

This guide uses upstream Linux v6.12. It proposes a BSP driver audit and regression plan; no RT kernel was built or booted, no interrupt policy was changed and no timing result is claimed. Vendor RT trees and later releases require their own source review.

Confirm the selected kernel model

The v6.12 preemption configuration makes PREEMPT_RT depend on EXPERT and ARCH_SUPPORTS_RT. Check the final generated configuration and architecture support instead of assuming that a low-latency or dynamically preemptible kernel is the same build.

Preserve a baseline image and the private driver revisions. Inventory paths entered from ordinary tasks, workqueues, primary IRQ handlers, threaded IRQ handlers, softirq-related processing and timers. Trace wrappers and error paths as well as the normal request path.

For each path, record held locks, interrupt state assumptions, per-CPU pointers, allocations and waits. This creates a reviewable context map. A source function’s name or its previous behavior under a non-RT kernel is not sufficient proof of its current context.

Distinguish spinlock_t from raw_spinlock_t

The v6.12 lock-type rules state that spinlock_t uses an RT-mutex-based implementation under PREEMPT_RT. Acquiring it does not disable preemption, although migration is prevented while it is held. Its hard-interrupt suffixes do not change the CPU’s interrupt-disabled state.

raw_spinlock_t remains a strict spinning lock on both configurations. It is reserved for appropriate low-level critical regions and retains non-preemptible constraints. Replacing every spinlock_t with a raw lock can restore an old accidental assumption while creating long latency and invalid nesting. It is not the default migration strategy.

A lock’s potentially blocking RT implementation does not grant permission to put arbitrary sleepable operations inside code that must also work with non-RT spinlocks. Preserve the documented API restrictions and review every configuration in the support matrix.

Three audit areas distinguish RT ordinary spinlocks, strict raw spinlocks and per-CPU serialization, followed by interrupt and lifetime checks.
Original PREEMPT_RT context-audit map; it contains no measured real-time guarantee.

Find combined operations that were split apart

A pattern that disables local interrupts and then takes an ordinary spinlock can be valid under one model yet invalid under RT, because the RT lock acquisition needs a fully preemptible context. The pinned lock guide specifically contrasts that sequence with the intended spin_lock_irq or spin_lock_irqsave form.

Review the whole protected resource before changing a call. If real hardware interrupt exclusion is required, explain which hard-interrupt path shares the state and why the selected primitive supplies that property. The suffix name alone cannot establish it on RT.

Also audit nesting: a raw-spinlocked region must not acquire an RT-converted ordinary spinlock. Hidden calls through allocation, logging or subsystem helpers can introduce the incompatible acquisition. Shorten and restructure the critical region rather than suppressing a context warning.

Make per-CPU ownership explicit

The lock guide gives a concrete migration hazard: get_cpu_ptr disables preemption, so taking a spinlock_t through that pointer afterward can be invalid on RT. Where the requirement is stable CPU placement while acquiring the appropriate lock, a reviewed migrate_disable-based sequence can preserve placement without disabling preemption.

Migration prevention is not mutual exclusion. Another task can preempt the current task on the same CPU and access the same per-CPU object. If the object needs serialization, use the correct shared protection scope, such as an appropriate local_lock design, rather than treating CPU pinning as a lock.

All paths sharing the object must use the same logical protection. Replacing two separate interrupt-disabled regions with two different local_lock objects does not serialize access to one common object on RT. Define the protected data set and verify each caller uses its intended lock.

Inspect which interrupt handlers actually move

PREEMPT_RT brings many interrupt handlers under scheduler control, but not every handler becomes an ordinary sleepable thread. The v6.12 forced-threading implementation skips actions marked IRQF_NO_THREAD, IRQF_PERCPU or IRQF_ONESHOT in that conversion path, and separately recognizes already-threaded arrangements.

Those flags do not all mean the same execution context. In particular, an already-threaded interrupt can use oneshot semantics. Inspect the actual primary handler, thread_fn, controller capabilities and registration flags, rather than translating the skip list into a blanket “runs in hard IRQ” rule.

The generic IRQ model distinguishes primary handling from threaded work. Keep immediate hardware attribution and acknowledgement in a permitted path, and verify how events are retained while threaded processing waits. A later thread can observe a changed device state unless the hardware or driver preserves the event.

Build a context regression matrix

Audit item Required evidence
Ordinary versus raw locks Intended exclusion, legal nesting and bounded protected work
Per-CPU state Placement and serialization proved separately
Primary/threaded IRQ split Actual registration, event retention and completion behavior
Wait and allocation paths Permitted context through normal and error callbacks
Shutdown and reset No pending handler uses retired state

Run appropriate lock and context diagnostics on recoverable RT and non-RT test builds. Exercise simultaneous requests, interrupts arriving during cleanup, bus delays and error unwinding. A clean boot reaches too few paths to qualify a migrated BSP.

Measure application deadlines separately from diagnostic correctness. Threaded interrupt work can now compete through scheduling policy, while raw critical regions and platform effects remain. Changing a priority may shift delay into another required task rather than reducing the total demand.

Accept a demonstrated property

For each corrected path, state the synchronization property restored: one owner, stable per-CPU placement, exclusion from a real hard handler or a bounded completion dependency. Keep that explanation beside the code change and the regression that exercises it.

Release only after the relevant driver matrix passes and the complete product meets its timing requirements under the declared workload. PREEMPT_RT changes important execution mechanics; enabling it does not certify a driver, eliminate every non-preemptible interval or establish a worst-case deadline by itself.

Similar Posts