[libvirt] [RFC PATCH] support multifunction PCI device

Wen Congyang wency at cn.fujitsu.com
Tue May 10 06:00:17 UTC 2011


We want to use more than 200+ device. Libvirt does not use multi
function PCI device and PCI-to-PCI bridge. So we can not use more
than 200+ device if it's a PCI device or it's controller is a PCI
device.

This patch adds the support of multi function PCI device. It
does not support to hot plug/unplug multi function PCI device.

TODO:
1. support to hot plug multi function PCI device
   We only can hot plug one device at one time. I think we should
   introduce another command 'virsh attach-devices XXX' to support
   hot plug more than one device at one time?

2. support to hot unplug multi function PCI device
   hot unpluging multi function PCI device meas that all the other
   devices on the same slot will be hot unpluged. So we should do
   some cleanup and remove the other devices too. If the other
   device on the same slot does not support hot unpluged, or it is a
   a controller and some other devices still use this controller,
   I think we should refuse to hot unplug this mutlti function PCI
   device.


>From 85a14928f2d445012f293638b44dd476a15aac3c Mon Sep 17 00:00:00 2001
From: Wen Congyang <wency at cn.fujitsu.com>
Date: Mon, 9 May 2011 14:59:16 +0800
Subject: [PATCH] multifunction PCI device

---
 src/conf/domain_conf.c       |    3 +
 src/qemu/qemu_capabilities.c |    5 +
 src/qemu/qemu_capabilities.h |    1 +
 src/qemu/qemu_command.c      |  339 ++++++++++++++++++++++++++++++++++++------
 src/qemu/qemu_command.h      |   13 ++-
 src/qemu/qemu_hotplug.c      |   76 +++++++++-
 src/qemu/qemu_process.c      |    6 +-
 tests/qemuhelptest.c         |    3 +-
 tests/qemuxml2argvtest.c     |    2 +-
 9 files changed, 390 insertions(+), 58 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index d3efec6..83fdfe8 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1237,6 +1237,9 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
 
 int virDomainDevicePCIAddressIsValid(virDomainDevicePCIAddressPtr addr)
 {
+    /* PCI bus has 32 slots and 8 functions per slot */
+    if (addr->slot >= 32 || addr->function >= 8)
+        return 0;
     return addr->domain || addr->bus || addr->slot;
 }
 
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 620143e..5d0145d 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -119,6 +119,8 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
               "device-spicevmc",
               "virtio-tx-alg",
               "device-qxl-vga",
+
+              "pci-multifunction", /* 60 */
     );
 
 struct qemu_feature_flags {
@@ -1024,6 +1026,9 @@ qemuCapsComputeCmdFlags(const char *help,
      */
      if (version >= 13000)
         qemuCapsSet(flags, QEMU_CAPS_MONITOR_JSON);
+
+    if (version >= 13000)
+        qemuCapsSet(flags, QEMU_CAPS_PCI_MULTIFUNCTION);
 }
 
 /* We parse the output of 'qemu -help' to get the QEMU
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index ab47f22..4691e90 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -95,6 +95,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_DEVICE_SPICEVMC   = 57, /* older -device spicevmc*/
     QEMU_CAPS_VIRTIO_TX_ALG     = 58, /* -device virtio-net-pci,tx=string */
     QEMU_CAPS_DEVICE_QXL_VGA    = 59, /* Is the primary and vga campatible qxl device named qxl-vga? */
+    QEMU_CAPS_PCI_MULTIFUNCTION = 60, /* -device multifunction=on|off */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 22b2634..9ac438c 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -667,9 +667,15 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps)
 
 
 #define QEMU_PCI_ADDRESS_LAST_SLOT 31
+#define QEMU_PCI_ADDRESS_LAST_FUNCTION 7
+#define QEMU_PCI_ADDRESS_LAST_DEVFN 0xFF
+#define QEMU_PCI_ADDRESS_DEVFN(slot, function) (((slot) << 3) + (function))
+#define QEMU_PCI_ADDRESS_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define QEMU_PCI_ADDRESS_FUNCTION(devfn) ((devfn) & 3)
 struct _qemuDomainPCIAddressSet {
     virHashTablePtr used;
     int nextslot;
+    int nextfunction;
 };
 
 
