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 v2 4/9] pci: layout: add grouped PCI and PCIe layouts
Date: Thu, 29 Oct 2026 14:31:39 +0100 [thread overview]
Message-ID: <DMHCYCC2OX41.2KW2QLY0DG8H@proxmox.com> (raw)
In-Reply-To: <20261015081041.743422-5-d.csapak@proxmox.com>
Comments inline.
On Thu Oct 15, 2026 at 10:10 AM CEST, Dominik Csapak wrote:
> The legacy layout grew over many releases and it shows: devices of one
> kind are spread over several buses, the bus a device lands on depends on
> how many devices of other kinds exist, and the remaining free slots are
> scattered. That makes it hard to raise any of the per-kind limits
> without further scattering the types over the available addresses.
>
> Add a second set of layouts that gives every device kind its own set of
> bridges instead. A device's address then only depends on its own index,
> so raising a limit adds bridges at the end rather than shifting anything
> that exists, and the built-in devices move to a bridge of their own to
> keep the root bus free.
>
> The new `layout` field of `HardwareConfig` selects which set is used,
> so `pci_bridge_for_device` now takes the config too. Since the layout
> is part of the guest ABI, the grouped one is only intended for new
> guests.
>
> `pci_bus_devices` returns its bridges and the hostpcie root ports,
> with the same function 0 handling as for the legacy layout.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
[snip]
> +
> +fn layout(pcie: bool) -> &'static Layout<'static> {
> + if pcie { &PCIE_LAYOUT } else { &PCI_LAYOUT }
> +}
> +
> +pub(crate) fn address(hw: &HardwareConfig, id: &str) -> Result<PciAddress, LayoutError> {
> + let layout = layout(hw.has_pcie());
> + layout.find_device(&id.parse()?)
> +}
> +
> +pub(crate) fn pci_bridge_for_device(hw: &HardwareConfig, id: &str) -> Option<Bus> {
> + let addr = address(hw, id).ok()?;
> + Some(addr.bus)
> +}
> +
> +pub(crate) fn pci_bus_devices(hw: &HardwareConfig) -> Result<Vec<String>, LayoutError> {
> + let mut bridges = Vec::new();
> + let layout = layout(hw.has_pcie());
> + let mut chassis_nr = 1;
> + for bridge in layout
> + .iter()
> + .filter(|d| matches!(d.device, Device::Bridge(_)))
> + {
> + let addr = bridge.addr;
> + let (model, id) = match bridge.device {
> + Device::Bridge(bus) => (bus.bridge_model(addr.bus), bus),
> + _ => continue,
> + };
> + let Some(model) = model else {
> + continue;
> + };
> + let mf = if addr.function == 0 {
> + ",multifunction=on"
Isn't the addr.function == 0 already encoded in AddressAssignment?
So we can just check for:
let mf = bridge.multifunction {
",multifunction=on"
> + } else {
> + ""
> + };
> +
> + let chassis = if model == "pci-bridge" {
> + let text = format!(",chassis_nr={chassis_nr}");
> + chassis_nr += 1;
We could consider deriving chassis_nr from something instead letting the
layout iteration assign a sequential ordering.
Similarly to how the legacy layout does it.
> + text
> + } else {
> + String::new()
> + };
> + bridges.push("-device".to_string());
> + bridges.push(format!(
> + "{model},id={id}{chassis}{}{mf}",
> + addr.to_qemu_addr()
> + ));
> + }
> +
> + for port in layout.root_ports(&hw.hostpcie()) {
> + bridges.push("-device".to_string());
> + bridges.push(port.to_qemu_device());
> + }
> +
> + Ok(bridges)
> +}
> +
[snip]
> diff --git a/pve-qemu-server-pci/src/layout/mod.rs b/pve-qemu-server-pci/src/layout/mod.rs
> index 5439c17..9a637ea 100644
> --- a/pve-qemu-server-pci/src/layout/mod.rs
> +++ b/pve-qemu-server-pci/src/layout/mod.rs
> @@ -13,6 +13,8 @@ macro_rules! single_device {
>
> pub(crate) mod legacy;
>
> +pub mod grouped;
> +
> /// Constructs a correctly sized list of bridge slots, starting at address 0x01
> /// because 0x00 is always taken by the bridge itself.
> ///
> diff --git a/pve-qemu-server-pci/src/lib.rs b/pve-qemu-server-pci/src/lib.rs
> index a5d82a4..5417fa5 100644
> --- a/pve-qemu-server-pci/src/lib.rs
> +++ b/pve-qemu-server-pci/src/lib.rs
> @@ -21,10 +21,45 @@ pub use error::LayoutError;
>
> pub mod layout;
>
> +/// The set of layouts a guest's addresses are taken from.
> +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
> +pub enum PciLayout {
> + /// The layouts reproducing the addresses of the Perl implementation.
> + #[default]
> + Legacy,
> + /// The layouts giving every device kind its own set of bridges, see
> + /// [`layout::grouped`].
> + Grouped,
> +}
> +
IMHO the code for PciLayout enum should live inside layout module.
e.g. in layout/mod.rs
> +impl std::str::FromStr for PciLayout {
> + type Err = LayoutError;
> +
> + fn from_str(s: &str) -> Result<Self, Self::Err> {
> + match s {
> + "legacy" => Ok(Self::Legacy),
> + "grouped" => Ok(Self::Grouped),
> + _ => Err(LayoutError::InvalidLayout),
> + }
> + }
> +}
> +
> +impl std::fmt::Display for PciLayout {
> + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
> + f.write_str(match self {
> + PciLayout::Legacy => "legacy",
> + PciLayout::Grouped => "grouped",
> + })
> + }
> +}
> +
> /// 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 pci_bridge_for_device(id: &str) -> Option<Bus> {
> - layout::legacy::pci_bridge_for_device(id)
> +pub fn pci_bridge_for_device(hw: &HardwareConfig, id: &str) -> Option<Bus> {
> + match hw.layout {
> + PciLayout::Legacy => layout::legacy::pci_bridge_for_device(id),
> + PciLayout::Grouped => layout::grouped::pci_bridge_for_device(hw, id),
> + }
> }
nit naming: The function is called pci_bridge_for_device() but returns a Bus.
IMO the semantics here are a bit ambiguous.
What about renaming it to something like:
bridge_bus_for_device()
IMO we can also omit the "pci" prefix, since we are in the PCI crate.
>
> /// Returns the QEMU arguments for all devices that form the buses `hw`
> @@ -33,10 +68,16 @@ pub fn pci_bridge_for_device(id: &str) -> Option<Bus> {
> /// They are ordered so that every bus is created before the devices on it,
> /// and have to be passed before any other device that sits on these buses.
> pub fn pci_bus_devices(hw: &HardwareConfig) -> Result<Vec<String>, LayoutError> {
nit naming: we could also consider dropping the pci prefix of the
function name to "bus_devices".
> - layout::legacy::pci_bus_devices(hw)
> + match hw.layout {
> + PciLayout::Legacy => layout::legacy::pci_bus_devices(hw),
> + PciLayout::Grouped => layout::grouped::pci_bus_devices(hw),
> + }
> }
>
> /// Returns the address of the device with the configuration id `id`.
> pub fn address(hw: &HardwareConfig, id: &str) -> Result<PciAddress, LayoutError> {
> - layout::legacy::address(hw, id)
> + match hw.layout {
> + PciLayout::Legacy => layout::legacy::address(hw, id),
> + PciLayout::Grouped => layout::grouped::address(hw, id),
> + }
> }
> diff --git a/pve-qemu-server-pci/src/types/mod.rs b/pve-qemu-server-pci/src/types/mod.rs
> index 1605066..43b08c0 100644
> --- a/pve-qemu-server-pci/src/types/mod.rs
> +++ b/pve-qemu-server-pci/src/types/mod.rs
> @@ -3,6 +3,9 @@ pub use bus::{Bus, PveBridge};
>
> mod device;
> pub use device::Device;
> +// only needed to enumerate all device kinds in tests
> +#[cfg(test)]
> +pub(crate) use device::DeviceDiscriminants;
>
> mod pci_address;
> pub use pci_address::PciAddress;
next prev parent reply other threads:[~2026-10-29 13:38 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 8:10 [RFC proxmox-perl-rs/qemu-server/qemu-server-rs v2 0/9] pci-handling rewrite (part 1/2/3) Dominik Csapak
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 1/9] add pve-qemu-server-pci crate for guest PCI address generation Dominik Csapak
2026-10-15 12:16 ` Wolfgang Bumiller
2026-10-29 13:31 ` Elias Huhsovitz
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 2/9] pci: legacy: generate pci bridges Dominik Csapak
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 3/9] pci: legacy: generate q35 chipset devices and hostpcie root ports Dominik Csapak
2026-10-29 13:31 ` Elias Huhsovitz
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 4/9] pci: layout: add grouped PCI and PCIe layouts Dominik Csapak
2026-10-29 13:31 ` Elias Huhsovitz [this message]
2026-10-15 8:10 ` [PATCH proxmox-perl-rs v2 5/9] pve: add bindings for `pve-qemu-server-pci` crate Dominik Csapak
2026-10-15 11:47 ` Wolfgang Bumiller
2026-10-15 8:10 ` [PATCH qemu-server v2 6/9] pci: use PVE::RS::PCI bindings for addresses and bus devices Dominik Csapak
2026-10-29 13:32 ` Elias Huhsovitz
2026-10-15 8:10 ` [PATCH qemu-server v2 7/9] tree-wide: use HardwareConfig for the machine version checks Dominik Csapak
2026-10-15 8:10 ` [PATCH qemu-server v2 8/9] cfg2cmd: q35: generate chipset devices with machine version 11.1+pve1 Dominik Csapak
2026-10-15 8:10 ` [PATCH qemu-server v2 9/9] machine: add 'pci-layout' option Dominik Csapak
2026-10-29 15:36 ` [RFC proxmox-perl-rs/qemu-server/qemu-server-rs v2 0/9] pci-handling rewrite (part 1/2/3) Fiona Ebner
2026-10-30 14:13 ` 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=DMHCYCC2OX41.2KW2QLY0DG8H@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