Skip to content

Commit

Permalink
futex: split backend into val2 and timespec versions, reorganize new …
Browse files Browse the repository at this point in the history
…functions
  • Loading branch information
danielschemmel committed Jul 27, 2024
1 parent 89dac89 commit 6012fad
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 104 deletions.
4 changes: 2 additions & 2 deletions src/backend/libc/thread/futex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::backend::c;
bitflags::bitflags! {
/// `FUTEX_*` flags for use with [`futex`].
///
/// [`futex`]: crate::thread::futex
/// [`futex`]: mod@crate::thread::futex
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FutexFlags: u32 {
Expand All @@ -16,7 +16,7 @@ bitflags::bitflags! {

/// `FUTEX_*` operations for use with [`futex`].
///
/// [`futex`]: crate::thread::futex
/// [`futex`]: mod@crate::thread::futex
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub enum FutexOperation {
Expand Down
95 changes: 87 additions & 8 deletions src/backend/libc/thread/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,81 @@ pub(crate) fn setresgid_thread(
unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) }
}

// TODO: This could be de-multiplexed.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex(
pub(crate) unsafe fn futex_val2(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
val: u32,
val2: u32,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
// the least significant four bytes of the timeout pointer are used as `val2`.
// ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html),
// so we perform that exact conversion in reverse to create the pointer.
let utime = val2 as usize as *const Timespec;

#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
{
// TODO: Upstream this to the libc crate.
#[allow(non_upper_case_globals)]
const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;

syscall! {
fn futex_time64(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}

ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
utime,
uaddr2,
val3,
))
}

#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const linux_raw_sys::general::__kernel_timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}

ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
utime.cast(),
uaddr2,
val3,
) as isize)
}
}

#[cfg(linux_kernel)]
pub(crate) unsafe fn futex_timespec(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
Expand Down Expand Up @@ -460,7 +532,7 @@ pub(crate) unsafe fn futex(
// See the comments in `rustix_clock_gettime_via_syscall` about
// emulation.
if err == io::Errno::NOSYS {
futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
futex_old_timespec(uaddr, op, flags, val, utime, uaddr2, val3)
} else {
Err(err)
}
Expand Down Expand Up @@ -500,7 +572,7 @@ pub(crate) unsafe fn futex(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
unsafe fn futex_old(
unsafe fn futex_old_timespec(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
Expand All @@ -520,15 +592,22 @@ unsafe fn futex_old(
) via SYS_futex -> c::c_long
}

let old_utime = linux_raw_sys::general::__kernel_old_timespec {
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
let old_utime = if utime.is_null() {
None
} else {
Some(linux_raw_sys::general::__kernel_old_timespec {
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
})
};
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
&old_utime,
old_utime
.as_ref()
.map(|r| r as *const linux_raw_sys::general::__kernel_old_timespec)
.unwrap_or(core::ptr::null()),
uaddr2,
val3,
) as isize)
Expand Down
4 changes: 2 additions & 2 deletions src/backend/linux_raw/thread/futex.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
bitflags::bitflags! {
/// `FUTEX_*` flags for use with [`futex`].
///
/// [`futex`]: crate::thread::futex
/// [`futex`]: mod@crate::thread::futex
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FutexFlags: u32 {
Expand All @@ -18,7 +18,7 @@ bitflags::bitflags! {

/// `FUTEX_*` operations for use with [`futex`].
///
/// [`futex`]: crate::thread::futex
/// [`futex`]: mod@crate::thread::futex
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub enum FutexOperation {
Expand Down
61 changes: 53 additions & 8 deletions src/backend/linux_raw/thread/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,47 @@ pub(crate) fn gettid() -> Pid {
}
}

// TODO: This could be de-multiplexed.
#[inline]
pub(crate) unsafe fn futex(
pub(crate) unsafe fn futex_val2(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
val: u32,
val2: u32,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
// the least significant four bytes of the timeout pointer are used as `val2`.
// ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html),
// so we perform that exact conversion in reverse to create the pointer.
let utime = val2 as usize as *const Timespec;

#[cfg(target_pointer_width = "32")]
{
ret_usize(syscall!(
__NR_futex_time64,
uaddr,
(op, flags),
c_uint(val),
utime,
uaddr2,
c_uint(val3)
))
}
#[cfg(target_pointer_width = "64")]
ret_usize(syscall!(
__NR_futex,
uaddr,
(op, flags),
c_uint(val),
utime,
uaddr2,
c_uint(val3)
))
}

#[inline]
pub(crate) unsafe fn futex_timespec(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
Expand All @@ -231,7 +269,7 @@ pub(crate) unsafe fn futex(
// See the comments in `rustix_clock_gettime_via_syscall` about
// emulation.
if err == io::Errno::NOSYS {
futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
futex_old_timespec(uaddr, op, flags, val, utime, uaddr2, val3)
} else {
Err(err)
}
Expand All @@ -250,7 +288,7 @@ pub(crate) unsafe fn futex(
}

#[cfg(target_pointer_width = "32")]
unsafe fn futex_old(
unsafe fn futex_old_timespec(
uaddr: *const AtomicU32,
op: FutexOperation,
flags: FutexFlags,
Expand All @@ -259,16 +297,23 @@ unsafe fn futex_old(
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
let old_utime = __kernel_old_timespec {
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
let old_utime = if utime.is_null() {
None
} else {
Some(__kernel_old_timespec {
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
})
};
ret_usize(syscall!(
__NR_futex,
uaddr,
(op, flags),
c_uint(val),
by_ref(&old_utime),
old_utime
.as_ref()
.map(|r| r as *const __kernel_old_timespec)
.unwrap_or(core::ptr::null()),
uaddr2,
c_uint(val3)
))
Expand Down
Loading

0 comments on commit 6012fad

Please sign in to comment.