Real-Time Scheduling
This page explains how to place R2Kernel's communication workers and your application's real-time loops on a Linux host. It covers the runtime settings, the package CPU claim and its admission, and the host conditions that an operator controls.
Scope Of The Guarantee
R2Kernel applies and verifies the scheduling policy, priority and CPU affinity that you configure. It does not establish a worst-case execution time or a general hard real-time guarantee. Finite runs with no observed misses establish only the recorded conditions. Readiness checks and functional smoke tests are not real-time results. Use the periodic qualification in Deployment Checks to measure a declared profile on your host.
Core Worker Scheduling
Control's internal communication workers (local Control transport, Event and
federation fast data paths) use the core_scheduling settings. Host status,
resource reconciliation, time-sync management, HTTP and Web Console tasks stay
on the normal executor.
core_scheduling:
policy: fifo # other, fifo, or rr
priority: 99 # other requires 0; fifo/rr require 1..99
cores: [] # empty: keep the affinity Control inherited
On Linux the defaults are fifo, priority 99 and cores: []. An empty
cores list keeps the CPU set that the Control process inherited; it does not
pin the workers. Each worker applies and reads back its own policy, priority
and affinity before it accepts work. A denied or invalid setting fails worker
startup instead of switching to other. Apply changes by restarting Control.
See Runtime Configuration
for the memory-lock settings that fifo and rr require.
Recommendations:
- give the Core workers dedicated CPUs with
cores, and keep application real-time loops off those CPUs - with
policy: other, Core workers compete with every ordinary task on their CPUs, so communication latency can grow when those CPUs are loaded; useotheronly when that is acceptable for the workload - confirm the observed policy, priority and affinity of each worker thread in
the status snapshot (
core_scheduling) or on the Settings page, not only the configured values
Application Real-Time Loops
core_scheduling never selects a policy for your subsystem processes. Declare
their scheduling in the package manifest under resources.cpu.
Place an application loop that must meet a deadline on its own CPUs, separate from the Core worker CPUs. If an application loop shares a CPU with Core workers and waits on Control or Event communication, run it at a lower priority than the Core workers; a loop that outranks the workers it depends on can delay its own requests. Priority inversion and starvation remain the application's responsibility.
As an example only, a host might run Core workers at FIFO 99 on CPUs 2 and 3 and a controller loop at FIFO 70 on CPU 4. Choose priorities from your own measurements.
Package CPU Claims
resources:
cpu:
cores: [4]
exclusive: true
policy: fifo
priority: 70
coreslists the allowed CPUs and must contain at least one core.policyisother,fifoorrr; an omitted policy isother.fifoandrrrequirepriorityfrom 1 through 99.exclusive: trueis refused when another admitted lease already holds any of those cores exclusively.
The resource service admits the claim before the process starts. The child
applies its affinity and scheduler before the package entrypoint runs. Launch
status reports cpu_enforcement: affinity_and_cgroup when the lease's cpuset
was applied, or affinity_only_cgroup_degraded when only affinity could be
applied (for example when the cpuset controller is not available to the
package cgroup root).
Admission compares package claims with each other. It does not reserve the
CPUs listed in core_scheduling; keep those CPUs out of package claims
yourself.
Per-Instance CPU Claims
One installed package can run several instances with different CPU claims. A
start request may replace the manifest's resources.cpu for one instance:
r2kernel launch run ./my_controller \
--instance left_arm \
--resources '{"cpu":{"cores":[4],"exclusive":true,"policy":"fifo","priority":70}}'
The REST start route accepts the same object:
POST /api/v1/control/packages/{package_id}/start
{"instance_id": "left_arm", "resources": {"cpu": {"cores": [4], "exclusive": true, "policy": "fifo", "priority": 70}}}
The instance claim replaces the manifest claim as a whole (fields are not
merged). It passes the same validation as a manifest claim and the same
admission in the resource service. The instance record keeps it while the
instance runs: a restart without resources reuses it, and
"resources": null returns to the manifest claim. Stopping an instance removes
its record, so a later start without resources uses the manifest claim.
Unknown fields in resources and in every claim under it (cpu, network,
link_layer, devices, limits, sandbox and their entries) are rejected,
in start requests and in package.yaml. RC8 ignored such fields; see the
RC9 upgrade notes. If the resource service does not
confirm that it applied the instance claim, Control ends that launch and
reports an error.
The instance status reports cpu_origin (manifest or instance) as reported
by the resource service for that launch.
A package can forbid instance claims:
resources:
cpu_instance_override: forbidden # allowed (default) or forbidden
With forbidden, a start request that carries resources.cpu is refused with
an error naming resources.cpu_instance_override: forbidden.
Real-Time Priority Limit Of Managed Packages
Managed packages cannot raise their real-time priority above the admitted
claim. Before the package entrypoint runs, the launcher sets the package's
RLIMIT_RTPRIO to the admitted priority, or to 0 for policy: other and for
packages without a CPU claim. Threads of a package admitted with fifo or rr
priority N may therefore select real-time priorities up to N; a package
admitted with other cannot select a real-time policy.
A package that needs a real-time thread must declare that priority in its CPU claim, in the manifest or as a per-instance claim. A limit inherited from a service unit or container does not let a managed package exceed its claim.
Host Conditions
These settings belong to the host administrator. R2Kernel does not change them, and they are guidance rather than product guarantees:
- isolate the real-time CPUs from general scheduling (for example
isolcpusandnohz_full), and move IRQs and kernel housekeeping work away from them; consider SMT siblings of isolated CPUs - review
kernel.sched_rt_runtime_us; real-time throttling limits how much CPU time real-time tasks receive in each period - use a
PREEMPT_RTkernel when the workload needs bounded kernel preemption latency - grant the memory-lock limit that real-time memory preparation needs: the
installed Control service sets
LimitMEMLOCK=64M, matching the defaulttimed_exchange.rt_memory_budget_bytes - grant real-time scheduling rights: the installed Control service sets
LimitRTPRIO=99; a directly started runtime needsCAP_SYS_NICEor an equivalentRLIMIT_RTPRIO, orcore_scheduling: {policy: other, priority: 0} - affinity restricts a task to CPUs; it does not by itself prove isolation, and CPU placement inside a virtual machine does not isolate the task from the hypervisor
- on a voluntary-preemption kernel, kernel worker items that run on a
real-time CPU delay a
SCHED_FIFOwakeup until they yield; on a qualification VM we measured 1.3-5 ms from removable-media polling of an emulated optical drive (/sys/block/<dev>/events_poll_msecs), free page reporting of a virtio balloon, and hypervisor vCPU preemption. Keep unbound workqueues (/sys/devices/virtual/workqueue/cpumask) and such polling off the real-time CPUs, or use aPREEMPT_RTorpreempt=fullkernel
Do not change global kernel settings merely to make a failing measurement pass.
Python
Python subsystems can use the same package CPU claims. For this release, the strict 1 ms periodic acceptance target is the declared C++ profile; Python results are functional and diagnostic evidence, not strict periodic qualification. A real-time priority does not make Python application code allocation-free or hard real-time.
Verification
- Check the observed Core worker scheduling in the status snapshot or on the Settings page.
- Launch the package and check
cpu_enforcement,cpu_originand the observed policy and affinity of its real-time threads. - Run the periodic qualification in Deployment Checks and review scheduled-start lateness, failure-inclusive SDK invocation time and scheduled completion (p50, p99 and max) together with skipped slots and deadline-violation counts.
Containers
Running Control, the resource service and packages in a container has additional requirements, including scheduling and memory-lock rights inside the container. See Container Deployment.