1#![allow(clippy::type_complexity)]
2
3#[cfg(not(feature = "common-os"))]
4use alloc::boxed::Box;
5use alloc::collections::{LinkedList, VecDeque};
6use alloc::rc::Rc;
7use alloc::sync::Arc;
8use core::cell::RefCell;
9use core::num::NonZeroU64;
10use core::{cmp, fmt};
11
12use ahash::RandomState;
13use crossbeam_utils::CachePadded;
14use hashbrown::HashMap;
15use hermit_sync::{OnceCell, RwSpinLock};
16use memory_addresses::VirtAddr;
17
18use crate::arch::core_local::*;
19use crate::arch::scheduler::TaskStacks;
20#[cfg(not(feature = "common-os"))]
21use crate::arch::scheduler::TaskTLS;
22use crate::errno::Errno;
23use crate::executor::poll_on;
24use crate::fd::stdio::*;
25use crate::fd::{FileDescriptor, ObjectInterface, 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 if let Some(queue) = &mut self.queues[queue_index] {
201 let task = queue.pop_front();
202
203 if queue.is_empty() {
204 *self.prio_bitmap &= !(1 << queue_index as u64);
205 }
206
207 task
208 } else {
209 None
210 }
211 }
212
213 pub fn pop(&mut self) -> Option<TaskHandle> {
215 if let Some(i) = msb(self.prio_bitmap.into_inner()) {
216 return self.pop_from_queue(i as usize);
217 }
218
219 None
220 }
221
222 pub fn remove(&mut self, task: TaskHandle) -> bool {
225 let queue_index = task.priority.into() as usize;
226 let mut success = false;
229 if let Some(queue) = &mut self.queues[queue_index] {
230 let mut i = 0;
231 while i != queue.len() {
232 if queue[i].id == task.id {
233 queue.remove(i);
234 success = true;
235 } else {
236 i += 1;
237 }
238 }
239
240 if queue.is_empty() {
241 *self.prio_bitmap &= !(1 << queue_index as u64);
242 }
243 }
244
245 success
246 }
247}
248
249pub(crate) struct PriorityTaskQueue {
251 queues: [LinkedList<Rc<RefCell<Task>>>; NO_PRIORITIES],
252 prio_bitmap: u64,
253}
254
255impl PriorityTaskQueue {
256 pub const fn new() -> PriorityTaskQueue {
258 const EMPTY_LIST: LinkedList<Rc<RefCell<Task>>> = LinkedList::new();
259 PriorityTaskQueue {
260 queues: [EMPTY_LIST; NO_PRIORITIES],
261 prio_bitmap: 0,
262 }
263 }
264
265 pub fn push(&mut self, task: Rc<RefCell<Task>>) {
267 let i = task.borrow().prio.into() as usize;
268 self.prio_bitmap |= (1 << i) as u64;
271 let queue = &mut self.queues[i];
272 queue.push_back(task);
273 }
274
275 fn pop_from_queue(&mut self, queue_index: usize) -> Option<Rc<RefCell<Task>>> {
276 let task = self.queues[queue_index].pop_front();
277 if self.queues[queue_index].is_empty() {
278 self.prio_bitmap &= !(1 << queue_index as u64);
279 }
280
281 task
282 }
283
284 fn remove_from_queue(
287 &mut self,
288 task_index: usize,
289 queue_index: usize,
290 ) -> Option<Rc<RefCell<Task>>> {
291 let queue = &mut self.queues[queue_index];
294 if task_index <= queue.len() {
295 let mut split_list = queue.split_off(task_index);
297 let element = split_list.pop_front();
298 queue.append(&mut split_list);
299 if queue.is_empty() {
300 self.prio_bitmap &= !(1 << queue_index as u64);
301 }
302 element
303 } else {
304 None
305 }
306 }
307
308 pub fn is_empty(&self) -> bool {
310 self.prio_bitmap == 0
311 }
312
313 #[allow(dead_code)]
315 #[inline]
316 pub fn get_priority_bitmap(&self) -> &u64 {
317 &self.prio_bitmap
318 }
319
320 pub fn pop(&mut self) -> Option<Rc<RefCell<Task>>> {
322 if let Some(i) = msb(self.prio_bitmap) {
323 return self.pop_from_queue(i as usize);
324 }
325
326 None
327 }
328
329 pub fn pop_with_prio(&mut self, prio: Priority) -> Option<Rc<RefCell<Task>>> {
331 if let Some(i) = msb(self.prio_bitmap)
332 && i >= u32::from(prio.into())
333 {
334 return self.pop_from_queue(i as usize);
335 }
336
337 None
338 }
339
340 #[cfg(all(any(target_arch = "x86_64", target_arch = "riscv64"), feature = "smp"))]
342 pub fn get_highest_priority(&self) -> Priority {
343 if let Some(i) = msb(self.prio_bitmap) {
344 Priority::from(i.try_into().unwrap())
345 } else {
346 IDLE_PRIO
347 }
348 }
349
350 pub fn set_priority(&mut self, handle: TaskHandle, prio: Priority) -> Result<(), ()> {
352 let old_priority = handle.get_priority().into() as usize;
353 if let Some(index) = self.queues[old_priority]
354 .iter()
355 .position(|current_task| current_task.borrow().id == handle.id)
356 {
357 let Some(task) = self.remove_from_queue(index, old_priority) else {
358 return Err(());
359 };
360 task.borrow_mut().prio = prio;
361 self.push(task);
362 return Ok(());
363 }
364
365 Err(())
366 }
367}
368
369#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
371#[cfg_attr(
372 not(any(target_arch = "x86_64", target_arch = "aarch64")),
373 repr(align(64))
374)]
375pub(crate) struct Task {
376 pub id: TaskId,
378 pub status: TaskStatus,
380 pub prio: Priority,
382 pub last_stack_pointer: VirtAddr,
384 pub user_stack_pointer: VirtAddr,
386 pub last_fpu_state: arch::processor::FPUState,
388 pub core_id: CoreId,
390 pub stacks: TaskStacks,
392 pub object_map: Arc<
394 RwSpinLock<
395 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
396 >,
397 >,
398 #[cfg(not(feature = "common-os"))]
400 pub tls: Option<Box<TaskTLS>>,
401 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
403 pub root_page_table: usize,
404}
405
406pub(crate) trait TaskFrame {
407 fn create_stack_frame(&mut self, func: unsafe extern "C" fn(usize), arg: usize);
409}
410
411impl Task {
412 pub fn new(
413 tid: TaskId,
414 core_id: CoreId,
415 task_status: TaskStatus,
416 task_prio: Priority,
417 stacks: TaskStacks,
418 object_map: Arc<
419 RwSpinLock<
420 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
421 >,
422 >,
423 ) -> Task {
424 debug!("Creating new task {tid} on core {core_id}");
425
426 Task {
427 id: tid,
428 status: task_status,
429 prio: task_prio,
430 last_stack_pointer: VirtAddr::zero(),
431 user_stack_pointer: VirtAddr::zero(),
432 last_fpu_state: arch::processor::FPUState::new(),
433 core_id,
434 stacks,
435 object_map,
436 #[cfg(not(feature = "common-os"))]
437 tls: None,
438 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
439 root_page_table: arch::create_new_root_page_table(),
440 }
441 }
442
443 pub fn new_idle(tid: TaskId, core_id: CoreId) -> Task {
444 debug!("Creating idle task {tid}");
445
446 static OBJECT_MAP: OnceCell<
448 Arc<
449 RwSpinLock<
450 HashMap<
451 FileDescriptor,
452 Arc<async_lock::RwLock<dyn ObjectInterface>>,
453 RandomState,
454 >,
455 >,
456 >,
457 > = OnceCell::new();
458
459 if core_id == 0 {
460 OBJECT_MAP
461 .set(Arc::new(RwSpinLock::new(HashMap::<
462 FileDescriptor,
463 Arc<async_lock::RwLock<dyn ObjectInterface>>,
464 RandomState,
465 >::with_hasher(
466 RandomState::with_seeds(0, 0, 0, 0),
467 ))))
468 .unwrap();
469 let objmap = OBJECT_MAP.get().unwrap().clone();
470 let _ = poll_on(async {
471 let mut guard = objmap.write();
472 if env::is_uhyve() {
473 guard
474 .try_insert(
475 STDIN_FILENO,
476 Arc::new(async_lock::RwLock::new(UhyveStdin::new())),
477 )
478 .map_err(|_| Errno::Io)?;
479 guard
480 .try_insert(
481 STDOUT_FILENO,
482 Arc::new(async_lock::RwLock::new(UhyveStdout::new())),
483 )
484 .map_err(|_| Errno::Io)?;
485 guard
486 .try_insert(
487 STDERR_FILENO,
488 Arc::new(async_lock::RwLock::new(UhyveStderr::new())),
489 )
490 .map_err(|_| Errno::Io)?;
491 } else {
492 guard
493 .try_insert(
494 STDIN_FILENO,
495 Arc::new(async_lock::RwLock::new(GenericStdin::new())),
496 )
497 .map_err(|_| Errno::Io)?;
498 guard
499 .try_insert(
500 STDOUT_FILENO,
501 Arc::new(async_lock::RwLock::new(GenericStdout::new())),
502 )
503 .map_err(|_| Errno::Io)?;
504 guard
505 .try_insert(
506 STDERR_FILENO,
507 Arc::new(async_lock::RwLock::new(GenericStderr::new())),
508 )
509 .map_err(|_| Errno::Io)?;
510 }
511
512 Ok(())
513 });
514 }
515
516 Task {
517 id: tid,
518 status: TaskStatus::Idle,
519 prio: IDLE_PRIO,
520 last_stack_pointer: VirtAddr::zero(),
521 user_stack_pointer: VirtAddr::zero(),
522 last_fpu_state: arch::processor::FPUState::new(),
523 core_id,
524 stacks: TaskStacks::from_boot_stacks(),
525 object_map: OBJECT_MAP.get().unwrap().clone(),
526 #[cfg(not(feature = "common-os"))]
527 tls: None,
528 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
529 root_page_table: *crate::scheduler::BOOT_ROOT_PAGE_TABLE.get().unwrap(),
530 }
531 }
532}
533
534struct BlockedTask {
541 task: Rc<RefCell<Task>>,
542 wakeup_time: Option<u64>,
543}
544
545impl BlockedTask {
546 pub fn new(task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) -> Self {
547 Self { task, wakeup_time }
548 }
549}
550
551pub(crate) struct BlockedTaskQueue {
552 list: LinkedList<BlockedTask>,
553 #[cfg(feature = "net")]
554 network_wakeup_time: Option<u64>,
555}
556
557impl BlockedTaskQueue {
558 pub const fn new() -> Self {
559 Self {
560 list: LinkedList::new(),
561 #[cfg(feature = "net")]
562 network_wakeup_time: None,
563 }
564 }
565
566 fn mark_ready(task: &RefCell<Task>) {
567 let mut borrowed = task.borrow_mut();
568 debug!(
569 "Waking up task {} on core {}",
570 borrowed.id, borrowed.core_id
571 );
572
573 assert!(
574 borrowed.core_id == core_id(),
575 "Try to wake up task {} on the wrong core {} != {}",
576 borrowed.id,
577 borrowed.core_id,
578 core_id()
579 );
580
581 assert!(
582 borrowed.status == TaskStatus::Blocked,
583 "Trying to wake up task {} which is not blocked",
584 borrowed.id
585 );
586 borrowed.status = TaskStatus::Ready;
587 }
588
589 #[cfg(feature = "net")]
590 pub fn add_network_timer(&mut self, wakeup_time: Option<u64>) {
591 self.network_wakeup_time = wakeup_time;
592
593 let next = self.list.front().and_then(|t| t.wakeup_time);
594
595 let time = match (wakeup_time, next) {
596 (Some(a), Some(b)) => Some(a.min(b)),
597 (a, b) => a.or(b),
598 };
599
600 arch::set_oneshot_timer(time);
601 }
602
603 pub fn add(&mut self, task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) {
605 {
606 let mut borrowed = task.borrow_mut();
608 debug!("Blocking task {}", borrowed.id);
609
610 assert_eq!(
611 borrowed.status,
612 TaskStatus::Running,
613 "Trying to block task {} which is not running",
614 borrowed.id
615 );
616 borrowed.status = TaskStatus::Blocked;
617 }
618
619 let new_node = BlockedTask::new(task, wakeup_time);
620
621 if let Some(wt) = wakeup_time {
623 let mut cursor = self.list.cursor_front_mut();
624 let set_oneshot_timer = || {
625 #[cfg(not(feature = "net"))]
626 arch::set_oneshot_timer(wakeup_time);
627 #[cfg(feature = "net")]
628 match self.network_wakeup_time {
629 Some(time) => {
630 if time > wt {
631 arch::set_oneshot_timer(wakeup_time);
632 } else {
633 arch::set_oneshot_timer(self.network_wakeup_time);
634 }
635 }
636 _ => arch::set_oneshot_timer(wakeup_time),
637 }
638 };
639
640 while let Some(node) = cursor.current() {
641 let node_wakeup_time = node.wakeup_time;
642 if node_wakeup_time.is_none() || wt < node_wakeup_time.unwrap() {
643 cursor.insert_before(new_node);
644
645 set_oneshot_timer();
646 return;
647 }
648
649 cursor.move_next();
650 }
651
652 set_oneshot_timer();
653 }
654
655 self.list.push_back(new_node);
656 }
657
658 pub fn custom_wakeup(&mut self, task: TaskHandle) -> Rc<RefCell<Task>> {
660 let mut first_task = true;
661 let mut cursor = self.list.cursor_front_mut();
662
663 #[cfg(feature = "net")]
664 if let Some(wakeup_time) = self.network_wakeup_time
665 && wakeup_time <= arch::processor::get_timer_ticks()
666 {
667 self.network_wakeup_time = None;
668 }
669
670 while let Some(node) = cursor.current() {
672 if node.task.borrow().id == task.get_id() {
673 let task_ref = node.task.clone();
675 cursor.remove_current();
676
677 #[cfg(feature = "net")]
680 if first_task {
681 arch::set_oneshot_timer(cursor.current().map_or_else(
682 || self.network_wakeup_time,
683 |node| match node.wakeup_time {
684 Some(wt) => {
685 if let Some(timer) = self.network_wakeup_time {
686 if wt < timer { Some(wt) } else { Some(timer) }
687 } else {
688 Some(wt)
689 }
690 }
691 None => self.network_wakeup_time,
692 },
693 ));
694 }
695 #[cfg(not(feature = "net"))]
696 if first_task {
697 arch::set_oneshot_timer(
698 cursor
699 .current()
700 .map_or_else(|| None, |node| node.wakeup_time),
701 );
702 }
703
704 Self::mark_ready(&task_ref);
706
707 return task_ref;
708 }
709
710 first_task = false;
711 cursor.move_next();
712 }
713
714 unreachable!();
715 }
716
717 pub fn handle_waiting_tasks(&mut self, ready_queue: &mut PriorityTaskQueue) {
722 let time = arch::processor::get_timer_ticks();
724
725 #[cfg(feature = "net")]
726 if let Some(mut guard) = crate::executor::network::NIC.try_lock()
727 && let crate::executor::network::NetworkState::Initialized(nic) = &mut *guard
728 {
729 let now = crate::executor::network::now();
730 nic.poll_common(now);
731 self.network_wakeup_time = nic.poll_delay(now).map(|d| d.total_micros() + time);
732 }
733
734 let newly_ready_tasks = self.list.extract_if(|blocked_task| {
738 blocked_task
739 .wakeup_time
740 .is_some_and(|wakeup_time| wakeup_time < time)
741 });
742
743 for task in newly_ready_tasks {
744 Self::mark_ready(&task.task);
745 ready_queue.push(task.task);
746 }
747
748 let new_task_wakeup_time = self.list.front().and_then(|task| task.wakeup_time);
749 cfg_if::cfg_if! {
750 if #[cfg(feature = "net")] {
751 let network_wakeup_time = self.network_wakeup_time;
752 } else {
753 let network_wakeup_time = None;
754 }
755 };
756 let timer_wakeup_time = match (new_task_wakeup_time, network_wakeup_time) {
757 (None, None) => None,
758 (None, Some(network_wt)) => Some(network_wt),
759 (Some(task_wt), None) => Some(task_wt),
760 (Some(task_wt), Some(network_wt)) => Some(u64::min(task_wt, network_wt)),
761 };
762
763 arch::set_oneshot_timer(timer_wakeup_time);
764 }
765}