* [PATCH 1/3] hw/pci: Have various pci_bus*() methods take a const PCIBus argument
2026-07-07 8:40 [PATCH 0/3] hw/pci: Consider reserved slots mask when checking PCI bus is full Philippe Mathieu-Daudé
@ 2026-07-07 8:40 ` Philippe Mathieu-Daudé
2026-07-07 9:11 ` Thomas Huth
2026-07-07 8:40 ` [PATCH 2/3] hw/qdev: Introduce BusClass::is_full() handler Philippe Mathieu-Daudé
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 8:40 UTC (permalink / raw)
To: qemu-devel
Cc: Daniel P. Berrangé, Paolo Bonzini, Mark Cave-Ayland,
Thomas Huth, Michael Tokarev, Artyom Tarasenko, Peter Maydell,
Michael S. Tsirkin, Philippe Mathieu-Daudé
Mark the PCIBus pointer argument const when methods
don't need to update the structure referenced.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
include/hw/pci/pci.h | 10 +++++-----
include/hw/pci/pci_bus.h | 10 +++++-----
hw/pci-bridge/pci_expander_bridge.c | 4 ++--
hw/pci/pci.c | 18 +++++++++---------
4 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index f2448e941a0..f05d4d867b4 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -308,8 +308,8 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq,
void *irq_opaque, int nirq);
void pci_bus_map_irqs(PCIBus *bus, pci_map_irq_fn map_irq);
void pci_bus_irqs_cleanup(PCIBus *bus);
-int pci_bus_get_irq_level(PCIBus *bus, int irq_num);
-uint32_t pci_bus_get_slot_reserved_mask(PCIBus *bus);
+int pci_bus_get_irq_level(const PCIBus *bus, int irq_num);
+uint32_t pci_bus_get_slot_reserved_mask(const PCIBus *bus);
void pci_bus_set_slot_reserved_mask(PCIBus *bus, uint32_t mask);
void pci_bus_clear_slot_reserved_mask(PCIBus *bus, uint32_t mask);
bool pci_bus_add_fw_cfg_extra_pci_roots(FWCfgState *fw_cfg,
@@ -345,14 +345,14 @@ static inline PCIBus *pci_get_bus(const PCIDevice *dev)
{
return PCI_BUS(qdev_get_parent_bus(DEVICE(dev)));
}
-int pci_bus_num(PCIBus *s);
-void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus);
+int pci_bus_num(const PCIBus *s);
+void pci_bus_range(const PCIBus *bus, int *min_bus, int *max_bus);
static inline int pci_dev_bus_num(const PCIDevice *dev)
{
return pci_bus_num(pci_get_bus(dev));
}
-int pci_bus_numa_node(PCIBus *bus);
+int pci_bus_numa_node(const PCIBus *bus);
void pci_for_each_device(PCIBus *bus, int bus_num,
pci_bus_dev_fn fn,
void *opaque);
diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h
index c7384467888..7a3f2e4dde9 100644
--- a/include/hw/pci/pci_bus.h
+++ b/include/hw/pci/pci_bus.h
@@ -15,8 +15,8 @@ struct PCIBusClass {
BusClass parent_class;
/*< public >*/
- int (*bus_num)(PCIBus *bus);
- uint16_t (*numa_node)(PCIBus *bus);
+ int (*bus_num)(const PCIBus *bus);
+ uint16_t (*numa_node)(const PCIBus *bus);
};
enum PCIBusFlags {
@@ -58,17 +58,17 @@ struct PCIBus {
Notifier machine_done;
};
-static inline bool pci_bus_is_cxl(PCIBus *bus)
+static inline bool pci_bus_is_cxl(const PCIBus *bus)
{
return !!(bus->flags & PCI_BUS_CXL);
}
-static inline bool pci_bus_is_root(PCIBus *bus)
+static inline bool pci_bus_is_root(const PCIBus *bus)
{
return !!(bus->flags & PCI_BUS_IS_ROOT);
}
-static inline bool pci_bus_allows_extended_config_space(PCIBus *bus)
+static inline bool pci_bus_allows_extended_config_space(const PCIBus *bus)
{
return !!(bus->flags & PCI_BUS_EXTENDED_CONFIG_SPACE);
}
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
index 40ffbc4e082..f981c47edee 100644
--- a/hw/pci-bridge/pci_expander_bridge.c
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -68,14 +68,14 @@ bool cxl_get_hb_passthrough(PCIHostState *hb)
return host->passthrough;
}
-static int pxb_bus_num(PCIBus *bus)
+static int pxb_bus_num(const PCIBus *bus)
{
PXBDev *pxb = PXB_DEV(bus->parent_dev);
return pxb->bus_nr;
}
-static uint16_t pxb_bus_numa_node(PCIBus *bus)
+static uint16_t pxb_bus_numa_node(const PCIBus *bus)
{
PXBDev *pxb = PXB_DEV(bus->parent_dev);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index d3191609e28..b04e94a150b 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -225,7 +225,7 @@ static void pci_bus_unrealize(BusState *qbus)
vmstate_unregister(NULL, &vmstate_pcibus, bus);
}
-static int pcibus_num(PCIBus *bus)
+static int pcibus_num(const PCIBus *bus)
{
if (pci_bus_is_root(bus)) {
return 0; /* pci host bridge */
@@ -233,7 +233,7 @@ static int pcibus_num(PCIBus *bus)
return bus->parent_dev->config[PCI_SECONDARY_BUS];
}
-static uint16_t pcibus_numa_node(PCIBus *bus)
+static uint16_t pcibus_numa_node(const PCIBus *bus)
{
return NUMA_NODE_UNASSIGNED;
}
@@ -411,7 +411,7 @@ static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
pci_bus_change_irq_level(bus, irq_num, change);
}
-int pci_bus_get_irq_level(PCIBus *bus, int irq_num)
+int pci_bus_get_irq_level(const PCIBus *bus, int irq_num)
{
assert(irq_num >= 0);
assert(irq_num < bus->nirq);
@@ -781,13 +781,13 @@ void pci_unregister_root_bus(PCIBus *bus)
pci_root_bus_cleanup(bus);
}
-int pci_bus_num(PCIBus *s)
+int pci_bus_num(const PCIBus *s)
{
return PCI_BUS_GET_CLASS(s)->bus_num(s);
}
/* Returns the min and max bus numbers of a PCI bus hierarchy */
-void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus)
+void pci_bus_range(const PCIBus *bus, int *min_bus, int *max_bus)
{
int i;
*min_bus = *max_bus = pci_bus_num(bus);
@@ -802,7 +802,7 @@ void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus)
}
}
-int pci_bus_numa_node(PCIBus *bus)
+int pci_bus_numa_node(const PCIBus *bus)
{
return PCI_BUS_GET_CLASS(bus)->numa_node(bus);
}
@@ -1304,17 +1304,17 @@ uint16_t pci_requester_id(PCIDevice *dev)
return pci_req_id_cache_extract(&dev->requester_id_cache);
}
-static bool pci_bus_devfn_available(PCIBus *bus, int devfn)
+static bool pci_bus_devfn_available(const PCIBus *bus, int devfn)
{
return !(bus->devices[devfn]);
}
-static bool pci_bus_devfn_reserved(PCIBus *bus, int devfn)
+static bool pci_bus_devfn_reserved(const PCIBus *bus, int devfn)
{
return bus->slot_reserved_mask & (1UL << PCI_SLOT(devfn));
}
-uint32_t pci_bus_get_slot_reserved_mask(PCIBus *bus)
+uint32_t pci_bus_get_slot_reserved_mask(const PCIBus *bus)
{
return bus->slot_reserved_mask;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/3] hw/qdev: Introduce BusClass::is_full() handler
2026-07-07 8:40 [PATCH 0/3] hw/pci: Consider reserved slots mask when checking PCI bus is full Philippe Mathieu-Daudé
2026-07-07 8:40 ` [PATCH 1/3] hw/pci: Have various pci_bus*() methods take a const PCIBus argument Philippe Mathieu-Daudé
@ 2026-07-07 8:40 ` Philippe Mathieu-Daudé
2026-07-07 8:40 ` [PATCH 3/3] hw/pci: Consider reserved slots mask when checking PCI bus is full Philippe Mathieu-Daudé
2026-07-07 22:05 ` [PATCH 0/3] " Mark Cave-Ayland
3 siblings, 0 replies; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 8:40 UTC (permalink / raw)
To: qemu-devel
Cc: Daniel P. Berrangé, Paolo Bonzini, Mark Cave-Ayland,
Thomas Huth, Michael Tokarev, Artyom Tarasenko, Peter Maydell,
Michael S. Tsirkin, Philippe Mathieu-Daudé
Allow BusClass implementation to have their own is_full() handler.
Use it in qbus_is_full().
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
include/hw/core/qdev.h | 5 +++++
system/qdev-monitor.c | 3 +++
2 files changed, 8 insertions(+)
diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h
index e1476223411..c1c17bd6f2b 100644
--- a/include/hw/core/qdev.h
+++ b/include/hw/core/qdev.h
@@ -345,6 +345,11 @@ struct BusClass {
*/
bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);
+ /*
+ * Return whether more devices can be plugged into @bus.
+ */
+ bool (*is_full)(const BusState *bus);
+
BusRealize realize;
BusUnrealize unrealize;
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 00fed791cce..496850bd8ed 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -454,6 +454,9 @@ static inline bool qbus_is_full(BusState *bus)
return true;
}
bus_class = BUS_GET_CLASS(bus);
+ if (bus_class->is_full && bus_class->is_full(bus)) {
+ return true;
+ }
return bus_class->max_dev && bus->num_children >= bus_class->max_dev;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/3] hw/pci: Consider reserved slots mask when checking PCI bus is full
2026-07-07 8:40 [PATCH 0/3] hw/pci: Consider reserved slots mask when checking PCI bus is full Philippe Mathieu-Daudé
2026-07-07 8:40 ` [PATCH 1/3] hw/pci: Have various pci_bus*() methods take a const PCIBus argument Philippe Mathieu-Daudé
2026-07-07 8:40 ` [PATCH 2/3] hw/qdev: Introduce BusClass::is_full() handler Philippe Mathieu-Daudé
@ 2026-07-07 8:40 ` Philippe Mathieu-Daudé
2026-07-07 10:20 ` Michael S. Tsirkin
2026-07-07 22:05 ` [PATCH 0/3] " Mark Cave-Ayland
3 siblings, 1 reply; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 8:40 UTC (permalink / raw)
To: qemu-devel
Cc: Daniel P. Berrangé, Paolo Bonzini, Mark Cave-Ayland,
Thomas Huth, Michael Tokarev, Artyom Tarasenko, Peter Maydell,
Michael S. Tsirkin, Philippe Mathieu-Daudé
Do not consider reserved slots as available.
Fixes: 8b8849844fd ("pci: add reserved slot check to do_pci_register_device")
Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
hw/pci/pci.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index b04e94a150b..2e5412137b3 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -62,6 +62,7 @@ static char *pcibus_get_dev_path(DeviceState *dev);
static char *pcibus_get_fw_dev_path(DeviceState *dev);
static void pcibus_reset_hold(Object *obj, ResetType type);
static bool pcie_has_upstream_port(PCIDevice *dev);
+static bool pci_bus_is_full(const BusState *qbus);
static void prop_pci_busnr_get(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
@@ -293,6 +294,7 @@ static void pci_bus_class_init(ObjectClass *klass, const void *data)
k->print_dev = pcibus_dev_print;
k->get_dev_path = pcibus_get_dev_path;
k->get_fw_dev_path = pcibus_get_fw_dev_path;
+ k->is_full = pci_bus_is_full;
k->realize = pci_bus_realize;
k->unrealize = pci_bus_unrealize;
@@ -1329,6 +1331,21 @@ void pci_bus_clear_slot_reserved_mask(PCIBus *bus, uint32_t mask)
bus->slot_reserved_mask &= ~mask;
}
+static bool pci_bus_is_full(const BusState *qbus)
+{
+ const PCIBus *bus = PCI_BUS(qbus);
+
+ for (int i = 0; i < PCI_DEVFN_MAX; i += PCI_FUNC_MAX) {
+ if (pci_bus_devfn_reserved(bus, i)) {
+ continue;
+ }
+ if (pci_bus_devfn_available(bus, i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev,
const char *name, int devfn,
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread