Add FUTEX_WAIT_BITSET and FUTEX_WAKE_BITSET options for futex syscall

This commit is contained in:
LI Qing 2020-11-16 16:23:28 +08:00 committed by Tate, Hongliang Tian
parent 52fcc622ea
commit 4769a2600e
3 changed files with 107 additions and 37 deletions

@ -4,7 +4,7 @@ use std::intrinsics::atomic_load;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use crate::prelude::*; use crate::prelude::*;
use crate::time::timespec_t; use crate::time::{timespec_t, ClockID};
/// `FutexOp`, `FutexFlags`, and `futex_op_and_flags_from_u32` are helper types and /// `FutexOp`, `FutexFlags`, and `futex_op_and_flags_from_u32` are helper types and
/// functions for handling the versatile commands and arguments of futex system /// functions for handling the versatile commands and arguments of futex system
@ -22,6 +22,7 @@ pub enum FutexOp {
FUTEX_UNLOCK_PI = 7, FUTEX_UNLOCK_PI = 7,
FUTEX_TRYLOCK_PI = 8, FUTEX_TRYLOCK_PI = 8,
FUTEX_WAIT_BITSET = 9, FUTEX_WAIT_BITSET = 9,
FUTEX_WAKE_BITSET = 10,
} }
const FUTEX_OP_MASK: u32 = 0x0000_000F; const FUTEX_OP_MASK: u32 = 0x0000_000F;
@ -38,6 +39,7 @@ impl FutexOp {
7 => Ok(FutexOp::FUTEX_UNLOCK_PI), 7 => Ok(FutexOp::FUTEX_UNLOCK_PI),
8 => Ok(FutexOp::FUTEX_TRYLOCK_PI), 8 => Ok(FutexOp::FUTEX_TRYLOCK_PI),
9 => Ok(FutexOp::FUTEX_WAIT_BITSET), 9 => Ok(FutexOp::FUTEX_WAIT_BITSET),
10 => Ok(FutexOp::FUTEX_WAKE_BITSET),
_ => return_errno!(EINVAL, "Unknown futex op"), _ => return_errno!(EINVAL, "Unknown futex op"),
} }
} }
@ -69,15 +71,47 @@ pub fn futex_op_and_flags_from_u32(bits: u32) -> Result<(FutexOp, FutexFlags)> {
Ok((op, flags)) Ok((op, flags))
} }
const FUTEX_BITSET_MATCH_ANY: u32 = 0xFFFF_FFFF;
#[derive(Debug, Copy, Clone)]
pub struct FutexTimeout {
clock_id: ClockID,
ts: timespec_t,
}
impl FutexTimeout {
pub fn new(clock_id: ClockID, ts: timespec_t) -> Self {
Self { clock_id, ts }
}
pub fn clock_id(&self) -> &ClockID {
&self.clock_id
}
pub fn ts(&self) -> &timespec_t {
&self.ts
}
}
/// Do futex wait /// Do futex wait
pub fn futex_wait( pub fn futex_wait(
futex_addr: *const i32, futex_addr: *const i32,
futex_val: i32, futex_val: i32,
timeout: &Option<timespec_t>, timeout: &Option<FutexTimeout>,
) -> Result<()> {
futex_wait_bitset(futex_addr, futex_val, timeout, FUTEX_BITSET_MATCH_ANY)
}
/// Do futex wait with bitset
pub fn futex_wait_bitset(
futex_addr: *const i32,
futex_val: i32,
timeout: &Option<FutexTimeout>,
bitset: u32,
) -> Result<()> { ) -> Result<()> {
debug!( debug!(
"futex_wait addr: {:#x}, val: {}, timeout: {:?}", "futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}",
futex_addr as usize, futex_val, timeout futex_addr as usize, futex_val, timeout, bitset
); );
// Get and lock the futex bucket // Get and lock the futex bucket
let futex_key = FutexKey::new(futex_addr); let futex_key = FutexKey::new(futex_addr);
@ -120,7 +154,7 @@ pub fn futex_wait(
// it cannot find the transition of futex value from val to new_val and enqueue // it cannot find the transition of futex value from val to new_val and enqueue
// to the bucket, which will cause the waiter to wait forever. // to the bucket, which will cause the waiter to wait forever.
let futex_item = FutexItem::new(futex_key); let futex_item = FutexItem::new(futex_key, bitset);
futex_bucket.enqueue_item(futex_item.clone()); futex_bucket.enqueue_item(futex_item.clone());
// Must make sure that no locks are holded by this thread before wait // Must make sure that no locks are holded by this thread before wait
@ -130,9 +164,14 @@ pub fn futex_wait(
/// Do futex wake /// Do futex wake
pub fn futex_wake(futex_addr: *const i32, max_count: usize) -> Result<usize> { pub fn futex_wake(futex_addr: *const i32, max_count: usize) -> Result<usize> {
futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY)
}
/// Do futex wake with bitset
pub fn futex_wake_bitset(futex_addr: *const i32, max_count: usize, bitset: u32) -> Result<usize> {
debug!( debug!(
"futex_wake addr: {:#x}, max_count: {}", "futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}",
futex_addr as usize, max_count futex_addr as usize, max_count, bitset
); );
// Get and lock the futex bucket // Get and lock the futex bucket
@ -141,7 +180,7 @@ pub fn futex_wake(futex_addr: *const i32, max_count: usize) -> Result<usize> {
let mut futex_bucket = futex_bucket_ref.lock().unwrap(); let mut futex_bucket = futex_bucket_ref.lock().unwrap();
// Dequeue and wake up the items in the bucket // Dequeue and wake up the items in the bucket
let count = futex_bucket.dequeue_and_wake_items(futex_key, max_count); let count = futex_bucket.dequeue_and_wake_items(futex_key, max_count, bitset);
Ok(count) Ok(count)
} }
@ -173,7 +212,8 @@ pub fn futex_requeue(
(futex_bucket, futex_new_bucket) (futex_bucket, futex_new_bucket)
} }
}; };
let nwakes = futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes); let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.requeue_items_to_another_bucket( futex_bucket.requeue_items_to_another_bucket(
futex_key, futex_key,
&mut futex_new_bucket, &mut futex_new_bucket,
@ -184,7 +224,8 @@ pub fn futex_requeue(
} else { } else {
// bucket_idx == new_bucket_idx // bucket_idx == new_bucket_idx
let mut futex_bucket = futex_bucket_ref.lock().unwrap(); let mut futex_bucket = futex_bucket_ref.lock().unwrap();
let nwakes = futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes); let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues); futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues);
nwakes nwakes
} }
@ -219,13 +260,15 @@ impl FutexKey {
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
struct FutexItem { struct FutexItem {
key: FutexKey, key: FutexKey,
bitset: u32,
waiter: WaiterRef, waiter: WaiterRef,
} }
impl FutexItem { impl FutexItem {
pub fn new(key: FutexKey) -> FutexItem { pub fn new(key: FutexKey, bitset: u32) -> FutexItem {
FutexItem { FutexItem {
key: key, key,
bitset,
waiter: Arc::new(Waiter::new()), waiter: Arc::new(Waiter::new()),
} }
} }
@ -234,7 +277,7 @@ impl FutexItem {
self.waiter().wake() self.waiter().wake()
} }
pub fn wait(&self, timeout: &Option<timespec_t>) -> Result<()> { pub fn wait(&self, timeout: &Option<FutexTimeout>) -> Result<()> {
if let Err(e) = self.waiter.wait_timeout(&timeout) { if let Err(e) = self.waiter.wait_timeout(&timeout) {
let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(self.key); let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(self.key);
let mut futex_bucket = futex_bucket_ref.lock().unwrap(); let mut futex_bucket = futex_bucket_ref.lock().unwrap();
@ -281,12 +324,17 @@ impl FutexBucket {
} }
// TODO: consider using std::future to improve the readability // TODO: consider using std::future to improve the readability
pub fn dequeue_and_wake_items(&mut self, key: FutexKey, max_count: usize) -> usize { pub fn dequeue_and_wake_items(
&mut self,
key: FutexKey,
max_count: usize,
bitset: u32,
) -> usize {
let mut count = 0; let mut count = 0;
let mut items_to_wake = Vec::new(); let mut items_to_wake = Vec::new();
self.queue.retain(|item| { self.queue.retain(|item| {
if count >= max_count || key != item.key { if count >= max_count || key != item.key || (bitset & item.bitset) == 0 {
true true
} else { } else {
items_to_wake.push(item.clone()); items_to_wake.push(item.clone());
@ -379,7 +427,7 @@ impl Waiter {
} }
} }
pub fn wait_timeout(&self, timeout: &Option<timespec_t>) -> Result<()> { pub fn wait_timeout(&self, timeout: &Option<FutexTimeout>) -> Result<()> {
let current = unsafe { sgx_thread_get_self() }; let current = unsafe { sgx_thread_get_self() };
if current != self.thread { if current != self.thread {
return Ok(()); return Ok(());
@ -434,19 +482,26 @@ impl PartialEq for Waiter {
unsafe impl Send for Waiter {} unsafe impl Send for Waiter {}
unsafe impl Sync for Waiter {} unsafe impl Sync for Waiter {}
fn wait_event_timeout(thread: *const c_void, timeout: &Option<timespec_t>) -> Result<()> { fn wait_event_timeout(thread: *const c_void, timeout: &Option<FutexTimeout>) -> Result<()> {
let mut ret: c_int = 0; let mut ret: c_int = 0;
let mut sgx_ret: c_int = 0; let mut sgx_ret: c_int = 0;
let timeout_ptr = timeout let (clockbit, ts_ptr) = timeout
.as_ref() .as_ref()
.map(|timeout_ref| timeout_ref as *const _) .map(|timeout| {
.unwrap_or(0 as *const _); let clockbit = match timeout.clock_id() {
ClockID::CLOCK_REALTIME => FutexFlags::FUTEX_CLOCK_REALTIME.bits() as i32,
_ => 0,
};
(clockbit, timeout.ts() as *const timespec_t)
})
.unwrap_or((0, 0 as *const _));
let mut errno: c_int = 0; let mut errno: c_int = 0;
unsafe { unsafe {
sgx_ret = sgx_thread_wait_untrusted_event_timeout_ocall( sgx_ret = sgx_thread_wait_untrusted_event_timeout_ocall(
&mut ret as *mut c_int, &mut ret as *mut c_int,
thread, thread,
timeout_ptr, clockbit,
ts_ptr,
&mut errno as *mut c_int, &mut errno as *mut c_int,
); );
assert!(sgx_ret == 0); assert!(sgx_ret == 0);
@ -493,6 +548,7 @@ extern "C" {
fn sgx_thread_wait_untrusted_event_timeout_ocall( fn sgx_thread_wait_untrusted_event_timeout_ocall(
ret: *mut c_int, ret: *mut c_int,
self_thread: *const c_void, self_thread: *const c_void,
clockbit: i32,
ts: *const timespec_t, ts: *const timespec_t,
errno: *mut c_int, errno: *mut c_int,
) -> c_int; ) -> c_int;

@ -1,11 +1,11 @@
use super::do_arch_prctl::ArchPrctlCode; use super::do_arch_prctl::ArchPrctlCode;
use super::do_clone::CloneFlags; use super::do_clone::CloneFlags;
use super::do_futex::{FutexFlags, FutexOp}; use super::do_futex::{FutexFlags, FutexOp, FutexTimeout};
use super::do_spawn::FileAction; use super::do_spawn::FileAction;
use super::prctl::PrctlCmd; use super::prctl::PrctlCmd;
use super::process::ProcessFilter; use super::process::ProcessFilter;
use crate::prelude::*; use crate::prelude::*;
use crate::time::timespec_t; use crate::time::{timespec_t, ClockID};
use crate::util::mem_util::from_user::*; use crate::util::mem_util::from_user::*;
use std::ptr::NonNull; use std::ptr::NonNull;
@ -130,6 +130,7 @@ pub fn do_futex(
futex_val: i32, futex_val: i32,
timeout: u64, timeout: u64,
futex_new_addr: *const i32, futex_new_addr: *const i32,
bitset: u32,
) -> Result<isize> { ) -> Result<isize> {
check_ptr(futex_addr)?; check_ptr(futex_addr)?;
let (futex_op, futex_flags) = super::do_futex::futex_op_and_flags_from_u32(futex_op)?; let (futex_op, futex_flags) = super::do_futex::futex_op_and_flags_from_u32(futex_op)?;
@ -141,27 +142,40 @@ pub fn do_futex(
Ok(val as usize) Ok(val as usize)
}; };
match futex_op { let get_futex_timeout = |timeout| -> Result<Option<FutexTimeout>> {
FutexOp::FUTEX_WAIT => {
let timeout = {
let timeout = timeout as *const timespec_t; let timeout = timeout as *const timespec_t;
if timeout.is_null() { if timeout.is_null() {
None return Ok(None);
} else { }
let ts = timespec_t::from_raw_ptr(timeout)?; let ts = timespec_t::from_raw_ptr(timeout)?;
ts.validate()?; ts.validate()?;
if futex_flags.contains(FutexFlags::FUTEX_CLOCK_REALTIME) { // TODO: use a secure clock to transfer the real time to monotonic time
warn!("CLOCK_REALTIME is not supported yet, use monotonic clock"); let clock_id = if futex_flags.contains(FutexFlags::FUTEX_CLOCK_REALTIME) {
} ClockID::CLOCK_REALTIME
Some(ts) } else {
} ClockID::CLOCK_MONOTONIC
}; };
Ok(Some(FutexTimeout::new(clock_id, ts)))
};
match futex_op {
FutexOp::FUTEX_WAIT => {
let timeout = get_futex_timeout(timeout)?;
super::do_futex::futex_wait(futex_addr, futex_val, &timeout).map(|_| 0) super::do_futex::futex_wait(futex_addr, futex_val, &timeout).map(|_| 0)
} }
FutexOp::FUTEX_WAIT_BITSET => {
let timeout = get_futex_timeout(timeout)?;
super::do_futex::futex_wait_bitset(futex_addr, futex_val, &timeout, bitset).map(|_| 0)
}
FutexOp::FUTEX_WAKE => { FutexOp::FUTEX_WAKE => {
let max_count = get_futex_val(futex_val)?; let max_count = get_futex_val(futex_val)?;
super::do_futex::futex_wake(futex_addr, max_count).map(|count| count as isize) super::do_futex::futex_wake(futex_addr, max_count).map(|count| count as isize)
} }
FutexOp::FUTEX_WAKE_BITSET => {
let max_count = get_futex_val(futex_val)?;
super::do_futex::futex_wake_bitset(futex_addr, max_count, bitset)
.map(|count| count as isize)
}
FutexOp::FUTEX_REQUEUE => { FutexOp::FUTEX_REQUEUE => {
check_ptr(futex_new_addr)?; check_ptr(futex_new_addr)?;
let max_nwakes = get_futex_val(futex_val)?; let max_nwakes = get_futex_val(futex_val)?;

@ -286,7 +286,7 @@ macro_rules! process_syscall_table_with_callback {
(Fremovexattr = 199) => handle_unsupported(), (Fremovexattr = 199) => handle_unsupported(),
(Tkill = 200) => do_tkill(tid: pid_t, sig: c_int), (Tkill = 200) => do_tkill(tid: pid_t, sig: c_int),
(Time = 201) => handle_unsupported(), (Time = 201) => handle_unsupported(),
(Futex = 202) => do_futex(futex_addr: *const i32, futex_op: u32, futex_val: i32, timeout: u64, futex_new_addr: *const i32), (Futex = 202) => do_futex(futex_addr: *const i32, futex_op: u32, futex_val: i32, timeout: u64, futex_new_addr: *const i32, bitset: u32),
(SchedSetaffinity = 203) => do_sched_setaffinity(pid: pid_t, cpusize: size_t, buf: *const c_uchar), (SchedSetaffinity = 203) => do_sched_setaffinity(pid: pid_t, cpusize: size_t, buf: *const c_uchar),
(SchedGetaffinity = 204) => do_sched_getaffinity(pid: pid_t, cpusize: size_t, buf: *mut c_uchar), (SchedGetaffinity = 204) => do_sched_getaffinity(pid: pid_t, cpusize: size_t, buf: *mut c_uchar),
(SetThreadArea = 205) => handle_unsupported(), (SetThreadArea = 205) => handle_unsupported(),