@@ -684,10 +690,11 @@ static char *qemuPCIAddressAsString(virDomainDeviceInfoPtr dev)
         return NULL;
     }
 
-    if (virAsprintf(&addr, "%d:%d:%d",
+    if (virAsprintf(&addr, "%d:%d:%d.%d",
                     dev->addr.pci.domain,
                     dev->addr.pci.bus,
-                    dev->addr.pci.slot) < 0) {
+                    dev->addr.pci.slot,
+                    dev->addr.pci.function) < 0) {
         virReportOOMError();
         return NULL;
     }
@@ -717,6 +724,29 @@ static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static void qemuGotoNextFunction(qemuDomainPCIAddressSetPtr addrs,
+                                 int current_slot, int current_function)
+{
+    int current_devfn, next_devfn;
+
+    current_devfn = QEMU_PCI_ADDRESS_DEVFN(current_slot, current_function);
+    next_devfn = QEMU_PCI_ADDRESS_DEVFN(addrs->nextslot, addrs->nextfunction);
+
+    if (current_devfn >= next_devfn) {
+        next_devfn = (current_devfn + 1) % (QEMU_PCI_ADDRESS_LAST_DEVFN + 1);
+        addrs->nextslot = QEMU_PCI_ADDRESS_SLOT(next_devfn);
+        addrs->nextfunction = QEMU_PCI_ADDRESS_FUNCTION(next_devfn);
+    }
+}
+
+static void qemuGotoNextSlot(qemuDomainPCIAddressSetPtr addrs,
+                             int current_slot)
+{
+    if (current_slot >= addrs->nextslot) {
+        addrs->nextslot = (current_slot + 1) % (QEMU_PCI_ADDRESS_LAST_SLOT +1);
+        addrs->nextfunction = 0;
+    }
+}
 
 int
 qemuDomainAssignPCIAddresses(virDomainDefPtr def)
@@ -734,7 +764,7 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def)
         if (!(addrs = qemuDomainPCIAddressSetCreate(def)))
             goto cleanup;
 
-        if (qemuAssignDevicePCISlots(def, addrs) < 0)
+        if (qemuAssignDevicePCISlots(def, addrs, qemuCaps) < 0)
             goto cleanup;
     }
 
@@ -777,6 +807,35 @@ error:
     return NULL;
 }
 
+/* check whether the slot is used by the other device
+ * Return 0 if the slot is not used by the other device, or -1 if the slot
+ * is used by the other device.
+ */
+static int qemuDomainPCIAddressCheckSlot(qemuDomainPCIAddressSetPtr addrs,
+                                         virDomainDeviceInfoPtr dev)
+{
+    char *addr;
+    virDomainDeviceInfo temp_dev;
+    int function;
+
+    temp_dev = *dev;
+    for (function = 0; function < QEMU_PCI_ADDRESS_LAST_FUNCTION; function++) {
+        temp_dev.addr.pci.function = function;
+        addr = qemuPCIAddressAsString(&temp_dev);
+        if (!addr)
+            return -1;
+
+        if (virHashLookup(addrs->used, addr)) {
+            VIR_FREE(addr);
+            return -1;
+        }
+
+        VIR_FREE(addr);
+    }
+
+    return 0;
+}
+
 int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev)
 {
@@ -800,36 +859,61 @@ int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
         return -1;
     }
 
-    if (dev->addr.pci.slot > addrs->nextslot) {
-        addrs->nextslot = dev->addr.pci.slot + 1;
-        if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot)
-            addrs->nextslot = 0;
-    }
+    qemuGotoNextFunction(addrs, dev->addr.pci.slot, dev->addr.pci.function);
 
     return 0;
 }
 
-int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
-                                    int slot)
+int qemuDomainPCIAddressReserveFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        int slot, int function)
 {
     virDomainDeviceInfo dev;
 
     dev.addr.pci.domain = 0;
     dev.addr.pci.bus = 0;
     dev.addr.pci.slot = slot;
+    dev.addr.pci.function = function;
 
     return qemuDomainPCIAddressReserveAddr(addrs, &dev);
 }
 
