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 crate::arch::core_local::*;
22use crate::arch::scheduler::TaskStacks;
23use crate::fd::stdio::*;
24use crate::fd::{FileDescriptor, ObjectInterface, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
25use crate::scheduler::CoreId;
26use crate::{arch, env};
27
28#[inline]
38fn msb(n: u64) -> Option<u32> {
39 NonZeroU64::new(n).map(|n| u64::BITS - 1 - n.leading_zeros())
40}
41
42#[derive(Copy, Clone, Debug, Eq, PartialEq)]
44pub(crate) enum TaskStatus {
45 Invalid,
46 Ready,
47 Running,
48 Blocked,
49 Finished,
50 Idle,
51}
52
53#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
55pub struct TaskId(i32);
56
57impl TaskId {
58 pub const fn into(self) -> i32 {
59 self.0
60 }
61
62 pub const fn from(x: i32) -> Self {
63 TaskId(x)
64 }
65}
66
67impl fmt::Display for TaskId {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
75pub struct Priority(u8);
76
77impl Priority {
78 pub const fn into(self) -> u8 {
79 self.0
80 }
81
82 pub const fn from(x: u8) -> Self {
83 Priority(x)
84 }
85}
86
87impl fmt::Display for Priority {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{}", self.0)
90 }
91}
92
93#[allow(dead_code)]
94pub const HIGH_PRIO: Priority = Priority::from(3);
95pub const NORMAL_PRIO: Priority = Priority::from(2);
96#[allow(dead_code)]
97pub const LOW_PRIO: Priority = Priority::from(1);
98pub const IDLE_PRIO: Priority = Priority::from(0);
99
100pub const NO_PRIORITIES: usize = 31;
102
103#[derive(Copy, Clone, Debug)]
104pub(crate) struct TaskHandle {
105 id: TaskId,
106 priority: Priority,
107 #[cfg(feature = "smp")]
108 core_id: CoreId,
109}
110
111impl TaskHandle {
112 pub fn new(id: TaskId, priority: Priority, #[cfg(feature = "smp")] core_id: CoreId) -> Self {
113 Self {
114 id,
115 priority,
116 #[cfg(feature = "smp")]
117 core_id,
118 }
119 }
120
121 #[cfg(feature = "smp")]
122 pub fn get_core_id(&self) -> CoreId {
123 self.core_id
124 }
125
126 pub fn get_id(&self) -> TaskId {
127 self.id
128 }
129
130 pub fn get_priority(&self) -> Priority {
131 self.priority
132 }
133}
134
135impl Ord for TaskHandle {
136 fn cmp(&self, other: &Self) -> cmp::Ordering {
137 self.id.cmp(&other.id)
138 }
139}
140
141impl PartialOrd for TaskHandle {
142 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
143 Some(self.cmp(other))
144 }
145}
146
147impl PartialEq for TaskHandle {
148 fn eq(&self, other: &Self) -> bool {
149 self.id == other.id
150 }
151}
152
153impl Eq for TaskHandle {}
154
155#[derive(Default)]
157pub(crate) struct TaskHandlePriorityQueue {
158 queues: [Option<VecDeque<TaskHandle>>; NO_PRIORITIES],
159 prio_bitmap: CachePadded<u64>,
160}
161
162impl TaskHandlePriorityQueue {
163 pub const fn new() -> Self {
165 Self {
166 queues: [const { None }; NO_PRIORITIES],
167 prio_bitmap: CachePadded::new(0),
168 }
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.prio_bitmap.into_inner() == 0
174 }
175
176 pub fn contains(&self, task: TaskHandle) -> bool {
179 matches!(self.queues[task.priority.into() as usize]
180 .as_ref(), Some(queue) if queue.iter().any(|queued| queued.id == task.id))
181 }
182
183 pub fn push(&mut self, task: TaskHandle) {
185 let i = task.priority.into() as usize;
186 *self.prio_bitmap |= (1 << i) as u64;
189 if let Some(queue) = &mut self.queues[i] {
190 queue.push_back(task);
191 } else {
192 let mut queue = VecDeque::new();
193 queue.push_back(task);
194 self.queues[i] = Some(queue);
195 }
196 }
197
198 fn pop_from_queue(&mut self, queue_index: usize) -> Option<TaskHandle> {
199 if let Some(queue) = &mut self.queues[queue_index] {
200 let task = queue.pop_front();
201
202 if queue.is_empty() {
203 *self.prio_bitmap &= !(1 << queue_index as u64);
204 }
205
206 task
207 } else {
208 None
209 }
210 }
211
212 pub fn pop(&mut self) -> Option<TaskHandle> {
214 if let Some(i) = msb(self.prio_bitmap.into_inner()) {
215 return self.pop_from_queue(i as usize);
216 }
217
218 None
219 }
220
221 pub fn remove(&mut self, task: TaskHandle) -> bool {
224 let queue_index = task.priority.into() as usize;
225 let mut success = false;
228 if let Some(queue) = &mut self.queues[queue_index] {
229 let mut i = 0;
230 while i != queue.len() {
231 if queue[i].id == task.id {
232 queue.remove(i);
233 success = true;
234 } else {
235 i += 1;
236 }
237 }
238
239 if queue.is_empty() {
240 *self.prio_bitmap &= !(1 << queue_index as u64);
241 }
242 }
243
244 success
245 }
246}
247
248pub(crate) struct PriorityTaskQueue {
250 queues: [LinkedList<Rc<RefCell<Task>>>; NO_PRIORITIES],
251 prio_bitmap: u64,
252}
253
254impl PriorityTaskQueue {
255 pub const fn new() -> PriorityTaskQueue {
257 const EMPTY_LIST: LinkedList<Rc<RefCell<Task>>> = LinkedList::new();
258 PriorityTaskQueue {
259 queues: [EMPTY_LIST; NO_PRIORITIES],
260 prio_bitmap: 0,
261 }
262 }
263
264 pub fn push(&mut self, task: Rc<RefCell<Task>>) {
266 let i = task.borrow().prio.into() as usize;
267 self.prio_bitmap |= (1 << i) as u64;
270 let queue = &mut self.queues[i];
271 queue.push_back(task);
272 }
273
274 fn pop_from_queue(&mut self, queue_index: usize) -> Option<Rc<RefCell<Task>>> {
275 let task = self.queues[queue_index].pop_front();
276 if self.queues[queue_index].is_empty() {
277 self.prio_bitmap &= !(1 << queue_index as u64);
278 }
279
280 task
281 }
282
283 fn remove_from_queue(
286 &mut self,
287 task_index: usize,
288 queue_index: usize,
289 ) -> Option<Rc<RefCell<Task>>> {
290 let queue = &mut self.queues[queue_index];
293 if task_index <= queue.len() {
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 } else {
303 None
304 }
305 }
306
307 pub fn is_empty(&self) -> bool {
309 self.prio_bitmap == 0
310 }
311
312 #[allow(dead_code)]
314 #[inline]
315 pub fn get_priority_bitmap(&self) -> &u64 {
316 &self.prio_bitmap
317 }
318
319 pub fn pop(&mut self) -> Option<Rc<RefCell<Task>>> {
321 if let Some(i) = msb(self.prio_bitmap) {
322 return self.pop_from_queue(i as usize);
323 }
324
325 None
326 }
327
328 pub fn pop_with_prio(&mut self, prio: Priority) -> Option<Rc<RefCell<Task>>> {
330 if let Some(i) = msb(self.prio_bitmap)
331 && i >= u32::from(prio.into())
332 {
333 return self.pop_from_queue(i as usize);
334 }
335
336 None
337 }
338
339 #[cfg(all(any(target_arch = "x86_64", target_arch = "riscv64"), feature = "smp"))]
341 pub fn get_highest_priority(&self) -> Priority {
342 if let Some(i) = msb(self.prio_bitmap) {
343 Priority::from(i.try_into().unwrap())
344 } else {
345 IDLE_PRIO
346 }
347 }
348
349 pub fn set_priority(&mut self, handle: TaskHandle, prio: Priority) -> Result<(), ()> {
351 let old_priority = handle.get_priority().into() as usize;
352 if let Some(index) = self.queues[old_priority]
353 .iter()
354 .position(|current_task| current_task.borrow().id == handle.id)
355 {
356 let Some(task) = self.remove_from_queue(index, old_priority) else {
357 return Err(());
358 };
359 task.borrow_mut().prio = prio;
360 self.push(task);
361 return Ok(());
362 }
363
364 Err(())
365 }
366}
367
368#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
370#[cfg_attr(
371 not(any(target_arch = "x86_64", target_arch = "aarch64")),
372 repr(align(64))
373)]
374pub(crate) struct Task {
375 pub id: TaskId,
377 pub status: TaskStatus,
379 pub prio: Priority,
381 pub last_stack_pointer: VirtAddr,
383 pub user_stack_pointer: VirtAddr,
385 pub last_fpu_state: arch::processor::FPUState,
387 pub core_id: CoreId,
389 pub stacks: TaskStacks,
391 pub object_map: Arc<
393 RwSpinLock<
394 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
395 >,
396 >,
397 #[cfg(not(feature = "common-os"))]
399 pub tls: Option<Tls>,
400 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
402 pub root_page_table: usize,
403}
404
405pub(crate) trait TaskFrame {
406 fn create_stack_frame(&mut self, func: unsafe extern "C" fn(usize), arg: usize);
408}
409
410impl Task {
411 pub fn new(
412 tid: TaskId,
413 core_id: CoreId,
414 task_status: TaskStatus,
415 task_prio: Priority,
416 stacks: TaskStacks,
417 object_map: Arc<
418 RwSpinLock<
419 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
420 >,
421 >,
422 ) -> Task {
423 debug!("Creating new task {tid} on core {core_id}");
424
425 Task {
426 id: tid,
427 status: task_status,
428 prio: task_prio,
429 last_stack_pointer: VirtAddr::zero(),
430 user_stack_pointer: VirtAddr::zero(),
431 last_fpu_state: arch::processor::FPUState::new(),
432 core_id,
433 stacks,
434 object_map,
435 #[cfg(not(feature = "common-os"))]
436 tls: None,
437 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
438 root_page_table: arch::create_new_root_page_table(),
439 }
440 }
441
442 pub fn new_idle(tid: TaskId, core_id: CoreId) -> Task {
443 debug!("Creating idle task {tid}");
444
445 static OBJECT_MAP: OnceCell<
447 Arc<
448 RwSpinLock<
449 HashMap<
450 FileDescriptor,
451 Arc<async_lock::RwLock<dyn ObjectInterface>>,
452 RandomState,
453 >,
454 >,
455 >,
456 > = OnceCell::new();
457
458 if core_id == 0 {
459 OBJECT_MAP
460 .set(Arc::new(RwSpinLock::new(HashMap::<
461 FileDescriptor,
462 Arc<async_lock::RwLock<dyn ObjectInterface>>,
463 RandomState,
464 >::with_hasher(
465 RandomState::with_seeds(0, 0, 0, 0),
466 ))))
467 .unwrap_or_else(|_| unreachable!());
470 let objmap = OBJECT_MAP.get().unwrap().clone();
471 let mut guard = objmap.write();
472 if env::is_uhyve() {
473 let stdin = Arc::new(async_lock::RwLock::new(UhyveStdin::new()));
474 let stdout = Arc::new(async_lock::RwLock::new(UhyveStdout::new()));
475 let stderr = Arc::new(async_lock::RwLock::new(UhyveStderr::new()));
476 guard.insert(STDIN_FILENO, stdin);
477 guard.insert(STDOUT_FILENO, stdout);
478 guard.insert(STDERR_FILENO, stderr);
479 } else {
480 let stdin = Arc::new(async_lock::RwLock::new(GenericStdin::new()));
481 let stdout = Arc::new(async_lock::RwLock::new(GenericStdout::new()));
482 let stderr = Arc::new(async_lock::RwLock::new(GenericStderr::new()));
483 guard.insert(STDIN_FILENO, stdin);
484 guard.insert(STDOUT_FILENO, stdout);
485 guard.insert(STDERR_FILENO, stderr);
486 }
487 }
488
489 Task {
490 id: tid,
491 status: TaskStatus::Idle,
492 prio: IDLE_PRIO,
493 last_stack_pointer: VirtAddr::zero(),
494 user_stack_pointer: VirtAddr::zero(),
495 last_fpu_state: arch::processor::FPUState::new(),
496 core_id,
497 stacks: TaskStacks::from_boot_stacks(),
498 object_map: OBJECT_MAP.get().unwrap().clone(),
499 #[cfg(not(feature = "common-os"))]
500 tls: None,
501 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
502 root_page_table: *crate::scheduler::BOOT_ROOT_PAGE_TABLE.get().unwrap(),
503 }
504 }
505}
506
507struct BlockedTask {
514 task: Rc<RefCell<Task>>,
515 wakeup_time: Option<u64>,
516}
517
518impl BlockedTask {
519 pub fn new(task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) -> Self {
520 Self { task, wakeup_time }
521 }
522}
523
524pub(crate) struct BlockedTaskQueue {
525 list: LinkedList<BlockedTask>,
526 #[cfg(feature = "net")]
527 network_wakeup_time: Option<u64>,
528}
529
530impl BlockedTaskQueue {
531 pub const fn new() -> Self {
532 Self {
533 list: LinkedList::new(),
534 #[cfg(feature = "net")]
535 network_wakeup_time: None,
536 }
537 }
538
539 fn mark_ready(task: &RefCell<Task>) {
540 let mut borrowed = task.borrow_mut();
541 debug!(
542 "Waking up task {} on core {}",
543 borrowed.id, borrowed.core_id
544 );
545
546 assert!(
547 borrowed.core_id == core_id(),
548 "Try to wake up task {} on the wrong core {} != {}",
549 borrowed.id,
550 borrowed.core_id,
551 core_id()
552 );
553
554 assert!(
555 borrowed.status == TaskStatus::Blocked,
556 "Trying to wake up task {} which is not blocked",
557 borrowed.id
558 );
559 borrowed.status = TaskStatus::Ready;
560 }
561
562 #[cfg(feature = "net")]
563 pub fn add_network_timer(&mut self, wakeup_time: Option<u64>) {
564 self.network_wakeup_time = wakeup_time;
565
566 let next = self.list.front().and_then(|t| t.wakeup_time);
567
568 let time = match (wakeup_time, next) {
569 (Some(a), Some(b)) => Some(a.min(b)),
570 (a, b) => a.or(b),
571 };
572
573 arch::set_oneshot_timer(time);
574 }
575
576 pub fn add(&mut self, task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) {
578 {
579 let mut borrowed = task.borrow_mut();
581 debug!("Blocking task {}", borrowed.id);
582
583 assert_eq!(
584 borrowed.status,
585 TaskStatus::Running,
586 "Trying to block task {} which is not running",
587 borrowed.id
588 );
589 borrowed.status = TaskStatus::Blocked;
590 }
591
592 let new_node = BlockedTask::new(task, wakeup_time);
593
594 if let Some(wt) = wakeup_time {
596 let mut cursor = self.list.cursor_front_mut();
597 let set_oneshot_timer = || {
598 #[cfg(not(feature = "net"))]
599 arch::set_oneshot_timer(wakeup_time);
600 #[cfg(feature = "net")]
601 match self.network_wakeup_time {
602 Some(time) => {
603 if time > wt {
604 arch::set_oneshot_timer(wakeup_time);
605 } else {
606 arch::set_oneshot_timer(self.network_wakeup_time);
607 }
608 }
609 _ => arch::set_oneshot_timer(wakeup_time),
610 }
611 };
612
613 while let Some(node) = cursor.current() {
614 let node_wakeup_time = node.wakeup_time;
615 if node_wakeup_time.is_none() || wt < node_wakeup_time.unwrap() {
616 cursor.insert_before(new_node);
617
618 set_oneshot_timer();
619 return;
620 }
621
622 cursor.move_next();
623 }
624
625 set_oneshot_timer();
626 }
627
628 self.list.push_back(new_node);
629 }
630
631 pub fn custom_wakeup(&mut self, task: TaskHandle) -> Rc<RefCell<Task>> {
633 let mut first_task = true;
634 let mut cursor = self.list.cursor_front_mut();
635
636 #[cfg(feature = "net")]
637 if let Some(wakeup_time) = self.network_wakeup_time
638 && wakeup_time <= arch::processor::get_timer_ticks()
639 {
640 self.network_wakeup_time = None;
641 }
642
643 while let Some(node) = cursor.current() {
645 if node.task.borrow().id == task.get_id() {
646 let task_ref = node.task.clone();
648 cursor.remove_current();
649
650 #[cfg(feature = "net")]
653 if first_task {
654 arch::set_oneshot_timer(cursor.current().map_or_else(
655 || self.network_wakeup_time,
656 |node| match node.wakeup_time {
657 Some(wt) => {
658 if let Some(timer) = self.network_wakeup_time {
659 if wt < timer { Some(wt) } else { Some(timer) }
660 } else {
661 Some(wt)
662 }
663 }
664 None => self.network_wakeup_time,
665 },
666 ));
667 }
668 #[cfg(not(feature = "net"))]
669 if first_task {
670 arch::set_oneshot_timer(
671 cursor
672 .current()
673 .map_or_else(|| None, |node| node.wakeup_time),
674 );
675 }
676
677 Self::mark_ready(&task_ref);
679
680 return task_ref;
681 }
682
683 first_task = false;
684 cursor.move_next();
685 }
686
687 unreachable!();
688 }
689
690 pub fn handle_waiting_tasks(&mut self, ready_queue: &mut PriorityTaskQueue) {
695 let time = arch::processor::get_timer_ticks();
697
698 #[cfg(feature = "net")]
699 if let Some(mut guard) = crate::executor::network::NIC.try_lock()
700 && let crate::executor::network::NetworkState::Initialized(nic) = &mut *guard
701 {
702 let now = crate::executor::network::now();
703 nic.poll_common(now);
704 self.network_wakeup_time = nic.poll_delay(now).map(|d| d.total_micros() + time);
705 }
706
707 let newly_ready_tasks = self.list.extract_if(|blocked_task| {
711 blocked_task
712 .wakeup_time
713 .is_some_and(|wakeup_time| wakeup_time < time)
714 });
715
716 for task in newly_ready_tasks {
717 Self::mark_ready(&task.task);
718 ready_queue.push(task.task);
719 }
720
721 let new_task_wakeup_time = self.list.front().and_then(|task| task.wakeup_time);
722 cfg_if::cfg_if! {
723 if #[cfg(feature = "net")] {
724 let network_wakeup_time = self.network_wakeup_time;
725 } else {
726 let network_wakeup_time = None;
727 }
728 };
729 let timer_wakeup_time = match (new_task_wakeup_time, network_wakeup_time) {
730 (None, None) => None,
731 (None, Some(network_wt)) => Some(network_wt),
732 (Some(task_wt), None) => Some(task_wt),
733 (Some(task_wt), Some(network_wt)) => Some(u64::min(task_wt, network_wt)),
734 };
735
736 arch::set_oneshot_timer(timer_wakeup_time);
737 }
738}