Skip to main content

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}