+int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
+                                    int slot)
+{
+    int function;
+
+    for (function = 0; function <= QEMU_PCI_ADDRESS_LAST_FUNCTION; function++) {
+        if (qemuDomainPCIAddressReserveFunction(addrs, slot, function) < 0)
+            goto cleanup;
+    }
+
+    return 0;
+
+cleanup:
+    for (function--; function >= 0; function--) {
+        qemuDomainPCIAddressReleaseFunction(addrs, slot, function);
+    }
+    return -1;
+}
 
 int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev)
 {
     int ret = 0;
-    if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
-        ret = qemuDomainPCIAddressReserveAddr(addrs, dev);
-    else
-        ret = qemuDomainPCIAddressSetNextAddr(addrs, dev);
+    if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+        /* We do not support hotplug multi-function PCI device now, so we should
+         * reserve the whole slot. The function of the PCI device must be 0.
+         */
+        if (dev->addr.pci.function != 0) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("Only PCI device addresses with function=0"
+                              " are supported"));
+            return -1;
+        }
+
+        ret = qemuDomainPCIAddressReserveSlot(addrs, dev->addr.pci.slot);
+    } else
+        ret = qemuDomainPCIAddressSetNextSlot(addrs, dev);
     return ret;
 }
 
@@ -851,6 +935,48 @@ int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs,
     return ret;
 }
 
+int qemuDomainPCIAddressReleaseFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        int slot, int function)
+{
+    virDomainDeviceInfo dev;
+
+    dev.addr.pci.domain = 0;
+    dev.addr.pci.bus = 0;
+    dev.addr.pci.slot = slot;
+    dev.addr.pci.function = function;
+
+    return qemuDomainPCIAddressReleaseAddr(addrs, &dev);
+}
+
+int qemuDomainPCIAddressReleaseSlot(qemuDomainPCIAddressSetPtr addrs, int slot)
+{
+    virDomainDeviceInfo dev;
+    char *addr;
+    int function;
+    int ret = 0;
+
+    dev.addr.pci.domain = 0;
+    dev.addr.pci.bus = 0;
+    dev.addr.pci.slot = slot;
+
+    for (function = 0; function <= QEMU_PCI_ADDRESS_LAST_FUNCTION; function++) {
+        addr = qemuPCIAddressAsString(&dev);
+        if (!addr)
+            return -1;
+
+        if (!virHashLookup(addrs->used, addr)) {
+            VIR_FREE(addr);
+            continue;
+        }
+
+        VIR_FREE(addr);
+
+        if (qemuDomainPCIAddressReleaseFunction(addrs, slot, function) < 0)
+            ret = -1;
+    }
+
+    return ret;
+}
 
 void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs)
 {
@@ -861,8 +987,7 @@ void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs)
     VIR_FREE(addrs);
 }
 
-
-int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
+int qemuDomainPCIAddressSetNextSlot(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev)
 {
     int i;
@@ -879,6 +1004,56 @@ int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
         maybe.addr.pci.domain = 0;
         maybe.addr.pci.bus = 0;
         maybe.addr.pci.slot = i;
+        maybe.addr.pci.function = 0;
+
+        if (!(addr = qemuPCIAddressAsString(&maybe)))
+            return -1;
+
+        if (qemuDomainPCIAddressCheckSlot(addrs, &maybe) < 0) {
+            VIR_DEBUG("PCI addr %s already in use", addr);
+            VIR_FREE(addr);
+            continue;
+        }
+
+        VIR_DEBUG("Allocating PCI addr %s", addr);
+        VIR_FREE(addr);
+
+        if (qemuDomainPCIAddressReserveSlot(addrs, i) < 0)
+            return -1;
+
+        dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+        dev->addr.pci = maybe.addr.pci;
+
+        qemuGotoNextSlot(addrs, maybe.addr.pci.slot);
+
+        return 0;
+    }
+
+    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                    "%s", _("No more available PCI addresses"));
+    return -1;
+}
+
+int qemuDomainPCIAddressSetNextFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        virDomainDeviceInfoPtr dev)
+{
+    int i;
+    int iteration;
+    int next_devfn;
+
+    next_devfn = QEMU_PCI_ADDRESS_DEVFN(addrs->nextslot, addrs->nextfunction);
+    for (i = next_devfn, iteration = 0;
+         iteration <= QEMU_PCI_ADDRESS_LAST_DEVFN; i++, iteration++) {
+        virDomainDeviceInfo maybe;
+        char *addr;
+
+        if (QEMU_PCI_ADDRESS_LAST_DEVFN < i)
+            i = 0;
+        memset(&maybe, 0, sizeof(maybe));
+        maybe.addr.pci.domain = 0;
+        maybe.addr.pci.bus = 0;
+        maybe.addr.pci.slot = QEMU_PCI_ADDRESS_SLOT(i);
+        maybe.addr.pci.function = QEMU_PCI_ADDRESS_FUNCTION(i);
 
         if (!(addr = qemuPCIAddressAsString(&maybe)))
             return -1;
@@ -899,9 +1074,8 @@ int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
         dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
         dev->addr.pci = maybe.addr.pci;
 
-        addrs->nextslot = i + 1;
-        if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot)
-            addrs->nextslot = 0;
+        qemuGotoNextFunction(addrs, maybe.addr.pci.slot,
+                             maybe.addr.pci.function);
 
         return 0;
     }
