aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Mammedov <imammedo@redhat.com>2023-01-12 15:02:43 +0100
committerMichael S. Tsirkin <mst@redhat.com>2023-01-28 06:21:29 -0500
commit2940a4b9e3d206cc759c7630dde2fb7ded3e9ec2 (patch)
tree24d39eb591b0adc81fc2bed6178ef4c0e71268b4
parent45284cfb49a47bb4536e29b4965a41a0ecb63149 (diff)
pci: acpihp: assign BSEL only to coldplugged bridges
ACPI PCI hotplug would broken after bridge hotplug and then migration if hotplugged bridge were specified on target at command line. Currently it's not possible since, 'hotplugged' property was made read-only for some time now. The issue would happen due to BSEL being assigned to all bridges during 1st 'reset': source seq: 1. start 'pc' machine => sets BSEL to 0 on pci.0 (host-bridge) 2. hotplug bridge, no bsel is assigned (so far is ok) target seq: 1. start 'pc' machine with -S -device pci-bridge,id=hp_br,hotplugged=on BSEL gets assigned to as follows hp_br: 0 pci.0: 1 as result hotplug requests with migrated AML generated on source would be misdirected to 'hp_br' instead of intended pci.0 While it's not issue at the moment, it's based on implicit assumptions * 'hotplugged' property is read-only * 1st reset happens before QEMU drops into monitor mode which lets add hotplugged on source bridges as hotplugged ones (anything added at that stage counts as hotplugged (yet another assumption)) All of it looks too fragile to me, so lets restrict BSEL only to cold-plugged bridges explicitly. Migration wise it shouldn't break anything since assignment order stays the same: * user can't specify 'hotplugged=on' on CLI * user can't specify 'hotplugged=off' at monitor stage or later on older QEMU versions where 'hotplugged' is RW, hotplug is broken after migration anyways and we cannot do anything to fix that. Signed-off-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20230112140312.3096331-12-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--hw/acpi/pcihp.c35
1 files changed, 22 insertions, 13 deletions
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
index 99a898d9ae..5dc7377411 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -85,31 +85,40 @@ static int acpi_pcihp_get_bsel(PCIBus *bus)
}
}
-/* Assign BSEL property to all buses. In the future, this can be changed
- * to only assign to buses that support hotplug.
- */
+typedef struct {
+ unsigned bsel_alloc;
+ bool has_bridge_hotplug;
+} BSELInfo;
+
+/* Assign BSEL property only to buses that support hotplug. */
static void *acpi_set_bsel(PCIBus *bus, void *opaque)
{
- unsigned *bsel_alloc = opaque;
+ BSELInfo *info = opaque;
unsigned *bus_bsel;
+ DeviceState *br = bus->qbus.parent;
+ bool is_bridge = IS_PCI_BRIDGE(br);
+ /* hotplugged bridges can't be described in ACPI ignore them */
if (qbus_is_hotpluggable(BUS(bus))) {
- bus_bsel = g_malloc(sizeof *bus_bsel);
+ if (!is_bridge || (!br->hotplugged && info->has_bridge_hotplug)) {
+ bus_bsel = g_malloc(sizeof *bus_bsel);
- *bus_bsel = (*bsel_alloc)++;
- object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
- bus_bsel, OBJ_PROP_FLAG_READ);
+ *bus_bsel = info->bsel_alloc++;
+ object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
+ bus_bsel, OBJ_PROP_FLAG_READ);
+ }
}
- return bsel_alloc;
+ return info;
}
-static void acpi_set_pci_info(void)
+static void acpi_set_pci_info(bool has_bridge_hotplug)
{
static bool bsel_is_set;
Object *host = acpi_get_i386_pci_host();
PCIBus *bus;
- unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT;
+ BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT,
+ .has_bridge_hotplug = has_bridge_hotplug };
if (bsel_is_set) {
return;
@@ -123,7 +132,7 @@ static void acpi_set_pci_info(void)
bus = PCI_HOST_BRIDGE(host)->bus;
if (bus) {
/* Scan all PCI buses. Set property to enable acpi based hotplug. */
- pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc);
+ pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info);
}
}
@@ -287,7 +296,7 @@ void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off)
if (acpihp_root_off) {
acpi_pcihp_disable_root_bus();
}
- acpi_set_pci_info();
+ acpi_set_pci_info(!s->legacy_piix);
acpi_pcihp_update(s);
}