YAML Specification#
Complete reference for the thread configuration file used by agnocast_cie_thread_configurator.
Top-level Structure#
hardware_info: # Optional: validated at startup (auto-generated by the prerun mode)
model_name: "..."
cpu_family: "..."
rt_throttling: # Optional: validated against kernel settings
period_us: 1000000
runtime_us: 950000
callback_groups: # Required: list of CallbackGroup configurations
- id: ...
...
non_ros_threads: # Optional: list of non-ROS thread configurations
- name: ...
...
kernel_threads: # Optional: list of kernel thread configurations (auto-generated by the prerun mode)
- comm: ...
...
irqs: # Optional: list of interrupt affinity configurations (auto-generated by the prerun mode)
- irq: ...
...
callback_groups#
Each entry configures one CallbackGroup's thread:
| Field | Required | Description |
|---|---|---|
id |
Yes | CallbackGroup identifier (auto-generated by the prerun mode) |
policy |
Yes | Scheduling policy |
priority |
SCHED_FIFO / SCHED_RR only |
Real-time priority (1–99, 99 = highest) |
nice |
SCHED_OTHER / SCHED_BATCH / SCHED_IDLE only |
Nice value (-20–19, lower = stronger) |
affinity |
No | List of CPU cores to pin the thread to |
domain_id |
No | ROS domain ID (defaults to the current domain) |
runtime |
SCHED_DEADLINE only |
Maximum execution time per period (ns) |
period |
SCHED_DEADLINE only |
Period length (ns) |
deadline |
SCHED_DEADLINE only |
Relative deadline from start of period (ns) |
CallbackGroup ID Format#
The id is auto-generated and composed of the node name followed by callback identifiers separated by @:
/<node_name>@<Callback1>@<Callback2>...
Callback identifiers include: Subscription(<topic>), Timer(<period_ns>), Service(<name>), Client(<name>).
Tip
Use the prerun mode to discover the exact IDs for your application rather than constructing them manually.
Warning
If a node has multiple CallbackGroups with timers of the same period, their auto-generated IDs will collide. To avoid this, slightly offset the timer periods (e.g., 100ms and 100ms + 1ns).
Scheduling Policies#
| Policy | Parameter | Description |
|---|---|---|
SCHED_FIFO |
priority: 1–99 (99 = highest) |
Real-time, first-in-first-out |
SCHED_RR |
priority: 1–99 (99 = highest) |
Real-time, round-robin |
SCHED_OTHER |
nice: -20–19 (lower = stronger) |
Default Linux scheduler |
SCHED_BATCH |
nice: -20–19 (lower = stronger) |
Batch processing |
SCHED_IDLE |
nice: -20–19 (no practical effect) |
Lowest priority |
SCHED_DEADLINE |
runtime, period, deadline |
Deadline-based |
The scheduling-parameter key is policy-dependent: real-time policies (SCHED_FIFO / SCHED_RR) require priority, while CFS policies (SCHED_OTHER / SCHED_BATCH / SCHED_IDLE) require nice. A missing required key is rejected at startup with an error naming the required one; a key belonging to the other policy class is ignored like any other unknown key. Values outside the valid range are also rejected.
SCHED_DEADLINE#
For SCHED_DEADLINE, specify runtime, period, and deadline (in nanoseconds) instead of priority or nice:
callback_groups:
- id: /my_node/my_deadline_group@Timer
policy: SCHED_DEADLINE
runtime: 1000000 # 1 ms
period: 10000000 # 10 ms
deadline: 10000000 # 10 ms
affinity: [2]
Note
SCHED_DEADLINE uses cgroup cpuset for CPU affinity, which requires cgroup v1. See Kernel boot parameter for setup.
non_ros_threads#
Threads not managed by the executor can also be configured. See Non-ROS Threads for how to use this feature in your application.
| Field | Required | Description |
|---|---|---|
name |
Yes | Thread name string |
policy |
Yes | Scheduling policy (same options as callback_groups) |
priority |
SCHED_FIFO / SCHED_RR only |
Real-time priority (1–99, 99 = highest) |
nice |
SCHED_OTHER / SCHED_BATCH / SCHED_IDLE only |
Nice value (-20–19, lower = stronger) |
affinity |
No | List of CPU cores |
kernel_threads#
Scheduling attributes for kernel threads. A kernel thread is a thread that the kernel itself runs, such as ksoftirqd/0 or a threaded interrupt handler.
The prerun mode lists every kernel thread it finds, one entry per comm, and fills each entry with the values it observed. An unedited entry therefore asks for the state the thread was in at generation time. The configurator applies nothing while the thread is still in that state. For a pool that shares one comm, the recorded values are those of the lowest thread id.
| Field | Required | Description |
|---|---|---|
comm |
Yes | Thread name from /proc/<pid>/comm, for example irq/103-nvidia |
policy |
No | Scheduling policy (same options as callback_groups) |
priority |
SCHED_FIFO / SCHED_RR only |
Real-time priority (1–99, 99 = highest) |
nice |
SCHED_OTHER / SCHED_BATCH / SCHED_IDLE only |
Nice value (-20–19, lower = stronger) |
affinity |
No | List of CPU cores |
runtime, period, deadline |
SCHED_DEADLINE only |
Same meaning as in callback_groups, in nanoseconds |
policy and its scheduling parameter go together. An entry that carries nice, priority, runtime, period or deadline without policy is rejected at startup and on every reload.
Thread names are not unique. One entry configures every running kernel thread whose comm matches. A pool of eight nfsd threads therefore takes eight sets of syscalls from one entry.
Each comm appears at most once in kernel_threads, and each irq at most once in irqs. A duplicate is rejected.
kworker/* threads stay out of the template, and the configurator rejects an entry for one. Those names carry worker-pool and CPU state that changes at runtime, so they never match a thread reliably.
If no running kernel thread matches a comm, the configurator logs a warning and skips the entry. Call the reload service once the thread appears.
Warning
The kernel fixes the affinity of a per-CPU kernel thread, which carries the PF_NO_SETAFFINITY flag. The prerun mode writes UNMANAGEABLE for such a thread. A different CPU list in its place fails on every apply pass.
Note
The prerun mode never writes SCHED_DEADLINE, because /proc does not report runtime, period and deadline. A thread whose policy the template cannot represent appears as policy: UNMANAGEABLE and priority: UNMANAGEABLE. A hand-written SCHED_DEADLINE entry works, but the configurator cannot tell whether it is already in effect, so it re-applies the entry on every pass.
Unset values#
Three forms tell the configurator to leave an attribute alone. They apply to the scheduling attributes of kernel_threads and to affinity in irqs.
| Form | Meaning |
|---|---|
Omitted key, or ~ (YAML null) |
You chose not to manage the attribute |
UNMANAGEABLE |
The kernel or the configurator cannot manage the attribute |
[] (affinity only) |
Same as null |
The prerun mode writes UNMANAGEABLE. It is valid only in kernel_threads and irqs, and an entry whose attributes are all unset appears in no reload-service array.
irqs#
CPU affinity for hardware interrupts, which is the only attribute a hardware interrupt has. A threaded interrupt handler is an ordinary kernel thread, so its policy and priority belong in kernel_threads under the matching irq/<N>-<device> name.
| Field | Required | Description |
|---|---|---|
irq |
Yes | Interrupt number in decimal, as listed under /proc/irq/ |
name |
No | Expected content of /sys/kernel/irq/<N>/actions, compared before any write. Omit the key or write ~ to skip the check |
affinity |
No | List of CPU cores |
The prerun mode lists every interrupt that a device claims, read from /sys/kernel/irq/, and fills affinity with the value it observed. An unedited entry causes no write while the interrupt still has that affinity.
Interrupt numbers are assigned at boot and change when hardware or drivers change. name guards against that. If the current actions content differs from name, the configurator reports an error and leaves the interrupt alone. Re-run the prerun mode. Then update the file.
Some interrupts reject every affinity write from user space, such as interrupt 0 (the timer) and kernel-managed MSI-X vectors. Set affinity to UNMANAGEABLE for those entries.
Writing /proc/irq/<N>/smp_affinity_list needs CAP_DAC_OVERRIDE on top of CAP_SYS_NICE. See Grant capabilities.
Warning
Stop irqbalance before you set interrupt affinity. It rewrites interrupt affinity periodically and undoes what the configurator writes. While it runs, the configurator fails every entry that needs a write instead of writing it. See Stop irqbalance.
rt_throttling#
Optional section to validate that the kernel's RT throttling settings match expectations:
| Field | Description |
|---|---|
period_us |
Expected value of kernel.sched_rt_period_us |
runtime_us |
Expected value of kernel.sched_rt_runtime_us |
The configurator reports an error at startup if the current kernel values don't match. These values must be pre-configured via /etc/sysctl.d/ (see RT Throttling).
hardware_info#
Optional section for hardware validation. If present, the configurator compares these values against the current system (via lscpu) and reports an error on mismatch. The prerun mode automatically populates this section.
| Field | Source (lscpu field) |
Example |
|---|---|---|
model_name |
Model name | "Intel(R) Core(TM) i9-12900K" |
cpu_family |
CPU family | "6" |
model |
Model | "151" |
threads_per_core |
Thread(s) per core | "2" |
frequency_boost |
Frequency boost | "enabled" or "disabled" (not all CPUs) |
cpu_max_mhz |
CPU max MHz | "5200.0000" |
cpu_min_mhz |
CPU min MHz | "800.0000" |
Full Example#
hardware_info:
model_name: "Intel(R) Core(TM) i9-12900K"
cpu_family: "6"
model: "151"
threads_per_core: "2"
cpu_max_mhz: "5200.0000"
cpu_min_mhz: "800.0000"
rt_throttling:
period_us: 1000000
runtime_us: 950000
callback_groups:
- id: /camera_node/image_cb_group@Subscription(/camera/image)
policy: SCHED_FIFO
priority: 90
affinity: [0, 1]
- id: /lidar_node/pointcloud_cb_group@Subscription(/lidar/points)
policy: SCHED_FIFO
priority: 85
affinity: [2, 3]
- id: /planning_node/planner_cb_group@Timer(100000000)
policy: SCHED_DEADLINE
runtime: 5000000
period: 100000000
deadline: 50000000
affinity: [4]
- id: /logging_node/default_cb_group@Subscription(/rosout)
policy: SCHED_OTHER
nice: 10
non_ros_threads:
- name: sensor_driver
policy: SCHED_FIFO
priority: 95
affinity: [5]
kernel_threads:
- comm: irq/103-nvidia
policy: SCHED_FIFO
priority: 50
affinity: [6]
- comm: ksoftirqd/0
policy: SCHED_OTHER
nice: 0
affinity: UNMANAGEABLE
irqs:
- irq: 103
name: nvidia
affinity: [6]
- irq: 0
name: timer
affinity: UNMANAGEABLE