@@ -944,7 +1118,8 @@ int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
  * skip over info.type == PCI
  */
 int
-qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
+qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs,
+                         virBitmapPtr qemuCaps)
 {
     int i;
     bool reservedIDE = false;
@@ -1021,16 +1196,28 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
 
         /* Only support VirtIO-9p-pci so far. If that changes,
          * we might need to skip devices here */
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->fss[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->fss[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs, &def->fss[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* Network interfaces */
     for (i = 0; i < def->nnets ; i++) {
         if (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
             continue;
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->nets[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->nets[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs, &def->nets[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* Sound cards */
@@ -1042,8 +1229,15 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
             def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK)
             continue;
 
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->sounds[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->sounds[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->sounds[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* Disk controllers (SCSI only for now) */
@@ -1061,8 +1255,15 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
 
         if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
             continue;
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->controllers[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->controllers[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->controllers[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* Disks (VirtIO only for now */
@@ -1074,8 +1275,15 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
         if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO)
             continue;
 
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->disks[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->disks[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->disks[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* Host PCI devices */
@@ -1086,32 +1294,60 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
             def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
             continue;
 
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->hostdevs[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->hostdevs[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->hostdevs[i]->info) < 0)
+                goto error;
+        }
     }
 
     /* VirtIO balloon */
     if (def->memballoon &&
         def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO &&
         def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->memballoon->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->memballoon->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->memballoon->info) < 0)
+                goto error;
+        }
     }
 
     /* A watchdog - skip IB700, it is not a PCI device */
     if (def->watchdog &&
         def->watchdog->model != VIR_DOMAIN_WATCHDOG_MODEL_IB700 &&
         def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->watchdog->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->watchdog->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->watchdog->info) < 0)
+                goto error;
+        }
     }
 
     /* Further non-primary video cards */
     for (i = 1; i < def->nvideos ; i++) {
         if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
             continue;
-        if (qemuDomainPCIAddressSetNextAddr(addrs, &def->videos[i]->info) < 0)
-            goto error;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (qemuDomainPCIAddressSetNextFunction(addrs,
+                                                    &def->videos[i]->info) < 0)
+                goto error;
+        } else {
+            if (qemuDomainPCIAddressSetNextSlot(addrs,
+                                                &def->videos[i]->info) < 0)
+                goto error;
+        }
     }
     for (i = 0; i < def->ninputs ; i++) {
         /* Nada - none are PCI based (yet) */
@@ -1149,10 +1385,20 @@ qemuBuildDeviceAddressStr(virBufferPtr buf,
                             _("Only PCI device addresses with bus=0 are supported"));
             return -1;
         }
-        if (info->addr.pci.function != 0) {
-            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                            _("Only PCI device addresses with function=0 are supported"));
-            return -1;
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (info->addr.pci.function > 7) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("The function of PCI device addresses must "
+                                  "less than 8"));
+                return -1;
+            }
+        } else {
+            if (info->addr.pci.function != 0) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("Only PCI device addresses with function=0 "
+                                  "are supported"));
+                return -1;
+            }
         }
 
         /* XXX
@@ -1162,9 +1408,14 @@ qemuBuildDeviceAddressStr(virBufferPtr buf,
          * to pciNN.0  where NN is the domain number
          */
         if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIBUS))
-            virBufferAsprintf(buf, ",bus=pci.0,addr=0x%x", info->addr.pci.slot);
+            virBufferAsprintf(buf, ",bus=pci.0");
+        else
+            virBufferAsprintf(buf, ",bus=pci");
+        if (qemuCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION))
+            virBufferAsprintf(buf, ",multifunction=on,addr=0x%x.0x%x",
+                              info->addr.pci.slot, info->addr.pci.function);
         else
