Files
RedBear-OS/local/patches/relibc/P7-pthread-affinity.patch
T
vasilito 34360e1e4f feat: P0-P6 kernel scheduler + relibc threading comprehensive implementation
P0-P2: Barrier SMP, sigmask/pthread_kill races, robust mutexes, RT scheduling, POSIX sched API
P3: PerCpuSched struct, per-CPU wiring, work stealing, load balancing, initial placement
P4: 64-shard futex table, REQUEUE, PI futexes (LOCK_PI/UNLOCK_PI/TRYLOCK_PI), robust futexes, vruntime tracking, min-vruntime SCHED_OTHER selection
P5: setpriority/getpriority, pthread_setaffinity_np, pthread_setname_np, pthread_setschedparam (Redox)
P6: Cache-affine scheduling (last_cpu + vruntime bonus), NUMA topology kernel hints + numad userspace daemon

Stability fixes: make_consistent stores 0 (dead TID fix), cond.rs error propagation, SPIN_COUNT adaptive spinning, Sys::open &str fix, PI futex CAS race, proc.rs lock ordering, barrier destroy

Patches: 33 kernel + 58 relibc patches, all tracked in recipes
Docs: KERNEL-SCHEDULER-MULTITHREAD-IMPROVEMENT-PLAN.md updated, SCHEDULER-REVIEW-FINAL.md created
Architecture: NUMA topology parsing stays userspace (numad daemon), kernel stores lightweight NumaTopology hints
2026-04-30 18:21:48 +01:00

232 lines
7.1 KiB
Diff

diff --git a/src/header/pthread/cbindgen.toml b/src/header/pthread/cbindgen.toml
--- a/src/header/pthread/cbindgen.toml
+++ b/src/header/pthread/cbindgen.toml
@@ -10,0 +11 @@ cpp_compat = true
+"cpu_set_t" = "struct cpu_set_t"
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
--- a/src/header/pthread/mod.rs
+++ b/src/header/pthread/mod.rs
@@ -6 +6,8 @@ use alloc::collections::LinkedList;
-use core::{cell::Cell, ptr::NonNull};
+use core::{cell::Cell, mem::size_of, ptr::NonNull};
+
+#[cfg(target_os = "linux")]
+use sc::syscall;
+#[cfg(target_os = "redox")]
+use redox_rt::proc::FdGuard;
+#[cfg(target_os = "redox")]
+use syscall;
@@ -9,0 +17 @@ use crate::{
+ header::errno::EINVAL,
@@ -14 +22 @@ use crate::{
- 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,
@@ -22,0 +31,3 @@ use crate::{
+#[cfg(target_os = "linux")]
+use crate::platform::sys::e_raw;
+
@@ -29,0 +41,93 @@ pub fn e(result: Result<(), Errno>) -> i32 {
+const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
+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::<cpu_set_t>()).contains(&cpusetsize) {
+ return Err(Errno(EINVAL));
+ }
+
+ Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), 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::<cpu_set_t>()).contains(&cpusetsize) {
+ return Err(Errno(EINVAL));
+ }
+
+ Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
+}
+
+fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
+ 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_u64 << 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)?;
+ bytes.fill(0);
+
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
+ }
+
+ Ok(())
+}
+
+#[cfg(target_os = "redox")]
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
+ let mut kernel_cpuset = cpu_set_t::default();
+ kernel_cpuset.__bits[0] = mask;
+
+ let handle = FdGuard::new(unsafe {
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
+ });
+ let _ = handle.write(unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
+ size_of::<cpu_set_t>(),
+ )
+ })?;
+
+ Ok(())
+}
+
+#[cfg(target_os = "redox")]
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
+ let handle = FdGuard::new(unsafe {
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
+ });
+ let mut kernel_cpuset = cpu_set_t::default();
+ let _ = handle.read(unsafe {
+ core::slice::from_raw_parts_mut(
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
+ size_of::<cpu_set_t>(),
+ )
+ })?;
+
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
+ return Err(Errno(EINVAL));
+ }
+
+ Ok(kernel_cpuset.__bits[0])
+}
+
@@ -188,0 +293,36 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
+#[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: &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::<c_void>()
+ )
+ })
+ .map(|_| ())
+ }
+ }
+ };
+
+ e(result)
+}
+
@@ -237,0 +378,36 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
+#[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: &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::<c_void>()
+ )
+ })
+ .map(|_| ())
+ }
+ }
+ };
+
+ e(result)
+}
+
diff --git a/src/header/sched/cbindgen.toml b/src/header/sched/cbindgen.toml
--- a/src/header/sched/cbindgen.toml
+++ b/src/header/sched/cbindgen.toml
@@ -22,0 +23,14 @@ 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",
+]
diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs
--- a/src/header/sched/mod.rs
+++ b/src/header/sched/mod.rs
@@ -12,0 +13,2 @@
+pub const CPU_SETSIZE: usize = 1024;
+
@@ -20,0 +23,7 @@
+/// Linux-compatible CPU affinity mask storage.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default)]
+pub struct cpu_set_t {
+ pub __bits: [u64; 16],
+}
+
@@ -143,0 +153,3 @@
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_cpu_set_t(_: cpu_set_t) {}