From: Dominik Csapak <d.csapak@proxmox.com>
To: Elias Huhsovitz <e.huhsovitz@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 16:04:26 +0200 [thread overview]
Message-ID: <bc261479-d42e-4131-a808-e61ad01c6a68@proxmox.com> (raw)
In-Reply-To: <DLQYRIN8PHM3.9NP2SN1WA6HE@proxmox.com>
On 9/28/26 2:55 PM, Elias Huhsovitz wrote:
> 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.
i think returning an error here would even be better. we already
have to touch the perl side, so an additional eval { }
shouldn't hurt (or we simply die there; it shouldn't be possible
anyway?)
>
>> + };
>> + 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`.
>
yeah, machine was not a very well chosen name :P
I'll think about what to name it, thanks for the suggestions!
>> + 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)
since i want to match (and at some point use) the pve config fields here
having an enum for the 3 machine types makes most sense IMO
>
>> + 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)
> }
true. This is a thing that should probably live in a helper crate
somewhere. I guess well reuse this kind of version comparison more than
once.
>
>> +}
>
next prev parent reply other threads:[~2026-09-28 14:04 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
2026-09-28 14:04 ` Dominik Csapak [this message]
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=bc261479-d42e-4131-a808-e61ad01c6a68@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=e.huhsovitz@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.