diff --git a/src/header/pthread/cbindgen.toml b/src/header/pthread/cbindgen.toml index 04b8d7da80..65b433470f 100644 --- a/src/header/pthread/cbindgen.toml +++ b/src/header/pthread/cbindgen.toml @@ -8,6 +8,7 @@ cpp_compat = true [export.rename] "timespec" = "struct timespec" "sched_param" = "struct sched_param" +"cpu_set_t" = "struct cpu_set_t" [enum] prefix_with_name = true diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs index a6721cadae..a6c3801398 100644 --- a/src/header/pthread/mod.rs +++ b/src/header/pthread/mod.rs @@ -3,15 +3,26 @@ //! See . use alloc::collections::LinkedList; -use core::{cell::Cell, ptr::NonNull}; +use core::{cell::Cell, mem::size_of, ptr::NonNull}; + +#[cfg(target_os = "redox")] +use redox_rt::proc::FdGuard; +#[cfg(target_os = "linux")] +use sc::syscall; +#[cfg(target_os = "redox")] +use syscall; use crate::{ error::Errno, - header::{bits_timespec::timespec, sched::*}, + header::{ + bits_timespec::timespec, + errno::{EINVAL, ERANGE}, + sched::*, + }, platform::{ Pal, Sys, types::{ - c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t, + c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t, pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t, pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t, pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t, @@ -20,6 +31,9 @@ use crate::{ pthread, }; +#[cfg(target_os = "linux")] +use crate::platform::sys::e_raw; + pub fn e(result: Result<(), Errno>) -> i32 { match result { Ok(()) => 0, @@ -27,6 +41,100 @@ pub fn e(result: Result<(), Errno>) -> i32 { } } +const RLCT_AFFINITY_BYTES: usize = size_of::(); +const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize; + +fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> { + if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::()).contains(&cpusetsize) { + return Err(Errno(EINVAL)); + } + + Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::(), cpusetsize) }) +} + +fn cpuset_bytes_mut<'a>(cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<&'a mut [u8], Errno> { + if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::()).contains(&cpusetsize) { + return Err(Errno(EINVAL)); + } + + Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::(), cpusetsize) }) +} + +fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result { + let bytes = cpuset_bytes(cpusetsize, cpuset)?; + let mut mask = 0_u64; + + for (byte_index, byte) in bytes.iter().copied().enumerate() { + for bit in 0..u8::BITS as usize { + if byte & (1 << bit) == 0 { + continue; + } + + let cpu = byte_index * u8::BITS as usize + bit; + if cpu >= RLCT_MAX_AFFINITY_CPUS { + return Err(Errno(EINVAL)); + } + + mask |= 1 << cpu; + } + } + + Ok(mask) +} + +fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> { + let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?; + let mut written = 0_usize; + + for (byte_index, byte) in bytes.iter_mut().enumerate() { + let mut value = 0_u8; + for bit in 0..u8::BITS as usize { + let cpu = byte_index * u8::BITS as usize + bit; + if cpu >= RLCT_MAX_AFFINITY_CPUS { + break; + } + if mask & (1 << cpu) != 0 { + value |= 1 << bit; + } + } + *byte = value; + written += 1; + } + + let _ = written; + Ok(()) +} + +#[cfg(target_os = "redox")] +fn redox_get_thread_affinity(thread: &crate::pthread::Pthread) -> Result { + let os_tid = unsafe { thread.os_tid.get().read() }; + let path = alloc::format!("proc:{}/sched-affinity", os_tid.thread_fd); + let fd = Sys::open(&path, crate::header::fcntl::O_RDONLY, 0)?; + + let mut buf = [0u8; RLCT_AFFINITY_BYTES]; + let read = Sys::read(fd, &mut buf)?; + let _ = Sys::close(fd); + if read != RLCT_AFFINITY_BYTES { + return Err(Errno(EINVAL)); + } + Ok(u64::from_ne_bytes(buf)) +} + +#[cfg(target_os = "redox")] +fn redox_set_thread_affinity(thread: &crate::pthread::Pthread, mask: u64) -> Result<(), Errno> { + let os_tid = unsafe { thread.os_tid.get().read() }; + let path = alloc::format!("proc:{}/sched-affinity", os_tid.thread_fd); + let fd = Sys::open(&path, crate::header::fcntl::O_WRONLY, 0)?; + + let bytes = mask.to_ne_bytes(); + let written = Sys::write(fd, &bytes)?; + let _ = Sys::close(fd); + if written != RLCT_AFFINITY_BYTES { + return Err(Errno(EINVAL)); + } + Ok(()) +} + #[derive(Clone)] pub(crate) struct RlctAttr { pub detachstate: c_uchar, @@ -186,6 +294,43 @@ pub unsafe extern "C" fn pthread_getcpuclockid( } } +/// GNU extension. See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getaffinity_np( + thread: pthread_t, + cpusetsize: size_t, + cpuset: *mut cpu_set_t, +) -> c_int { + let thread: &crate::pthread::Pthread = unsafe { &*thread.cast() }; + + let result = { + #[cfg(target_os = "redox")] + { + redox_get_thread_affinity(thread) + .and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset)) + } + + #[cfg(target_os = "linux")] + { + if cpuset.is_null() { + Err(Errno(EINVAL)) + } else { + e_raw(unsafe { + syscall!( + SCHED_GETAFFINITY, + thread.os_tid.get().read().thread_id, + cpusetsize, + cpuset.cast::() + ) + }) + .map(|_| ()) + } + } + }; + + e(result) +} + /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_getschedparam( @@ -235,6 +380,43 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t { core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _ } +/// GNU extension. See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setaffinity_np( + thread: pthread_t, + cpusetsize: size_t, + cpuset: *const cpu_set_t, +) -> c_int { + let thread: &crate::pthread::Pthread = unsafe { &*thread.cast() }; + + let result = { + #[cfg(target_os = "redox")] + { + cpuset_to_u64(cpusetsize, cpuset) + .and_then(|mask| redox_set_thread_affinity(thread, mask)) + } + + #[cfg(target_os = "linux")] + { + if cpuset.is_null() { + Err(Errno(EINVAL)) + } else { + e_raw(unsafe { + syscall!( + SCHED_SETAFFINITY, + thread.os_tid.get().read().thread_id, + cpusetsize, + cpuset.cast::() + ) + }) + .map(|_| ()) + } + } + }; + + e(result) +} + /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int { @@ -357,3 +539,82 @@ pub(crate) unsafe fn run_destructor_stack() { (entry.routine)(entry.arg); } } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setname_np(thread: pthread_t, name: *const c_char) -> c_int { + if name.is_null() { + return EINVAL; + } + + let cstr = unsafe { core::ffi::CStr::from_ptr(name) }; + let name_bytes = cstr.to_bytes(); + let len = name_bytes.len().min(31); + + #[cfg(target_os = "redox")] + { + let thread = unsafe { &*thread.cast::() }; + let os_tid = unsafe { thread.os_tid.get().read() }; + let path = alloc::format!("proc:{}/name", os_tid.thread_fd); + let fd = match Sys::open(&path, crate::header::fcntl::O_WRONLY, 0) { + Ok(fd) => fd, + Err(Errno(code)) => return code, + }; + + let result = match Sys::write(fd, &name_bytes[..len]) { + Ok(written) if written == len => 0, + Ok(_) => crate::header::errno::EIO, + Err(Errno(code)) => code, + }; + let _ = Sys::close(fd); + result + } + #[cfg(not(target_os = "redox"))] + { + let _ = thread; + 0 + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getname_np( + thread: pthread_t, + name: *mut c_char, + len: size_t, +) -> c_int { + if name.is_null() { + return EINVAL; + } + if len == 0 { + return ERANGE; + } + + #[cfg(target_os = "redox")] + { + let thread = unsafe { &*thread.cast::() }; + let os_tid = unsafe { thread.os_tid.get().read() }; + let path = alloc::format!("proc:{}/name", os_tid.thread_fd); + let fd = match Sys::open(&path, crate::header::fcntl::O_RDONLY, 0) { + Ok(fd) => fd, + Err(Errno(code)) => return code, + }; + + let mut buf = [0u8; 31]; + let result = match Sys::read(fd, &mut buf) { + Ok(read) if read < len => { + unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), name.cast(), read) }; + unsafe { *name.add(read) = 0 }; + 0 + } + Ok(_) => ERANGE, + Err(Errno(code)) => code, + }; + let _ = Sys::close(fd); + result + } + #[cfg(not(target_os = "redox"))] + { + let _ = thread; + unsafe { *name = 0 }; + 0 + } +} diff --git a/src/header/sched/cbindgen.toml b/src/header/sched/cbindgen.toml index 67d6f36c84..570e8f6f0b 100644 --- a/src/header/sched/cbindgen.toml +++ b/src/header/sched/cbindgen.toml @@ -18,5 +18,19 @@ cpp_compat = true [enum] prefix_with_name = true +[export] +include = [ + "sched_param", + "cpu_set_t", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getparam", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_setparam", + "sched_setscheduler", + "sched_yield", +] + [export.rename] "timespec" = "struct timespec" diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs index 6066550d5f..7219b87e14 100644 --- a/src/header/sched/mod.rs +++ b/src/header/sched/mod.rs @@ -26,6 +26,16 @@ pub const SCHED_RR: c_int = 1; /// See . pub const SCHED_OTHER: c_int = 2; +/// See . +pub const CPU_SETSIZE: usize = 1024; + +/// Linux-compatible CPU affinity mask storage. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct cpu_set_t { + pub __bits: [u64; 16], +} + /// See . #[unsafe(no_mangle)] pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int { @@ -141,3 +151,6 @@ pub extern "C" fn sched_yield() -> c_int { #[unsafe(no_mangle)] pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_sched_param(_: sched_param) {} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_cpu_set_t(_: cpu_set_t) {}