strat9_kernel/process/scheduler/
task_ops.rs1use super::{runtime_ops::finish_switch, *};
2use crate::{memory::UserSliceWrite, sync::FixedQueue};
3
4const PENDING_SILO_CLEANUPS_CAPACITY: usize = 256;
5
6static PENDING_SILO_CLEANUPS: SpinLock<FixedQueue<TaskId, PENDING_SILO_CLEANUPS_CAPACITY>> =
7 SpinLock::new(FixedQueue::new());
8
9pub fn exit_current_task(exit_code: i32) -> ! {
15 if let Some(task) = current_task_clone() {
19 let tidptr = task
20 .clear_child_tid
21 .load(core::sync::atomic::Ordering::Relaxed);
22 if tidptr != 0 {
23 let zero = 0u32.to_ne_bytes();
24 if (tidptr & 3) == 0 {
28 if let Ok(user) = UserSliceWrite::new(tidptr, zero.len()) {
29 user.copy_from(&zero);
30 }
31 let _ = crate::syscall::futex::sys_futex_wake(tidptr, u32::MAX);
33 }
34 }
35
36 crate::syscall::robust_list::cleanup_robust_list(&task);
40 }
41
42 let cpu_index = current_cpu_index();
43 let mut parent_to_signal: Option<TaskId> = None;
44 let mut ipi_to_cpu: Option<usize> = None;
45 {
46 let saved_flags = save_flags_and_cli();
47 let mut scheduler = GLOBAL_SCHED_STATE.lock();
48 let current = {
49 let local = LOCAL_SCHEDULERS[cpu_index].lock();
50 local.as_ref().and_then(|cpu| cpu.current_task.clone())
51 };
52 if let Some(ref mut sched) = *scheduler {
53 if let Some(current) = current {
54 let current_id = current.id;
55 let current_pid = current.pid;
56 let parent = {
57 let identity = SCHED_IDENTITY.read();
58 identity.parent_of.get(¤t_id).copied()
59 };
60 let _ = sched.clear_task_wake_deadline_locked(current_id);
61 current.set_state(TaskState::Dead);
62 sched.task_cpu.remove(¤t_id);
69 {
70 let mut identity = SCHED_IDENTITY.write();
71 GlobalSchedState::unregister_identity_locked(
72 &mut identity,
73 current_id,
74 current_pid,
75 current.tid,
76 );
77 identity.parent_of.remove(¤t_id);
78 }
79
80 ipi_to_cpu = {
81 let mut identity = SCHED_IDENTITY.write();
82 reparent_children(sched, &mut identity, current_id)
83 };
84
85 if parent.is_some() {
86 sched.zombies.insert(current_id, (exit_code, current_pid));
87 }
88 if let Some(parent_id) = parent {
89 let (_, ipi_wake) = sched.wake_task_locked(parent_id);
90 if ipi_to_cpu.is_none() {
91 ipi_to_cpu = ipi_wake;
92 }
93 parent_to_signal = Some(parent_id);
94 }
95 }
96 }
97 drop(scheduler);
98 restore_flags(saved_flags);
99 }
100 if let Some(ci) = ipi_to_cpu {
101 send_resched_ipi_to_cpu(ci);
102 }
103
104 if let Some(parent_id) = parent_to_signal {
105 let _ =
107 crate::process::signal::send_signal(parent_id, crate::process::signal::Signal::SIGCHLD);
108 }
109
110 yield_dead_task();
116
117 loop {
119 crate::arch::x86_64::hlt();
120 }
121}
122
123pub fn current_task_id() -> Option<TaskId> {
125 let saved_flags = save_flags_and_cli();
126 let cpu_index = current_cpu_index();
127 let id = LOCAL_SCHEDULERS[cpu_index]
128 .lock()
129 .as_ref()
130 .and_then(|cpu| cpu.current_task.as_ref().map(|t| t.id));
131 restore_flags(saved_flags);
132 id
133}
134
135pub fn current_task_id_try() -> Option<TaskId> {
137 let saved_flags = save_flags_and_cli();
138 let cpu_index = current_cpu_index();
139 let id = LOCAL_SCHEDULERS[cpu_index]
140 .try_lock_no_irqsave()
141 .and_then(|guard| {
142 guard
143 .as_ref()
144 .and_then(|cpu| cpu.current_task.as_ref().map(|t| t.id))
145 });
146 restore_flags(saved_flags);
147 id
148}
149
150pub fn current_pid() -> Option<Pid> {
152 current_task_clone().map(|t| t.pid)
153}
154
155pub fn current_tid() -> Option<Tid> {
157 current_task_clone().map(|t| t.tid)
158}
159
160pub fn current_pgid() -> Option<Pid> {
162 current_task_clone().map(|t| t.pgid.load(Ordering::Relaxed))
163}
164
165pub fn current_sid() -> Option<Pid> {
167 current_task_clone().map(|t| t.sid.load(Ordering::Relaxed))
168}
169
170#[track_caller]
172pub fn current_task_clone() -> Option<Arc<Task>> {
173 let saved_flags = save_flags_and_cli();
174 let cpu_index = current_cpu_index();
175 let caller = core::panic::Location::caller();
176 let task = LOCAL_SCHEDULERS[cpu_index].lock().as_ref().and_then(|cpu| {
177 let arc = cpu.current_task.as_ref()?;
178 let strong = Arc::strong_count(arc);
179 if strong == 0 || strong > (isize::MAX as usize) / 2 {
183 let ptr = Arc::as_ptr(arc) as *const u8;
184 crate::serial_println!(
185 "[sched] suspicious Arc refcount (heuristic): cpu={} strong={:#x} ptr={:p} caller={}:{}",
186 cpu_index,
187 strong,
188 ptr,
189 caller.file(),
190 caller.line(),
191 );
192 }
193 Some(arc.clone())
194 });
195 restore_flags(saved_flags);
196 task
197}
198
199#[track_caller]
204pub fn current_task_clone_try() -> Option<Arc<Task>> {
205 let saved_flags = save_flags_and_cli();
206 let cpu_index = current_cpu_index();
207 let caller = core::panic::Location::caller();
208 let task = LOCAL_SCHEDULERS[cpu_index]
209 .try_lock_no_irqsave()
210 .and_then(|guard| {
211 guard.as_ref().and_then(|cpu| {
212 let arc = cpu.current_task.as_ref()?;
213 let strong = Arc::strong_count(arc);
214 if strong == 0 || strong > (isize::MAX as usize) / 2 {
218 let ptr = Arc::as_ptr(arc) as *const u8;
219 crate::serial_println!(
220 "[sched] suspicious Arc refcount (heuristic): cpu={} strong={:#x} ptr={:p} caller={}:{}",
221 cpu_index,
222 strong,
223 ptr,
224 caller.file(),
225 caller.line(),
226 );
227 }
228 Some(arc.clone())
229 })
230 });
231 restore_flags(saved_flags);
232 task
233}
234
235pub fn current_task_clone_spin_debug(trace_label: &str) -> Option<Arc<Task>> {
240 let saved_flags = save_flags_and_cli();
241 let cpu_index = current_cpu_index();
242 let mut spins = 0usize;
243 let result = loop {
244 if let Some(guard) = LOCAL_SCHEDULERS[cpu_index].try_lock_no_irqsave() {
245 break guard.as_ref().and_then(|cpu| {
246 if cpu.current_task.is_none() {
247 unsafe { core::arch::asm!("mov al, 'N'; out 0xe9, al", out("al") _) };
248 return None;
249 }
250 let arc = cpu.current_task.as_ref().unwrap();
251 let strong = Arc::strong_count(arc);
252 if strong == 0 || strong > (isize::MAX as usize) / 2 {
256 let ptr = Arc::as_ptr(arc) as *const u8;
257 crate::serial_force_println!(
258 "[trace][sched] {} suspicious current_task heuristic cpu={} strong={:#x} ptr={:p}",
259 trace_label,
260 cpu_index,
261 strong,
262 ptr,
263 );
264 }
265 Some(arc.clone())
266 });
267 }
268
269 spins = spins.saturating_add(1);
270 if spins == 2_000_000 {
271 crate::serial_force_println!(
272 "[trace][sched] {} waiting current_task cpu={} owner_cpu={}",
273 trace_label,
274 cpu_index,
275 GLOBAL_SCHED_STATE.owner_cpu()
276 );
277 spins = 0;
278 }
279 core::hint::spin_loop();
280 };
281 restore_flags(saved_flags);
282 result
283}
284
285pub fn get_task_id_by_pid(pid: Pid) -> Option<TaskId> {
287 SCHED_IDENTITY.read().pid_to_task.get(&pid).copied()
288}
289
290pub fn get_task_by_pid(pid: Pid) -> Option<Arc<Task>> {
292 let tid = get_task_id_by_pid(pid)?;
293 get_task_by_id(tid)
294}
295
296pub fn get_child_task_id_by_pid(parent: TaskId, pid: Pid) -> Option<TaskId> {
304 let saved_flags = save_flags_and_cli();
305 let out = {
306 let scheduler = GLOBAL_SCHED_STATE.lock();
307 if let Some(ref sched) = *scheduler {
308 let children = {
309 let identity = SCHED_IDENTITY.read();
310 identity
311 .children_of
312 .get(&parent)
313 .cloned()
314 .unwrap_or_default()
315 };
316 if children.is_empty() {
317 None
318 } else {
319 children.iter().copied().find(|child_id| {
320 sched
321 .all_tasks
322 .get(child_id)
323 .map(|task| task.pid == pid)
324 .unwrap_or(false)
325 })
326 }
327 } else {
328 None
329 }
330 };
331 restore_flags(saved_flags);
332 out
333}
334
335pub fn get_task_id_by_tid(tid: Tid) -> Option<TaskId> {
337 let identity = SCHED_IDENTITY.read();
338 identity
339 .tid_to_task
340 .get(&tid)
341 .copied()
342 .or_else(|| identity.pid_to_task.get(&(tid as Pid)).copied())
343}
344
345pub fn get_child_task_id_by_tid(parent: TaskId, tid: Tid) -> Option<TaskId> {
353 let saved_flags = save_flags_and_cli();
354 let out = {
355 let scheduler = GLOBAL_SCHED_STATE.lock();
356 if let Some(ref sched) = *scheduler {
357 let children = {
358 let identity = SCHED_IDENTITY.read();
359 identity
360 .children_of
361 .get(&parent)
362 .cloned()
363 .unwrap_or_default()
364 };
365 if children.is_empty() {
366 None
367 } else {
368 children.iter().copied().find(|child_id| {
369 sched
370 .all_tasks
371 .get(child_id)
372 .map(|task| task.tid == tid)
373 .unwrap_or(false)
374 })
375 }
376 } else {
377 None
378 }
379 };
380 restore_flags(saved_flags);
381 out
382}
383
384pub fn get_pgid_by_pid(pid: Pid) -> Option<Pid> {
386 SCHED_IDENTITY.read().pid_to_pgid.get(&pid).copied()
387}
388
389pub fn get_sid_by_pid(pid: Pid) -> Option<Pid> {
391 SCHED_IDENTITY.read().pid_to_sid.get(&pid).copied()
392}
393
394pub fn get_task_ids_in_pgid(pgid: Pid) -> alloc::vec::Vec<TaskId> {
396 use alloc::vec::Vec;
397 SCHED_IDENTITY
398 .read()
399 .pgid_members
400 .get(&pgid)
401 .cloned()
402 .unwrap_or_else(Vec::new)
403}
404
405pub fn get_task_ids_in_tgid(tgid: Pid) -> alloc::vec::Vec<TaskId> {
407 use alloc::vec::Vec;
408 let saved_flags = save_flags_and_cli();
409 let out = {
410 let scheduler = GLOBAL_SCHED_STATE.lock();
411 if let Some(ref sched) = *scheduler {
412 sched
413 .all_tasks
414 .values()
415 .filter(|task| task.tgid == tgid)
416 .map(|task| task.id)
417 .collect::<Vec<_>>()
418 } else {
419 Vec::new()
420 }
421 };
422 restore_flags(saved_flags);
423 out
424}
425
426pub fn set_process_group(
428 requester: TaskId,
429 target_pid: Option<Pid>,
430 new_pgid: Option<Pid>,
431) -> Result<Pid, crate::syscall::error::SyscallError> {
432 use crate::syscall::error::SyscallError;
433
434 let saved_flags = save_flags_and_cli();
435 let result = (|| -> Result<Pid, SyscallError> {
436 let scheduler = GLOBAL_SCHED_STATE.lock();
437 let sched = scheduler.as_ref().ok_or(SyscallError::Fault)?;
438
439 let requester_task = sched
440 .all_tasks
441 .get(&requester)
442 .cloned()
443 .ok_or(SyscallError::Fault)?;
444 let requester_sid = requester_task.sid.load(Ordering::Relaxed);
445
446 let target_id = match target_pid {
447 None => requester,
448 Some(pid) => SCHED_IDENTITY
449 .read()
450 .pid_to_task
451 .get(&pid)
452 .copied()
453 .ok_or(SyscallError::NotFound)?,
454 };
455
456 if target_id != requester {
457 let is_child = SCHED_IDENTITY
458 .read()
459 .children_of
460 .get(&requester)
461 .map(|children| children.iter().any(|child| *child == target_id))
462 .unwrap_or(false);
463 if !is_child {
464 return Err(SyscallError::PermissionDenied);
465 }
466 }
467
468 let target_task = sched
469 .all_tasks
470 .get(&target_id)
471 .cloned()
472 .ok_or(SyscallError::NotFound)?;
473 let target_pid_value = target_task.pid;
474 let target_sid = target_task.sid.load(Ordering::Relaxed);
475
476 if target_sid != requester_sid {
477 return Err(SyscallError::PermissionDenied);
478 }
479
480 if target_pid_value == target_sid {
481 return Err(SyscallError::PermissionDenied);
482 }
483
484 let desired_pgid = new_pgid.unwrap_or(target_pid_value);
485 if desired_pgid != target_pid_value {
486 let group_leader_tid = SCHED_IDENTITY
487 .read()
488 .pid_to_task
489 .get(&desired_pgid)
490 .copied()
491 .ok_or(SyscallError::NotFound)?;
492 let group_leader = sched
493 .all_tasks
494 .get(&group_leader_tid)
495 .ok_or(SyscallError::NotFound)?;
496 if group_leader.sid.load(Ordering::Relaxed) != target_sid {
497 return Err(SyscallError::PermissionDenied);
498 }
499 }
500
501 let old_pgid = target_task.pgid.load(Ordering::Relaxed);
503 let actual_new_pgid = new_pgid.unwrap_or(target_task.pid);
504 target_task.pgid.store(actual_new_pgid, Ordering::Relaxed);
505 {
506 let mut identity = SCHED_IDENTITY.write();
507 GlobalSchedState::member_remove(&mut identity.pgid_members, old_pgid, target_id);
508 GlobalSchedState::member_add(&mut identity.pgid_members, actual_new_pgid, target_id);
509 identity
510 .pid_to_pgid
511 .insert(target_task.pid, actual_new_pgid);
512 }
513 Ok(actual_new_pgid)
514 })();
515 restore_flags(saved_flags);
516 result
517}
518
519pub fn create_session(requester: TaskId) -> Result<Pid, crate::syscall::error::SyscallError> {
521 use crate::syscall::error::SyscallError;
522
523 let saved_flags = save_flags_and_cli();
524 let result = (|| -> Result<Pid, SyscallError> {
525 let scheduler = GLOBAL_SCHED_STATE.lock();
526 let sched = scheduler.as_ref().ok_or(SyscallError::Fault)?;
527 let requester_task = sched
528 .all_tasks
529 .get(&requester)
530 .cloned()
531 .ok_or(SyscallError::Fault)?;
532
533 let pid = requester_task.pid;
534 if requester_task.pgid.load(Ordering::Relaxed) == pid {
535 return Err(SyscallError::PermissionDenied);
536 }
537
538 let old_sid = requester_task.sid.load(Ordering::Relaxed);
539 let old_pgid = requester_task.pgid.load(Ordering::Relaxed);
540 requester_task.sid.store(pid, Ordering::Relaxed);
541 requester_task.pgid.store(pid, Ordering::Relaxed);
542 {
543 let mut identity = SCHED_IDENTITY.write();
544 GlobalSchedState::member_remove(&mut identity.sid_members, old_sid, requester);
545 GlobalSchedState::member_remove(&mut identity.pgid_members, old_pgid, requester);
546 GlobalSchedState::member_add(&mut identity.sid_members, pid, requester);
547 GlobalSchedState::member_add(&mut identity.pgid_members, pid, requester);
548 identity.pid_to_sid.insert(pid, pid);
549 identity.pid_to_pgid.insert(pid, pid);
550 }
551 Ok(pid)
552 })();
553 restore_flags(saved_flags);
554 result
555}
556
557pub fn get_task_by_id(id: TaskId) -> Option<Arc<Task>> {
559 let saved_flags = save_flags_and_cli();
560 let task = {
561 let scheduler = GLOBAL_SCHED_STATE.lock();
562 if let Some(ref sched) = *scheduler {
563 sched.all_tasks.get(&id).cloned()
564 } else {
565 None
566 }
567 };
568 restore_flags(saved_flags);
569 task
570}
571
572pub fn set_task_sched_policy(id: TaskId, policy: crate::process::sched::SchedPolicy) -> bool {
574 let saved_flags = save_flags_and_cli();
575 let mut ipi_to_cpu: Option<usize> = None;
576 let updated = {
577 let mut scheduler = GLOBAL_SCHED_STATE.lock();
578 if let Some(ref mut sched) = *scheduler {
579 let cpu_index = sched.task_cpu.get(&id).copied().unwrap_or(0);
580 let task = match sched.all_tasks.get(&id).cloned() {
581 Some(t) => t,
582 None => return false,
583 };
584 task.set_sched_policy(policy);
585 let class = sched.class_table.class_for_task(&task);
586
587 if let Some(ref mut local_cpu) = *LOCAL_SCHEDULERS[cpu_index].lock() {
588 if local_cpu.class_rqs.remove(id) {
590 local_cpu.class_rqs.enqueue(class, task.clone());
591 }
592 local_cpu.need_resched = true;
593 }
594 if cpu_index != current_cpu_index() {
595 ipi_to_cpu = Some(cpu_index);
596 }
597 sched_trace(format_args!(
598 "set_policy task={} cpu={} policy={:?}",
599 id.as_u64(),
600 cpu_index,
601 policy
602 ));
603 true
604 } else {
605 false
606 }
607 };
608 if let Some(ci) = ipi_to_cpu {
609 send_resched_ipi_to_cpu(ci);
610 }
611 restore_flags(saved_flags);
612 updated
613}
614
615pub fn get_parent_id(child: TaskId) -> Option<TaskId> {
617 SCHED_IDENTITY.read().parent_of.get(&child).copied()
618}
619
620pub fn get_parent_pid(child: TaskId) -> Option<Pid> {
622 let parent_tid = get_parent_id(child)?;
623 let parent = get_task_by_id(parent_tid)?;
624 Some(parent.pid)
625}
626
627pub fn try_wait_child(parent: TaskId, target: Option<TaskId>) -> WaitChildResult {
631 let saved_flags = save_flags_and_cli();
632 let result = {
633 let mut scheduler = GLOBAL_SCHED_STATE.lock();
634 if let Some(ref mut sched) = *scheduler {
635 sched.try_reap_child_locked(parent, target)
636 } else {
637 WaitChildResult::NoChildren
638 }
639 };
640 restore_flags(saved_flags);
641 result
642}
643
644pub fn block_current_task() {
667 let saved_flags = save_flags_and_cli();
668 let cpu_index = current_cpu_index();
669
670 let switch_target = {
671 let mut blocked = super::BLOCKED_TASKS.lock();
675 let mut local = LOCAL_SCHEDULERS[cpu_index].lock();
676 let out = if let Some(ref mut cpu) = *local {
677 if let Some(ref current) = cpu.current_task {
678 if current
679 .wake_pending
680 .swap(false, core::sync::atomic::Ordering::AcqRel)
681 {
682 None
684 } else {
685 current.set_state(TaskState::Blocked);
686 current
688 .home_cpu
689 .store(cpu_index, core::sync::atomic::Ordering::Relaxed);
690 blocked.insert(current.id, current.clone());
691 super::core_impl::yield_cpu_local(cpu, cpu_index)
692 }
693 } else {
694 None
695 }
696 } else {
697 None
698 };
699 drop(local);
700 drop(blocked);
701 out
702 }; if let Some(ref target) = switch_target {
705 unsafe {
706 crate::process::task::do_switch_context(target);
707 }
708 finish_switch();
709 }
710
711 restore_flags(saved_flags);
712}
713
714pub fn wake_task(id: TaskId) -> bool {
733 let saved_flags = save_flags_and_cli();
734
735 let mut ipi_cpu: Option<usize> = None;
738 let mut woken = false;
739
740 {
741 let mut blocked = super::BLOCKED_TASKS.lock();
742 if let Some(task) = blocked.remove(&id) {
743 task.set_state(TaskState::Ready);
744 let home = task.home_cpu.load(core::sync::atomic::Ordering::Relaxed);
745 let cpu_index = if home != usize::MAX { home } else { 0 };
746
747 let class = {
749 use crate::process::sched::SchedClassId;
750 match task.sched_policy() {
751 crate::process::sched::SchedPolicy::RealTimeRR { .. }
752 | crate::process::sched::SchedPolicy::RealTimeFifo { .. } => {
753 SchedClassId::RealTime
754 }
755 crate::process::sched::SchedPolicy::Fair(_) => SchedClassId::Fair,
756 crate::process::sched::SchedPolicy::Idle => SchedClassId::Idle,
757 }
758 };
759
760 if let Some(ref mut local_cpu) = *LOCAL_SCHEDULERS[cpu_index].lock() {
761 local_cpu.class_rqs.enqueue(class, task.clone());
762 local_cpu.need_resched = true;
763 }
764
765 ipi_cpu = if cpu_index != current_cpu_index() {
766 Some(cpu_index)
767 } else {
768 None
769 };
770 woken = true;
771 }
772 } if woken {
775 if let Some(ci) = ipi_cpu {
776 send_resched_ipi_to_cpu(ci);
777 }
778 restore_flags(saved_flags);
779 return true;
780 }
781
782 {
785 let mut scheduler = GLOBAL_SCHED_STATE.lock();
786 if let Some(ref mut sched) = *scheduler {
787 let (fallback_woken, fallback_ipi) = sched.wake_task_locked(id);
788 woken = fallback_woken;
789 if ipi_cpu.is_none() {
790 ipi_cpu = fallback_ipi;
791 }
792 }
793 }
794
795 if let Some(ci) = ipi_cpu {
796 send_resched_ipi_to_cpu(ci);
797 }
798 restore_flags(saved_flags);
799 woken
800}
801
802pub fn set_task_wake_deadline(id: TaskId, deadline_ns: u64) -> bool {
804 let saved_flags = save_flags_and_cli();
805 let out = {
806 let mut scheduler = GLOBAL_SCHED_STATE.lock();
807 if let Some(ref mut sched) = *scheduler {
808 sched.set_task_wake_deadline_locked(id, deadline_ns)
809 } else {
810 false
811 }
812 };
813 restore_flags(saved_flags);
814 out
815}
816
817pub fn clear_task_wake_deadline(id: TaskId) -> bool {
819 set_task_wake_deadline(id, 0)
820}
821
822pub fn suspend_task(id: TaskId) -> bool {
831 let saved_flags = save_flags_and_cli();
832
833 let mut switch_target: Option<SwitchTarget> = None;
834 let mut suspended = false;
835 let mut ipi_to_cpu: Option<usize> = None;
836
837 let my_cpu = current_cpu_index();
838 let n = active_cpu_count();
839
840 for ci in 0..n {
842 let task_id_on_cpu = LOCAL_SCHEDULERS[ci]
843 .lock()
844 .as_ref()
845 .and_then(|cpu| cpu.current_task.as_ref().map(|t| (t.id, t.clone())));
846 if let Some((tid, current)) = task_id_on_cpu {
847 if tid == id {
848 current.set_state(TaskState::Blocked);
849 current
850 .home_cpu
851 .store(ci, core::sync::atomic::Ordering::Relaxed);
852 super::BLOCKED_TASKS
853 .lock()
854 .insert(current.id, current.clone());
855 suspended = true;
856 if ci == my_cpu {
857 let mut local = LOCAL_SCHEDULERS[ci].lock();
862 if let Some(ref mut cpu) = *local {
863 switch_target = super::core_impl::yield_cpu_local(cpu, ci);
864 }
865 } else {
866 ipi_to_cpu = Some(ci);
868 }
869 break;
870 }
871 }
872 }
873
874 if !suspended {
876 for ci in 0..n {
877 let removed = {
878 let mut local = LOCAL_SCHEDULERS[ci].lock();
879 if let Some(ref mut cpu) = *local {
880 cpu.class_rqs.remove(id)
881 } else {
882 false
883 }
884 };
885 if removed {
886 if let Some(task) = get_task_by_id(id) {
887 task.set_state(TaskState::Blocked);
888 task.home_cpu
889 .store(ci, core::sync::atomic::Ordering::Relaxed);
890 super::BLOCKED_TASKS.lock().insert(task.id, task.clone());
891 }
892 suspended = true;
893 break;
894 }
895 }
896 }
897
898 if !suspended && super::BLOCKED_TASKS.lock().contains_key(&id) {
900 suspended = true;
901 }
902
903 if let Some(ref target) = switch_target {
904 unsafe {
905 crate::process::task::do_switch_context(target);
906 }
907 finish_switch();
908 }
909
910 if let Some(ci) = ipi_to_cpu {
911 send_resched_ipi_to_cpu(ci);
912 }
913
914 restore_flags(saved_flags);
915 suspended
916}
917
918pub fn resume_task(id: TaskId) -> bool {
922 let saved_flags = save_flags_and_cli();
923 let mut ipi_to_cpu: Option<usize> = None;
924
925 let mut task_to_enqueue: Option<Arc<Task>> = None;
926 {
927 let mut blocked = super::BLOCKED_TASKS.lock();
928 if let Some(task) = blocked.remove(&id) {
929 task.set_state(TaskState::Ready);
930 let home = task.home_cpu.load(core::sync::atomic::Ordering::Relaxed);
931 let cpu_index = if home != usize::MAX { home } else { 0 };
932
933 let class = {
934 use crate::process::sched::SchedClassId;
935 match task.sched_policy() {
936 crate::process::sched::SchedPolicy::RealTimeRR { .. }
937 | crate::process::sched::SchedPolicy::RealTimeFifo { .. } => {
938 SchedClassId::RealTime
939 }
940 crate::process::sched::SchedPolicy::Fair(_) => SchedClassId::Fair,
941 crate::process::sched::SchedPolicy::Idle => SchedClassId::Idle,
942 }
943 };
944
945 if let Some(ref mut local_cpu) = *LOCAL_SCHEDULERS[cpu_index].lock() {
946 local_cpu.class_rqs.enqueue(class, task.clone());
947 local_cpu.need_resched = true;
948 }
949
950 if cpu_index != current_cpu_index() {
951 ipi_to_cpu = Some(cpu_index);
952 }
953 drop(blocked);
954 task_to_enqueue = Some(task);
955 }
956 }
957
958 if let Some(ci) = ipi_to_cpu {
959 send_resched_ipi_to_cpu(ci);
960 }
961 restore_flags(saved_flags);
962 task_to_enqueue.is_some()
963}
964
965pub fn kill_task(id: TaskId) -> bool {
976 let pid = crate::process::get_task_by_id(id)
977 .map(|t| t.pid)
978 .unwrap_or(0);
979 crate::audit::log(
980 crate::audit::AuditCategory::Process,
981 pid,
982 crate::silo::task_silo_id(id).unwrap_or(0),
983 alloc::format!("kill_task tid={}", id.as_u64()),
984 );
985 let saved_flags = save_flags_and_cli();
986
987 let mut switch_target: Option<SwitchTarget> = None;
988 let mut killed = false;
989 let mut ipi_to_cpu: Option<usize> = None;
990 let mut parent_to_signal: Option<TaskId> = None;
991
992 {
993 let mut scheduler = GLOBAL_SCHED_STATE.lock();
994 if let Some(ref mut sched) = *scheduler {
995 const FORCED_KILL_EXIT_CODE: i32 = 1;
998 let my_cpu = current_cpu_index();
999
1000 let n = active_cpu_count();
1002 let mut running_hit: Option<(usize, Arc<Task>)> = None;
1003 for ci in 0..n {
1004 let hit = LOCAL_SCHEDULERS[ci].lock().as_ref().and_then(|cpu| {
1005 cpu.current_task
1006 .as_ref()
1007 .map(|t| (t.id, t.get_state(), t.clone()))
1008 });
1009 if let Some((tid, state, current)) = hit {
1010 if tid == id {
1011 if state != TaskState::Dead {
1013 running_hit = Some((ci, current));
1014 }
1015 break;
1016 }
1017 }
1018 }
1019 if let Some((ci, current)) = running_hit {
1020 let task_pid = current.pid;
1021 let _ = sched.clear_task_wake_deadline_locked(id);
1022 current.set_state(TaskState::Dead);
1023 sched.task_cpu.remove(&id);
1028 {
1029 let mut identity = SCHED_IDENTITY.write();
1030 GlobalSchedState::unregister_identity_locked(
1031 &mut identity,
1032 id,
1033 task_pid,
1034 current.tid,
1035 );
1036 }
1037 let (parent, ipi_death) =
1038 finalize_forced_death(sched, id, FORCED_KILL_EXIT_CODE, task_pid);
1039 parent_to_signal = parent;
1040 killed = true;
1041 if ci == my_cpu {
1042 let mut local = LOCAL_SCHEDULERS[ci].lock();
1043 if let Some(ref mut cpu) = *local {
1044 switch_target = super::core_impl::yield_cpu_local(cpu, ci);
1045 }
1046 } else {
1047 ipi_to_cpu = Some(ci);
1048 }
1049 if ipi_to_cpu.is_none() {
1050 ipi_to_cpu = ipi_death;
1051 }
1052 }
1053
1054 if !killed {
1056 let mut removed_from_ready = false;
1057 for ci in 0..n {
1058 let removed = {
1059 let mut local = LOCAL_SCHEDULERS[ci].lock();
1060 if let Some(ref mut cpu) = *local {
1061 cpu.class_rqs.remove(id)
1062 } else {
1063 false
1064 }
1065 };
1066 if removed {
1067 removed_from_ready = true;
1068 break;
1069 }
1070 }
1071 if removed_from_ready {
1072 let _ = sched.clear_task_wake_deadline_locked(id);
1073 if let Some(task) = sched.remove_all_task_locked(id) {
1074 let task_pid = task.pid;
1075 task.set_state(TaskState::Dead);
1076 cleanup_task_resources(&task);
1077 sched.task_cpu.remove(&id);
1078 {
1079 let mut identity = SCHED_IDENTITY.write();
1080 GlobalSchedState::unregister_identity_locked(
1081 &mut identity,
1082 id,
1083 task_pid,
1084 task.tid,
1085 );
1086 }
1087 let (parent, ipi_death) =
1088 finalize_forced_death(sched, id, FORCED_KILL_EXIT_CODE, task_pid);
1089 parent_to_signal = parent;
1090 if ipi_to_cpu.is_none() {
1091 ipi_to_cpu = ipi_death;
1092 }
1093 }
1094 killed = true;
1095 }
1096 }
1097
1098 if !killed {
1100 if let Some(task) = super::BLOCKED_TASKS.lock().remove(&id) {
1101 let task_pid = task.pid;
1102 let _ = sched.clear_task_wake_deadline_locked(id);
1103 task.set_state(TaskState::Dead);
1104 cleanup_task_resources(&task);
1105 let _ = sched.remove_all_task_locked(id);
1106 sched.task_cpu.remove(&id);
1107 {
1108 let mut identity = SCHED_IDENTITY.write();
1109 GlobalSchedState::unregister_identity_locked(
1110 &mut identity,
1111 id,
1112 task_pid,
1113 task.tid,
1114 );
1115 }
1116 let (parent, ipi_death) =
1117 finalize_forced_death(sched, id, FORCED_KILL_EXIT_CODE, task_pid);
1118 parent_to_signal = parent;
1119 if ipi_to_cpu.is_none() {
1120 ipi_to_cpu = ipi_death;
1121 }
1122 killed = true;
1123 }
1124 }
1125 }
1126 } if let Some(ref target) = switch_target {
1129 unsafe {
1130 crate::process::task::do_switch_context(target);
1131 }
1132 finish_switch();
1133 }
1134
1135 if let Some(ci) = ipi_to_cpu {
1136 send_resched_ipi_to_cpu(ci);
1137 }
1138
1139 if let Some(parent_id) = parent_to_signal {
1140 let _ =
1142 crate::process::signal::send_signal(parent_id, crate::process::signal::Signal::SIGCHLD);
1143 }
1144
1145 restore_flags(saved_flags);
1146 killed
1147}
1148
1149fn finalize_forced_death(
1151 sched: &mut GlobalSchedState,
1152 task_id: TaskId,
1153 exit_code: i32,
1154 task_pid: Pid,
1155) -> (Option<TaskId>, Option<usize>) {
1156 let ipi_reparent = {
1157 let mut identity = SCHED_IDENTITY.write();
1158 reparent_children(sched, &mut identity, task_id)
1159 };
1160 let parent = {
1161 let mut identity = SCHED_IDENTITY.write();
1162 identity.parent_of.remove(&task_id)
1163 };
1164 if let Some(parent_id) = parent {
1165 sched.zombies.insert(task_id, (exit_code, task_pid));
1166 let (_, ipi_wake) = sched.wake_task_locked(parent_id);
1167 (Some(parent_id), ipi_reparent.or(ipi_wake))
1168 } else {
1169 (None, ipi_reparent)
1170 }
1171}
1172
1173fn reparent_children(
1187 sched: &mut GlobalSchedState,
1188 identity: &mut SchedIdentity,
1189 dying: TaskId,
1190) -> Option<usize> {
1191 let children = match identity.children_of.remove(&dying) {
1192 Some(c) => c,
1193 None => return None,
1194 };
1195
1196 let init_id = identity.pid_to_task.get(&1).copied();
1198
1199 let Some(init_id) = init_id else {
1200 for child in &children {
1202 identity.parent_of.remove(child);
1203 }
1204 return None;
1205 };
1206 if init_id == dying {
1207 for child in &children {
1209 identity.parent_of.remove(child);
1210 }
1211 return None;
1212 }
1213
1214 let mut has_zombie = false;
1215 let init_children = identity.children_of.entry(init_id).or_default();
1216 for child in children {
1217 if !has_zombie && sched.zombies.contains_key(&child) {
1218 has_zombie = true;
1219 }
1220 identity.parent_of.insert(child, init_id);
1221 init_children.push(child);
1222 }
1223 if has_zombie {
1224 let (_, ipi) = sched.wake_task_locked(init_id);
1225 ipi
1226 } else {
1227 None
1228 }
1229}
1230
1231fn queue_silo_cleanup(task_id: TaskId) {
1240 let mut guard = PENDING_SILO_CLEANUPS.lock();
1241 guard
1242 .push_back(task_id)
1243 .unwrap_or_else(|_| panic!("pending silo cleanup queue overflow"));
1244}
1245
1246pub fn flush_deferred_silo_cleanups() {
1247 let mut guard = match PENDING_SILO_CLEANUPS.try_lock() {
1248 Some(g) => g,
1249 None => return, };
1251 if guard.is_empty() {
1252 return;
1253 }
1254 let mut drained = FixedQueue::<TaskId, PENDING_SILO_CLEANUPS_CAPACITY>::new();
1255 core::mem::swap(&mut *guard, &mut drained);
1256 drop(guard);
1257 while let Some(task_id) = drained.pop_front() {
1258 crate::silo::on_task_terminated(task_id);
1259 }
1260}
1261
1262pub(crate) fn cleanup_task_resources(task: &Arc<Task>) {
1263 crate::ipc::port::cleanup_ports_for_task(task.id);
1264 queue_silo_cleanup(task.id);
1265
1266 let is_last_process_ref = Arc::strong_count(&task.process) == 1;
1270 if !is_last_process_ref {
1271 return;
1272 }
1273
1274 unsafe {
1275 (&mut *task.process.fd_table.get()).close_all();
1276 let capabilities = (&mut *task.process.capabilities.get()).take_all();
1277 for capability in &capabilities {
1278 crate::capability::release_capability(capability, Some(task.id));
1279 }
1280 }
1281
1282 let as_ref = task.process.address_space_arc();
1283 if !as_ref.is_kernel() && Arc::strong_count(&as_ref) == 1 {
1284 as_ref.unmap_all_user_regions();
1285 }
1286}