mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Enable FUTEX_WAKE_OP
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
860bb6c07f
commit
627dd0386b
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use int_to_c_enum::TryFromInt;
|
||||
use ostd::{
|
||||
cpu::num_cpus,
|
||||
sync::{Waiter, Waker},
|
||||
@ -117,6 +118,154 @@ pub fn futex_wake_bitset(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// This struct encodes the operation and comparison that are to be performed during
|
||||
/// the futex operation with `FUTEX_WAKE_OP`.
|
||||
///
|
||||
/// The encoding is as follows:
|
||||
///
|
||||
/// +---+---+-----------+-----------+
|
||||
/// |op |cmp| oparg | cmparg |
|
||||
/// +---+---+-----------+-----------+
|
||||
/// 4 4 12 12 <== # of bits
|
||||
///
|
||||
/// Reference: https://man7.org/linux/man-pages/man2/futex.2.html.
|
||||
struct FutexWakeOpEncode {
|
||||
op: FutexWakeOp,
|
||||
/// A flag indicating that the operation will use `1 << oparg`
|
||||
/// as the operand instead of `oparg` when it is set `true`.
|
||||
///
|
||||
/// e.g. With this flag, [`FutexWakeOp::FUTEX_OP_ADD`] will be interpreted
|
||||
/// as `res = (1 << oparg) + oldval`.
|
||||
is_oparg_shift: bool,
|
||||
cmp: FutexWakeCmp,
|
||||
oparg: u32,
|
||||
cmparg: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
|
||||
#[repr(u32)]
|
||||
#[expect(non_camel_case_types)]
|
||||
enum FutexWakeOp {
|
||||
/// Calculate `res = oparg`.
|
||||
FUTEX_OP_SET = 0,
|
||||
/// Calculate `res = oparg + oldval`.
|
||||
FUTEX_OP_ADD = 1,
|
||||
/// Calculate `res = oparg | oldval`.
|
||||
FUTEX_OP_OR = 2,
|
||||
/// Calculate `res = oparg & !oldval`.
|
||||
FUTEX_OP_ANDN = 3,
|
||||
/// Calculate `res = oparg ^ oldval`.
|
||||
FUTEX_OP_XOR = 4,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
|
||||
#[repr(u32)]
|
||||
#[expect(non_camel_case_types)]
|
||||
enum FutexWakeCmp {
|
||||
/// If (oldval == cmparg) do wake.
|
||||
FUTEX_OP_CMP_EQ = 0,
|
||||
/// If (oldval != cmparg) do wake.
|
||||
FUTEX_OP_CMP_NE = 1,
|
||||
/// If (oldval < cmparg) do wake.
|
||||
FUTEX_OP_CMP_LT = 2,
|
||||
/// If (oldval <= cmparg) do wake.
|
||||
FUTEX_OP_CMP_LE = 3,
|
||||
/// If (oldval > cmparg) do wake.
|
||||
FUTEX_OP_CMP_GT = 4,
|
||||
/// If (oldval >= cmparg) do wake.
|
||||
FUTEX_OP_CMP_GE = 5,
|
||||
}
|
||||
|
||||
impl FutexWakeOpEncode {
|
||||
fn from_u32(bits: u32) -> Result<Self> {
|
||||
let is_oparg_shift = (bits >> 31) & 1 == 1;
|
||||
let op = FutexWakeOp::try_from((bits >> 28) & 0x7)?;
|
||||
let cmp = FutexWakeCmp::try_from((bits >> 24) & 0xf)?;
|
||||
let oparg = (bits >> 12) & 0xfff;
|
||||
let cmparg = bits & 0xfff;
|
||||
|
||||
Ok(FutexWakeOpEncode {
|
||||
op,
|
||||
is_oparg_shift,
|
||||
cmp,
|
||||
oparg,
|
||||
cmparg,
|
||||
})
|
||||
}
|
||||
|
||||
fn calculate_new_val(&self, old_val: u32) -> u32 {
|
||||
let oparg = if self.is_oparg_shift {
|
||||
1 << self.oparg
|
||||
} else {
|
||||
self.oparg
|
||||
};
|
||||
|
||||
match self.op {
|
||||
FutexWakeOp::FUTEX_OP_SET => oparg,
|
||||
FutexWakeOp::FUTEX_OP_ADD => oparg.wrapping_add(old_val),
|
||||
FutexWakeOp::FUTEX_OP_OR => oparg | old_val,
|
||||
FutexWakeOp::FUTEX_OP_ANDN => oparg & !old_val,
|
||||
FutexWakeOp::FUTEX_OP_XOR => oparg ^ old_val,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_wake(&self, old_val: u32) -> bool {
|
||||
match self.cmp {
|
||||
FutexWakeCmp::FUTEX_OP_CMP_EQ => old_val == self.cmparg,
|
||||
FutexWakeCmp::FUTEX_OP_CMP_NE => old_val != self.cmparg,
|
||||
FutexWakeCmp::FUTEX_OP_CMP_LT => old_val < self.cmparg,
|
||||
FutexWakeCmp::FUTEX_OP_CMP_LE => old_val <= self.cmparg,
|
||||
FutexWakeCmp::FUTEX_OP_CMP_GT => old_val > self.cmparg,
|
||||
FutexWakeCmp::FUTEX_OP_CMP_GE => old_val >= self.cmparg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn futex_wake_op(
|
||||
futex_addr_1: Vaddr,
|
||||
futex_addr_2: Vaddr,
|
||||
max_count_1: usize,
|
||||
max_count_2: usize,
|
||||
wake_op_bits: u32,
|
||||
ctx: &Context,
|
||||
pid: Option<Pid>,
|
||||
) -> Result<usize> {
|
||||
let wake_op = FutexWakeOpEncode::from_u32(wake_op_bits)?;
|
||||
|
||||
let futex_key_1 = FutexKey::new(futex_addr_1, FUTEX_BITSET_MATCH_ANY, pid);
|
||||
let futex_key_2 = FutexKey::new(futex_addr_2, FUTEX_BITSET_MATCH_ANY, pid);
|
||||
let (index_1, futex_bucket_ref_1) = get_futex_bucket(futex_key_1);
|
||||
let (index_2, futex_bucket_ref_2) = get_futex_bucket(futex_key_2);
|
||||
|
||||
let (mut futex_bucket_1, mut futex_bucket_2) = if index_1 == index_2 {
|
||||
(futex_bucket_ref_1.lock(), None)
|
||||
} else {
|
||||
// Ensure that we always lock the buckets in a consistent order to avoid deadlocks.
|
||||
if index_1 < index_2 {
|
||||
let bucket_1 = futex_bucket_ref_1.lock();
|
||||
let bucket_2 = futex_bucket_ref_2.lock();
|
||||
(bucket_1, Some(bucket_2))
|
||||
} else {
|
||||
let bucket_2 = futex_bucket_ref_2.lock();
|
||||
let bucket_1 = futex_bucket_ref_1.lock();
|
||||
(bucket_1, Some(bucket_2))
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: This should be an atomic read-modify-write memory access here.
|
||||
let old_val = ctx.user_space().read_val(futex_addr_2)?;
|
||||
let new_val = wake_op.calculate_new_val(old_val);
|
||||
ctx.user_space().write_val(futex_addr_2, &new_val)?;
|
||||
|
||||
let mut res = futex_bucket_1.remove_and_wake_items(futex_key_1, max_count_1);
|
||||
if wake_op.should_wake(old_val) {
|
||||
let bucket = futex_bucket_2.as_mut().unwrap_or(&mut futex_bucket_1);
|
||||
res += bucket.remove_and_wake_items(futex_key_2, max_count_2);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Does futex requeue
|
||||
pub fn futex_requeue(
|
||||
futex_addr: Vaddr,
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
prelude::*,
|
||||
process::posix_thread::futex::{
|
||||
futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake,
|
||||
futex_wake_bitset, FutexFlags, FutexOp,
|
||||
futex_wake_bitset, futex_wake_op, FutexFlags, FutexOp,
|
||||
},
|
||||
syscall::SyscallReturn,
|
||||
time::{
|
||||
@ -120,6 +120,19 @@ pub fn sys_futex(
|
||||
pid,
|
||||
)
|
||||
}
|
||||
FutexOp::FUTEX_WAKE_OP => {
|
||||
let futex_val_2 = utime_addr as u32;
|
||||
|
||||
futex_wake_op(
|
||||
futex_addr,
|
||||
futex_new_addr,
|
||||
futex_val_to_max_count(futex_val),
|
||||
futex_val_to_max_count(futex_val_2),
|
||||
bitset,
|
||||
ctx,
|
||||
pid,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
warn!("futex op = {:?}", futex_op);
|
||||
return_errno_with_message!(Errno::EINVAL, "unsupported futex op");
|
||||
|
@ -3,10 +3,7 @@ PrivateFutexTest.*
|
||||
RobustFutexTest.*
|
||||
SharedFutexTest.WakeInterprocessFile_NoRandomSave
|
||||
|
||||
SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondSuccess_NoRandomSave/*
|
||||
SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondFailure_NoRandomSave/*
|
||||
SharedPrivate/PrivateAndSharedFutexTest.NoWakeInterprocessPrivateAnon_NoRandomSave/*
|
||||
SharedPrivate/PrivateAndSharedFutexTest.WakeWrongKind_NoRandomSave/1
|
||||
|
||||
SharedPrivate/PrivateAndSharedFutexTest.PIBasic/*
|
||||
SharedPrivate/PrivateAndSharedFutexTest.PIConcurrency_NoRandomSave/*
|
||||
|
Reference in New Issue
Block a user