diff --git a/kernel/src/process/posix_thread/exit.rs b/kernel/src/process/posix_thread/exit.rs index 7143a7b6d..6984a896c 100644 --- a/kernel/src/process/posix_thread/exit.rs +++ b/kernel/src/process/posix_thread/exit.rs @@ -23,7 +23,7 @@ pub fn do_exit(thread: &Thread, posix_thread: &PosixThread, term_status: TermSta let mut clear_ctid = posix_thread.clear_child_tid().lock(); // If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr. if *clear_ctid != 0 { - futex_wake(*clear_ctid, 1)?; + futex_wake(*clear_ctid, 1, None)?; // FIXME: the correct write length? CurrentUserSpace::get() .write_val(*clear_ctid, &0u32) @@ -44,7 +44,7 @@ pub fn do_exit(thread: &Thread, posix_thread: &PosixThread, term_status: TermSta do_exit_group(term_status); } - futex_wake(Arc::as_ptr(&posix_thread.process()) as Vaddr, 1)?; + futex_wake(Arc::as_ptr(&posix_thread.process()) as Vaddr, 1, None)?; Ok(()) } diff --git a/kernel/src/process/posix_thread/futex.rs b/kernel/src/process/posix_thread/futex.rs index 1decbde3e..a232baea2 100644 --- a/kernel/src/process/posix_thread/futex.rs +++ b/kernel/src/process/posix_thread/futex.rs @@ -9,7 +9,7 @@ use ostd::{ }; use spin::Once; -use crate::{prelude::*, time::wait::TimerBuilder}; +use crate::{prelude::*, process::Pid, time::wait::TimerBuilder}; type FutexBitSet = u32; type FutexBucketRef = Arc>; @@ -24,6 +24,7 @@ pub fn futex_wait( futex_val: i32, timer_builder: Option, ctx: &Context, + pid: Option, ) -> Result<()> { futex_wait_bitset( futex_addr as _, @@ -31,6 +32,7 @@ pub fn futex_wait( timer_builder, FUTEX_BITSET_MATCH_ANY, ctx, + pid, ) } @@ -41,6 +43,7 @@ pub fn futex_wait_bitset( timer_builder: Option, bitset: FutexBitSet, ctx: &Context, + pid: Option, ) -> Result<()> { debug!( "futex_wait_bitset addr: {:#x}, val: {}, bitset: {:#x}", @@ -51,7 +54,7 @@ pub fn futex_wait_bitset( return_errno_with_message!(Errno::EINVAL, "at least one bit should be set"); } - let futex_key = FutexKey::new(futex_addr, bitset); + let futex_key = FutexKey::new(futex_addr, bitset, pid); let (futex_item, waiter) = FutexItem::create(futex_key); let (_, futex_bucket_ref) = get_futex_bucket(futex_key); @@ -74,8 +77,8 @@ pub fn futex_wait_bitset( } /// Does futex wake -pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result { - futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY) +pub fn futex_wake(futex_addr: Vaddr, max_count: usize, pid: Option) -> Result { + futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY, pid) } /// Does futex wake with bitset @@ -83,6 +86,7 @@ pub fn futex_wake_bitset( futex_addr: Vaddr, max_count: usize, bitset: FutexBitSet, + pid: Option, ) -> Result { debug!( "futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}", @@ -93,7 +97,7 @@ pub fn futex_wake_bitset( return_errno_with_message!(Errno::EINVAL, "at least one bit should be set"); } - let futex_key = FutexKey::new(futex_addr, bitset); + let futex_key = FutexKey::new(futex_addr, bitset, pid); let (_, futex_bucket_ref) = get_futex_bucket(futex_key); let mut futex_bucket = futex_bucket_ref.lock(); let res = futex_bucket.remove_and_wake_items(futex_key, max_count); @@ -108,13 +112,14 @@ pub fn futex_requeue( max_nwakes: usize, max_nrequeues: usize, futex_new_addr: Vaddr, + pid: Option, ) -> Result { if futex_new_addr == futex_addr { - return futex_wake(futex_addr, max_nwakes); + return futex_wake(futex_addr, max_nwakes, pid); } - let futex_key = FutexKey::new(futex_addr, FUTEX_BITSET_MATCH_ANY); - let futex_new_key = FutexKey::new(futex_new_addr, FUTEX_BITSET_MATCH_ANY); + let futex_key = FutexKey::new(futex_addr, FUTEX_BITSET_MATCH_ANY, pid); + let futex_new_key = FutexKey::new(futex_new_addr, FUTEX_BITSET_MATCH_ANY, pid); let (bucket_idx, futex_bucket_ref) = get_futex_bucket(futex_key); let (new_bucket_idx, futex_new_bucket_ref) = get_futex_bucket(futex_new_key); @@ -332,11 +337,14 @@ impl FutexItem { struct FutexKey { addr: Vaddr, bitset: FutexBitSet, + /// Specify whether this `FutexKey` is process private or shared. If `pid` is + /// None, then this `FutexKey` is shared. + pid: Option, } impl FutexKey { - pub fn new(addr: Vaddr, bitset: FutexBitSet) -> Self { - Self { addr, bitset } + pub fn new(addr: Vaddr, bitset: FutexBitSet, pid: Option) -> Self { + Self { addr, bitset, pid } } pub fn load_val(&self, ctx: &Context) -> Result { @@ -354,7 +362,8 @@ impl FutexKey { } pub fn match_up(&self, another: &Self) -> bool { - self.addr == another.addr && (self.bitset & another.bitset) != 0 + // TODO: Use hash value to do match_up + self.addr == another.addr && (self.bitset & another.bitset) != 0 && self.pid == another.pid } } diff --git a/kernel/src/process/posix_thread/robust_list.rs b/kernel/src/process/posix_thread/robust_list.rs index 1f9298f19..808bc0f31 100644 --- a/kernel/src/process/posix_thread/robust_list.rs +++ b/kernel/src/process/posix_thread/robust_list.rs @@ -2,10 +2,7 @@ //! The implementation of robust list is from occlum. -use crate::{ - prelude::*, - process::{posix_thread::futex::futex_wake, Pid}, -}; +use crate::{prelude::*, process::posix_thread::futex::futex_wake, thread::Tid}; #[repr(C)] #[derive(Clone, Copy, Debug, Pod)] @@ -125,7 +122,7 @@ const FUTEX_TID_MASK: u32 = 0x3FFF_FFFF; /// Wakeup one robust futex owned by the thread /// FIXME: requires atomic operations here -pub fn wake_robust_futex(futex_addr: Vaddr, tid: Pid) -> Result<()> { +pub fn wake_robust_futex(futex_addr: Vaddr, tid: Tid) -> Result<()> { let user_space = CurrentUserSpace::get(); let futex_val = { if futex_addr == 0 { @@ -150,7 +147,7 @@ pub fn wake_robust_futex(futex_addr: Vaddr, tid: Pid) -> Result<()> { // Wakeup one waiter if cur_val & FUTEX_WAITERS != 0 { debug!("wake robust futex addr: {:?}", futex_addr); - futex_wake(futex_addr, 1)?; + futex_wake(futex_addr, 1, None)?; } break; } diff --git a/kernel/src/syscall/futex.rs b/kernel/src/syscall/futex.rs index ec1712f49..8715a8e87 100644 --- a/kernel/src/syscall/futex.rs +++ b/kernel/src/syscall/futex.rs @@ -26,7 +26,6 @@ pub fn sys_futex( bitset: u64, ctx: &Context, ) -> Result { - // FIXME: we current ignore futex flags let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _)?; debug!( "futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}, futex_val = 0x{:x}", @@ -84,10 +83,15 @@ pub fn sys_futex( ))) }; + let pid = if futex_flags.contains(FutexFlags::FUTEX_PRIVATE) { + Some(ctx.process.pid()) + } else { + None + }; let res = match futex_op { FutexOp::FUTEX_WAIT => { let timer_builder = get_futex_timer_builder(utime_addr)?; - futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx).map(|_| 0) + futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx, pid).map(|_| 0) } FutexOp::FUTEX_WAIT_BITSET => { let timer_builder = get_futex_timer_builder(utime_addr)?; @@ -97,16 +101,22 @@ pub fn sys_futex( timer_builder, bitset as _, ctx, + pid, ) .map(|_| 0) } FutexOp::FUTEX_WAKE => { - let max_count = get_futex_val(futex_val as i32)?; - futex_wake(futex_addr as _, max_count).map(|count| count as isize) + // From gVisor/test/syscalls/linux/futex.cc:260: "The Linux kernel wakes one waiter even if val is 0 or negative." + // To be consistent with Linux, we set the max_count to 1 if it is 0 or negative. + let max_count = (futex_val as i32).max(1) as usize; + futex_wake(futex_addr as _, max_count, pid).map(|count| count as isize) } FutexOp::FUTEX_WAKE_BITSET => { - let max_count = get_futex_val(futex_val as i32)?; - futex_wake_bitset(futex_addr as _, max_count, bitset as _).map(|count| count as isize) + // From gVisor/test/syscalls/linux/futex.cc:260: "The Linux kernel wakes one waiter even if val is 0 or negative." + // To be consistent with Linux, we set the max_count to 1 if it is 0 or negative. + let max_count = (futex_val as i32).max(1) as usize; + futex_wake_bitset(futex_addr as _, max_count, bitset as _, pid) + .map(|count| count as isize) } FutexOp::FUTEX_REQUEUE => { let max_nwakes = get_futex_val(futex_val as i32)?; @@ -116,6 +126,7 @@ pub fn sys_futex( max_nwakes, max_nrequeues, futex_new_addr as _, + pid, ) .map(|nwakes| nwakes as _) }