Skip to main content

ax_task/runtime/
config.rs

1//! Scheduler configuration and capacity limits.
2
3/// Normalized fair scheduling request in nanoseconds.
4pub const NORMALIZED_FAIR_SLICE_NS: u64 = 700_000;
5/// Default scheduler timing granularity used to bound EEVDF lag.
6pub const DEFAULT_TIMING_GRANULARITY_NS: u64 = 1_000_000;
7/// Default periodic fair balancing interval in nanoseconds.
8pub const DEFAULT_BALANCE_INTERVAL_NS: u64 = 10_000_000;
9/// Default round-robin quantum in nanoseconds.
10pub const DEFAULT_RR_QUANTUM_NS: u64 = 100_000_000;
11/// Default RT bandwidth period in nanoseconds.
12pub const DEFAULT_RT_PERIOD_NS: u64 = 1_000_000_000;
13/// Default unthrottled RT runtime matching Linux without `RT_GROUP_SCHED`.
14pub const DEFAULT_RT_RUNTIME_NS: u64 = DEFAULT_RT_PERIOD_NS;
15/// Default Deadline admission percentage.
16pub const DEFAULT_DEADLINE_CAP_PERCENT: u8 = 95;
17/// Default maximum number of scheduler threads.
18///
19/// Every CPU reserves class linkage, task-deadline, and Deadline membership
20/// storage for this many generation-bearing tasks. Like Linux embedding these
21/// nodes in `task_struct`, scheduler hot paths therefore never discover a
22/// capacity failure after a thread has been published.
23pub const DEFAULT_THREAD_CAPACITY: usize = 4096;
24/// Default bounded work budget for scheduler inboxes and timers.
25pub const DEFAULT_BATCH_LIMIT: usize = 64;
26/// Maximum PI owner-chain depth walked in one non-preemptible transaction.
27///
28/// Linux can use a larger default because `rt_mutex_adjust_prio_chain()` drops
29/// its local locks between steps. ax-task currently owns one scheduler graph
30/// transaction for the complete walk, so the bound must also cap worst-case
31/// non-preemptible latency.
32pub const DEFAULT_PI_CHAIN_LIMIT: usize = 64;
33
34/// Immutable sizing and bandwidth policy for one task system.
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub struct TaskSystemConfig {
37    cpu_count: usize,
38    fair_slice_ns: u64,
39    timing_granularity_ns: u64,
40    balance_interval_ns: u64,
41    rr_quantum_ns: u64,
42    rt_period_ns: u64,
43    rt_runtime_ns: u64,
44    deadline_cap_percent: u8,
45    thread_capacity: usize,
46    batch_limit: usize,
47    pi_chain_limit: usize,
48}
49
50impl TaskSystemConfig {
51    /// Creates a configuration with the project defaults.
52    pub const fn new(cpu_count: usize) -> Self {
53        Self {
54            cpu_count,
55            fair_slice_ns: NORMALIZED_FAIR_SLICE_NS * linux_logarithmic_cpu_factor(cpu_count),
56            timing_granularity_ns: DEFAULT_TIMING_GRANULARITY_NS,
57            balance_interval_ns: DEFAULT_BALANCE_INTERVAL_NS,
58            rr_quantum_ns: DEFAULT_RR_QUANTUM_NS,
59            rt_period_ns: DEFAULT_RT_PERIOD_NS,
60            rt_runtime_ns: DEFAULT_RT_RUNTIME_NS,
61            deadline_cap_percent: DEFAULT_DEADLINE_CAP_PERCENT,
62            thread_capacity: DEFAULT_THREAD_CAPACITY,
63            batch_limit: DEFAULT_BATCH_LIMIT,
64            pi_chain_limit: DEFAULT_PI_CHAIN_LIMIT,
65        }
66    }
67
68    /// Returns the topology size.
69    pub const fn cpu_count(self) -> usize {
70        self.cpu_count
71    }
72
73    /// Returns the fair service request.
74    pub const fn fair_slice_ns(self) -> u64 {
75        self.fair_slice_ns
76    }
77
78    /// Returns the scheduler timing granularity used to bound EEVDF lag.
79    pub const fn timing_granularity_ns(self) -> u64 {
80        self.timing_granularity_ns
81    }
82
83    /// Returns the balancing interval.
84    pub const fn balance_interval_ns(self) -> u64 {
85        self.balance_interval_ns
86    }
87
88    /// Returns the default round-robin quantum.
89    pub const fn rr_quantum_ns(self) -> u64 {
90        self.rr_quantum_ns
91    }
92
93    /// Returns the RT bandwidth period.
94    pub const fn rt_period_ns(self) -> u64 {
95        self.rt_period_ns
96    }
97
98    /// Returns the RT runtime budget.
99    pub const fn rt_runtime_ns(self) -> u64 {
100        self.rt_runtime_ns
101    }
102
103    /// Returns the Deadline admission cap in percent.
104    pub const fn deadline_cap_percent(self) -> u8 {
105        self.deadline_cap_percent
106    }
107
108    /// Returns the maximum number of published scheduler threads.
109    pub const fn thread_capacity(self) -> usize {
110        self.thread_capacity
111    }
112
113    /// Returns the maximum work items processed at one safe point.
114    pub const fn batch_limit(self) -> usize {
115        self.batch_limit
116    }
117
118    /// Returns the maximum owner-chain depth of one PI graph transaction.
119    pub const fn pi_chain_limit(self) -> usize {
120        self.pi_chain_limit
121    }
122
123    /// Overrides the Deadline admission cap.
124    pub const fn with_deadline_cap_percent(mut self, percent: u8) -> Self {
125        self.deadline_cap_percent = percent;
126        self
127    }
128
129    /// Overrides the minimum interval between owner-CPU fair migrations.
130    pub const fn with_balance_interval_ns(mut self, interval_ns: u64) -> Self {
131        self.balance_interval_ns = interval_ns;
132        self
133    }
134
135    /// Enables Linux-style RT group bandwidth with an explicit period and quota.
136    pub const fn with_rt_bandwidth(mut self, period_ns: u64, runtime_ns: u64) -> Self {
137        self.rt_period_ns = period_ns;
138        self.rt_runtime_ns = runtime_ns;
139        self
140    }
141
142    /// Overrides the scheduler thread capacity prepared by every CPU.
143    pub const fn with_thread_capacity(mut self, capacity: usize) -> Self {
144        self.thread_capacity = capacity;
145        self
146    }
147
148    /// Overrides the bounded scheduler work batch.
149    pub const fn with_batch_limit(mut self, limit: usize) -> Self {
150        self.batch_limit = limit;
151        self
152    }
153
154    /// Overrides the maximum owner-chain depth of one PI graph transaction.
155    pub const fn with_pi_chain_limit(mut self, limit: usize) -> Self {
156        self.pi_chain_limit = limit;
157        self
158    }
159}
160
161const fn linux_logarithmic_cpu_factor(cpu_count: usize) -> u64 {
162    let capped = if cpu_count == 0 {
163        1
164    } else if cpu_count > 8 {
165        8
166    } else {
167        cpu_count
168    };
169    1 + capped.ilog2() as u64
170}