strat9_kernel/shell/commands/sys/
scheduler.rs1use crate::{
2 process::{
3 configure_class_table, log_scheduler_state, reset_scheduler_metrics, scheduler_class_table,
4 scheduler_metrics_snapshot, scheduler_state_snapshot, scheduler_verbose_enabled,
5 set_scheduler_verbose,
6 },
7 shell::ShellError,
8 shell_println,
9};
10use alloc::string::String;
11
12fn parse_class(s: &str) -> Option<crate::process::sched::SchedClassId> {
14 crate::process::sched::SchedClassId::parse(s)
15}
16
17fn parse_kind(s: &str) -> Option<crate::process::sched::SchedPolicyKind> {
19 crate::process::sched::SchedPolicyKind::parse(s)
20}
21
22fn print_table(table: crate::process::sched::SchedClassTable) {
24 let pick = table.pick_order();
25 let steal = table.steal_order();
26 shell_println!(
27 "class table: pick=[{},{},{}] steal=[{},{}]",
28 pick[0].as_str(),
29 pick[1].as_str(),
30 pick[2].as_str(),
31 steal[0].as_str(),
32 steal[1].as_str()
33 );
34 for entry in table.entries().iter() {
35 shell_println!(
36 " class={} name={} rank={}",
37 entry.id.as_str(),
38 entry.name,
39 entry.rank
40 );
41 }
42 for kind in [
43 crate::process::sched::SchedPolicyKind::Fair,
44 crate::process::sched::SchedPolicyKind::RealTime,
45 crate::process::sched::SchedPolicyKind::Idle,
46 ] {
47 let class = table.policy_class(kind);
48 shell_println!(" policy_map: {}->{}", kind.as_str(), class.as_str());
49 }
50}
51
52fn print_table_kv(table: crate::process::sched::SchedClassTable) {
54 let pick = table.pick_order();
55 let steal = table.steal_order();
56 shell_println!("scheduler.pick.0={}", pick[0].as_str());
57 shell_println!("scheduler.pick.1={}", pick[1].as_str());
58 shell_println!("scheduler.pick.2={}", pick[2].as_str());
59 shell_println!("scheduler.steal.0={}", steal[0].as_str());
60 shell_println!("scheduler.steal.1={}", steal[1].as_str());
61 for entry in table.entries().iter() {
62 shell_println!("scheduler.class.{}.name={}", entry.id.as_str(), entry.name);
63 shell_println!("scheduler.class.{}.rank={}", entry.id.as_str(), entry.rank);
64 }
65 for kind in [
66 crate::process::sched::SchedPolicyKind::Fair,
67 crate::process::sched::SchedPolicyKind::RealTime,
68 crate::process::sched::SchedPolicyKind::Idle,
69 ] {
70 let class = table.policy_class(kind);
71 shell_println!("scheduler.policy_map.{}={}", kind.as_str(), class.as_str());
72 }
73}
74
75fn print_metrics_kv(m: crate::process::SchedulerMetricsSnapshot) {
77 shell_println!("scheduler.cpu_count={}", m.cpu_count);
78 for i in 0..m.cpu_count.min(crate::arch::x86_64::percpu::MAX_CPUS) {
79 shell_println!("scheduler.cpu.{}.rt={}", i, m.rt_runtime_ticks[i]);
80 shell_println!("scheduler.cpu.{}.fair={}", i, m.fair_runtime_ticks[i]);
81 shell_println!("scheduler.cpu.{}.idle={}", i, m.idle_runtime_ticks[i]);
82 shell_println!("scheduler.cpu.{}.switch={}", i, m.switch_count[i]);
83 shell_println!("scheduler.cpu.{}.preempt={}", i, m.preempt_count[i]);
84 shell_println!("scheduler.cpu.{}.steal_in={}", i, m.steal_in_count[i]);
85 shell_println!("scheduler.cpu.{}.steal_out={}", i, m.steal_out_count[i]);
86 shell_println!(
87 "scheduler.cpu.{}.try_lock_fail={}",
88 i,
89 m.try_lock_fail_count[i]
90 );
91 }
92}
93
94fn wants_kv(args: &[String], idx: usize) -> bool {
96 args.get(idx).map(|s| s.as_str()) == Some("kv")
97}
98
99pub fn cmd_scheduler(args: &[String]) -> Result<(), ShellError> {
101 if args.is_empty() {
102 shell_println!("Usage: scheduler <debug|class|policy|metrics|dump> ...");
103 return Err(ShellError::InvalidArguments);
104 }
105
106 match args[0].as_str() {
107 "debug" => {
108 if args.len() != 2 {
109 shell_println!("Usage: scheduler debug on|off|dump");
110 return Err(ShellError::InvalidArguments);
111 }
112 match args[1].as_str() {
113 "on" => {
114 set_scheduler_verbose(true);
115 shell_println!("scheduler debug: on");
116 Ok(())
117 }
118 "off" => {
119 set_scheduler_verbose(false);
120 shell_println!("scheduler debug: off");
121 Ok(())
122 }
123 "dump" => {
124 let table = scheduler_class_table();
125 shell_println!(
126 "scheduler debug: {}",
127 if scheduler_verbose_enabled() {
128 "on"
129 } else {
130 "off"
131 }
132 );
133 print_table(table);
134 log_scheduler_state("shell");
135 Ok(())
136 }
137 _ => {
138 shell_println!("Usage: scheduler debug on|off|dump");
139 Err(ShellError::InvalidArguments)
140 }
141 }
142 }
143 "class" => {
144 if args.len() == 1 || (args.len() == 2 && args[1].as_str() == "kv") {
145 let table = scheduler_class_table();
146 if args.len() == 2 {
147 print_table_kv(table);
148 } else {
149 print_table(table);
150 }
151 return Ok(());
152 }
153 if args.len() >= 3 && args[1].as_str() == "order" && args[2].as_str() == "set" {
154 if args.len() != 6 && args.len() != 7 {
155 shell_println!("Usage: scheduler class order set <c1> <c2> <c3> [kv]");
156 return Err(ShellError::InvalidArguments);
157 }
158 let c1 = parse_class(args[3].as_str()).ok_or(ShellError::InvalidArguments)?;
159 let c2 = parse_class(args[4].as_str()).ok_or(ShellError::InvalidArguments)?;
160 let c3 = parse_class(args[5].as_str()).ok_or(ShellError::InvalidArguments)?;
161 let mut table = scheduler_class_table();
162 if !table.set_pick_order([c1, c2, c3]) || !configure_class_table(table) {
163 shell_println!("scheduler: rejected class order");
164 return Err(ShellError::InvalidArguments);
165 }
166 if args.len() == 7 {
167 if !wants_kv(args, 6) {
168 shell_println!("Usage: scheduler class order set <c1> <c2> <c3> [kv]");
169 return Err(ShellError::InvalidArguments);
170 }
171 print_table_kv(table);
172 } else {
173 print_table(table);
174 }
175 return Ok(());
176 }
177 if args.len() >= 3 && args[1].as_str() == "steal" && args[2].as_str() == "set" {
178 if args.len() != 5 && args.len() != 6 {
179 shell_println!("Usage: scheduler class steal set <c1> <c2> [kv]");
180 return Err(ShellError::InvalidArguments);
181 }
182 let c1 = parse_class(args[3].as_str()).ok_or(ShellError::InvalidArguments)?;
183 let c2 = parse_class(args[4].as_str()).ok_or(ShellError::InvalidArguments)?;
184 let mut table = scheduler_class_table();
185 if !table.set_steal_order([c1, c2]) || !configure_class_table(table) {
186 shell_println!("scheduler: rejected steal order");
187 return Err(ShellError::InvalidArguments);
188 }
189 if args.len() == 6 {
190 if !wants_kv(args, 5) {
191 shell_println!("Usage: scheduler class steal set <c1> <c2> [kv]");
192 return Err(ShellError::InvalidArguments);
193 }
194 print_table_kv(table);
195 } else {
196 print_table(table);
197 }
198 return Ok(());
199 }
200 shell_println!(
201 "Usage: scheduler class [kv | order set <c1> <c2> <c3> [kv] | steal set <c1> <c2> [kv]]"
202 );
203 Err(ShellError::InvalidArguments)
204 }
205 "policy" => {
206 if args.len() == 1 || (args.len() == 2 && args[1].as_str() == "map") {
207 print_table(scheduler_class_table());
208 return Ok(());
209 }
210 if args.len() >= 3 && args[1].as_str() == "map" && args[2].as_str() == "show" {
211 let table = scheduler_class_table();
212 if args.len() == 4 {
213 if args[3].as_str() != "kv" {
214 shell_println!("Usage: scheduler policy map show [kv]");
215 return Err(ShellError::InvalidArguments);
216 }
217 print_table_kv(table);
218 } else if args.len() == 3 {
219 print_table(table);
220 } else {
221 shell_println!("Usage: scheduler policy map show [kv]");
222 return Err(ShellError::InvalidArguments);
223 }
224 return Ok(());
225 }
226 if args.len() >= 3 && args[1].as_str() == "map" && args[2].as_str() == "set" {
227 if args.len() != 5 && args.len() != 6 {
228 shell_println!(
229 "Usage: scheduler policy map set <fair|rt|idle> <rt|fair|idle> [kv]"
230 );
231 return Err(ShellError::InvalidArguments);
232 }
233 let kind = parse_kind(args[3].as_str()).ok_or(ShellError::InvalidArguments)?;
234 let class = parse_class(args[4].as_str()).ok_or(ShellError::InvalidArguments)?;
235 let mut table = scheduler_class_table();
236 if !table.set_policy_class(kind, class) || !configure_class_table(table) {
237 shell_println!("scheduler: rejected policy map");
238 return Err(ShellError::InvalidArguments);
239 }
240 if args.len() == 6 {
241 if args[5].as_str() != "kv" {
242 shell_println!(
243 "Usage: scheduler policy map set <fair|rt|idle> <rt|fair|idle> [kv]"
244 );
245 return Err(ShellError::InvalidArguments);
246 }
247 print_table_kv(table);
248 } else {
249 print_table(table);
250 }
251 return Ok(());
252 }
253 shell_println!(
254 "Usage: scheduler policy map <show [kv] | set <fair|rt|idle> <rt|fair|idle> [kv]>"
255 );
256 Err(ShellError::InvalidArguments)
257 }
258 "metrics" => {
259 if args.len() == 2 && args[1].as_str() == "reset" {
260 reset_scheduler_metrics();
261 shell_println!("scheduler metrics: reset");
262 return Ok(());
263 }
264 if args.len() > 2 || (args.len() == 2 && args[1].as_str() != "kv") {
265 shell_println!("Usage: scheduler metrics [reset|kv]");
266 return Err(ShellError::InvalidArguments);
267 }
268 let m = scheduler_metrics_snapshot();
269 if args.len() == 2 && args[1].as_str() == "kv" {
270 print_metrics_kv(m);
271 return Ok(());
272 }
273 for i in 0..m.cpu_count.min(crate::arch::x86_64::percpu::MAX_CPUS) {
274 shell_println!(
275 "cpu{} rt={} fair={} idle={} sw={} pre={} st+={} st-={} tlm={}",
276 i,
277 m.rt_runtime_ticks[i],
278 m.fair_runtime_ticks[i],
279 m.idle_runtime_ticks[i],
280 m.switch_count[i],
281 m.preempt_count[i],
282 m.steal_in_count[i],
283 m.steal_out_count[i],
284 m.try_lock_fail_count[i]
285 );
286 }
287 Ok(())
288 }
289 "dump" => {
290 let kv = args.len() == 2 && args[1].as_str() == "kv";
291 if args.len() > 2 || (args.len() == 2 && !kv) {
292 shell_println!("Usage: scheduler dump [kv]");
293 return Err(ShellError::InvalidArguments);
294 }
295 let s = scheduler_state_snapshot();
296 if kv {
297 shell_println!(
298 "scheduler.initialized={}",
299 if s.initialized { 1 } else { 0 }
300 );
301 shell_println!("scheduler.boot_phase={}", s.boot_phase);
302 shell_println!("scheduler.cpu_count={}", s.cpu_count);
303 shell_println!("scheduler.pick.0={}", s.pick_order[0].as_str());
304 shell_println!("scheduler.pick.1={}", s.pick_order[1].as_str());
305 shell_println!("scheduler.pick.2={}", s.pick_order[2].as_str());
306 shell_println!("scheduler.steal.0={}", s.steal_order[0].as_str());
307 shell_println!("scheduler.steal.1={}", s.steal_order[1].as_str());
308 shell_println!("scheduler.blocked={}", s.blocked_tasks);
309 for i in 0..s.cpu_count.min(crate::arch::x86_64::percpu::MAX_CPUS) {
310 shell_println!("scheduler.cpu.{}.current={}", i, s.current_task[i]);
311 shell_println!("scheduler.cpu.{}.rq_rt={}", i, s.rq_rt[i]);
312 shell_println!("scheduler.cpu.{}.rq_fair={}", i, s.rq_fair[i]);
313 shell_println!("scheduler.cpu.{}.rq_idle={}", i, s.rq_idle[i]);
314 shell_println!(
315 "scheduler.cpu.{}.need_resched={}",
316 i,
317 if s.need_resched[i] { 1 } else { 0 }
318 );
319 }
320 } else {
321 shell_println!(
322 "scheduler dump: initialized={} phase={} cpus={} blocked={} pick=[{},{},{}] steal=[{},{}]",
323 s.initialized,
324 s.boot_phase,
325 s.cpu_count,
326 s.blocked_tasks,
327 s.pick_order[0].as_str(),
328 s.pick_order[1].as_str(),
329 s.pick_order[2].as_str(),
330 s.steal_order[0].as_str(),
331 s.steal_order[1].as_str()
332 );
333 for i in 0..s.cpu_count.min(crate::arch::x86_64::percpu::MAX_CPUS) {
334 shell_println!(
335 " cpu{} current={} rq_rt={} rq_fair={} rq_idle={} need_resched={}",
336 i,
337 s.current_task[i],
338 s.rq_rt[i],
339 s.rq_fair[i],
340 s.rq_idle[i],
341 s.need_resched[i]
342 );
343 }
344 }
345 Ok(())
346 }
347 _ => {
348 shell_println!("Usage: scheduler <debug|class|policy|metrics|dump> ...");
349 Err(ShellError::InvalidArguments)
350 }
351 }
352}