Skip to content

Conversation

@iximeow
Copy link
Member

@iximeow iximeow commented Dec 17, 2025

this is the interim option I'd described in #984.

add bindings for processor_bind(), the mechanism to make this happen. then, in propolis-standalone, allow a choice of how binding should be done: allow vCPUs to roam with cpu_binding = "any" or pin vCPUs to CPUs 1:1 from the last processor downwards with
cpu_binding = "from-last".

in propolis-server, pick between these two strategies automatically based on how the vCPU count compares to the number of available processors. a VM that would use more than half of available processors is bound in the same way as propolis-standalone with cpu_binding = "from-last", but otherwise continues to have unbound vCPUs.

in the future, Nexus will dictate propolis-server's vCPU bindings, but exactly how is a bit of an open question.

@leftwo leftwo added the local storage Relating to the local storage project label Dec 17, 2025
@leftwo leftwo added this to the 18 milestone Dec 17, 2025
add bindings for `processor_bind()`, the mechanism to make this happen.
then, in propolis-standalone, allow a choice of how binding should be
done: allow vCPUs to roam with `cpu_binding = "any"` or pin vCPUs to
CPUs 1:1 from the last processor downwards with
`cpu_binding = "from-last"`.

in `propolis-server`, pick between these two strategies automatically
based on how the vCPU count compares to the number of available
processors. a VM that would use more than half of available
processors is bound in the same way as `propolis-standalone` with
`cpu_binding = "from-last"`, but otherwise continues to have unbound
vCPUs.

in the future, Nexus will dictate `propolis-server`'s vCPU bindings,
but exactly how is a bit of an open question.
@@ -0,0 +1,8 @@
[package]
name = "pbind"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, since this is a low-level binding to the C headers, should this perhaps be pbind-sys?


res.try_into().map_err(|_| {
// sysconf() reports more than 2^31 processors?!
Error::other(format!("too many processors: {}", res))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turbo nit: perhaps res could be inlined here?

Suggested change
Error::other(format!("too many processors: {}", res))
Error::other(format!("too many processors: {res}"))

Comment on lines +48 to +50
// Returns an `i32` to match `processorid_t`, so that `0..online_cpus()`
// produces a range of processor IDs without additional translation needed.
pub fn online_cpus() -> Result<i32, Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe worth noting in the comment that this is a wrapper around sysconf(_SC_NPROCESSORS_ONLN)?

Comment on lines +294 to +295
let vcpu_count: i32 =
machine.vcpus.len().try_into().expect("<2^31 vCPUs");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, considered not that big a deal: this is an error in the user's config file, rather than e.g. a syscall we expect should always work not working. can we make that a little clearer perhaps?

Comment on lines +80 to +82
/// vCPUs are bound to the highest-numbered processors in the system, one
/// vCPU per CPU, with the last vCPU bound to the last physical processor.
FromLast,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...descending?

Comment on lines +591 to +600
let bind_cpus = if vcpu_count > total_cpus / 2 {
let mut bind_cpus = Vec::new();
for i in 0..vcpu_count {
// Bind to the upper range of CPUs, fairly arbitrary.
bind_cpus.push(total_cpus - vcpu_count + i);
}
Some(bind_cpus)
} else {
None
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we ought to log someplace whether we have decided to bind vCPUs or not?

Comment on lines +592 to +596
let mut bind_cpus = Vec::new();
for i in 0..vcpu_count {
// Bind to the upper range of CPUs, fairly arbitrary.
bind_cpus.push(total_cpus - vcpu_count + i);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like this could be

Suggested change
let mut bind_cpus = Vec::new();
for i in 0..vcpu_count {
// Bind to the upper range of CPUs, fairly arbitrary.
bind_cpus.push(total_cpus - vcpu_count + i);
}
((total_cpus-vcpu_count)..total_cpus).iter().collect()

or something but maybe that's weirder and worse

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

local storage Relating to the local storage project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants