diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 00102bb26778..3a9e55f5738d 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -165,8 +165,8 @@ case $HOST_TARGET in # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe - TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe affinity + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe affinity TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index c0872daee8d6..533a741fea3f 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -56,6 +56,70 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } + "cpuset_getaffinity" => { + // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically. + let [level, which, id, set_size, mask] = + this.check_shim(abi, Conv::C, link_name, args)?; + + let level = this.read_scalar(level)?.to_i32()?; + let which = this.read_scalar(which)?.to_i32()?; + let id = this.read_scalar(id)?.to_i64()?; + let set_size = this.read_target_usize(set_size)?; // measured in bytes + let mask = this.read_pointer(mask)?; + + let _level_root = this.eval_libc_i32("CPU_LEVEL_ROOT"); + let _level_cpuset = this.eval_libc_i32("CPU_LEVEL_CPUSET"); + let level_which = this.eval_libc_i32("CPU_LEVEL_WHICH"); + + let _which_tid = this.eval_libc_i32("CPU_WHICH_TID"); + let which_pid = this.eval_libc_i32("CPU_WHICH_PID"); + let _which_jail = this.eval_libc_i32("CPU_WHICH_JAIL"); + let _which_cpuset = this.eval_libc_i32("CPU_WHICH_CPUSET"); + let _which_irq = this.eval_libc_i32("CPU_WHICH_IRQ"); + + // For sched_getaffinity, the current process is identified by -1. + // TODO: Use gettid? I'm (LorrensP-2158466) not that familiar with this api . + let id = match id { + -1 => this.active_thread(), + _ => + throw_unsup_format!( + "`cpuset_getaffinity` is only supported with a pid of -1 (indicating the current thread)" + ), + }; + + if this.ptr_is_null(mask)? { + this.set_last_error_and_return(LibcError("EFAULT"), dest)?; + } + // We only support CPU_LEVEL_WHICH and CPU_WHICH_PID for now. + // This is the bare minimum to make the tests pass. + else if level != level_which || which != which_pid { + throw_unsup_format!( + "`cpuset_getaffinity` is only supported with `level` set to CPU_LEVEL_WHICH and `which` set to CPU_WHICH_PID." + ); + } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&id) { + // `cpusetsize` must be large enough to contain the entire CPU mask. + // FreeBSD only uses `cpusetsize` to verify that it's sufficient for the kernel's CPU mask. + // If it's too small, the syscall returns ERANGE. + // If it's large enough, copying the kernel mask to user space is safe, regardless of the actual size. + // See https://github.com/freebsd/freebsd-src/blob/909aa6781340f8c0b4ae01c6366bf1556ee2d1be/sys/kern/kern_cpuset.c#L1985 + if set_size < u64::from(this.machine.num_cpus).div_ceil(8) { + this.set_last_error_and_return(LibcError("ERANGE"), dest)?; + } else { + let cpuset = cpuset.clone(); + let byte_count = + Ord::min(cpuset.as_slice().len(), set_size.try_into().unwrap()); + this.write_bytes_ptr( + mask, + cpuset.as_slice()[..byte_count].iter().copied(), + )?; + this.write_null(dest)?; + } + } else { + // `id` is always that of the active thread, so this is currently unreachable. + unreachable!(); + } + } + // Synchronization primitives "_umtx_op" => { let [obj, op, val, uaddr, uaddr2] = diff --git a/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs b/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs new file mode 100644 index 000000000000..9a868128d271 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs @@ -0,0 +1,51 @@ +//@only-target: freebsd +//@compile-flags: -Zmiri-num-cpus=256 + +use std::mem; + +fn getaffinity() { + let mut set: libc::cpuset_t = unsafe { mem::zeroed() }; + unsafe { + if libc::cpuset_getaffinity( + libc::CPU_LEVEL_WHICH, + libc::CPU_WHICH_PID, + -1, + size_of::(), + &mut set, + ) == 0 + { + assert!(libc::CPU_COUNT(&set) == 256); + } + } +} + +fn get_small_cpu_mask() { + let mut set: libc::cpuset_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; + + // 256 CPUs so we need 32 bytes to represent this mask. + // According to Freebsd only when `cpusetsize` is smaller than this value, does it return with ERANGE + + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 32, &mut set) + }; + assert_eq!(err, 0, "Success Expected"); + + // 31 is not enough, so it should fail. + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 31, &mut set) + }; + assert_eq!(err, -1, "Expected Failure"); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE); + + // Zero should fail as well. + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 0, &mut set) + }; + assert_eq!(err, -1, "Expected Failure"); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE); +} + +fn main() { + getaffinity(); + get_small_cpu_mask(); +}