Skip to main content

strat9_kernel/shell/commands/sys/
scheduler.rs

1use 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
12/// Parses class.
13fn parse_class(s: &str) -> Option<crate::process::sched::SchedClassId> {
14    crate::process::sched::SchedClassId::parse(s)
15}
16
17/// Parses kind.
18fn parse_kind(s: &str) -> Option<crate::process::sched::SchedPolicyKind> {
19    crate::process::sched::SchedPolicyKind::parse(s)
20}
21
22/// Performs the print table operation.
23fn 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
52/// Performs the print table kv operation.
53fn 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
75/// Performs the print metrics kv operation.
76fn 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
94/// Performs the wants kv operation.
95fn wants_kv(args: &[String], idx: usize) -> bool {
96    args.get(idx).map(|s| s.as_str()) == Some("kv")
97}
98
99/// scheduler debug on|off|dump | class [kv] | policy map show [kv] | metrics [reset|kv] | dump [kv]
100pub 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}