1#![allow(clippy::type_complexity)]
2
3use alloc::boxed::Box;
4use alloc::collections::{BTreeMap, VecDeque};
5use alloc::rc::Rc;
6use alloc::sync::Arc;
7#[cfg(feature = "smp")]
8use alloc::vec::Vec;
9use core::cell::RefCell;
10use core::ptr;
11#[cfg(all(target_arch = "x86_64", feature = "smp"))]
12use core::sync::atomic::AtomicBool;
13use core::sync::atomic::{AtomicI32, AtomicU32, Ordering};
14
15use ahash::RandomState;
16use crossbeam_utils::Backoff;
17use hashbrown::HashMap;
18use hermit_sync::*;
19#[cfg(target_arch = "riscv64")]
20use riscv::register::sstatus;
21
22use crate::arch::core_local::*;
23#[cfg(target_arch = "riscv64")]
24use crate::arch::switch::switch_to_task;
25#[cfg(target_arch = "x86_64")]
26use crate::arch::switch::{switch_to_fpu_owner, switch_to_task};
27use crate::arch::{get_processor_count, interrupts};
28use crate::errno::Errno;
29use crate::fd::{FileDescriptor, ObjectInterface};
30use crate::kernel::scheduler::TaskStacks;
31use crate::scheduler::task::*;
32use crate::{arch, io};
33
34pub mod task;
35
36static NO_TASKS: AtomicU32 = AtomicU32::new(0);
37#[cfg(feature = "smp")]
39static SCHEDULER_INPUTS: SpinMutex<Vec<&InterruptTicketMutex<SchedulerInput>>> =
40 SpinMutex::new(Vec::new());
41#[cfg(all(target_arch = "x86_64", feature = "smp"))]
42static CORE_HLT_STATE: SpinMutex<Vec<&AtomicBool>> = SpinMutex::new(Vec::new());
43static WAITING_TASKS: InterruptTicketMutex<BTreeMap<TaskId, VecDeque<TaskHandle>>> =
45 InterruptTicketMutex::new(BTreeMap::new());
46static TASKS: InterruptTicketMutex<BTreeMap<TaskId, TaskHandle>> =
48 InterruptTicketMutex::new(BTreeMap::new());
49
50pub type CoreId = u32;
52
53#[cfg(feature = "smp")]
54pub(crate) struct SchedulerInput {
55 new_tasks: VecDeque<NewTask>,
57 wakeup_tasks: VecDeque<TaskHandle>,
59}
60
61#[cfg(feature = "smp")]
62impl SchedulerInput {
63 pub fn new() -> Self {
64 Self {
65 new_tasks: VecDeque::new(),
66 wakeup_tasks: VecDeque::new(),
67 }
68 }
69}
70
71#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
72#[cfg_attr(
73 not(any(target_arch = "x86_64", target_arch = "aarch64")),
74 repr(align(64))
75)]
76pub(crate) struct PerCoreScheduler {
77 #[cfg(feature = "smp")]
79 core_id: CoreId,
80 current_task: Rc<RefCell<Task>>,
82 idle_task: Rc<RefCell<Task>>,
84 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
86 fpu_owner: Rc<RefCell<Task>>,
87 ready_queue: PriorityTaskQueue,
89 finished_tasks: VecDeque<Rc<RefCell<Task>>>,
91 blocked_tasks: BlockedTaskQueue,
93}
94
95pub(crate) trait PerCoreSchedulerExt {
96 fn reschedule(self);
99
100 #[cfg(feature = "net")]
101 fn add_network_timer(self, wakeup_time: Option<u64>);
102
103 fn exit(self, exit_code: i32) -> !;
105}
106
107impl PerCoreSchedulerExt for &mut PerCoreScheduler {
108 #[cfg(target_arch = "x86_64")]
109 fn reschedule(self) {
110 without_interrupts(|| {
111 if let Some(last_stack_pointer) = self.scheduler() {
112 let (new_stack_pointer, is_idle) = {
113 let borrowed = self.current_task.borrow();
114 (
115 borrowed.last_stack_pointer,
116 borrowed.status == TaskStatus::Idle,
117 )
118 };
119
120 if is_idle || Rc::ptr_eq(&self.current_task, &self.fpu_owner) {
121 unsafe {
122 switch_to_fpu_owner(
123 last_stack_pointer,
124 new_stack_pointer.as_u64() as usize,
125 );
126 }
127 } else {
128 unsafe {
129 switch_to_task(last_stack_pointer, new_stack_pointer.as_u64() as usize);
130 }
131 }
132 }
133 });
134 }
135
136 #[cfg(target_arch = "aarch64")]
138 fn reschedule(self) {
139 use core::arch::asm;
140
141 use arm_gic::IntId;
142 use arm_gic::gicv3::{GicV3, SgiTarget, SgiTargetGroup};
143
144 use crate::interrupts::SGI_RESCHED;
145
146 unsafe {
147 asm!("dsb nsh", "isb", options(nostack, nomem, preserves_flags));
148 }
149
150 let reschedid = IntId::sgi(SGI_RESCHED.into());
151 #[cfg(feature = "smp")]
152 let core_id = self.core_id;
153 #[cfg(not(feature = "smp"))]
154 let core_id = 0;
155
156 GicV3::send_sgi(
157 reschedid,
158 SgiTarget::List {
159 affinity3: 0,
160 affinity2: 0,
161 affinity1: 0,
162 target_list: 1 << core_id,
163 },
164 SgiTargetGroup::CurrentGroup1,
165 );
166
167 interrupts::enable();
168 }
169
170 #[cfg(target_arch = "riscv64")]
171 fn reschedule(self) {
172 without_interrupts(|| self.scheduler());
173 }
174
175 #[cfg(feature = "net")]
176 fn add_network_timer(self, wakeup_time: Option<u64>) {
177 without_interrupts(|| {
178 self.blocked_tasks.add_network_timer(wakeup_time);
179 });
180 }
181
182 fn exit(self, exit_code: i32) -> ! {
183 without_interrupts(|| {
184 let mut current_task_borrowed = self.current_task.borrow_mut();
186 assert_ne!(
187 current_task_borrowed.status,
188 TaskStatus::Idle,
189 "Trying to terminate the idle task"
190 );
191
192 debug!(
194 "Finishing task {} with exit code {}",
195 current_task_borrowed.id, exit_code
196 );
197 current_task_borrowed.status = TaskStatus::Finished;
198 NO_TASKS.fetch_sub(1, Ordering::SeqCst);
199
200 let current_id = current_task_borrowed.id;
201 drop(current_task_borrowed);
202
203 if let Some(mut queue) = WAITING_TASKS.lock().remove(¤t_id) {
205 while let Some(task) = queue.pop_front() {
206 self.custom_wakeup(task);
207 }
208 }
209 });
210
211 self.reschedule();
212 unreachable!()
213 }
214}
215
216struct NewTask {
217 tid: TaskId,
218 func: unsafe extern "C" fn(usize),
219 arg: usize,
220 prio: Priority,
221 core_id: CoreId,
222 stacks: TaskStacks,
223 object_map: Arc<
224 RwSpinLock<
225 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
226 >,
227 >,
228}
229
230impl From<NewTask> for Task {
231 fn from(value: NewTask) -> Self {
232 let NewTask {
233 tid,
234 func,
235 arg,
236 prio,
237 core_id,
238 stacks,
239 object_map,
240 } = value;
241 let mut task = Self::new(tid, core_id, TaskStatus::Ready, prio, stacks, object_map);
242 task.create_stack_frame(func, arg);
243 task
244 }
245}
246
247impl PerCoreScheduler {
248 pub unsafe fn spawn(
250 func: unsafe extern "C" fn(usize),
251 arg: usize,
252 prio: Priority,
253 core_id: CoreId,
254 stack_size: usize,
255 ) -> TaskId {
256 let tid = get_tid();
258 let stacks = TaskStacks::new(stack_size);
259 let new_task = NewTask {
260 tid,
261 func,
262 arg,
263 prio,
264 core_id,
265 stacks,
266 object_map: core_scheduler().get_current_task_object_map(),
267 };
268
269 let wakeup = {
271 #[cfg(feature = "smp")]
272 let mut input_locked = get_scheduler_input(core_id).lock();
273 WAITING_TASKS.lock().insert(tid, VecDeque::with_capacity(1));
274 TASKS.lock().insert(
275 tid,
276 TaskHandle::new(
277 tid,
278 prio,
279 #[cfg(feature = "smp")]
280 core_id,
281 ),
282 );
283 NO_TASKS.fetch_add(1, Ordering::SeqCst);
284
285 #[cfg(feature = "smp")]
286 if core_id == core_scheduler().core_id {
287 let task = Rc::new(RefCell::new(Task::from(new_task)));
288 core_scheduler().ready_queue.push(task);
289 false
290 } else {
291 input_locked.new_tasks.push_back(new_task);
292 true
293 }
294 #[cfg(not(feature = "smp"))]
295 if core_id == 0 {
296 let task = Rc::new(RefCell::new(Task::from(new_task)));
297 core_scheduler().ready_queue.push(task);
298 false
299 } else {
300 panic!("Invalid core_id {}!", core_id)
301 }
302 };
303
304 debug!("Creating task {tid} with priority {prio} on core {core_id}");
305
306 if wakeup {
307 arch::wakeup_core(core_id);
308 }
309
310 tid
311 }
312
313 #[cfg(feature = "newlib")]
314 fn clone_impl(&self, func: extern "C" fn(usize), arg: usize) -> TaskId {
315 static NEXT_CORE_ID: AtomicU32 = AtomicU32::new(1);
316
317 let core_id: CoreId = {
319 let id = NEXT_CORE_ID.fetch_add(1, Ordering::SeqCst);
321
322 if id == arch::get_processor_count() {
324 NEXT_CORE_ID.store(0, Ordering::SeqCst);
325 0
326 } else {
327 id
328 }
329 };
330
331 let current_task_borrowed = self.current_task.borrow();
333
334 let tid = get_tid();
336 let clone_task = NewTask {
337 tid,
338 func,
339 arg,
340 prio: current_task_borrowed.prio,
341 core_id,
342 stacks: TaskStacks::new(current_task_borrowed.stacks.get_user_stack_size()),
343 object_map: current_task_borrowed.object_map.clone(),
344 };
345
346 let wakeup = {
348 #[cfg(feature = "smp")]
349 let mut input_locked = get_scheduler_input(core_id).lock();
350 WAITING_TASKS.lock().insert(tid, VecDeque::with_capacity(1));
351 TASKS.lock().insert(
352 tid,
353 TaskHandle::new(
354 tid,
355 current_task_borrowed.prio,
356 #[cfg(feature = "smp")]
357 core_id,
358 ),
359 );
360 NO_TASKS.fetch_add(1, Ordering::SeqCst);
361 #[cfg(feature = "smp")]
362 if core_id == core_scheduler().core_id {
363 let clone_task = Rc::new(RefCell::new(Task::from(clone_task)));
364 core_scheduler().ready_queue.push(clone_task);
365 false
366 } else {
367 input_locked.new_tasks.push_back(clone_task);
368 true
369 }
370 #[cfg(not(feature = "smp"))]
371 if core_id == 0 {
372 let clone_task = Rc::new(RefCell::new(Task::from(clone_task)));
373 core_scheduler().ready_queue.push(clone_task);
374 false
375 } else {
376 panic!("Invalid core_id {}!", core_id);
377 }
378 };
379
380 if wakeup {
382 arch::wakeup_core(core_id);
383 }
384
385 tid
386 }
387
388 #[cfg(feature = "newlib")]
389 pub fn clone(&self, func: extern "C" fn(usize), arg: usize) -> TaskId {
390 without_interrupts(|| self.clone_impl(func, arg))
391 }
392
393 #[inline]
395 #[cfg(all(any(target_arch = "x86_64", target_arch = "riscv64"), feature = "smp"))]
396 pub fn is_scheduling(&self) -> bool {
397 self.current_task.borrow().prio < self.ready_queue.get_highest_priority()
398 }
399
400 #[inline]
401 pub fn handle_waiting_tasks(&mut self) {
402 without_interrupts(|| {
403 crate::executor::run();
404 self.blocked_tasks
405 .handle_waiting_tasks(&mut self.ready_queue);
406 });
407 }
408
409 #[cfg(not(feature = "smp"))]
410 pub fn custom_wakeup(&mut self, task: TaskHandle) {
411 without_interrupts(|| {
412 let task = self.blocked_tasks.custom_wakeup(task);
413 self.ready_queue.push(task);
414 });
415 }
416
417 #[cfg(feature = "smp")]
418 pub fn custom_wakeup(&mut self, task: TaskHandle) {
419 if task.get_core_id() == self.core_id {
420 without_interrupts(|| {
421 let task = self.blocked_tasks.custom_wakeup(task);
422 self.ready_queue.push(task);
423 });
424 } else {
425 get_scheduler_input(task.get_core_id())
426 .lock()
427 .wakeup_tasks
428 .push_back(task);
429 arch::wakeup_core(task.get_core_id());
431 }
432 }
433
434 #[inline]
435 pub fn block_current_task(&mut self, wakeup_time: Option<u64>) {
436 without_interrupts(|| {
437 self.blocked_tasks
438 .add(self.current_task.clone(), wakeup_time);
439 });
440 }
441
442 #[inline]
443 pub fn get_current_task_handle(&self) -> TaskHandle {
444 without_interrupts(|| {
445 let current_task_borrowed = self.current_task.borrow();
446
447 TaskHandle::new(
448 current_task_borrowed.id,
449 current_task_borrowed.prio,
450 #[cfg(feature = "smp")]
451 current_task_borrowed.core_id,
452 )
453 })
454 }
455
456 #[inline]
457 pub fn get_current_task_id(&self) -> TaskId {
458 without_interrupts(|| self.current_task.borrow().id)
459 }
460
461 #[inline]
462 pub fn get_current_task_object_map(
463 &self,
464 ) -> Arc<
465 RwSpinLock<
466 HashMap<FileDescriptor, Arc<async_lock::RwLock<dyn ObjectInterface>>, RandomState>,
467 >,
468 > {
469 without_interrupts(|| self.current_task.borrow().object_map.clone())
470 }
471
472 #[inline]
475 pub fn get_object(
476 &self,
477 fd: FileDescriptor,
478 ) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
479 without_interrupts(|| {
480 let current_task = self.current_task.borrow();
481 let object_map = current_task.object_map.read();
482 object_map.get(&fd).cloned().ok_or(Errno::Badf)
483 })
484 }
485
486 #[cfg(feature = "common-os")]
489 #[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))]
490 pub fn recreate_objmap(&self) -> io::Result<()> {
491 let mut map = HashMap::<
492 FileDescriptor,
493 Arc<async_lock::RwLock<dyn ObjectInterface>>,
494 RandomState,
495 >::with_hasher(RandomState::with_seeds(0, 0, 0, 0));
496
497 without_interrupts(|| {
498 let mut current_task = self.current_task.borrow_mut();
499 let object_map = current_task.object_map.read();
500
501 for i in 0..3 {
503 if let Some(obj) = object_map.get(&i) {
504 map.insert(i, obj.clone());
505 }
506 }
507
508 drop(object_map);
509 current_task.object_map = Arc::new(RwSpinLock::new(map));
510 });
511
512 Ok(())
513 }
514
515 pub fn insert_object(
518 &self,
519 obj: Arc<async_lock::RwLock<dyn ObjectInterface>>,
520 ) -> io::Result<FileDescriptor> {
521 without_interrupts(|| {
522 let current_task = self.current_task.borrow();
523 let mut object_map = current_task.object_map.write();
524
525 let new_fd = || -> io::Result<FileDescriptor> {
526 let mut fd: FileDescriptor = 0;
527 loop {
528 if !object_map.contains_key(&fd) {
529 break Ok(fd);
530 } else if fd == FileDescriptor::MAX {
531 break Err(Errno::Overflow);
532 }
533
534 fd = fd.saturating_add(1);
535 }
536 };
537
538 let fd = new_fd()?;
539 let _ = object_map.insert(fd, obj.clone());
540 Ok(fd)
541 })
542 }
543
544 pub fn dup_object(&self, fd: FileDescriptor) -> io::Result<FileDescriptor> {
547 without_interrupts(|| {
548 let current_task = self.current_task.borrow();
549 let mut object_map = current_task.object_map.write();
550
551 let obj = (*(object_map.get(&fd).ok_or(Errno::Inval)?)).clone();
552
553 let new_fd = || -> io::Result<FileDescriptor> {
554 let mut fd: FileDescriptor = 0;
555 loop {
556 if !object_map.contains_key(&fd) {
557 break Ok(fd);
558 } else if fd == FileDescriptor::MAX {
559 break Err(Errno::Overflow);
560 }
561
562 fd = fd.saturating_add(1);
563 }
564 };
565
566 let fd = new_fd()?;
567 if object_map.try_insert(fd, obj).is_err() {
568 Err(Errno::Mfile)
569 } else {
570 Ok(fd)
571 }
572 })
573 }
574
575 pub fn dup_object2(
576 &self,
577 fd1: FileDescriptor,
578 fd2: FileDescriptor,
579 ) -> io::Result<FileDescriptor> {
580 without_interrupts(|| {
581 let current_task = self.current_task.borrow();
582 let mut object_map = current_task.object_map.write();
583
584 let obj = object_map.get(&fd1).cloned().ok_or(Errno::Badf)?;
585
586 if object_map.try_insert(fd2, obj).is_err() {
587 Err(Errno::Mfile)
588 } else {
589 Ok(fd2)
590 }
591 })
592 }
593
594 pub fn remove_object(
596 &self,
597 fd: FileDescriptor,
598 ) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
599 without_interrupts(|| {
600 let current_task = self.current_task.borrow();
601 let mut object_map = current_task.object_map.write();
602
603 object_map.remove(&fd).ok_or(Errno::Badf)
604 })
605 }
606
607 #[inline]
608 pub fn get_current_task_prio(&self) -> Priority {
609 without_interrupts(|| self.current_task.borrow().prio)
610 }
611
612 #[allow(dead_code)]
614 #[inline]
615 pub fn get_priority_bitmap(&self) -> &u64 {
616 self.ready_queue.get_priority_bitmap()
617 }
618
619 #[cfg(target_arch = "x86_64")]
620 pub fn set_current_kernel_stack(&self) {
621 let current_task_borrowed = self.current_task.borrow();
622 let tss = unsafe { &mut *CoreLocal::get().tss.get() };
623
624 let rsp = current_task_borrowed.stacks.get_kernel_stack()
625 + current_task_borrowed.stacks.get_kernel_stack_size() as u64
626 - TaskStacks::MARKER_SIZE as u64;
627 tss.privilege_stack_table[0] = rsp.into();
628 CoreLocal::get().kernel_stack.set(rsp.as_mut_ptr());
629 let ist_start = current_task_borrowed.stacks.get_interrupt_stack()
630 + current_task_borrowed.stacks.get_interrupt_stack_size() as u64
631 - TaskStacks::MARKER_SIZE as u64;
632 tss.interrupt_stack_table[0] = ist_start.into();
633 }
634
635 pub fn set_current_task_priority(&mut self, prio: Priority) {
636 without_interrupts(|| {
637 trace!("Change priority of the current task");
638 self.current_task.borrow_mut().prio = prio;
639 });
640 }
641
642 pub fn set_priority(&mut self, id: TaskId, prio: Priority) -> Result<(), ()> {
643 trace!("Change priority of task {id} to priority {prio}");
644
645 without_interrupts(|| {
646 let task = get_task_handle(id).ok_or(())?;
647 #[cfg(feature = "smp")]
648 let other_core = task.get_core_id() != self.core_id;
649 #[cfg(not(feature = "smp"))]
650 let other_core = false;
651
652 if other_core {
653 warn!("Have to change the priority on another core");
654 } else if self.current_task.borrow().id == task.get_id() {
655 self.current_task.borrow_mut().prio = prio;
656 } else {
657 self.ready_queue
658 .set_priority(task, prio)
659 .expect("Do not find valid task in ready queue");
660 }
661
662 Ok(())
663 })
664 }
665
666 #[cfg(target_arch = "riscv64")]
667 pub fn set_current_kernel_stack(&self) {
668 let current_task_borrowed = self.current_task.borrow();
669
670 let stack = (current_task_borrowed.stacks.get_kernel_stack()
671 + current_task_borrowed.stacks.get_kernel_stack_size() as u64
672 - TaskStacks::MARKER_SIZE as u64)
673 .as_u64();
674 CoreLocal::get().kernel_stack.set(stack);
675 }
676
677 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
680 pub fn fpu_switch(&mut self) {
681 if !Rc::ptr_eq(&self.current_task, &self.fpu_owner) {
682 debug!(
683 "Switching FPU owner from task {} to {}",
684 self.fpu_owner.borrow().id,
685 self.current_task.borrow().id
686 );
687
688 self.fpu_owner.borrow_mut().last_fpu_state.save();
689 self.current_task.borrow().last_fpu_state.restore();
690 self.fpu_owner = self.current_task.clone();
691 }
692 }
693
694 fn cleanup_tasks(&mut self) {
696 while let Some(finished_task) = self.finished_tasks.pop_front() {
698 debug!("Cleaning up task {}", finished_task.borrow().id);
699 }
700 }
701
702 #[cfg(feature = "smp")]
703 pub fn check_input(&mut self) {
704 let mut input_locked = CoreLocal::get().scheduler_input.lock();
705
706 while let Some(task) = input_locked.wakeup_tasks.pop_front() {
707 let task = self.blocked_tasks.custom_wakeup(task);
708 self.ready_queue.push(task);
709 }
710
711 while let Some(new_task) = input_locked.new_tasks.pop_front() {
712 let task = Rc::new(RefCell::new(Task::from(new_task)));
713 self.ready_queue.push(task.clone());
714 }
715 }
716
717 pub fn run() -> ! {
721 let backoff = Backoff::new();
722
723 loop {
724 let core_scheduler = core_scheduler();
725 interrupts::disable();
726
727 crate::executor::run();
729
730 #[cfg(feature = "smp")]
732 core_scheduler.check_input();
733 core_scheduler.cleanup_tasks();
734
735 if core_scheduler.ready_queue.is_empty() {
736 if backoff.is_completed() {
737 interrupts::enable_and_wait();
738 backoff.reset();
739 } else {
740 interrupts::enable();
741 backoff.snooze();
742 }
743 } else {
744 interrupts::enable();
745 core_scheduler.reschedule();
746 backoff.reset();
747 }
748 }
749 }
750
751 #[inline]
752 #[cfg(target_arch = "aarch64")]
753 pub fn get_last_stack_pointer(&self) -> memory_addresses::VirtAddr {
754 self.current_task.borrow().last_stack_pointer
755 }
756
757 pub fn scheduler(&mut self) -> Option<*mut usize> {
760 crate::executor::run();
762
763 self.cleanup_tasks();
766
767 let (id, last_stack_pointer, prio, status) = {
769 let mut borrowed = self.current_task.borrow_mut();
770 (
771 borrowed.id,
772 ptr::from_mut(&mut borrowed.last_stack_pointer).cast::<usize>(),
773 borrowed.prio,
774 borrowed.status,
775 )
776 };
777
778 let mut new_task = None;
779
780 if status == TaskStatus::Running {
781 if let Some(task) = self.ready_queue.pop_with_prio(prio) {
784 new_task = Some(task);
785 }
786 } else {
787 if status == TaskStatus::Finished {
788 self.current_task.borrow_mut().status = TaskStatus::Invalid;
790 self.finished_tasks.push_back(self.current_task.clone());
791 }
792
793 if let Some(task) = self.ready_queue.pop() {
796 debug!("Task is available.");
798 new_task = Some(task);
799 } else if status != TaskStatus::Idle {
800 debug!("Only Idle Task is available.");
802 new_task = Some(self.idle_task.clone());
803 }
804 }
805
806 if let Some(task) = new_task {
807 if status == TaskStatus::Running {
811 self.current_task.borrow_mut().status = TaskStatus::Ready;
813 self.ready_queue.push(self.current_task.clone());
814 }
815
816 let (new_id, new_stack_pointer) = {
818 let mut borrowed = task.borrow_mut();
819 if borrowed.status != TaskStatus::Idle {
820 borrowed.status = TaskStatus::Running;
822 }
823
824 (borrowed.id, borrowed.last_stack_pointer)
825 };
826
827 if id != new_id {
828 debug!(
830 "Switching task from {} to {} (stack {:#X} => {:p})",
831 id,
832 new_id,
833 unsafe { *last_stack_pointer },
834 new_stack_pointer
835 );
836 #[cfg(not(target_arch = "riscv64"))]
837 {
838 self.current_task = task;
839 }
840
841 #[cfg(not(target_arch = "riscv64"))]
843 return Some(last_stack_pointer);
844
845 #[cfg(target_arch = "riscv64")]
846 {
847 if sstatus::read().fs() == sstatus::FS::Dirty {
848 self.current_task.borrow_mut().last_fpu_state.save();
849 }
850 task.borrow().last_fpu_state.restore();
851 self.current_task = task;
852 unsafe {
853 switch_to_task(last_stack_pointer, new_stack_pointer.as_usize());
854 }
855 }
856 }
857 }
858
859 None
860 }
861}
862
863fn get_tid() -> TaskId {
864 static TID_COUNTER: AtomicI32 = AtomicI32::new(0);
865 let guard = TASKS.lock();
866
867 loop {
868 let id = TaskId::from(TID_COUNTER.fetch_add(1, Ordering::SeqCst));
869 if !guard.contains_key(&id) {
870 return id;
871 }
872 }
873}
874
875#[inline]
876pub(crate) fn abort() -> ! {
877 core_scheduler().exit(-1)
878}
879
880pub(crate) fn add_current_core() {
882 let core_id = core_id();
884 let tid = get_tid();
885 let idle_task = Rc::new(RefCell::new(Task::new_idle(tid, core_id)));
886
887 WAITING_TASKS.lock().insert(tid, VecDeque::with_capacity(1));
889 TASKS.lock().insert(
890 tid,
891 TaskHandle::new(
892 tid,
893 IDLE_PRIO,
894 #[cfg(feature = "smp")]
895 core_id,
896 ),
897 );
898 debug!("Initializing scheduler for core {core_id} with idle task {tid}");
900 let boxed_scheduler = Box::new(PerCoreScheduler {
901 #[cfg(feature = "smp")]
902 core_id,
903 current_task: idle_task.clone(),
904 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
905 fpu_owner: idle_task.clone(),
906 idle_task,
907 ready_queue: PriorityTaskQueue::new(),
908 finished_tasks: VecDeque::new(),
909 blocked_tasks: BlockedTaskQueue::new(),
910 });
911
912 let scheduler = Box::into_raw(boxed_scheduler);
913 set_core_scheduler(scheduler);
914 #[cfg(feature = "smp")]
915 {
916 SCHEDULER_INPUTS.lock().insert(
917 core_id.try_into().unwrap(),
918 &CoreLocal::get().scheduler_input,
919 );
920 #[cfg(target_arch = "x86_64")]
921 CORE_HLT_STATE
922 .lock()
923 .insert(core_id.try_into().unwrap(), &CoreLocal::get().hlt);
924 }
925}
926
927#[inline]
928#[cfg(all(target_arch = "x86_64", feature = "smp", not(feature = "idle-poll")))]
929pub(crate) fn take_core_hlt_state(core_id: CoreId) -> bool {
930 CORE_HLT_STATE.lock()[usize::try_from(core_id).unwrap()].swap(false, Ordering::Acquire)
931}
932
933#[inline]
934#[cfg(feature = "smp")]
935fn get_scheduler_input(core_id: CoreId) -> &'static InterruptTicketMutex<SchedulerInput> {
936 SCHEDULER_INPUTS.lock()[usize::try_from(core_id).unwrap()]
937}
938
939pub unsafe fn spawn(
940 func: unsafe extern "C" fn(usize),
941 arg: usize,
942 prio: Priority,
943 stack_size: usize,
944 selector: isize,
945) -> TaskId {
946 static CORE_COUNTER: AtomicU32 = AtomicU32::new(1);
947
948 let core_id = if selector < 0 {
949 CORE_COUNTER.fetch_add(1, Ordering::SeqCst) % get_processor_count()
951 } else {
952 selector as u32
953 };
954
955 unsafe { PerCoreScheduler::spawn(func, arg, prio, core_id, stack_size) }
956}
957
958#[allow(clippy::result_unit_err)]
959pub fn join(id: TaskId) -> Result<(), ()> {
960 let core_scheduler = core_scheduler();
961
962 debug!(
963 "Task {} is waiting for task {}",
964 core_scheduler.get_current_task_id(),
965 id
966 );
967
968 loop {
969 let mut waiting_tasks_guard = WAITING_TASKS.lock();
970
971 if let Some(queue) = waiting_tasks_guard.get_mut(&id) {
972 queue.push_back(core_scheduler.get_current_task_handle());
973 core_scheduler.block_current_task(None);
974
975 drop(waiting_tasks_guard);
977 core_scheduler.reschedule();
978 } else {
979 return Ok(());
980 }
981 }
982}
983
984pub fn shutdown(arg: i32) -> ! {
985 crate::syscalls::shutdown(arg)
986}
987
988fn get_task_handle(id: TaskId) -> Option<TaskHandle> {
989 TASKS.lock().get(&id).copied()
990}
991
992#[cfg(all(target_arch = "x86_64", feature = "common-os"))]
993pub(crate) static BOOT_ROOT_PAGE_TABLE: OnceCell<usize> = OnceCell::new();
994
995#[cfg(all(target_arch = "x86_64", feature = "common-os"))]
996pub(crate) fn get_root_page_table() -> usize {
997 let current_task_borrowed = core_scheduler().current_task.borrow_mut();
998 current_task_borrowed.root_page_table
999}