@@ -37,6 +37,7 @@ extern crate alloc;
3737use super :: tasks:: TASK_ACTIVE_OFFSET ;
3838use super :: tasks:: TASK_CUR_CPU_OFFSET ;
3939use super :: tasks:: TaskExitStatus ;
40+ use super :: tasks:: TaskWaitListAdapter ;
4041use super :: {
4142 INITIAL_TASK_ID , KernelThreadStartInfo , Task , TaskListAdapter , TaskPointer , TaskRunListAdapter ,
4243} ;
@@ -281,7 +282,7 @@ impl TaskList {
281282 /// # Safety
282283 /// The caller must ensure that `task` is already a member of this task
283284 /// list.
284- unsafe fn terminate ( & mut self , task : TaskPointer ) -> Option < TaskPointer > {
285+ unsafe fn terminate ( & mut self , task : TaskPointer ) -> LinkedList < TaskWaitListAdapter > {
285286 // Set the task state as terminated. If the task being terminated is the
286287 // current task then the task context will still need to be in scope until
287288 // the next schedule() has completed. Schedule will keep a reference to this
@@ -291,7 +292,7 @@ impl TaskList {
291292 let mut cursor = unsafe { self . list ( ) . cursor_mut_from_ptr ( task. as_ref ( ) ) } ;
292293 cursor. remove ( ) ;
293294
294- // Inform the caller about any task that may need to be woken.
295+ // Inform the caller about any tasks that may need to be woken.
295296 wakeup
296297 }
297298}
@@ -432,11 +433,11 @@ fn current_task_terminated() {
432433 // SAFETY: the scheduler guarantees that `current_task` always points to a
433434 // valid task, and every task has its pointer pushed into the global task
434435 // list during its creation.
435- let wakeup = unsafe { TASKLIST . lock ( ) . terminate ( task_node. clone ( ) ) } ;
436+ let mut wakeup = unsafe { TASKLIST . lock ( ) . terminate ( task_node. clone ( ) ) } ;
436437
437- // If another thread must be woken as a result of the termination, then
438- // schedule it now .
439- if let Some ( wake_task) = wakeup {
438+ // Wake all tasks that were waiting for this task to terminate, scheduling
439+ // each on the current runqueue .
440+ while let Some ( wake_task) = wakeup. pop_front ( ) {
440441 rq. prepare_run_task ( wake_task) ;
441442 }
442443}
@@ -902,11 +903,13 @@ const CONTEXT_SWITCH_RESTORE_TOKEN: VirtAddr = SVSM_CONTEXT_SWITCH_SHADOW_STACK.
902903mod test {
903904 extern crate alloc;
904905 use super :: KernelThreadStartInfo ;
906+ use super :: schedule;
905907 use super :: set_affinity;
906908 use super :: start_kernel_task;
907909 use super :: wait_for_termination;
908910 use crate :: cpu:: percpu:: { PERCPU_AREAS , this_cpu} ;
909911 use alloc:: string:: String ;
912+ use core:: sync:: atomic:: AtomicBool ;
910913 use core:: sync:: atomic:: AtomicU32 ;
911914 use core:: sync:: atomic:: Ordering ;
912915
@@ -960,6 +963,93 @@ mod test {
960963 wait_for_termination ( task) ;
961964 }
962965
966+ static MULTI_WAITER_COUNTER : AtomicU32 = AtomicU32 :: new ( 0 ) ;
967+ static MULTI_WAITER_WAITING_COUNTER : AtomicU32 = AtomicU32 :: new ( 0 ) ;
968+ static MULTI_WAITER_TARGET_RELEASED : AtomicBool = AtomicBool :: new ( false ) ;
969+ const MULTI_WAITER_TASK_INCREMENT : u32 = 10 ;
970+
971+ fn multi_waiter_target ( _: usize ) {
972+ let current_cpu = this_cpu ( ) . get_cpu_index ( ) ;
973+ let target_cpu = PERCPU_AREAS . len ( ) - 1 ;
974+ set_affinity ( if current_cpu == target_cpu {
975+ 0
976+ } else {
977+ target_cpu
978+ } ) ;
979+
980+ while !MULTI_WAITER_TARGET_RELEASED . load ( Ordering :: Acquire ) {
981+ core:: hint:: spin_loop ( ) ;
982+ }
983+
984+ MULTI_WAITER_COUNTER . fetch_add ( 1 , Ordering :: Relaxed ) ;
985+ }
986+
987+ fn waiting_task ( target_id : usize ) {
988+ // Look up the target task by id and wait for it to terminate.
989+ let task = super :: TASKLIST
990+ . lock ( )
991+ . get_task ( target_id as u32 )
992+ . expect ( "Failed to find multi-waiter target task" ) ;
993+ super :: preemption_checks ( ) ;
994+ let guard = task
995+ . wait_for_exit ( )
996+ . expect ( "Multi-waiter target terminated before waiter blocked" ) ;
997+ MULTI_WAITER_WAITING_COUNTER . fetch_add ( 1 , Ordering :: Release ) ;
998+ super :: select_new_task ( false , Some ( guard) ) ;
999+ MULTI_WAITER_COUNTER . fetch_add ( MULTI_WAITER_TASK_INCREMENT , Ordering :: Relaxed ) ;
1000+ }
1001+
1002+ #[ test]
1003+ #[ cfg_attr( not( test_in_svsm) , ignore = "Can only be run inside guest" ) ]
1004+ fn test_wait_for_termination_multiple_waiters ( ) {
1005+ MULTI_WAITER_COUNTER . store ( 0 , Ordering :: Relaxed ) ;
1006+ MULTI_WAITER_WAITING_COUNTER . store ( 0 , Ordering :: Relaxed ) ;
1007+ MULTI_WAITER_TARGET_RELEASED . store ( false , Ordering :: Relaxed ) ;
1008+
1009+ if PERCPU_AREAS . len ( ) < 2 {
1010+ return ;
1011+ }
1012+
1013+ // Start the task that will be waited on.
1014+ let target = start_kernel_task (
1015+ KernelThreadStartInfo :: new ( multi_waiter_target, 0 ) ,
1016+ String :: from ( "multi-waiter target" ) ,
1017+ )
1018+ . expect ( "Failed to start target task" ) ;
1019+ let target_id = target. get_task_id ( ) as usize ;
1020+
1021+ // Start two waiter tasks that each wait on the same target task.
1022+ let waiter1 = start_kernel_task (
1023+ KernelThreadStartInfo :: new ( waiting_task, target_id) ,
1024+ String :: from ( "waiter 1" ) ,
1025+ )
1026+ . expect ( "Failed to start waiter 1" ) ;
1027+
1028+ let waiter2 = start_kernel_task (
1029+ KernelThreadStartInfo :: new ( waiting_task, target_id) ,
1030+ String :: from ( "waiter 2" ) ,
1031+ )
1032+ . expect ( "Failed to start waiter 2" ) ;
1033+
1034+ while MULTI_WAITER_WAITING_COUNTER . load ( Ordering :: Acquire ) < 2 {
1035+ schedule ( ) ;
1036+ }
1037+
1038+ // Wait for the target task from this task too, ensuring it has
1039+ // terminated before we check results.
1040+ MULTI_WAITER_TARGET_RELEASED . store ( true , Ordering :: Release ) ;
1041+ wait_for_termination ( target) ;
1042+ wait_for_termination ( waiter1) ;
1043+ wait_for_termination ( waiter2) ;
1044+
1045+ // Both waiter tasks must have run: multi_waiter_target adds 1,
1046+ // each waiting_task adds MULTI_WAITER_TASK_INCREMENT.
1047+ assert_eq ! (
1048+ MULTI_WAITER_COUNTER . load( Ordering :: Relaxed ) ,
1049+ 1 + ( MULTI_WAITER_TASK_INCREMENT * 2 )
1050+ ) ;
1051+ }
1052+
9631053 #[ test]
9641054 #[ cfg_attr( not( test_in_svsm) , ignore = "Can only be run inside guest" ) ]
9651055 fn test_set_affinity ( ) {
0 commit comments