aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2010-12-27 11:21:38 +0200
committerMichael S. Tsirkin <mst@redhat.com>2010-12-27 11:21:38 +0200
commita6a7005d14b3c32d4864a718fb1cb19c789f58a5 (patch)
tree484e7cc06e35a9f6e49e180d86cc6420b1e5a705
parent2ae63bda50ec864a3e61d375b53c8e453ad50140 (diff)
pci: fix migration path for devices behind bridges
The device path used for migration is currently broken for for all devices behind a nested bridge. Replace this by a hierarchical list of slot/function numbers, walking the path from root down to device. Add :00 after the domain number so that if there are no nested bridges, this is compatible with what we have now. Note: as pointed out by Gleb, using openfirmware paths might be cleaner, doing this would break compatibility though, and the IDs used are not guest or user visible at all, so breaking the compatibility is probably not worth it. Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--hw/pci.c48
1 files changed, 37 insertions, 11 deletions
diff --git a/hw/pci.c b/hw/pci.c
index 44bb3b9a91..d0b51b80bd 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -2014,17 +2014,43 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev)
static char *pcibus_get_dev_path(DeviceState *dev)
{
- PCIDevice *d = (PCIDevice *)dev;
- char path[16];
-
- snprintf(path, sizeof(path), "%04x:%02x:%02x.%x",
- pci_find_domain(d->bus),
- 0 /* TODO: need a persistent path for nested buses.
- * Note: pci_bus_num(d->bus) is not right as it's guest
- * assigned. */,
- PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
-
- return strdup(path);
+ PCIDevice *d = container_of(dev, PCIDevice, qdev);
+ PCIDevice *t;
+ int slot_depth;
+ /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function.
+ * 00 is added here to make this format compatible with
+ * domain:Bus:Slot.Func for systems without nested PCI bridges.
+ * Slot.Function list specifies the slot and function numbers for all
+ * devices on the path from root to the specific device. */
+ int domain_len = strlen("DDDD:00");
+ int slot_len = strlen(":SS.F");
+ int path_len;
+ char *path, *p;
+
+ /* Calculate # of slots on path between device and root. */;
+ slot_depth = 0;
+ for (t = d; t; t = t->bus->parent_dev) {
+ ++slot_depth;
+ }
+
+ path_len = domain_len + slot_len * slot_depth;
+
+ /* Allocate memory, fill in the terminating null byte. */
+ path = malloc(path_len + 1 /* For '\0' */);
+ path[path_len] = '\0';
+
+ /* First field is the domain. */
+ snprintf(path, domain_len, "%04x:00", pci_find_domain(d->bus));
+
+ /* Fill in slot numbers. We walk up from device to root, so need to print
+ * them in the reverse order, last to first. */
+ p = path + path_len;
+ for (t = d; t; t = t->bus->parent_dev) {
+ p -= slot_len;
+ snprintf(p, slot_len, ":%02x.%x", PCI_SLOT(t->devfn), PCI_FUNC(d->devfn));
+ }
+
+ return path;
}
static int pci_qdev_find_recursive(PCIBus *bus,