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