1#![allow(clippy::type_complexity)]
2
3#[cfg(not(feature = "common-os"))]
4pub(crate) mod tls;
5
6use alloc::collections::{LinkedList, VecDeque};
7use alloc::rc::Rc;
8use alloc::sync::Arc;
9use core::cell::RefCell;
10use core::num::NonZeroU64;
11use core::{cmp, fmt};
12
13use ahash::RandomState;
14use crossbeam_utils::CachePadded;
15use hashbrown::HashMap;
16use hermit_sync::{OnceCell, RwSpinLock};
17use memory_addresses::VirtAddr;
18
19#[cfg(not(feature = "common-os"))]
20use self::tls::Tls;
21use super::timer_interrupts::{Source, create_timer_abs};
22use crate::arch::core_local::*;
23use crate::arch::scheduler::TaskStacks;
24use crate::fd::stdio::*;
25use crate::fd::{Fd, RawFd, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
26use crate::scheduler::CoreId;
27use crate::{arch, env};
28
29#[inline]
39fn msb(n: u64) -> Option<u32> {
40 NonZeroU64::new(n).map(|n| u64::BITS - 1 - n.leading_zeros())
41}
42
43#[derive(Copy, Clone, Debug, Eq, PartialEq)]
45pub(crate) enum TaskStatus {
46 Invalid,
47 Ready,
48 Running,
49 Blocked,
50 Finished,
51 Idle,
52}
53
54#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
56pub struct TaskId(i32);
57
58impl TaskId {
59 pub const fn into(self) -> i32 {
60 self.0
61 }
62
63 pub const fn from(x: i32) -> Self {
64 TaskId(x)
65 }
66}
67
68impl fmt::Display for TaskId {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}", self.0)
71 }
72}
73
74#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
76pub struct Priority(u8);
77
78impl Priority {
79 pub const fn into(self) -> u8 {
80 self.0
81 }
82
83 pub const fn from(x: u8) -> Self {
84 Priority(x)
85 }
86}
87
88impl fmt::Display for Priority {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{}", self.0)
91 }
92}
93
94#[allow(dead_code)]
95pub const HIGH_PRIO: Priority = Priority::from(3);
96pub const NORMAL_PRIO: Priority = Priority::from(2);
97#[allow(dead_code)]
98pub const LOW_PRIO: Priority = Priority::from(1);
99pub const IDLE_PRIO: Priority = Priority::from(0);
100
101pub const NO_PRIORITIES: usize = 31;
103
104#[derive(Copy, Clone, Debug)]
105pub(crate) struct TaskHandle {
106 id: TaskId,
107 priority: Priority,
108 #[cfg(feature = "smp")]
109 core_id: CoreId,
110}
111
112impl TaskHandle {
113 pub fn new(id: TaskId, priority: Priority, #[cfg(feature = "smp")] core_id: CoreId) -> Self {
114 Self {
115 id,
116 priority,
117 #[cfg(feature = "smp")]
118 core_id,
119 }
120 }
121
122 #[cfg(feature = "smp")]
123 pub fn get_core_id(&self) -> CoreId {
124 self.core_id
125 }
126
127 pub fn get_id(&self) -> TaskId {
128 self.id
129 }
130
131 pub fn get_priority(&self) -> Priority {
132 self.priority
133 }
134}
135
136impl Ord for TaskHandle {
137 fn cmp(&self, other: &Self) -> cmp::Ordering {
138 self.id.cmp(&other.id)
139 }
140}
141
142impl PartialOrd for TaskHandle {
143 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
144 Some(self.cmp(other))
145 }
146}
147
148impl PartialEq for TaskHandle {
149 fn eq(&self, other: &Self) -> bool {
150 self.id == other.id
151 }
152}
153
154impl Eq for TaskHandle {}
155
156#[derive(Default)]
158pub(crate) struct TaskHandlePriorityQueue {
159 queues: [Option<VecDeque<TaskHandle>>; NO_PRIORITIES],
160 prio_bitmap: CachePadded<u64>,
161}
162
163impl TaskHandlePriorityQueue {
164 pub const fn new() -> Self {
166 Self {
167 queues: [const { None }; NO_PRIORITIES],
168 prio_bitmap: CachePadded::new(0),
169 }
170 }
171
172 pub fn is_empty(&self) -> bool {
174 self.prio_bitmap.into_inner() == 0
175 }
176
177 pub fn contains(&self, task: TaskHandle) -> bool {
180 matches!(self.queues[task.priority.into() as usize]
181 .as_ref(), Some(queue) if queue.iter().any(|queued| queued.id == task.id))
182 }
183
184 pub fn push(&mut self, task: TaskHandle) {
186 let i = task.priority.into() as usize;
187 *self.prio_bitmap |= (1 << i) as u64;
190 if let Some(queue) = &mut self.queues[i] {
191 queue.push_back(task);
192 } else {
193 let mut queue = VecDeque::new();
194 queue.push_back(task);
195 self.queues[i] = Some(queue);
196 }
197 }
198
199 fn pop_from_queue(&mut self, queue_index: usize) -> Option<TaskHandle> {
200 let queue = self.queues[queue_index].as_mut()?;
201
202 let task = queue.pop_front();
203
204 if queue.is_empty() {
205 *self.prio_bitmap &= !(1 << queue_index as u64);
206 }
207
208 task
209 }
210
211 pub fn pop(&mut self) -> Option<TaskHandle> {
213 let i = msb(self.prio_bitmap.into_inner())?;
214
215 self.pop_from_queue(i as usize)
216 }
217
218 pub fn remove(&mut self, task: TaskHandle) -> bool {
221 let queue_index = task.priority.into() as usize;
222 let mut success = false;
225 if let Some(queue) = &mut self.queues[queue_index] {
226 let mut i = 0;
227 while i != queue.len() {
228 if queue[i].id == task.id {
229 queue.remove(i);
230 success = true;
231 } else {
232 i += 1;
233 }
234 }
235
236 if queue.is_empty() {
237 *self.prio_bitmap &= !(1 << queue_index as u64);
238 }
239 }
240
241 success
242 }
243}
244
245pub(crate) struct PriorityTaskQueue {
247 queues: [LinkedList<Rc<RefCell<Task>>>; NO_PRIORITIES],
248 prio_bitmap: u64,
249}
250
251impl PriorityTaskQueue {
252 pub const fn new() -> PriorityTaskQueue {
254 const EMPTY_LIST: LinkedList<Rc<RefCell<Task>>> = LinkedList::new();
255 PriorityTaskQueue {
256 queues: [EMPTY_LIST; NO_PRIORITIES],
257 prio_bitmap: 0,
258 }
259 }
260
261 pub fn push(&mut self, task: Rc<RefCell<Task>>) {
263 let i = task.borrow().prio.into() as usize;
264 self.prio_bitmap |= (1 << i) as u64;
267 let queue = &mut self.queues[i];
268 queue.push_back(task);
269 }
270
271 fn pop_from_queue(&mut self, queue_index: usize) -> Option<Rc<RefCell<Task>>> {
272 let task = self.queues[queue_index].pop_front();
273 if self.queues[queue_index].is_empty() {
274 self.prio_bitmap &= !(1 << queue_index as u64);
275 }
276
277 task
278 }
279
280 fn remove_from_queue(
283 &mut self,
284 task_index: usize,
285 queue_index: usize,
286 ) -> Option<Rc<RefCell<Task>>> {
287 let queue = &mut self.queues[queue_index];
290 if queue.len() < task_index {
291 return None;
292 }
293
294 let mut split_list = queue.split_off(task_index);
296 let element = split_list.pop_front();
297 queue.append(&mut split_list);
298 if queue.is_empty() {
299 self.prio_bitmap &= !(1 << queue_index as u64);
300 }
301 element
302 }
303
304 pub fn is_empty(&self) -> bool {
306 self.prio_bitmap == 0
307 }
308
309 #[allow(dead_code)]
311 #[inline]
312 pub fn get_priority_bitmap(&self) -> &u64 {
313 &self.prio_bitmap
314 }
315
316 pub fn pop(&mut self) -> Option<Rc<RefCell<Task>>> {
318 let i = msb(self.prio_bitmap)?;
319
320 self.pop_from_queue(i as usize)
321 }
322
323 pub fn pop_with_prio(&mut self, prio: Priority) -> Option<Rc<RefCell<Task>>> {
325 let i = msb(self.prio_bitmap)?;
326
327 if i < u32::from(prio.into()) {
328 return None;
329 }
330
331 self.pop_from_queue(i as usize)
332 }
333
334 #[cfg(all(any(target_arch = "x86_64", target_arch = "riscv64"), feature = "smp"))]
336 pub fn get_highest_priority(&self) -> Priority {
337 let Some(i) = msb(self.prio_bitmap) else {
338 return IDLE_PRIO;
339 };
340
341 Priority::from(i.try_into().unwrap())
342 }
343
344 pub fn set_priority(&mut self, handle: TaskHandle, prio: Priority) -> Result<(), ()> {
346 let old_priority = handle.get_priority().into() as usize;
347 let index = self.queues[old_priority]
348 .iter()
349 .position(|current_task| current_task.borrow().id == handle.id)
350 .ok_or(())?;
351
352 let task = self.remove_from_queue(index, old_priority).ok_or(())?;
353 task.borrow_mut().prio = prio;
354 self.push(task);
355 Ok(())
356 }
357}
358
359#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
361#[cfg_attr(
362 not(any(target_arch = "x86_64", target_arch = "aarch64")),
363 repr(align(64))
364)]
365pub(crate) struct Task {
366 pub id: TaskId,
368 pub status: TaskStatus,
370 pub prio: Priority,
372 pub last_stack_pointer: VirtAddr,
374 pub user_stack_pointer: VirtAddr,
376 pub last_fpu_state: arch::processor::FPUState,
378 pub core_id: CoreId,
380 pub stacks: TaskStacks,
382 pub object_map: Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
384 #[cfg(not(feature = "common-os"))]
386 pub tls: Option<Tls>,
387 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
389 pub root_page_table: usize,
390}
391
392pub(crate) trait TaskFrame {
393 fn create_stack_frame(&mut self, func: unsafe extern "C" fn(usize), arg: usize);
395}
396
397impl Task {
398 pub fn new(
399 tid: TaskId,
400 core_id: CoreId,
401 task_status: TaskStatus,
402 task_prio: Priority,
403 stacks: TaskStacks,
404 object_map: Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
405 ) -> Task {
406 debug!("Creating new task {tid} on core {core_id}");
407
408 Task {
409 id: tid,
410 status: task_status,
411 prio: task_prio,
412 last_stack_pointer: VirtAddr::zero(),
413 user_stack_pointer: VirtAddr::zero(),
414 last_fpu_state: arch::processor::FPUState::new(),
415 core_id,
416 stacks,
417 object_map,
418 #[cfg(not(feature = "common-os"))]
419 tls: None,
420 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
421 root_page_table: arch::create_new_root_page_table(),
422 }
423 }
424
425 pub fn new_idle(tid: TaskId, core_id: CoreId) -> Task {
426 debug!("Creating idle task {tid}");
427
428 static OBJECT_MAP: OnceCell<
430 Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
431 > = OnceCell::new();
432
433 if core_id == 0 {
434 OBJECT_MAP
435 .set(Arc::new(RwSpinLock::new(HashMap::<
436 RawFd,
437 Arc<async_lock::RwLock<Fd>>,
438 RandomState,
439 >::with_hasher(
440 RandomState::with_seeds(0, 0, 0, 0),
441 ))))
442 .unwrap_or_else(|_| unreachable!());
445 let objmap = OBJECT_MAP.get().unwrap().clone();
446 let mut guard = objmap.write();
447 if env::is_uhyve() {
448 let stdin = Arc::new(async_lock::RwLock::new(UhyveStdin::new().into()));
449 let stdout = Arc::new(async_lock::RwLock::new(UhyveStdout::new().into()));
450 let stderr = Arc::new(async_lock::RwLock::new(UhyveStderr::new().into()));
451 guard.insert(STDIN_FILENO, stdin);
452 guard.insert(STDOUT_FILENO, stdout);
453 guard.insert(STDERR_FILENO, stderr);
454 } else {
455 let stdin = Arc::new(async_lock::RwLock::new(GenericStdin::new().into()));
456 let stdout = Arc::new(async_lock::RwLock::new(GenericStdout::new().into()));
457 let stderr = Arc::new(async_lock::RwLock::new(GenericStderr::new().into()));
458 guard.insert(STDIN_FILENO, stdin);
459 guard.insert(STDOUT_FILENO, stdout);
460 guard.insert(STDERR_FILENO, stderr);
461 }
462 }
463
464 #[cfg(not(feature = "common-os"))]
465 let tls = if cfg!(feature = "instrument-mcount") {
466 Tls::from_env().inspect(Tls::set_thread_ptr)
467 } else {
468 None
469 };
470
471 Task {
472 id: tid,
473 status: TaskStatus::Idle,
474 prio: IDLE_PRIO,
475 last_stack_pointer: VirtAddr::zero(),
476 user_stack_pointer: VirtAddr::zero(),
477 last_fpu_state: arch::processor::FPUState::new(),
478 core_id,
479 stacks: TaskStacks::from_boot_stacks(),
480 object_map: OBJECT_MAP.get().unwrap().clone(),
481 #[cfg(not(feature = "common-os"))]
482 tls,
483 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
484 root_page_table: *crate::scheduler::BOOT_ROOT_PAGE_TABLE.get().unwrap(),
485 }
486 }
487}
488
489struct BlockedTask {
496 task: Rc<RefCell<Task>>,
497 wakeup_time: Option<u64>,
498}
499
500impl BlockedTask {
501 pub fn new(task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) -> Self {
502 Self { task, wakeup_time }
503 }
504}
505
506pub(crate) struct BlockedTaskQueue {
507 list: LinkedList<BlockedTask>,
508}
509
510impl BlockedTaskQueue {
511 pub const fn new() -> Self {
512 Self {
513 list: LinkedList::new(),
514 }
515 }
516
517 fn mark_ready(task: &RefCell<Task>) {
518 let mut borrowed = task.borrow_mut();
519 debug!(
520 "Waking up task {} on core {}",
521 borrowed.id, borrowed.core_id
522 );
523
524 assert!(
525 borrowed.core_id == core_id(),
526 "Try to wake up task {} on the wrong core {} != {}",
527 borrowed.id,
528 borrowed.core_id,
529 core_id()
530 );
531
532 assert!(
533 borrowed.status == TaskStatus::Blocked,
534 "Trying to wake up task {} which is not blocked",
535 borrowed.id
536 );
537 borrowed.status = TaskStatus::Ready;
538 }
539
540 pub fn add(&mut self, task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) {
542 {
543 let mut borrowed = task.borrow_mut();
545 debug!("Blocking task {}", borrowed.id);
546
547 assert_eq!(
548 borrowed.status,
549 TaskStatus::Running,
550 "Trying to block task {} which is not running",
551 borrowed.id
552 );
553 borrowed.status = TaskStatus::Blocked;
554 }
555
556 let new_node = BlockedTask::new(task, wakeup_time);
557
558 if let Some(wt) = wakeup_time {
560 let mut cursor = self.list.cursor_front_mut();
561
562 while let Some(node) = cursor.current() {
563 let node_wakeup_time = node.wakeup_time;
564 if node_wakeup_time.is_none() || wt < node_wakeup_time.unwrap() {
565 cursor.insert_before(new_node);
566
567 create_timer_abs(Source::Scheduler, wt);
568 return;
569 }
570
571 cursor.move_next();
572 }
573
574 create_timer_abs(Source::Scheduler, wt);
575 }
576
577 self.list.push_back(new_node);
578 }
579
580 pub fn custom_wakeup(&mut self, task: TaskHandle) -> Rc<RefCell<Task>> {
582 let mut first_task = true;
583 let mut cursor = self.list.cursor_front_mut();
584
585 while let Some(node) = cursor.current() {
587 if node.task.borrow().id == task.get_id() {
588 let task_ref = node.task.clone();
590 cursor.remove_current();
591
592 if first_task
595 && let Some(wakeup) = cursor
596 .current()
597 .map_or_else(|| None, |node| node.wakeup_time)
598 {
599 create_timer_abs(Source::Scheduler, wakeup);
600 }
601
602 Self::mark_ready(&task_ref);
604
605 return task_ref;
606 }
607
608 first_task = false;
609 cursor.move_next();
610 }
611
612 unreachable!();
613 }
614
615 pub fn handle_waiting_tasks(&mut self, ready_queue: &mut PriorityTaskQueue) {
620 let time = arch::processor::get_timer_ticks();
622
623 let newly_ready_tasks = self.list.extract_if(|blocked_task| {
627 blocked_task
628 .wakeup_time
629 .is_some_and(|wakeup_time| wakeup_time < time)
630 });
631
632 for task in newly_ready_tasks {
633 Self::mark_ready(&task.task);
634 ready_queue.push(task.task);
635 }
636
637 let new_task_wakeup_time = self.list.front().and_then(|task| task.wakeup_time);
638
639 if let Some(wakeup) = new_task_wakeup_time {
640 create_timer_abs(Source::Scheduler, wakeup);
641 }
642 }
643}