-            virBufferAsprintf(buf, ",bus=pci,addr=0x%x", info->addr.pci.slot);
+            virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot);
     }
     return 0;
 }
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 528031d..1a4f471 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -146,19 +146,28 @@ virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
 
 int qemuDomainAssignPCIAddresses(virDomainDefPtr def);
 qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def);
+int qemuDomainPCIAddressReserveFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        int slot, int function);
 int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
                                     int slot);
 int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev);
-int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
+int qemuDomainPCIAddressSetNextSlot(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev);
+int qemuDomainPCIAddressSetNextFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        virDomainDeviceInfoPtr dev);
 int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs,
                                    virDomainDeviceInfoPtr dev);
 int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs,
                                     virDomainDeviceInfoPtr dev);
+int qemuDomainPCIAddressReleaseFunction(qemuDomainPCIAddressSetPtr addrs,
+                                        int slot, int function);
+int qemuDomainPCIAddressReleaseSlot(qemuDomainPCIAddressSetPtr addrs, int slot);
 
 void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs);
-int  qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs);
+int  qemuAssignDevicePCISlots(virDomainDefPtr def,
+                              qemuDomainPCIAddressSetPtr addrs,
+                              virBitmapPtr qemuCaps);
 
 int qemuDomainNetVLAN(virDomainNetDefPtr def);
 int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx);
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index dae2269..4731959 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -221,7 +221,8 @@ error:
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
         (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
         releaseaddr &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        disk->info.addr.pci.slot) < 0)
         VIR_WARN("Unable to release PCI address on %s", disk->src);
 
     if (virSecurityManagerRestoreImageLabel(driver->securityManager,
@@ -290,7 +291,8 @@ cleanup:
         qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
         (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
         releaseaddr &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &controller->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        controller->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on controller");
 
     VIR_FREE(devstr);
@@ -697,7 +699,8 @@ cleanup:
         qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
         (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
         releaseaddr &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        net->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on NIC");
 
     if (ret != 0)
@@ -828,7 +831,8 @@ error:
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
         (hostdev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
         releaseaddr &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &hostdev->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        hostdev->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on host device");
 
     qemuDomainReAttachHostdevDevices(driver, &hostdev, 1);
@@ -1100,6 +1104,30 @@ static inline int qemuFindDisk(virDomainDefPtr def, const char *dst)
     return -1;
 }
 
+static int qemuComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED,
+                                virDomainDeviceInfoPtr dev1,
+                                void *opaque)
+{
+    virDomainDeviceInfoPtr dev2 = opaque;
+
+    if (dev1->type !=  VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI ||
+        dev2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
+        return 0;
+
+    if (dev1->addr.pci.slot == dev2->addr.pci.slot &&
+        dev1->addr.pci.function != dev2->addr.pci.function)
+        return -1;
+    return 0;
+}
+
+static bool qemuIsMultiFunctionDevice(virDomainDefPtr def,
+                                      virDomainDeviceInfoPtr dev)
+{
+    if (virDomainDeviceInfoIterate(def, qemuComparePCIDevice, dev) < 0)
+        return true;
+    return false;
+}
+
 
 int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver,
                                   virDomainObjPtr vm,
@@ -1121,6 +1149,13 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver,
 
     detach = vm->def->disks[i];
 
+    if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("cannot hot unplug multifunction PCI device: %s"),
+                        dev->data.disk->dst);
+        goto cleanup;
+    }
+
     if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
         if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) {
             qemuReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1167,7 +1202,8 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver,
     qemuAuditDisk(vm, detach, NULL, "detach", ret >= 0);
 
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        detach->info.addr.pci.slot) < 0)
         VIR_WARN("Unable to release PCI address on %s", dev->data.disk->src);
 
     virDomainDiskRemove(vm->def, i);
