kernel/sched/state/sched.rs
1// SPDX-License-Identifier: GPL-3.0-only
2//! Root scheduler data struct
3//!
4//! Authors: MarioS271
5
6use crate::lib::sync::irq_mutex::IrqMutex;
7use crate::lib::sync::unchecked_cell::UncheckedCell;
8use crate::sched::process::{AtomicPid, Pid, Process};
9use alloc::collections::BTreeMap;
10use core::sync::atomic::Ordering;
11
12/// Wrapper type for `BTreeMap<Pid, Process`
13pub type ProcessMap = BTreeMap<Pid, Process>;
14
15/// Holds the scheduler state, including active process data, scheduler info and more
16pub struct Sched {
17 procs: UncheckedCell<IrqMutex<ProcessMap>>,
18 active_pid: AtomicPid,
19 next_pid: AtomicPid
20}
21
22impl Sched {
23 /// Constructor; initializes all values zeroed
24 pub const fn new() -> Self {
25 Self {
26 procs: UncheckedCell::new(),
27 active_pid: AtomicPid::new(0),
28 next_pid: AtomicPid::new(0)
29 }
30 }
31
32 /// Initialize the sched state struct to its correct default values
33 ///
34 /// # Safety
35 /// The caller must guarantee the following:
36 /// - That this method has never been called before and will never be called again
37 /// - That at the time of calling this method, no references or pointers to this data exist
38 /// - While this method is being called, no other CPU is working with the given data
39 pub fn init(&self) {
40 unsafe { self.procs.init(IrqMutex::new(BTreeMap::new())); }
41 self.next_pid.store(1, Ordering::Relaxed);
42 }
43
44 /// Hand out the next free PID
45 pub fn alloc_pid(&self) -> Pid {
46 self.next_pid.fetch_add(1, Ordering::Relaxed)
47 }
48
49 /// Getter for `Sched::active_pid`
50 pub fn active_pid(&self) -> Pid {
51 self.active_pid.load(Ordering::Acquire)
52 }
53
54 /// Set the value of `Sched::active_pid`
55 pub fn set_active_pid(&self, new_active_pid: Pid) {
56 self.active_pid.store(new_active_pid, Ordering::Release);
57 }
58
59 /// Run `f` with the process with belongs to `pid`
60 ///
61 /// # Important
62 /// The process table is held for the entire closure. NEVER touch user memory inside `f`
63 /// as that could cause a page fault which would attempt to lock the process table, resulting
64 /// in a deadlock. Copy necessary values and then modify after the closure returns.
65 ///
66 /// # Safety
67 /// The caller must guarantee that [`Sched::init`] has already been called
68 pub unsafe fn with_process<R>(&self, pid: Pid, f: impl FnOnce(&mut Process) -> R) -> Option<R> {
69 let mut procs = unsafe { self.procs.get().lock() };
70 procs.get_mut(&pid).map(f)
71 }
72
73 /// Run `f` with the process which is currently running
74 ///
75 /// # Safety
76 /// The caller must guarantee that [`Sched::init`] has already been called
77 pub unsafe fn with_active_process<R>(&self, f: impl FnOnce(&mut Process) -> R) -> Option<R> {
78 unsafe { self.with_process(self.active_pid(), f) }
79 }
80
81 /// Add a process to the process table
82 ///
83 /// # Safety
84 /// The caller must guarantee that [`Sched::init`] has already been called
85 pub unsafe fn insert_process(&self, process: Process) {
86 unsafe { self.procs.get() }.lock().insert(process.pid, process);
87 }
88
89 /// Remove a process from the process table and return it, or `None` if it did not exist
90 ///
91 /// # Safety
92 /// The caller must guarantee that [`Sched::init`] has already been called
93 pub unsafe fn remove_process(&self, pid: Pid) -> Option<Process> {
94 unsafe { self.procs.get() }.lock().remove(&pid)
95 }
96}