From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 05B151FF0BC for ; Thu, 29 Oct 2026 14:38:46 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 9BACA214DD; Thu, 29 Oct 2026 14:38:32 +0100 (CET) Content-Type: text/plain; charset=UTF-8 Date: Thu, 29 Oct 2026 14:31:24 +0100 Message-Id: Subject: Re: [PATCH pve-qemu-server-rs v2 3/9] pci: legacy: generate q35 chipset devices and hostpcie root ports From: "Elias Huhsovitz" To: "Dominik Csapak" , Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 X-Mailer: aerc 0.20.0 References: <20261015081041.743422-1-d.csapak@proxmox.com> <20261015081041.743422-4-d.csapak@proxmox.com> In-Reply-To: <20261015081041.743422-4-d.csapak@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793280684736 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.521 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_MAILER 2 Automated Mailer Tag Left in Email RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: AKRENROEB5P3BQT5FGKSLBAYGJWZPRFE X-Message-ID-Hash: AKRENROEB5P3BQT5FGKSLBAYGJWZPRFE X-MailFrom: e.huhsovitz@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Comments inline. On Thu Oct 15, 2026 at 10:10 AM CEST, Dominik Csapak wrote: > Until now, the q35 chipset devices were added via the pve-q35-4.0.cfg > machine config file, and the root ports for hostpcie4 and above were > added by the caller for each configured device. > > The latter can leave a root port without a function 0 in its slot, > e.g. when only hostpci5 is configured. Since guests only scan the > other functions of a slot when function 0 exists (Linux stops in > `pci_scan_slot()`), such a device was not visible in the guest. > > To fix that and to have the layout as the single source for the pci > hierarchy, generate both in `pci_bus_devices`: > * the chipset devices with the same ids, properties and order as in > the config file, which a test checks against a copy of it. Machines > older than 4.0 used pve-q35.cfg instead, but those are not supported > anymore. > * the root ports of the configured hostpcie devices, plus the one on > function 0 of each used slot. Devices that were already visible keep > their address, and Windows 7 guests still don't get any root ports. > > Since the devices of running guests must not change for live > migration, we only generate the chipset devices starting with machine > version 11.1+pve1. For older machines, `uses_q35_config_file` tells > the caller to keep using the config file. > > This replaces `print_pcie_root_port`, as we now decide here which root > ports are needed. > > Signed-off-by: Dominik Csapak > --- [snip] > diff --git a/pve-qemu-server-pci/src/lib.rs b/pve-qemu-server-pci/src/lib= .rs > index fc982ce..a5d82a4 100644 > --- a/pve-qemu-server-pci/src/lib.rs > +++ b/pve-qemu-server-pci/src/lib.rs > @@ -11,7 +11,7 @@ > pub mod constants; > =20 > mod hardware_config; > -pub use hardware_config::HardwareConfig; > +pub use hardware_config::{HardwareConfig, HostpcieDevice}; > =20 > mod types; > pub use types::*; > diff --git a/pve-qemu-server-pci/src/types/layout.rs b/pve-qemu-server-pc= i/src/types/layout.rs > index 889b961..91b1d78 100644 > --- a/pve-qemu-server-pci/src/types/layout.rs > +++ b/pve-qemu-server-pci/src/types/layout.rs > @@ -1,4 +1,4 @@ > -use std::collections::VecDeque; > +use std::collections::{HashSet, VecDeque}; > =20 > use crate::constants::{FUNCTIONS_PER_DEVICE, SLOTS_PER_BUS, USABLE_SLOTS= _PER_BUS}; > use crate::{Bus, Device, LayoutError, PciAddress, PveBridge}; > @@ -48,6 +48,46 @@ pub struct Layout<'a> { > } > =20 > impl Layout<'_> { > + /// Returns the root ports `devices` are attached to in layout order= , plus > + /// the one on function 0 of each used slot, as guests only look for= further > + /// functions if function 0 exists. > + pub fn root_ports(&self, devices: &[Device]) -> Vec { > + let attached: HashSet =3D devices > + .iter() > + .filter_map(|device| self.find_device(device).ok()) > + .collect(); > + let is_used =3D |port: &RootPort| attached.contains(&PciAddress:= :new(port.id, 0, 0)); > + > + let ports: Vec =3D self > + .iter() > + .filter_map(|used| match used.device { > + #[expect(deprecated)] > + Device::RootPort(id @ (Bus::RootPort(index) | Bus::Legac= yRootPort(index))) =3D> { > + Some(RootPort { > + addr: used.addr, > + id, > + index, > + }) > + } > + _ =3D> None, > + }) > + .collect(); > + let used_slots: HashSet<(Bus, u8)> =3D ports > + .iter() > + .filter(|port| is_used(port)) > + .map(|port| (port.addr.bus, port.addr.device)) > + .collect(); > + > + ports > + .into_iter() > + .filter(|port| { > + is_used(port) > + || (port.addr.function =3D=3D 0 > + && used_slots.contains(&(port.addr.bus, port.add= r.device))) > + }) > + .collect() > + } > + > /// Looks up the address of a device in this layout. > /// > /// The first match wins, so the order of the layout is significant:= a > @@ -68,6 +108,27 @@ impl Layout<'_> { > } > } > =20 > +/// A PCIe root port of a layout. > +#[derive(Debug, Clone, Copy, PartialEq, Eq)] > +pub struct RootPort { > + pub addr: PciAddress, > + /// The bus the root port forms. > + pub id: Bus, > + /// The number of the root port, also used as port and chassis numbe= r. > + pub index: u8, > +} > + > +impl RootPort { > + /// Renders the QEMU `-device` argument for this root port. > + pub fn to_qemu_device(&self) -> String { > + let RootPort { addr, id, index } =3D self; > + format!( > + "pcie-root-port,id=3D{id},addr=3D{:02x}.{},x-speed=3D16,x-wi= dth=3D32,multifunction=3Don,bus=3D{},port=3D{index},chassis=3D{index}", > + addr.device, addr.function, addr.bus, nit: I understand that setting multifunction=3Don shouldnt cause any harm here, but IMO it should still be encoded in RootPort. e.g. pub struct RootPort { pub addr: PciAddress, /// The bus the root port forms. pub id: Bus, /// The number of the root port, also used as port and chassis number. pub index: u8, pub multifunction: bool, } > + ) > + } > +} > + nit: I understand that setting multifunction=3Don shouldnt cause any harm here, but IMO it should still be encoded in RootPort. e.g. pub struct RootPort { pub addr: PciAddress, /// The bus the root port forms. pub id: Bus, /// The number of the root port, also used as port and chassis number. pub index: u8, pub multifunction: bool, } > /// The address a layout assigns to a device. > #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] > pub struct AddressAssignment { [snip]