-
Notifications
You must be signed in to change notification settings - Fork 385
implement cpuset_getaffinity for freebsd #4287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
implement cpuset_getaffinity for freebsd #4287
Conversation
So the tests use the libc function The source of this struct is this: pub struct cpuset_t {
#[cfg(all(any(freebsd15, freebsd14), target_pointer_width = "64"))]
__bits: [c_long; 16],
#[cfg(all(any(freebsd15, freebsd14), target_pointer_width = "32"))]
__bits: [c_long; 32],
#[cfg(all(not(any(freebsd15, freebsd14)), target_pointer_width = "64"))]
__bits: [c_long; 4],
#[cfg(all(not(any(freebsd15, freebsd14)), target_pointer_width = "32"))]
__bits: [c_long; 8],
} So on newer FreeBSD version, this struct is 128 bytes large, otherwise 32 bytes. During testing I have found out that the current compilation results in the 32 bytes variant. This means we can only "use" 256 CPUs since the size of the mask is: 32 * 8 = 256. Miri's maximum number is 1024 and the test also sets and expects this value. I have no idea how to alter compilation to use the 128 size variant, which would result in 1024 bits being used. But the test passes when using |
@rustbot ready |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should still test that 1024 works on platforms that support larger numbers of CPUs.
The standard library is built for compatibility with FreeBSD 12, so we'd have to wait until that gets bumped to FreeBSD 14. What happens on FreeBSD when setting num-cpus too high? We should at least fail somewhat gracefully. |
The user mask is only 32 bytes, so it would only write 32 bytes of the mask (which is 128). You can try and write 128 bytes, but then miri catches a buffer overflow. |
So I'd say make that test have two revisions, one for 256 cores and one for 1024 cores. The 256 revision should always give the right reply, and the 1024 revision will then presumably just return 256 on FreeBSD. |
Nothing is truncated per se, but the full mask that Miri holds can't be written to the user-provided mask, writing more causes UB:
Revision as in 2 test files? |
No, as in one test file with 2 revisions. Grep the test suite for
Wait, so calling this safe standard library method causes UB? That's bad. It's either a bug in Miri or a bug in the standard library. |
Alright thanks!
No, hahaha, I'm explaining this very badly. The implementation handles this correctly, either writing The correct thing to do here is this (in pseudo-code): let byte_count = Ord::min(set_size, miri_cpu_set_of_thread.byte_len());
// copy byte_count amount of bytes from miri_cpu_set_of_thread into user_mask Writing the full mask held in Miri would cause UB, because the user mask in older FreeBSD versions is much smaller. Sorry for the bad explanation :(. |
Yeah, Miri needs to clamp somewhere, agreed. That's exactly why I want to see a test of "mask too small". :)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rustbot author
// This is the bare minimum to make the tests pass. | ||
// TODO: Support more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// This is the bare minimum to make the tests pass. | |
// TODO: Support more. | |
// This is the bare minimum to make the tests pass. |
There's no point in having such a TODO in the code IMO. If we want to track this, we can have an issue.
// The thread whose ID is pid could not be found. | ||
this.set_last_error_and_return(LibcError("ESRCH"), dest)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be unreachable!()
, right? The ID is always that of the active thread, after all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going by convention, but it should be unreachable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The place where you copied this from likely supports other IDs, so it makes sense there, but it doesn't make sense to follow the same convention here.
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&id) { | ||
let cpuset = cpuset.clone(); | ||
let byte_count = set_size.try_into().unwrap(); | ||
this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this ICE Miri if byte_count
is bigger than the mask?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes indeed, the changes are local, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add a test directly calling cpuset_getaffinity
.
Reminder, once the PR becomes ready for a review, use |
Fixes #4265.
This implements the FreeBSD syscall
cpuset_getaffinity
. In practice, this syscall behaves like a more fine-grained api thansched_getaffinity
.The tests only require one specific combination of parameters to pass, so this is all that is implemented. This can be done in future work (or now if needed).
However, I had to change 1 test file
available-parallelism-miri-num-cpus
. This test sets the number of CPUs to 1024, but the libc API is structured so that only 256 can be accepted. (I'll explain in a comment.)I have not changed the ci script since I don't know what I should put there.