Skip to content

Commit e065e27

Browse files
authored
Merge pull request #4287 from LorrensP-2158466/freebsd-num-cpus
implement cpuset_getaffinity for freebsd
2 parents febe988 + 5dfcb12 commit e065e27

File tree

3 files changed

+117
-2
lines changed

3 files changed

+117
-2
lines changed

src/tools/miri/ci/ci.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ case $HOST_TARGET in
165165
# Partially supported targets (tier 2)
166166
BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
167167
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
168-
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe
169-
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe
168+
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe affinity
169+
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe affinity
170170
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd
171171
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
172172
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std

src/tools/miri/src/shims/unix/freebsd/foreign_items.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,70 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5656
this.write_scalar(res, dest)?;
5757
}
5858

59+
"cpuset_getaffinity" => {
60+
// The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically.
61+
let [level, which, id, set_size, mask] =
62+
this.check_shim(abi, Conv::C, link_name, args)?;
63+
64+
let level = this.read_scalar(level)?.to_i32()?;
65+
let which = this.read_scalar(which)?.to_i32()?;
66+
let id = this.read_scalar(id)?.to_i64()?;
67+
let set_size = this.read_target_usize(set_size)?; // measured in bytes
68+
let mask = this.read_pointer(mask)?;
69+
70+
let _level_root = this.eval_libc_i32("CPU_LEVEL_ROOT");
71+
let _level_cpuset = this.eval_libc_i32("CPU_LEVEL_CPUSET");
72+
let level_which = this.eval_libc_i32("CPU_LEVEL_WHICH");
73+
74+
let _which_tid = this.eval_libc_i32("CPU_WHICH_TID");
75+
let which_pid = this.eval_libc_i32("CPU_WHICH_PID");
76+
let _which_jail = this.eval_libc_i32("CPU_WHICH_JAIL");
77+
let _which_cpuset = this.eval_libc_i32("CPU_WHICH_CPUSET");
78+
let _which_irq = this.eval_libc_i32("CPU_WHICH_IRQ");
79+
80+
// For sched_getaffinity, the current process is identified by -1.
81+
// TODO: Use gettid? I'm (LorrensP-2158466) not that familiar with this api .
82+
let id = match id {
83+
-1 => this.active_thread(),
84+
_ =>
85+
throw_unsup_format!(
86+
"`cpuset_getaffinity` is only supported with a pid of -1 (indicating the current thread)"
87+
),
88+
};
89+
90+
if this.ptr_is_null(mask)? {
91+
this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
92+
}
93+
// We only support CPU_LEVEL_WHICH and CPU_WHICH_PID for now.
94+
// This is the bare minimum to make the tests pass.
95+
else if level != level_which || which != which_pid {
96+
throw_unsup_format!(
97+
"`cpuset_getaffinity` is only supported with `level` set to CPU_LEVEL_WHICH and `which` set to CPU_WHICH_PID."
98+
);
99+
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&id) {
100+
// `cpusetsize` must be large enough to contain the entire CPU mask.
101+
// FreeBSD only uses `cpusetsize` to verify that it's sufficient for the kernel's CPU mask.
102+
// If it's too small, the syscall returns ERANGE.
103+
// If it's large enough, copying the kernel mask to user space is safe, regardless of the actual size.
104+
// See https://github.com/freebsd/freebsd-src/blob/909aa6781340f8c0b4ae01c6366bf1556ee2d1be/sys/kern/kern_cpuset.c#L1985
105+
if set_size < u64::from(this.machine.num_cpus).div_ceil(8) {
106+
this.set_last_error_and_return(LibcError("ERANGE"), dest)?;
107+
} else {
108+
let cpuset = cpuset.clone();
109+
let byte_count =
110+
Ord::min(cpuset.as_slice().len(), set_size.try_into().unwrap());
111+
this.write_bytes_ptr(
112+
mask,
113+
cpuset.as_slice()[..byte_count].iter().copied(),
114+
)?;
115+
this.write_null(dest)?;
116+
}
117+
} else {
118+
// `id` is always that of the active thread, so this is currently unreachable.
119+
unreachable!();
120+
}
121+
}
122+
59123
// Synchronization primitives
60124
"_umtx_op" => {
61125
let [obj, op, val, uaddr, uaddr2] =
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//@only-target: freebsd
2+
//@compile-flags: -Zmiri-num-cpus=256
3+
4+
use std::mem;
5+
6+
fn getaffinity() {
7+
let mut set: libc::cpuset_t = unsafe { mem::zeroed() };
8+
unsafe {
9+
if libc::cpuset_getaffinity(
10+
libc::CPU_LEVEL_WHICH,
11+
libc::CPU_WHICH_PID,
12+
-1,
13+
size_of::<libc::cpuset_t>(),
14+
&mut set,
15+
) == 0
16+
{
17+
assert!(libc::CPU_COUNT(&set) == 256);
18+
}
19+
}
20+
}
21+
22+
fn get_small_cpu_mask() {
23+
let mut set: libc::cpuset_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
24+
25+
// 256 CPUs so we need 32 bytes to represent this mask.
26+
// According to Freebsd only when `cpusetsize` is smaller than this value, does it return with ERANGE
27+
28+
let err = unsafe {
29+
libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 32, &mut set)
30+
};
31+
assert_eq!(err, 0, "Success Expected");
32+
33+
// 31 is not enough, so it should fail.
34+
let err = unsafe {
35+
libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 31, &mut set)
36+
};
37+
assert_eq!(err, -1, "Expected Failure");
38+
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE);
39+
40+
// Zero should fail as well.
41+
let err = unsafe {
42+
libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 0, &mut set)
43+
};
44+
assert_eq!(err, -1, "Expected Failure");
45+
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE);
46+
}
47+
48+
fn main() {
49+
getaffinity();
50+
get_small_cpu_mask();
51+
}

0 commit comments

Comments
 (0)