mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13: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
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use int_to_c_enum::TryFromInt;
|
||||||
use ostd::{
|
use ostd::{
|
||||||
cpu::num_cpus,
|
cpu::num_cpus,
|
||||||
sync::{Waiter, Waker},
|
sync::{Waiter, Waker},
|
||||||
@ -117,6 +118,154 @@ pub fn futex_wake_bitset(
|
|||||||
Ok(res)
|
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
|
/// Does futex requeue
|
||||||
pub fn futex_requeue(
|
pub fn futex_requeue(
|
||||||
futex_addr: Vaddr,
|
futex_addr: Vaddr,
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
process::posix_thread::futex::{
|
process::posix_thread::futex::{
|
||||||
futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake,
|
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,
|
syscall::SyscallReturn,
|
||||||
time::{
|
time::{
|
||||||
@ -120,6 +120,19 @@ pub fn sys_futex(
|
|||||||
pid,
|
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);
|
warn!("futex op = {:?}", futex_op);
|
||||||
return_errno_with_message!(Errno::EINVAL, "unsupported futex op");
|
return_errno_with_message!(Errno::EINVAL, "unsupported futex op");
|
||||||
|
@ -3,10 +3,7 @@ PrivateFutexTest.*
|
|||||||
RobustFutexTest.*
|
RobustFutexTest.*
|
||||||
SharedFutexTest.WakeInterprocessFile_NoRandomSave
|
SharedFutexTest.WakeInterprocessFile_NoRandomSave
|
||||||
|
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondSuccess_NoRandomSave/*
|
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondFailure_NoRandomSave/*
|
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.NoWakeInterprocessPrivateAnon_NoRandomSave/*
|
SharedPrivate/PrivateAndSharedFutexTest.NoWakeInterprocessPrivateAnon_NoRandomSave/*
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.WakeWrongKind_NoRandomSave/1
|
|
||||||
|
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.PIBasic/*
|
SharedPrivate/PrivateAndSharedFutexTest.PIBasic/*
|
||||||
SharedPrivate/PrivateAndSharedFutexTest.PIConcurrency_NoRandomSave/*
|
SharedPrivate/PrivateAndSharedFutexTest.PIConcurrency_NoRandomSave/*
|
||||||
|
Reference in New Issue
Block a user