@@ -1351,6 +1387,13 @@ int qemuDomainDetachPciControllerDevice(struct qemud_driver *driver,
         goto cleanup;
     }
 
+    if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("cannot hot unplug multifunction PCI device: %s"),
+                        dev->data.disk->dst);
+        goto cleanup;
+    }
+
     if (qemuDomainControllerIsBusy(vm, detach)) {
         qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
                         _("device cannot be detached: device is busy"));
@@ -1392,7 +1435,8 @@ int qemuDomainDetachPciControllerDevice(struct qemud_driver *driver,
     }
 
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        detach->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on controller");
 
     virDomainControllerDefFree(detach);
@@ -1438,6 +1482,13 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
         goto cleanup;
     }
 
+    if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("cannot hot unplug multifunction PCI device :%s"),
+                        dev->data.disk->dst);
+        goto cleanup;
+    }
+
     if ((vlan = qemuDomainNetVLAN(detach)) < 0) {
         qemuReportError(VIR_ERR_OPERATION_FAILED,
                         "%s", _("unable to determine original VLAN"));
@@ -1484,7 +1535,8 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
     qemuAuditNet(vm, detach, NULL, "detach", true);
 
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        detach->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on NIC");
 
     virDomainConfNWFilterTeardown(detach);
@@ -1567,6 +1619,13 @@ int qemuDomainDetachHostPciDevice(struct qemud_driver *driver,
         return -1;
     }
 
+    if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("cannot hot unplug multifunction PCI device: %s"),
+                        dev->data.disk->dst);
+        return -1;
+    }
+
     if (!virDomainDeviceAddressIsValid(&detach->info,
                                        VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
         qemuReportError(VIR_ERR_OPERATION_FAILED,
@@ -1601,7 +1660,8 @@ int qemuDomainDetachHostPciDevice(struct qemud_driver *driver,
     }
 
     if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
-        qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+        qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+                                        detach->info.addr.pci.slot) < 0)
         VIR_WARN0("Unable to release PCI address on host device");
 
     if (vm->def->nhostdevs > 1) {
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index bd7c932..f3fe78c 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1921,7 +1921,8 @@ qemuProcessReconnect(void *payload, const void *name ATTRIBUTE_UNUSED, void *opa
         priv->persistentAddrs = 1;
 
         if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(obj->def)) ||
-            qemuAssignDevicePCISlots(obj->def, priv->pciaddrs) < 0)
+            qemuAssignDevicePCISlots(obj->def, priv->pciaddrs,
+                                     priv->qemuCaps) < 0)
             goto error;
     }
 
@@ -2187,7 +2188,8 @@ int qemuProcessStart(virConnectPtr conn,
 
 
         /* Assign any remaining addresses */
-        if (qemuAssignDevicePCISlots(vm->def, priv->pciaddrs) < 0)
+        if (qemuAssignDevicePCISlots(vm->def, priv->pciaddrs,
+                                     priv->qemuCaps) < 0)
             goto cleanup;
 
         priv->persistentAddrs = 1;
diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c
index 2522396..2a0a923 100644
--- a/tests/qemuhelptest.c
+++ b/tests/qemuhelptest.c
@@ -430,7 +430,8 @@ mymain(void)
             QEMU_CAPS_VGA_NONE,
             QEMU_CAPS_MIGRATE_QEMU_FD,
             QEMU_CAPS_DRIVE_AIO,
-            QEMU_CAPS_DEVICE_SPICEVMC);
+            QEMU_CAPS_DEVICE_SPICEVMC,
+            QEMU_CAPS_PCI_MULTIFUNCTION);
     DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0,
             QEMU_CAPS_VNC_COLON,
             QEMU_CAPS_NO_REBOOT,
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index a7e4cc0..04b8326 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -96,7 +96,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
         if (!(pciaddrs = qemuDomainPCIAddressSetCreate(vmdef)))
             goto fail;
 
-        if (qemuAssignDevicePCISlots(vmdef, pciaddrs) < 0)
+        if (qemuAssignDevicePCISlots(vmdef, pciaddrs, extraFlags) < 0)
             goto fail;
 
         qemuDomainPCIAddressSetFree(pciaddrs);
-- 
1.7.1




More information about the libvir-list mailing list