From: "Elias Huhsovitz" <e.huhsovitz@proxmox.com>
To: "Dominik Csapak" <d.csapak@proxmox.com>, <pve-devel@lists.proxmox.com>
Subject: Re: [PATCH qemu-server-rs v3 1/3] cpu: add pve-qemu-server-cpu crate for vCPU binding
Date: Tue, 03 Nov 2026 15:00:15 +0100 [thread overview]
Message-ID: <DMLMOYPNVOYJ.1MDINFF1K1G9O@proxmox.com> (raw)
In-Reply-To: <d4d06bac-6d5d-4bdd-9db7-da06b52303e5@proxmox.com>
On Fri Oct 30, 2026 at 2:46 PM CET, Dominik Csapak wrote:
> high level:
>
> I'd like to see more comments for methods to explain why they do what
> they do, or if they are complex a short summary of what they do.
>
> some comments inline
>
> On 10/22/26 11:23 AM, Elias Huhsovitz wrote:
>> Add a new library crate that implements CPU binding on top of libhwloc.
>> Two public entry points are exposed:
>>
>> * validate_binding(mode, sockets, cores, cfg) - pure pre-start
>> validation. Checks cfg syntax, host CPU existence, vCPU range, and
>> NUMA node existence against the local topology. No side effects.
>>
>> * bind_vcpus(mode, cfg, cpuinfo_json) - applies the binding to a running
>> QEMU process. Consumes the query-cpus-fast QMP response and binds each
>> vCPU thread by kernel TID.
>>
>> Five modes are implemented:
>> * none: no binding
>> * manual: explicit vCPU:hostCPU mapping
>> * one-to-one: one vCPU per physical core, SMT stripped
>> * numa: per-vNUMA-node set-to-set binding
>> * numa-manual: explicit vNUMA->host NUMA mapping
>>
>> hwloc is accessed via a small FFI subset (src/bindings.rs).
>>
>> Thread binding uses hwloc_linux_set_tid_cpubind, which accepts a kernel
>> TID directly. The TID used is reported by qemu via query-cpus-fast.
>>
>> Signed-off-by: Elias Huhsovitz <e.huhsovitz@proxmox.com>
>> ---
>> Cargo.toml | 2 +-
>> build.sh | 2 +-
>> pve-qemu-server-cpu/Cargo.toml | 16 +
>> pve-qemu-server-cpu/debian/changelog | 5 +
>> pve-qemu-server-cpu/debian/control | 41 ++
>> pve-qemu-server-cpu/debian/copyright | 18 +
>> pve-qemu-server-cpu/debian/debcargo.toml | 9 +
>> pve-qemu-server-cpu/src/bind.rs | 478 ++++++++++++++++++++++
>> pve-qemu-server-cpu/src/error.rs | 38 ++
>> pve-qemu-server-cpu/src/hwloc/bindings.rs | 171 ++++++++
>> pve-qemu-server-cpu/src/hwloc/mod.rs | 222 ++++++++++
>> pve-qemu-server-cpu/src/lib.rs | 92 +++++
>> pve-qemu-server-cpu/src/qmp_cpu.rs | 99 +++++
>> 13 files changed, 1191 insertions(+), 2 deletions(-)
>> create mode 100644 pve-qemu-server-cpu/Cargo.toml
>> create mode 100644 pve-qemu-server-cpu/debian/changelog
>> create mode 100644 pve-qemu-server-cpu/debian/control
>> create mode 100644 pve-qemu-server-cpu/debian/copyright
>> create mode 100644 pve-qemu-server-cpu/debian/debcargo.toml
>> create mode 100644 pve-qemu-server-cpu/src/bind.rs
>> create mode 100644 pve-qemu-server-cpu/src/error.rs
>> create mode 100644 pve-qemu-server-cpu/src/hwloc/bindings.rs
>> create mode 100644 pve-qemu-server-cpu/src/hwloc/mod.rs
>> create mode 100644 pve-qemu-server-cpu/src/lib.rs
>> create mode 100644 pve-qemu-server-cpu/src/qmp_cpu.rs
>>
>> diff --git a/Cargo.toml b/Cargo.toml
>> index 71bc8e5..c100e5a 100644
>> --- a/Cargo.toml
>> +++ b/Cargo.toml
>> @@ -1,5 +1,6 @@
>> [workspace]
>> members = [
>> + "pve-qemu-server-cpu",
>> "pve-qemu-server-pci"
>> ]
>> exclude = [
>> @@ -26,4 +27,3 @@ strum = { version = "0.26", features = ["derive"] }
>>
>> # proxmox dependencies
>> pve-api-types = "8.1"
>> -
>> diff --git a/build.sh b/build.sh
>> index 3c2293b..c3ee7b6 100755
>> --- a/build.sh
>> +++ b/build.sh
>> @@ -28,7 +28,7 @@ debcargo package \
>> --no-overlay-write-back \
>> --directory "$PWD/"${BUILDDIR}"/${CRATE}" \
>> "${CRATE}" \
>> - "$(dpkg-parsechangelog -l "${CRATE}/debian/changelog" -SVersion | sed -e 's/-.*//')"
>> + "$(dpkg-parsechangelog -l "${CRATE}/debian/changelog" -SVersion | sed -e 's/-.*//' -e 's/~/-/')"
>>
>> cd "${BUILDDIR}/${CRATE}"
>> rm -f debian/source/format.debcargo.hint
>> diff --git a/pve-qemu-server-cpu/Cargo.toml b/pve-qemu-server-cpu/Cargo.toml
>> new file mode 100644
>> index 0000000..b236796
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/Cargo.toml
>> @@ -0,0 +1,16 @@
>> +[package]
>> +name = "pve-qemu-server-cpu"
>> +version = "0.1.0"
>> +authors.workspace = true
>> +edition.workspace = true
>> +license.workspace = true
>> +repository.workspace = true
>> +homepage.workspace = true
>> +exclude.workspace = true
>> +rust-version.workspace = true
>> +
>> +[dependencies]
>> +serde = { version = "1", features = ["derive"] }
>> +serde_json = "1"
>> +thiserror = "2"
>> +libc = "0.2"
>> diff --git a/pve-qemu-server-cpu/debian/changelog b/pve-qemu-server-cpu/debian/changelog
>> new file mode 100644
>> index 0000000..3731cfd
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/debian/changelog
>> @@ -0,0 +1,5 @@
>> +rust-pve-qemu-server-cpu (0.1.0) trixie; urgency=medium
>> +
>> + * initial release
>> +
>> + -- Proxmox Support Team <support@proxmox.com> Tue, 01 Sep 2026 14:02:25 +0200
>> diff --git a/pve-qemu-server-cpu/debian/control b/pve-qemu-server-cpu/debian/control
>> new file mode 100644
>> index 0000000..1298d61
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/debian/control
>> @@ -0,0 +1,41 @@
>> +Source: rust-pve-qemu-server-cpu
>> +Section: rust
>> +Priority: optional
>> +Build-Depends: debhelper-compat (= 13),
>> + dh-sequence-cargo,
>> + libhwloc-dev (>= 2.12)
>> +Build-Depends-Arch: cargo:native <!nocheck>,
>> + rustc:native (>= 1.94) <!nocheck>,
>> + libstd-rust-dev <!nocheck>,
>> + librust-libc-0.2+default-dev <!nocheck>,
>> + librust-serde-1+default-dev <!nocheck>,
>> + librust-serde-1+derive-dev <!nocheck>,
>> + librust-serde-json-1+default-dev <!nocheck>,
>> + librust-thiserror-2+default-dev <!nocheck>
>> +Maintainer: Proxmox Support Team <support@proxmox.com>
>> +Standards-Version: 4.7.2
>> +Vcs-Git: git://git.proxmox.com/git/pve-qemu-server-rs.git
>> +Vcs-Browser: https://git.proxmox.com/?p=pve-qemu-server-rs.git
>> +Homepage: https://proxmox.com
>> +X-Cargo-Crate: pve-qemu-server-cpu
>> +
>> +Package: librust-pve-qemu-server-cpu-dev
>> +Architecture: any
>> +Multi-Arch: same
>> +Depends:
>> + ${misc:Depends},
>> + librust-libc-0.2+default-dev,
>> + librust-serde-1+default-dev,
>> + librust-serde-1+derive-dev,
>> + librust-serde-json-1+default-dev,
>> + librust-thiserror-2+default-dev
>> +Provides:
>> + librust-pve-qemu-server-cpu+default-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0+default-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0.1-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0.1+default-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0.1.0-dev (= ${binary:Version}),
>> + librust-pve-qemu-server-cpu-0.1.0+default-dev (= ${binary:Version})
>> +Description: Rust crate "pve-qemu-server-cpu" - Rust source code
>> + Source code for Debianized Rust crate "pve-qemu-server-cpu"
>> diff --git a/pve-qemu-server-cpu/debian/copyright b/pve-qemu-server-cpu/debian/copyright
>> new file mode 100644
>> index 0000000..01138fa
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/debian/copyright
>> @@ -0,0 +1,18 @@
>> +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
>> +
>> +Files:
>> + *
>> +Copyright: 2026 Proxmox Server Solutions GmbH <support@proxmox.com>
>> +License: AGPL-3.0-or-later
>> + This program is free software: you can redistribute it and/or modify it under
>> + the terms of the GNU Affero General Public License as published by the Free
>> + Software Foundation, either version 3 of the License, or (at your option) any
>> + later version.
>> + .
>> + This program is distributed in the hope that it will be useful, but WITHOUT
>> + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
>> + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
>> + details.
>> + .
>> + You should have received a copy of the GNU Affero General Public License along
>> + with this program. If not, see <https://www.gnu.org/licenses/>.
>> diff --git a/pve-qemu-server-cpu/debian/debcargo.toml b/pve-qemu-server-cpu/debian/debcargo.toml
>> new file mode 100644
>> index 0000000..23feea4
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/debian/debcargo.toml
>> @@ -0,0 +1,9 @@
>> +overlay = "."
>> +crate_src_path = ".."
>> +maintainer = "Proxmox Support Team <support@proxmox.com>"
>> +allow_prerelease_deps = true
>> +
>> +[source]
>> +vcs_git = "git://git.proxmox.com/git/pve-qemu-server-rs.git"
>> +vcs_browser = "https://git.proxmox.com/?p=pve-qemu-server-rs.git"
>> +build_depends = ["libhwloc-dev (>= 2.12)"]
>> diff --git a/pve-qemu-server-cpu/src/bind.rs b/pve-qemu-server-cpu/src/bind.rs
>> new file mode 100644
>> index 0000000..4f2927d
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/src/bind.rs
>> @@ -0,0 +1,478 @@
>> +use std::collections::HashSet;
>> +
>> +use crate::hwloc::{self, CpuSet, Topology};
>> +use crate::qmp_cpu::QmpCpu;
>> +use crate::BindError;
>> +
>> +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
>> +struct Cpu(u32);
>> +
>> +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
>> +struct NumaNodeId(u32);
>> +
>> +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
>> +struct Vcpu(u32);
>
> for these i think it would make sense to implement
> deref too, so we can simply use it as their inner values?
>
>> +
True, i forgot about this, thanks!
>> +#[derive(Debug, Clone, PartialEq, Eq, Hash)]
>> +struct VnumaNode {
>> + id: NumaNodeId,
>> + cpus: Vec<Vcpu>,
>
>
> wouldn't it make more sense for this to be a hashset? otherwise
> i could have duplicates in the list?
> AFAICS the only way we use it is by iterating, so a hashset
> makes more sense imo
>
>
I wanted to preserve the order. I was also thinking about using a
BTreeSet. But I guess a HashSet will also do.
>> +}
>> +
>> +#[derive(Debug, Clone)]
>> +struct BindRule {
>> + vcpus: Vec<Vcpu>,
>
> same here
>
>> + cpuset: CpuSet,
>> +}
>> +
>> +#[derive(Debug, Clone)]
>> +struct NumaBindRule {
>> + vnode: VnumaNode,
>> + host_node: NumaNodeId,
>> +}
>> +
>> +impl From<u32> for Cpu {
>> + fn from(v: u32) -> Self {
>> + Self(v)
>> + }
>> +}
>> +impl From<u32> for Vcpu {
>> + fn from(v: u32) -> Self {
>> + Self(v)
>> + }
>> +}
>> +impl From<u32> for NumaNodeId {
>> + fn from(v: u32) -> Self {
>> + Self(v)
>> + }
>> +}
>> +
>> +trait BindableRule {
>> + fn vcpus(&self) -> &[Vcpu];
>> + fn resolve_cpuset(&self, topo: &Topology) -> Result<CpuSet, BindError>;
>> +}
>
> i'd really like to see some documentation for these here. What does a
> 'bindablerule' do? and what should it resolve to?
>
>> +
>> +impl BindableRule for BindRule {
>> + fn vcpus(&self) -> &[Vcpu] {
>> + &self.vcpus
>> + }
>> + fn resolve_cpuset(&self, _topo: &Topology) -> Result<CpuSet, BindError> {
>> + Ok(self.cpuset.clone())
>> + }
>> +}
>> +
>> +impl BindableRule for NumaBindRule {
>> + fn vcpus(&self) -> &[Vcpu] {
>> + &self.vnode.cpus
>> + }
>> + fn resolve_cpuset(&self, topo: &Topology) -> Result<CpuSet, BindError> {
>> + hwloc::numa_node_cpuset(topo, self.host_node.0).ok_or(BindError::UnknownHostCpu(self.host_node.0))
>> + }
>> +}
>
> instead of having a trait here, having an
>
> enum BindRule {
> Regular { .. },
> Numa { .. }
> }
>
> would make the code more readable i think and forgo the
> generic functions.
>
> I don't forsee many more rule types and since the trait is not
> public, others can't implment it anyway.
>
Noted, I will give it a try. I might have just done too much OOP
recently...
>> +
>> +fn bind(topo: &Topology, vcpu: &Vcpu, tid: u32, set: &CpuSet) -> Result<(), BindError> {
>> + // Note: DO NOT singlify here — we want the scheduler to be free to move
>> + // the vCPU within the specified set.
>> + hwloc::bind_thread(topo, tid as i32, set).map_err(|message| BindError::Bind {
>> + vcpu: vcpu.0,
>> + message,
>> + })
>> +}
>> +
>> +fn bind_rules<R: BindableRule>(
>> + topo: &Topology,
>> + rules: &[R],
>> + qmp_cpuinfo: &[QmpCpu],
>> +) -> Result<(), BindError> {
>> + for rule in rules {
>> + let set = rule.resolve_cpuset(topo)?;
>> + for vcpu in rule.vcpus() {
>> + let tid = qmp_cpuinfo
>> + .get(vcpu.0 as usize)
>> + .ok_or(BindError::ThreadNotFound(vcpu.0))?
>> + .thread_id;
>> + bind(topo, vcpu, tid, &set)?;
>> + }
>> + }
>> + Ok(())
>> +}
>> +
>> +pub enum Mode {
>> + None,
>> + Manual,
>> + OneToOne,
>> + Numa,
>> + NumaManual,
>> +}
>> +
>> +impl std::str::FromStr for Mode {
>> + type Err = BindError;
>> + fn from_str(s: &str) -> Result<Self, Self::Err> {
>> + match s {
>> + "none" => Ok(Mode::None),
>> + "manual" => Ok(Mode::Manual),
>> + "one-to-one" => Ok(Mode::OneToOne),
>> + "numa" => Ok(Mode::Numa),
>> + "numa-manual" => Ok(Mode::NumaManual),
>> + other => Err(BindError::BadSyntax(format!("unknown cpu-bind-mode '{other}'"))),
>> + }
>> + }
>> +}
>> +
>> +fn to_cpuset(cpus: &[u32]) -> CpuSet {
>> + let mut set = CpuSet::new();
>> + for &c in cpus {
>> + set.set(c);
>> + }
>> + set
>> +}
>> +
>
> this could be a `From<&[u32]>` implementation on CpuSet
> (or simply have it inline in the one place it's needed)
>
Seems sensible.
>> +fn parse_cpu_binding_map(map: &str) -> Result<Vec<BindRule>, BindError> {
>> + if map.is_empty() {
>> + return Err(BindError::BadSyntax("binding map is empty".into()));
>> + }
>> +
>> + let mut rules = Vec::new();
>> + for entry in map.split(';') {
>> + let (vcpu_side, host_side) = entry
>> + .split_once(':')
>> + .ok_or_else(|| BindError::MissingColon(entry.to_string()))?;
>> +
>> + let vcpus: Vec<Vcpu> = parse_side(vcpu_side)?.into_iter().map(Vcpu::from).collect();
>> + let cpuset = to_cpuset(&parse_side(host_side)?);
>> +
>> + rules.push(BindRule { vcpus, cpuset });
>> + }
>> + Ok(rules)
>> +}
>> +
>> +fn parse_side(s: &str) -> Result<Vec<u32>, BindError> {
>> + if let Some(inner) = s.strip_prefix('{').and_then(|r| r.strip_suffix('}')) {
>> + let mut out = Vec::new();
>> + for part in inner.split(',') {
>> + out.extend(parse_element(part)?);
>> + }
>> + Ok(out)
>> + } else {
>> + if s.contains(',') {
>> + return Err(BindError::BadSyntax(format!(
>> + "unions require braces: write '{{0,1}}', not '{s}'"
>> + )));
>> + }
>> + parse_element(s)
>> + }
>> +}
>> +
>> +fn parse_element(s: &str) -> Result<Vec<u32>, BindError> {
>> + if let Some((a, b)) = s.split_once('-') {
>> + let a: u32 = a
>> + .parse()
>> + .map_err(|_| BindError::BadSyntax(format!("bad index in '{s}'")))?;
>> + let b: u32 = b
>> + .parse()
>> + .map_err(|_| BindError::BadSyntax(format!("bad index in '{s}'")))?;
>> + if a > b {
>> + return Err(BindError::BadSyntax(format!("reversed range '{s}'")));
>> + }
>> + Ok((a..=b).collect())
>
> this seems excessive? a config like 0-40000000 allocates a bunch of
> memory for saving a list of vcpus?
>
> we should either limit these upfront somewhere, or just saving a sparse
> list/range/etc.
>
This will probably be refactored now anyways, since I chaning the config
to primarily respect the existing affinity & numa[n] settings.
>> + } else {
>> + Ok(vec![s
>> + .parse()
>> + .map_err(|_| BindError::BadSyntax(format!("bad index '{s}'")))?])
>> + }
>> +}
>> +
>> +fn parse_index_list<T: From<u32>>(s: &str) -> Result<Vec<T>, BindError> {
>> + if s.is_empty() {
>> + return Ok(Vec::new());
>> + }
>> + let mut out = Vec::new();
>> + for part in s.split(';') {
>> + out.extend(parse_element(part)?.into_iter().map(T::from));
>> + }
>> + Ok(out)
>> +}
>> +
>> +fn parse_numa_binding_map(map: &str) -> Result<Vec<(NumaNodeId, NumaNodeId)>, BindError> {
>> + if map.is_empty() {
>> + return Err(BindError::BadSyntax("numa binding map is empty".into()));
>> + }
>> + let mut pairs = Vec::new();
>> + for entry in map.split(';') {
>> + let entry = entry.trim();
>> + let (v, h) = entry
>> + .split_once(':')
>> + .ok_or_else(|| BindError::MissingColon(entry.to_string()))?;
>> + let vnode: u32 = v
>> + .trim()
>> + .parse()
>> + .map_err(|_| BindError::BadSyntax(format!("bad vNUMA node in '{entry}'")))?;
>> + let hnode: u32 = h
>> + .trim()
>> + .parse()
>> + .map_err(|_| BindError::BadSyntax(format!("bad host NUMA node in '{entry}'")))?;
>> + pairs.push((NumaNodeId(vnode), NumaNodeId(hnode)));
>> + }
>> + Ok(pairs)
>> +}
>> +
>> +fn collect_vnuma_nodes(qmp_cpuinfo: &[QmpCpu]) -> Vec<VnumaNode> {
>> + let max_node = qmp_cpuinfo
>> + .iter()
>> + .map(|c| c.props.node_id)
>> + .max()
>> + .unwrap_or(0);
>> + let mut buckets: Vec<Vec<Vcpu>> = vec![Vec::new(); (max_node + 1) as usize];
>> + for cpu in qmp_cpuinfo {
>> + buckets[cpu.props.node_id as usize].push(Vcpu::from(cpu.cpu_index));
>> + }
>> + buckets
>> + .into_iter()
>> + .enumerate()
>> + .map(|(id, cpus)| VnumaNode {
>> + id: NumaNodeId(id as u32),
>> + cpus,
>> + })
>> + .collect()
>> +}
>
> couldn't this be also inferred from the config instead from the qmp
> output? I guess it does not make much difference, but I'd like
> to use the config as 'authority' since the actual vm should (must?)
> follow the config anyway, otherwise we'd have a bug somewhere....
>
I guess it can. But i think using the qmp output is more consistent. I
had the following problem in mind:
If the user configures something like:
numa0 => 'cpus=0-1,hostnodes=0,memory=2048,policy=bind',
numa1 => 'cpus=2-3,hostnodes=1,memory=2048,policy=bind',
We can easily associate the vCPUs with the vNUMA nodes.
But i was under operating under the assumption that the following is
also a valid config (omitting the cpus option):
numa0 => 'hostnodes=0,memory=2048,policy=bind',
numa1 => 'hostnodes=1,memory=2048,policy=bind',
Since, in theory we have all of the information we need to bind numa0:
hostnode0, numa1:hostnode1 from the qmp "query-cpus-fast" command.
Otherwise the user must set the cpus here, which basically brings us
back to implementing a "manual" mode.
> [snip]
>
>> diff --git a/pve-qemu-server-cpu/src/error.rs b/pve-qemu-server-cpu/src/error.rs
>> new file mode 100644
>> index 0000000..73a569e
>> --- /dev/null
>> +++ b/pve-qemu-server-cpu/src/error.rs
>> @@ -0,0 +1,38 @@
>> +use thiserror::Error;
>> +
>> +#[derive(Debug, Error)]
>> +pub enum BindError {
>> + // Parse errors
>> + #[error("map entry '{0}' is missing a ':' separator")]
>> + MissingColon(String),
>> +
>> + #[error("invalid syntax in binding map: '{0}'")]
>> + BadSyntax(String),
>> +
>> + // Validation errors (pre-start)
>> + #[error("host CPU {0} does not exist in this topology")]
>> + UnknownHostCpu(u32),
>> +
>> + #[error("vCPU {0} appears in more than one map entry")]
>> + VcpuReused(u32),
>> +
>> + #[error("vCPU {vcpu} is out of range (VM has vCPUs 0..{max})")]
>> + VcpuOutOfRange { vcpu: u32, max: u32 },
>> +
>> + // Runtime errors (post-start)
>> + #[error("no running thread found for vCPU {0}")]
>> + ThreadNotFound(u32),
>> +
>> + #[error("invalid kernel TID {tid} for vCPU {vcpu}")]
>> + BadThreadId { vcpu: u32, tid: u32 },
>> +
>> + #[error("failed to init host topology: '{0}'")]
>> + TopologyInit(String),
>> +
>> + #[error("binding vCPU {vcpu} failed: {message}")]
>> + Bind {
>> + vcpu: u32,
>> + /// Human-readable error from libhwloc (errno-based).
>> + message: String,
>> + },
>> +}
>
> i we'd reuse the numaX binding modes, most of these would not be
> necessary (as well as the parsing etc.)
>
I will change it to utilize the existing numa[n] settings.
> in general if we'd restrict ourselves (for the moment) on the basic
> modes + using affinity to limit the set further.
>
> the amount of information we'd need from sysfs would
> be a numa node -> vcpu list sysfs read
>
> everything else (no smt sibling, only 'big' cores, etc.) we could
> do via the affinity and the vcpu -> hostnuma node manual pinning
> we could do via numaX
I will focus on the basic pinning for now, but i will try to make
the code easily extensible if we should need stuff like manual-pinning,
SMT sibling filtering, etc in the future.
>
> the manual mode is not really necessary since a user can simply do
> it themselves if they want to.
next prev parent reply other threads:[~2026-11-03 14:00 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 9:22 [PATCH proxmox-perl-rs/qemu-server/qemu-server-rs v3 0/3] vCPU binding for qemu guests Elias Huhsovitz
2026-10-22 9:22 ` [PATCH qemu-server-rs v3 1/3] cpu: add pve-qemu-server-cpu crate for vCPU binding Elias Huhsovitz
2026-10-30 13:46 ` Dominik Csapak
2026-11-03 14:00 ` Elias Huhsovitz [this message]
2026-11-03 14:32 ` Dominik Csapak
2026-11-04 10:25 ` Elias Huhsovitz
2026-10-22 9:22 ` [PATCH proxmox-perl-rs v3 2/3] pve: cpu: add PVE::RS::CPU::Bind perl bindings Elias Huhsovitz
2026-10-22 9:22 ` [PATCH qemu-server v3 3/3] cpu: add CPU binding Elias Huhsovitz
2026-10-30 13:46 ` Dominik Csapak
2026-10-30 13:46 ` [PATCH proxmox-perl-rs/qemu-server/qemu-server-rs v3 0/3] vCPU binding for qemu guests Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DMLMOYPNVOYJ.1MDINFF1K1G9O@proxmox.com \
--to=e.huhsovitz@proxmox.com \
--cc=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.