From: "Elias Huhsovitz" <e.huhsovitz@proxmox.com>
To: "Dominik Csapak" <d.csapak@proxmox.com>, <pve-devel@lists.proxmox.com>
Subject: Re: [PATCH pve-qemu-server-rs 2/9] pci: add machine abstraction and PCI bridge generation
Date: Mon, 28 Sep 2026 14:55:10 +0200 [thread overview]
Message-ID: <DLQYRIN8PHM3.9NP2SN1WA6HE@proxmox.com> (raw)
In-Reply-To: <20260922105550.2084078-3-d.csapak@proxmox.com>
Comments inline.
On Tue Sep 22, 2026 at 12:55 PM CEST, Dominik Csapak wrote:
> Besides the address of a single device, callers also need the list of
> bridge devices a guest has to be started with. Which bridges those are
> depends on more than one property of the guest config, so pass them in
> as a `Machine` struct rather than as a growing list of parameters. It
> can be extended with the layout variant and other guest wide settings
> later on, without touching every call site again.
>
> The bridge list itself follows the Perl implementation: q35 machines
> already define the bridges up to three in their machine definition,
> pci.3 is only needed for virtio-scsi-single, and pci.4 only once a
> SCSI controller above index one exists or the machine version is new
> enough to always include it.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
[snip]
> +/// Renders the bridge devices a guest needs, see
> +/// [`crate::get_pci_bridges`].
> +pub(crate) fn get_pci_bridges(machine: &Machine) -> Vec<String> {
nit: naming: If you remove the prefix from the print_ functions, i would
also remove the prefix here for consistency. i.e., `pci_bridges`.
> + // at most four bridges, each contributing a flag and its value
> + let mut res = Vec::with_capacity(8);
> +
> + // some SCSI controllers can only have 7 disks each, so scsi14 and upwards
> + // need scsihw2 and above, which live on pci.4
> + let include_pci4 = machine.version_at_least(11, 1, 0) || machine.max_scsihw > 1;
> +
> + for i in 1..=4 {
> + // q35.cfg already includes the bridges up to three
> + if i < 4 && machine.q35 {
> + continue;
> + }
> +
> + if i == 3 && !machine.virtio_scsi_single() {
> + continue;
> + }
> +
> + if i == 4 && !include_pci4 {
> + continue;
> + }
> +
> + let id = if i == 2 && machine.legacy_igd {
> + format!("pci.{i}-igd")
> + } else {
> + format!("pci.{i}")
> + };
> +
> + let addr = match print_pci_addr(&id, machine.arch) {
> + Ok(addr) => addr,
> + Err(err) => {
> + log::warn!("could not find address for bridge {id}: {err}");
> + continue;
> + }
Consider `log::error!` instead of `log::warn!`.
IMO a missing bridge is a cause for concern and should be reflected with
an error.
> + };
> + res.push("-device".to_string());
> + res.push(format!("pci-bridge,id=pci.{i},chassis_nr={i}{addr}"));
> + }
> +
> + res
> +}
> +
[snip]
> }
> diff --git a/pve-qemu-server-pci/src/lib.rs b/pve-qemu-server-pci/src/lib.rs
> index 5e8b8a7..7d0bd92 100644
> --- a/pve-qemu-server-pci/src/lib.rs
> +++ b/pve-qemu-server-pci/src/lib.rs
> @@ -10,6 +10,9 @@
>
> pub mod constants;
>
> +mod machine;
> +pub use machine::Machine;
> +
> mod types;
> use pve_api_types::ClusterResourceHostArch as Arch;
> pub use types::*;
> @@ -42,3 +45,18 @@ pub fn print_pcie_addr(id: &str) -> Result<String, PciConfigError> {
> pub fn print_pcie_root_port(index: u8) -> Result<String, PciConfigError> {
> layout::legacy::print_pcie_root_port(index)
> }
> +
> +/// Returns the number of the PCI bridge `id` sits on, or `None` if it is on
> +/// the root bus or not a known device.
> +pub fn get_pci_bridge_for_device(id: &str) -> Option<u8> {
> + layout::legacy::get_pci_bridge_for_device(id)
> +}
> +
> +/// Returns the QEMU arguments for all PCI bridges `machine` needs, as
> +/// alternating `-device` flags and their values.
> +///
> +/// Bridges whose address cannot be determined are skipped with a warning
> +/// rather than failing the whole guest.
> +pub fn get_pci_bridges(machine: &Machine) -> Vec<String> {
> + layout::legacy::get_pci_bridges(machine)
> +}
> diff --git a/pve-qemu-server-pci/src/machine.rs b/pve-qemu-server-pci/src/machine.rs
> new file mode 100644
> index 0000000..10ee9db
> --- /dev/null
> +++ b/pve-qemu-server-pci/src/machine.rs
> @@ -0,0 +1,99 @@
> +use pve_api_types::{ClusterResourceHostArch as Arch, QemuConfigOstype, QemuConfigScsihw};
> +
> +/// The parts of a guest configuration that influence which PCI layout is used
> +/// and which quirks have to be applied to it.
> +///
> +/// This is deliberately not the full guest config: it only carries what the
> +/// address generation needs, so that callers can build it from whatever
> +/// configuration representation they have.
> +#[derive(Clone, Debug)]
> +pub struct Machine {
nit naming: Machine seems a bit strange to me here, since we a passing
the relevant guest config. So why not just call it `LayoutContext`, or
`GuestConfig | `GuestLayoutConfig`.
> + pub arch: Arch,
> + pub q35: bool,
Since q35 is a boolean, it is possible to configure:
Machine { arch: Arch::Aarch64, q35: true }
What about making the chipset part of the Arch enum like this:
pub enum X86Chipset {
I440fx,
Q35,
}
pub enum Arch {
X8664(X86Chipset),
Aarch64,
}
This would allow us to remove the q35 boolean value.
Or if you want to keep a field here, what about a replacing q35 with an
enum `machine_type` with 3 staes (Q35, I440fx, Virt)
> + pub scsihw: Option<QemuConfigScsihw>,
> + /// Highest `scsihw` controller index in use. Controllers from index 2 on
> + /// live on `pci.4`, so this decides whether that bridge is needed.
> + pub max_scsihw: u8,
> + pub legacy_igd: bool,
> + pub ostype: Option<QemuConfigOstype>,
> + /// QEMU machine version as `(major, minor, pve)`, where the PVE specific
> + /// revision is optional.
> + pub version: (u16, u16, Option<u16>),
nit: naming: Consequently we should rename `version` -> `machine_version`
> +}
> +
> +impl Machine {
> + /// Builds a [`Machine`] from the raw configuration values.
> + ///
> + /// Unparsable `arch`, `scsihw` and `ostype` values are not an error here:
> + /// `arch` falls back to x86_64 and the other two to `None`, which is the
> + /// same defaulting the Perl implementation does.
> + #[allow(clippy::too_many_arguments)]
> + pub fn new(
> + arch: &str,
> + q35: bool,
> + scsihw: Option<String>,
> + max_scsihw: u8,
> + legacy_igd: bool,
> + ostype: Option<String>,
> + major: u16,
> + minor: u16,
> + pve: Option<u16>,
> + ) -> Self {
> + let arch = arch.parse().unwrap_or(Arch::X8664);
> + let scsihw = scsihw.and_then(|hw| hw.parse().ok());
> + let ostype = ostype.and_then(|ostype| ostype.parse().ok());
> + Self {
> + arch,
> + q35,
> + scsihw,
> + max_scsihw,
> + legacy_igd,
> + ostype,
> + version: (major, minor, pve),
> + }
> + }
> +
> + /// Whether the guest's root bus is PCIe. True for q35 machines and for
> + /// aarch64, which only has a PCIe host bridge.
> + pub fn is_pcie(&self) -> bool {
> + self.q35 || self.arch == Arch::Aarch64
> + }
> +
> + /// Whether each SCSI disk gets its own controller, which needs the extra
> + /// `pci.3` bridge.
> + pub fn virtio_scsi_single(&self) -> bool {
> + self.scsihw == Some(QemuConfigScsihw::VirtioScsiSingle)
> + }
> +
> + /// Whether the machine version is at least `major.minor.pve`.
> + ///
> + /// A machine without a PVE revision counts as being at least any requested
> + /// one, since the plain QEMU version already implies all PVE changes made
> + /// for it.
> + pub fn version_at_least(&self, major: u16, minor: u16, pve: u16) -> bool {
> + if self.version.0 > major {
> + return true;
> + }
> + if self.version.0 < major {
> + return false;
> + }
> +
> + if self.version.1 > minor {
> + return true;
> + }
> + if self.version.1 < minor {
> + return false;
> + }
> +
> + if let Some(our_pve) = self.version.2 {
> + if our_pve > pve {
> + return true;
> + }
> + if our_pve < pve {
> + return false;
> + }
> + }
> +
> + true
> + }
IMO we can just let rusts tuple compare logic handle this:
pub fn version_at_least(&self, major: u16, minor: u16, pve: u16) -> bool {
let (our_major, our_minor, our_pve) = self.version;
(our_major, our_minor, our_pve.unwrap_or(u16::MAX)) >= (major, minor, pve)
}
> +}
next prev parent reply other threads:[~2026-09-28 12:55 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-22 10:55 [RFC proxmox-perl-rs/qemu-server/qemu-server-rs 0/9] pci-handling rewrite (part 1) Dominik Csapak
2026-09-22 10:55 ` [PATCH pve-qemu-server-rs 1/9] add pve-qemu-server-pci crate for guest PCI address generation Dominik Csapak
2026-09-28 12:53 ` Elias Huhsovitz
2026-09-28 14:00 ` Dominik Csapak
2026-09-22 10:55 ` [PATCH pve-qemu-server-rs 2/9] pci: add machine abstraction and PCI bridge generation Dominik Csapak
2026-09-28 12:55 ` Elias Huhsovitz [this message]
2026-09-28 14:04 ` Dominik Csapak
2026-09-22 10:55 ` [PATCH pve-qemu-server-rs 3/9] pci: layout: add v2 PCI and PCIe layouts Dominik Csapak
2026-09-28 12:55 ` Elias Huhsovitz
2026-09-28 14:05 ` Dominik Csapak
2026-09-22 10:55 ` [PATCH pve-qemu-server-rs 4/9] fixup! add pve-qemu-server-pci crate for guest PCI address generation Dominik Csapak
2026-09-22 10:55 ` [PATCH proxmox-perl-rs 5/9] pve: add bindings for `pve-qemu-server-pci` crate Dominik Csapak
2026-09-28 12:56 ` Elias Huhsovitz
2026-09-28 14:06 ` Dominik Csapak
2026-09-22 10:55 ` [PATCH proxmox-perl-rs 6/9] pve: pci bindings: add bindings for the `Machine` struct Dominik Csapak
2026-09-22 10:55 ` [PATCH qemu-server 7/9] pci: use PVE::RS::PCI bindings Dominik Csapak
2026-09-22 10:55 ` [PATCH qemu-server 8/9] helpers: factor out the version parts parsing Dominik Csapak
2026-09-22 10:55 ` [PATCH qemu-server 9/9] pci: bridges: use the rust `Machine` struct to pass parameters Dominik Csapak
2026-09-22 11:06 ` [RFC proxmox-perl-rs/qemu-server/qemu-server-rs 0/9] pci-handling rewrite (part 1) 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=DLQYRIN8PHM3.9NP2SN1WA6HE@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox