[libvirt] PATCH: PCI device passthrough in Xen driver

Daniel P. Berrange berrange at redhat.com
Mon Mar 16 11:34:29 UTC 2009


This patch provides initial support for PCI device passthrough in
Xen, at time of boot. It does not (yet) implement device hotplug
for PCI, since it works in an annoyingly different way to device
hotplug of disks & nics. The patch has several aspects

 - Refactors the pci.c file to make it easier to support both
   Xen's pci-back.ko and upstream's pcistub.ko, which are almost
   identical
 - Add code to pci.c register the PCI device with 'new_slot' before
   invoking 'bind', since pci-back.ko wants this extra step
 - In xen_unified.c, add implmentations of APIs to dettach, reattach
   and reset PCI devices - this just calls into shared pci.c code that
   QEMU/KVM uses.
 - In xend_internal.c implement SEXPR conversion.
 - In xm_internal.c implement parsing of 'pci' config parameter
 - Add lots of test cases

XenD only supports 'unmanaged' PCI devices - ie mgmt app is responsible
for detaching/reattaching PCI devices from/to  host device drivers.
XenD itself won't automatically do this for us, though perhaps someone
might like to implement this as an optional feature for XenD, since it
is more convenient in some cases.

 src/pci.c                                    |  182 +++++++++++++--------
 src/xen_unified.c                            |  122 ++++++++++++++
 src/xend_internal.c                          |  232 +++++++++++++++++++++++++++
 src/xm_internal.c                            |  156 ++++++++++++++++++
 tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr |    2 
 tests/sexpr2xmldata/sexpr2xml-pci-devs.xml   |   37 ++++
 tests/sexpr2xmltest.c                        |    1 
 tests/xmconfigdata/test-pci-devs.cfg         |   24 ++
 tests/xmconfigdata/test-pci-devs.xml         |   56 ++++++
 tests/xmconfigtest.c                         |    1 
 tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr |    1 
 tests/xml2sexprdata/xml2sexpr-pci-devs.xml   |   33 +++
 tests/xml2sexprtest.c                        |    1 
 13 files changed, 784 insertions(+), 64 deletions(-)

Daniel

Index: src/pci.c
===================================================================
RCS file: /data/cvs/libvirt/src/pci.c,v
retrieving revision 1.4
diff -u -p -r1.4 pci.c
--- src/pci.c	3 Mar 2009 17:00:18 -0000	1.4
+++ src/pci.c	16 Mar 2009 11:31:29 -0000
@@ -614,46 +614,80 @@ pciResetDevice(virConnectPtr conn, pciDe
     return ret;
 }
 
-static int
-pciBindDeviceToStub(virConnectPtr conn, pciDevice *dev, const char *stub_module)
+
+static void
+pciDriverDir(char *buf, size_t buflen, const char *driver)
 {
-    char stub_dir[PATH_MAX];
-    char path[PATH_MAX];
+    snprintf(buf, buflen, PCI_SYSFS "drivers/%s", driver);
+}
 
-    snprintf(stub_dir, sizeof(stub_dir), PCI_SYSFS "drivers/%s", stub_module);
+static void
+pciDriverFile(char *buf, size_t buflen, const char *driver, const char *file)
+{
+    snprintf(buf, buflen, PCI_SYSFS "drivers/%s/%s", driver, file);
+}
 
-    /* Try loading the stub module if it isn't already loaded;
-     * Do not return an error if the stub module is not available.
-     */
-    if (!virFileExists(stub_dir)) {
-        const char *const modprobeargv[] = { MODPROBE, stub_module, NULL };
+static void
+pciDeviceFile(char *buf, size_t buflen, const char *device, const char *file)
+{
+    snprintf(buf, buflen, PCI_SYSFS "devices/%s/%s", device, file);
+}
+
+
+static const char *
+pciFindStubDriver(virConnectPtr conn)
+{
+    char drvpath[PATH_MAX];
+    int probed = 0;
 
-        if (virRun(conn, modprobeargv, NULL) < 0) {
+recheck:
+    pciDriverDir(drvpath, sizeof(drvpath), "pci-stub");
+    if (virFileExists(drvpath))
+        return "pci-stub";
+    pciDriverDir(drvpath, sizeof(drvpath), "pciback");
+    if (virFileExists(drvpath))
+        return "pciback";
+
+    if (!probed) {
+        const char *const stubprobe[] = { MODPROBE, "pci-stub", NULL };
+        const char *const backprobe[] = { MODPROBE, "pciback", NULL };
+
+        probed = 1;
+        if (virRun(conn, stubprobe, NULL) < 0 &&
+            virRun(conn, backprobe, NULL) < 0) {
             char ebuf[1024];
-            VIR_WARN(_("modprobe %s failed: %s"), stub_module,
+            VIR_WARN(_("failed to load pci-stub or pciback drivers: %s"),
                      virStrerror(errno, ebuf, sizeof ebuf));
+            return 0;
         }
+
+        goto recheck;
     }
 
-    if (!virFileExists(stub_dir)) {
-        VIR_WARN(_("%s module not available, cannot bind device %s to it"),
-                 stub_module, dev->name);
-    } else {
-        /* Add the PCI device ID to the stub's dynamic ID table;
-         * this is needed to allow us to bind the device to the stub.
-         * Note: if the device is not currently bound to any driver,
-         * stub will immediately be bound to the device. Also, note
-         * that if a new device with this ID is hotplugged, or if a probe
-         * is triggered for such a device, it will also be immediately
-         * bound by the stub.
-         */
-        snprintf(path, sizeof(path), "%s/new_id", stub_dir);
-        if (virFileWriteStr(path, dev->id) < 0) {
-            virReportSystemError(conn, errno,
-                                 _("Failed to add PCI device ID '%s' to %s"),
-                                 dev->id, stub_module);
-            return -1;
-        }
+    return NULL;
+}
+
+
+static int
+pciBindDeviceToStub(virConnectPtr conn, pciDevice *dev, const char *driver)
+{
+    char drvdir[PATH_MAX];
+    char path[PATH_MAX];
+
+    /* Add the PCI device ID to the stub's dynamic ID table;
+     * this is needed to allow us to bind the device to the stub.
+     * Note: if the device is not currently bound to any driver,
+     * stub will immediately be bound to the device. Also, note
+     * that if a new device with this ID is hotplugged, or if a probe
+     * is triggered for such a device, it will also be immediately
+     * bound by the stub.
+     */
+    pciDriverFile(path, sizeof(path), driver, "new_id");
+    if (virFileWriteStr(path, dev->id) < 0) {
+        virReportSystemError(conn, errno,
+                             _("Failed to add PCI device ID '%s' to %s"),
+                             dev->id, driver);
+        return -1;
     }
 
     /* If the device is already bound to a driver, unbind it.
@@ -661,66 +695,79 @@ pciBindDeviceToStub(virConnectPtr conn, 
      * PCI device happens to be IDE controller for the disk hosting
      * your root filesystem.
      */
-    snprintf(path, sizeof(path),
-             PCI_SYSFS "devices/%s/driver/unbind", dev->name);
+    pciDeviceFile(path, sizeof(path), dev->name, "driver/unbind");
     if (virFileExists(path) && virFileWriteStr(path, dev->name) < 0) {
         virReportSystemError(conn, errno,
                              _("Failed to unbind PCI device '%s'"), dev->name);
         return -1;
     }
 
-    if (virFileExists(stub_dir)) {
-        /* If the device isn't already bound to pci-stub, try binding it now.
-         */
-        snprintf(path, sizeof(path), PCI_SYSFS "devices/%s/driver", dev->name);
-        if (!virFileLinkPointsTo(path, stub_dir)) {
-            snprintf(path, sizeof(path), "%s/bind", stub_dir);
-            if (virFileWriteStr(path, dev->name) < 0) {
-                virReportSystemError(conn, errno,
-                                     _("Failed to bind PCI device '%s' to %s"),
-                                     dev->name, stub_module);
-                return -1;
-            }
+    /* If the device isn't already bound to pci-stub, try binding it now.
+     */
+    pciDriverDir(drvdir, sizeof(drvdir), driver);
+    pciDeviceFile(path, sizeof(path), dev->name, "driver");
+    if (!virFileLinkPointsTo(path, drvdir)) {
+        /* Xen's pciback.ko wants you to use new_slot first */
+        pciDriverFile(path, sizeof(path), driver, "new_slot");
+        if (virFileExists(path) && virFileWriteStr(path, dev->name) < 0) {
+            virReportSystemError(conn, errno,
+                                 _("Failed to add slot for PCI device '%s' to %s"),
+                                 dev->name, driver);
+            return -1;
         }
 
-        /* If 'remove_id' exists, remove the device id from pci-stub's dynamic
-         * ID table so that 'drivers_probe' works below.
-         */
-        snprintf(path, sizeof(path), "%s/remove_id", stub_dir);
-        if (virFileExists(path) && virFileWriteStr(path, dev->id) < 0) {
+        pciDriverFile(path, sizeof(path), driver, "bind");
+        if (virFileWriteStr(path, dev->name) < 0) {
             virReportSystemError(conn, errno,
-                                 _("Failed to remove PCI ID '%s' from %s"),
-                                 dev->id, stub_module);
+                                 _("Failed to bind PCI device '%s' to %s"),
+                                 dev->name, driver);
             return -1;
         }
     }
 
+    /* If 'remove_id' exists, remove the device id from pci-stub's dynamic
+     * ID table so that 'drivers_probe' works below.
+     */
+    pciDriverFile(path, sizeof(path), driver, "remove_id");
+    if (virFileExists(path) && virFileWriteStr(path, dev->id) < 0) {
+        virReportSystemError(conn, errno,
+                             _("Failed to remove PCI ID '%s' from %s"),
+                             dev->id, driver);
+        return -1;
+    }
+
     return 0;
 }
 
 int
 pciDettachDevice(virConnectPtr conn, pciDevice *dev)
 {
-    return pciBindDeviceToStub(conn, dev, "pci-stub");
+    const char *driver = pciFindStubDriver(conn);
+    if (!driver) {
+        pciReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot find any PCI stub module"));
+        return -1;
+    }
+
+    return pciBindDeviceToStub(conn, dev, driver);
 }
 
 static int
-pciUnBindDeviceFromStub(virConnectPtr conn, pciDevice *dev, const char *stub_module)
+pciUnBindDeviceFromStub(virConnectPtr conn, pciDevice *dev, const char *driver)
 {
-    char stub_dir[PATH_MAX];
+    char drvdir[PATH_MAX];
     char path[PATH_MAX];
 
-    snprintf(stub_dir, sizeof(stub_dir), PCI_SYSFS "drivers/%s", stub_module);
-
     /* If the device is bound to stub, unbind it.
      */
-    snprintf(path, sizeof(path), PCI_SYSFS "devices/%s/driver", dev->name);
-    if (virFileExists(stub_dir) && virFileLinkPointsTo(path, stub_dir)) {
-        snprintf(path, sizeof(path), "%s/unbind", stub_dir);
+    pciDriverDir(drvdir, sizeof(drvdir), driver);
+    pciDeviceFile(path, sizeof(path), dev->name, "driver");
+    if (virFileExists(drvdir) && virFileLinkPointsTo(path, drvdir)) {
+        pciDriverFile(path, sizeof(path), driver, "unbind");
         if (virFileWriteStr(path, dev->name) < 0) {
             virReportSystemError(conn, errno,
                                  _("Failed to bind PCI device '%s' to %s"),
-                                 dev->name, stub_module);
+                                 dev->name, driver);
             return -1;
         }
     }
@@ -730,8 +777,8 @@ pciUnBindDeviceFromStub(virConnectPtr co
      * available, then re-probing would just cause the device to be
      * re-bound to the stub.
      */
-    snprintf(path, sizeof(path), "%s/remove_id", stub_dir);
-    if (!virFileExists(stub_dir) || virFileExists(path)) {
+    pciDriverFile(path, sizeof(path), driver, "remove_id");
+    if (!virFileExists(drvdir) || virFileExists(path)) {
         if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name) < 0) {
             virReportSystemError(conn, errno,
                                  _("Failed to trigger a re-probe for PCI device '%s'"),
@@ -746,7 +793,14 @@ pciUnBindDeviceFromStub(virConnectPtr co
 int
 pciReAttachDevice(virConnectPtr conn, pciDevice *dev)
 {
-    return pciUnBindDeviceFromStub(conn, dev, "pci-stub");
+    const char *driver = pciFindStubDriver(conn);
+    if (!driver) {
+        pciReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot find any PCI stub module"));
+        return -1;
+    }
+
+    return pciUnBindDeviceFromStub(conn, dev, driver);
 }
 
 static char *
Index: src/xen_unified.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_unified.c,v
retrieving revision 1.83
diff -u -p -r1.83 xen_unified.c
--- src/xen_unified.c	5 Feb 2009 16:03:11 -0000	1.83
+++ src/xen_unified.c	16 Mar 2009 11:31:29 -0000
@@ -43,6 +43,8 @@
 #include "xml.h"
 #include "util.h"
 #include "memory.h"
+#include "node_device_conf.h"
+#include "pci.h"
 
 #define VIR_FROM_THIS VIR_FROM_XEN
 
@@ -1420,6 +1422,123 @@ xenUnifiedDomainEventDeregister (virConn
     return ret;
 }
 
+
+static int
+xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev,
+                           unsigned *domain,
+                           unsigned *bus,
+                           unsigned *slot,
+                           unsigned *function)
+{
+    virNodeDeviceDefPtr def = NULL;
+    virNodeDevCapsDefPtr cap;
+    char *xml = NULL;
+    int ret = -1;
+
+    xml = virNodeDeviceGetXMLDesc(dev, 0);
+    if (!xml)
+        goto out;
+
+    def = virNodeDeviceDefParseString(dev->conn, xml);
+    if (!def)
+        goto out;
+
+    cap = def->caps;
+    while (cap) {
+        if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) {
+            *domain   = cap->data.pci_dev.domain;
+            *bus      = cap->data.pci_dev.bus;
+            *slot     = cap->data.pci_dev.slot;
+            *function = cap->data.pci_dev.function;
+            break;
+        }
+
+        cap = cap->next;
+    }
+
+    if (!cap) {
+        xenUnifiedError(dev->conn, VIR_ERR_INVALID_ARG,
+                        _("device %s is not a PCI device"), dev->name);
+        goto out;
+    }
+
+    ret = 0;
+out:
+    virNodeDeviceDefFree(def);
+    VIR_FREE(xml);
+    return ret;
+}
+
+static int
+xenUnifiedNodeDeviceDettach (virNodeDevicePtr dev)
+{
+    pciDevice *pci;
+    unsigned domain, bus, slot, function;
+    int ret = -1;
+
+    if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0)
+        return -1;
+
+    pci = pciGetDevice(dev->conn, domain, bus, slot, function);
+    if (!pci)
+        return -1;
+
+    if (pciDettachDevice(dev->conn, pci) < 0)
+        goto out;
+
+    ret = 0;
+out:
+    pciFreeDevice(dev->conn, pci);
+    return ret;
+}
+
+static int
+xenUnifiedNodeDeviceReAttach (virNodeDevicePtr dev)
+{
+    pciDevice *pci;
+    unsigned domain, bus, slot, function;
+    int ret = -1;
+
+    if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0)
+        return -1;
+
+    pci = pciGetDevice(dev->conn, domain, bus, slot, function);
+    if (!pci)
+        return -1;
+
+    if (pciReAttachDevice(dev->conn, pci) < 0)
+        goto out;
+
+    ret = 0;
+out:
+    pciFreeDevice(dev->conn, pci);
+    return ret;
+}
+
+static int
+xenUnifiedNodeDeviceReset (virNodeDevicePtr dev)
+{
+    pciDevice *pci;
+    unsigned domain, bus, slot, function;
+    int ret = -1;
+
+    if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0)
+        return -1;
+
+    pci = pciGetDevice(dev->conn, domain, bus, slot, function);
+    if (!pci)
+        return -1;
+
+    if (pciResetDevice(dev->conn, pci) < 0)
+        goto out;
+
+    ret = 0;
+out:
+    pciFreeDevice(dev->conn, pci);
+    return ret;
+}
+
+
 /*----- Register with libvirt.c, and initialise Xen drivers. -----*/
 
 /* The interface which we export upwards to libvirt.c. */
@@ -1481,6 +1600,9 @@ static virDriver xenUnifiedDriver = {
     .getFreeMemory = xenUnifiedNodeGetFreeMemory,
     .domainEventRegister = xenUnifiedDomainEventRegister,
     .domainEventDeregister = xenUnifiedDomainEventDeregister,
+    .nodeDeviceDettach = xenUnifiedNodeDeviceDettach,
+    .nodeDeviceReAttach = xenUnifiedNodeDeviceReAttach,
+    .nodeDeviceReset = xenUnifiedNodeDeviceReset,
 };
 
 /**
Index: src/xend_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xend_internal.c,v
retrieving revision 1.253
diff -u -p -r1.253 xend_internal.c
--- src/xend_internal.c	10 Mar 2009 11:13:32 -0000	1.253
+++ src/xend_internal.c	16 Mar 2009 11:31:29 -0000
@@ -92,6 +92,11 @@ xenDaemonFormatSxprNet(virConnectPtr con
                        int xendConfigVersion,
                        int isAttach);
 static int
+xenDaemonFormatSxprOnePCI(virConnectPtr conn,
+                          virDomainHostdevDefPtr def,
+                          virBufferPtr buf);
+
+static int
 virDomainXMLDevID(virDomainPtr domain,
                   virDomainDeviceDefPtr dev,
                   char *class,
@@ -2145,6 +2150,131 @@ error:
     return -1;
 }
 
+/**
+ * xenDaemonParseSxprPCI
+ * @conn: connection
+ * @root: root sexpr
+ *
+ * This parses out block devices from the domain sexpr
+ *
+ * Returns 0 if successful or -1 if failed.
+ */
+static int
+xenDaemonParseSxprPCI(virConnectPtr conn,
+                      virDomainDefPtr def,
+                      const struct sexpr *root)
+{
+    const struct sexpr *cur, *tmp = NULL, *node;
+    virDomainHostdevDefPtr dev = NULL;
+
+    /*
+     * With the (domain ...) block we have the following odd setup
+     *
+     * (device
+     *    (pci
+     *       (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0))
+     *       (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0))
+     *    )
+     * )
+     *
+     * Normally there is one (device ...) block per device, but in
+     * wierd world of Xen PCI, once (device ...) covers multiple
+     * devices.
+     */
+
+    for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
+        node = cur->u.s.car;
+        if ((tmp = sexpr_lookup(node, "device/pci")) != NULL)
+            break;
+    }
+
+    if (!tmp)
+        return 0;
+
+    for (cur = tmp; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
+        const char *domain = NULL;
+        const char *bus = NULL;
+        const char *slot = NULL;
+        const char *func = NULL;
+        int domainID;
+        int busID;
+        int slotID;
+        int funcID;
+
+        node = cur->u.s.car;
+        if (!sexpr_lookup(node, "dev"))
+            continue;
+
+        if (!(domain = sexpr_node(node, "dev/domain"))) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("missing PCI domain"));
+            goto error;
+        }
+        if (!(bus = sexpr_node(node, "dev/bus"))) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("missing PCI bus"));
+            goto error;
+        }
+        if (!(slot = sexpr_node(node, "dev/slot"))) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("missing PCI slot"));
+            goto error;
+        }
+        if (!(func = sexpr_node(node, "dev/func"))) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("missing PCI func"));
+            goto error;
+        }
+
+        if (virStrToLong_i(domain, NULL, 0, &domainID) < 0) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("cannot parse PCI domain '%s'"), domain);
+            goto error;
+        }
+        if (virStrToLong_i(bus, NULL, 0, &busID) < 0) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("cannot parse PCI bus '%s'"), bus);
+            goto error;
+        }
+        if (virStrToLong_i(slot, NULL, 0, &slotID) < 0) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("cannot parse PCI slot '%s'"), slot);
+            goto error;
+        }
+        if (virStrToLong_i(func, NULL, 0, &funcID) < 0) {
+            virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+                         _("cannot parse PCI func '%s'"), func);
+            goto error;
+        }
+
+        if (VIR_ALLOC(dev) < 0)
+            goto no_memory;
+
+        dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
+        dev->managed = 0;
+        dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+        dev->source.subsys.u.pci.domain = domainID;
+        dev->source.subsys.u.pci.bus = busID;
+        dev->source.subsys.u.pci.slot = slotID;
+        dev->source.subsys.u.pci.function = funcID;
+
+        if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) {
+            goto no_memory;
+        }
+
+        def->hostdevs[def->nhostdevs++] = dev;
+    }
+
+    return 0;
+
+no_memory:
+    virReportOOMError(conn);
+
+error:
+    virDomainHostdevDefFree(dev);
+    return -1;
+}
+
 
 /**
  * xenDaemonParseSxpr:
@@ -2310,6 +2440,9 @@ xenDaemonParseSxpr(virConnectPtr conn,
     if (xenDaemonParseSxprNets(conn, def, root) < 0)
         goto error;
 
+    if (xenDaemonParseSxprPCI(conn, def, root) < 0)
+        goto error;
+
     /* New style graphics device config */
     if (xenDaemonParseSxprGraphicsNew(conn, def, root) < 0)
         goto error;
@@ -3953,6 +4086,20 @@ xenDaemonAttachDevice(virDomainPtr domai
             goto cleanup;
         break;
 
+    case VIR_DOMAIN_DEVICE_HOSTDEV:
+        if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+            if (xenDaemonFormatSxprOnePCI(domain->conn,
+                                          dev->data.hostdev,
+                                          &buf) < 0)
+                goto cleanup;
+        } else {
+            virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s",
+                         _("unsupported device type"));
+            goto cleanup;
+        }
+        break;
+
     default:
         virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s",
                      _("unsupported device type"));
@@ -5263,6 +5410,85 @@ xenDaemonFormatSxprNet(virConnectPtr con
     return 0;
 }
 
+
+static void
+xenDaemonFormatSxprPCI(virDomainHostdevDefPtr def,
+                       virBufferPtr buf)
+{
+    virBufferVSprintf(buf, "(dev (domain 0x%04x)(bus 0x%02x)(slot 0x%02x)(func 0x%x))",
+                      def->source.subsys.u.pci.domain,
+                      def->source.subsys.u.pci.bus,
+                      def->source.subsys.u.pci.slot,
+                      def->source.subsys.u.pci.function);
+}
+
+static int
+xenDaemonFormatSxprOnePCI(virConnectPtr conn,
+                          virDomainHostdevDefPtr def,
+                          virBufferPtr buf)
+{
+    if (def->managed) {
+        virXendError(conn, VIR_ERR_NO_SUPPORT, "%s",
+                     _("managed PCI devices not supported with XenD"));
+        return -1;
+    }
+
+    virBufferAddLit(buf, "(pci ");
+    xenDaemonFormatSxprPCI(def, buf);
+    virBufferAddLit(buf, ")");
+
+    return 0;
+}
+
+static int
+xenDaemonFormatSxprAllPCI(virConnectPtr conn,
+                          virDomainDefPtr def,
+                          virBufferPtr buf)
+{
+    int hasPCI = 0;
+    int i;
+
+    for (i = 0 ; i < def->nhostdevs ; i++)
+        if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+            hasPCI = 1;
+
+    if (!hasPCI)
+        return 0;
+
+    /*
+     * With the (domain ...) block we have the following odd setup
+     *
+     * (device
+     *    (pci
+     *       (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0))
+     *       (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0))
+     *    )
+     * )
+     *
+     * Normally there is one (device ...) block per device, but in
+     * wierd world of Xen PCI, once (device ...) covers multiple
+     * devices.
+     */
+
+    virBufferAddLit(buf, "(device (pci ");
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+            if (def->hostdevs[i]->managed) {
+                virXendError(conn, VIR_ERR_NO_SUPPORT, "%s",
+                             _("managed PCI devices not supported with XenD"));
+                return -1;
+            }
+
+            xenDaemonFormatSxprPCI(def->hostdevs[i], buf);
+        }
+    }
+    virBufferAddLit(buf, "))");
+
+    return 0;
+}
+
 int
 xenDaemonFormatSxprSound(virConnectPtr conn,
                          virDomainDefPtr def,
@@ -5529,6 +5755,9 @@ xenDaemonFormatSxpr(virConnectPtr conn,
                                    &buf, hvm, xendConfigVersion, 0) < 0)
             goto error;
 
+    if (xenDaemonFormatSxprAllPCI(conn, def, &buf) < 0)
+        goto error;
+
     /* New style PV graphics config xen >= 3.0.4,
      * or HVM graphics config xen >= 3.0.5 */
     if ((xendConfigVersion >= XEND_CONFIG_MIN_VERS_PVFB_NEWCONF && !hvm) ||
@@ -5611,6 +5840,9 @@ virDomainXMLDevID(virDomainPtr domain,
         strncpy(ref, xref, ref_len);
         free(xref);
         ref[ref_len - 1] = '\0';
+    } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
+               dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+               dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
     } else {
         virXendError(NULL, VIR_ERR_NO_SUPPORT,
                      "%s", _("hotplug of device type not supported"));
Index: src/xm_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xm_internal.c,v
retrieving revision 1.117
diff -u -p -r1.117 xm_internal.c
--- src/xm_internal.c	5 Feb 2009 16:03:11 -0000	1.117
+++ src/xm_internal.c	16 Mar 2009 11:31:29 -0000
@@ -673,6 +673,7 @@ xenXMDomainConfigParse(virConnectPtr con
     virDomainDiskDefPtr disk = NULL;
     virDomainNetDefPtr net = NULL;
     virDomainGraphicsDefPtr graphics = NULL;
+    virDomainHostdevDefPtr hostdev = NULL;
     int i;
     const char *defaultArch, *defaultMachine;
 
@@ -1115,6 +1116,88 @@ xenXMDomainConfigParse(virConnectPtr con
         }
     }
 
+    list = virConfGetValue(conf, "pci");
+    if (list && list->type == VIR_CONF_LIST) {
+        list = list->list;
+        while (list) {
+            char domain[5];
+            char bus[3];
+            char slot[3];
+            char func[2];
+            char *key, *nextkey;
+            int domainID;
+            int busID;
+            int slotID;
+            int funcID;
+
+            domain[0] = bus[0] = slot[0] = func[0] = '\0';
+
+            if ((list->type != VIR_CONF_STRING) || (list->str == NULL))
+                goto skippci;
+
+            /* pci=['0000:00:1b.0','0000:00:13.0'] */
+            key = list->str;
+            if (!(key = list->str))
+                goto skippci;
+            if (!(nextkey = strchr(key, ':')))
+                goto skippci;
+
+            if ((nextkey - key) > (sizeof(domain)-1))
+                goto skippci;
+
+            strncpy(domain, key, sizeof(domain));
+            domain[sizeof(domain)-1] = '\0';
+
+            key = nextkey + 1;
+            if (!(nextkey = strchr(key, ':')))
+                goto skippci;
+
+            strncpy(bus, key, sizeof(bus));
+            bus[sizeof(bus)-1] = '\0';
+
+            key = nextkey + 1;
+            if (!(nextkey = strchr(key, '.')))
+                goto skippci;
+
+            strncpy(slot, key, sizeof(slot));
+            slot[sizeof(slot)-1] = '\0';
+
+            key = nextkey + 1;
+            if (strlen(key) != 1)
+                goto skippci;
+
+            strncpy(func, key, sizeof(func));
+            func[sizeof(func)-1] = '\0';
+
+            if (virStrToLong_i(domain, NULL, 16, &domainID) < 0)
+                goto skippci;
+            if (virStrToLong_i(bus, NULL, 16, &busID) < 0)
+                goto skippci;
+            if (virStrToLong_i(slot, NULL, 16, &slotID) < 0)
+                goto skippci;
+            if (virStrToLong_i(func, NULL, 16, &funcID) < 0)
+                goto skippci;
+
+            if (VIR_ALLOC(hostdev) < 0)
+                goto cleanup;
+
+            hostdev->managed = 0;
+            hostdev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+            hostdev->source.subsys.u.pci.domain = domainID;
+            hostdev->source.subsys.u.pci.bus = busID;
+            hostdev->source.subsys.u.pci.slot = slotID;
+            hostdev->source.subsys.u.pci.function = funcID;
+
+            if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0)
+                goto no_memory;
+            def->hostdevs[def->nhostdevs++] = hostdev;
+            hostdev = NULL;
+
+        skippci:
+            list = list->next;
+        }
+    }
+
     if (hvm) {
         if (xenXMConfigGetString(conn, conf, "usbdevice", &str, NULL) < 0)
             goto cleanup;
@@ -1939,6 +2022,76 @@ cleanup:
 
 
 
+static int
+xenXMDomainConfigFormatPCI(virConnectPtr conn,
+                           virConfPtr conf,
+                           virDomainDefPtr def)
+{
+
+    virConfValuePtr pciVal = NULL;
+    int hasPCI = 0;
+    int i;
+
+    for (i = 0 ; i < def->nhostdevs ; i++)
+        if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+            hasPCI = 1;
+
+    if (!hasPCI)
+        return 0;
+
+    if (VIR_ALLOC(pciVal) < 0)
+        return -1;
+
+    pciVal->type = VIR_CONF_LIST;
+    pciVal->list = NULL;
+
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+            virConfValuePtr val, tmp;
+            char *buf;
+
+            if (virAsprintf(&buf, "%04x:%02x:%02x.%x",
+                            def->hostdevs[i]->source.subsys.u.pci.domain,
+                            def->hostdevs[i]->source.subsys.u.pci.bus,
+                            def->hostdevs[i]->source.subsys.u.pci.slot,
+                            def->hostdevs[i]->source.subsys.u.pci.function) < 0)
+                goto error;
+
+            if (VIR_ALLOC(val) < 0) {
+                VIR_FREE(buf);
+                virReportOOMError(conn);
+                goto error;
+            }
+            val->type = VIR_CONF_STRING;
+            val->str = buf;
+            tmp = pciVal->list;
+            while (tmp && tmp->next)
+                tmp = tmp->next;
+            if (tmp)
+                tmp->next = val;
+            else
+                pciVal->list = val;
+        }
+    }
+
+    if (pciVal->list != NULL) {
+        int ret = virConfSetValue(conf, "pci", pciVal);
+        pciVal = NULL;
+        if (ret < 0)
+            return -1;
+    }
+    VIR_FREE(pciVal);
+
+    return 0;
+
+error:
+    virConfFreeValue(pciVal);
+    return -1;
+}
+
+
 virConfPtr xenXMDomainConfigFormat(virConnectPtr conn,
                                    virDomainDefPtr def) {
     virConfPtr conf = NULL;
@@ -2260,6 +2413,9 @@ virConfPtr xenXMDomainConfigFormat(virCo
     }
     VIR_FREE(netVal);
 
+    if (xenXMDomainConfigFormatPCI(conn, conf, def) < 0)
+        goto cleanup;
+
     if (hvm) {
         if (def->nparallels) {
             virBuffer buf = VIR_BUFFER_INITIALIZER;
Index: tests/sexpr2xmltest.c
===================================================================
RCS file: /data/cvs/libvirt/tests/sexpr2xmltest.c,v
retrieving revision 1.36
diff -u -p -r1.36 sexpr2xmltest.c
--- tests/sexpr2xmltest.c	29 Jan 2009 17:02:00 -0000	1.36
+++ tests/sexpr2xmltest.c	16 Mar 2009 11:31:29 -0000
@@ -142,6 +142,7 @@ mymain(int argc, char **argv)
     DO_TEST("net-e1000", "net-e1000", 2);
     DO_TEST("bridge-ipaddr", "bridge-ipaddr", 3);
     DO_TEST("no-source-cdrom", "no-source-cdrom", 2);
+    DO_TEST("pci-devs", "pci-devs", 2);
 
     DO_TEST("fv-utc", "fv-utc", 1);
     DO_TEST("fv-localtime", "fv-localtime", 1);
Index: tests/xmconfigtest.c
===================================================================
RCS file: /data/cvs/libvirt/tests/xmconfigtest.c,v
retrieving revision 1.26
diff -u -p -r1.26 xmconfigtest.c
--- tests/xmconfigtest.c	8 Jan 2009 19:52:15 -0000	1.26
+++ tests/xmconfigtest.c	16 Mar 2009 11:31:29 -0000
@@ -231,6 +231,7 @@ mymain(int argc, char **argv)
 
     DO_TEST("escape-paths", 2);
     DO_TEST("no-source-cdrom", 2);
+    DO_TEST("pci-devs", 2);
 
     virCapabilitiesFree(caps);
 
Index: tests/xml2sexprtest.c
===================================================================
RCS file: /data/cvs/libvirt/tests/xml2sexprtest.c,v
retrieving revision 1.34
diff -u -p -r1.34 xml2sexprtest.c
--- tests/xml2sexprtest.c	23 Jan 2009 01:48:47 -0000	1.34
+++ tests/xml2sexprtest.c	16 Mar 2009 11:31:29 -0000
@@ -128,6 +128,7 @@ mymain(int argc, char **argv)
     DO_TEST("net-e1000", "net-e1000", "pvtest", 2);
     DO_TEST("bridge-ipaddr", "bridge-ipaddr", "pvtest", 2);
     DO_TEST("no-source-cdrom", "no-source-cdrom", "test", 2);
+    DO_TEST("pci-devs", "pci-devs", "pvtest", 2);
 
     DO_TEST("fv-utc", "fv-utc", "fvtest", 1);
     DO_TEST("fv-localtime", "fv-localtime", "fvtest", 1);
Index: tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr
===================================================================
RCS file: tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr
diff -N tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1,2 @@
+(domain (domid 6)(name 'pvtest')(memory 420)(maxmem 420)(vcpus 2)(uuid '596a5d2171f48fb2e068e2386a5c413e')(on_poweroff 'destroy')(on_reboot 'destroy')(on_crash 'destroy')(image (linux (kernel '/var/lib/xen/vmlinuz.2Dn2YT')(ramdisk '/var/lib/xen/initrd.img.0u-Vhq')(args ' method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os  ')))(device (pci (backend 0)(dev (domain 0x0001) (bus 0x0c) (slot 0x1b) (func 0x2))(dev (domain 0x0000) (bus 0x01) (slot 0x13) (func 0x0))))(device (vbd (dev 'xvda')(uname 'phy:/dev/MainVG/GuestVG')(mode 'w'))))
+
Index: tests/sexpr2xmldata/sexpr2xml-pci-devs.xml
===================================================================
RCS file: tests/sexpr2xmldata/sexpr2xml-pci-devs.xml
diff -N tests/sexpr2xmldata/sexpr2xml-pci-devs.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/sexpr2xmldata/sexpr2xml-pci-devs.xml	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1,37 @@
+<domain type='xen' id='6'>
+  <name>pvtest</name>
+  <uuid>596a5d21-71f4-8fb2-e068-e2386a5c413e</uuid>
+  <memory>430080</memory>
+  <currentMemory>430080</currentMemory>
+  <vcpu>2</vcpu>
+  <os>
+    <type>linux</type>
+    <kernel>/var/lib/xen/vmlinuz.2Dn2YT</kernel>
+    <initrd>/var/lib/xen/initrd.img.0u-Vhq</initrd>
+    <cmdline> method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os  </cmdline>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <disk type='block' device='disk'>
+      <driver name='phy'/>
+      <source dev='/dev/MainVG/GuestVG'/>
+      <target dev='xvda' bus='xen'/>
+    </disk>
+    <console type='pty'>
+      <target port='0'/>
+    </console>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/>
+      </source>
+    </hostdev>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/>
+      </source>
+    </hostdev>
+  </devices>
+</domain>
Index: tests/xmconfigdata/test-pci-devs.cfg
===================================================================
RCS file: tests/xmconfigdata/test-pci-devs.cfg
diff -N tests/xmconfigdata/test-pci-devs.cfg
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/xmconfigdata/test-pci-devs.cfg	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1,24 @@
+name = "test"
+uuid = "cc2315e7-d26a-307a-438c-6d188ec4c09c"
+maxmem = 382
+memory = 350
+vcpus = 1
+builder = "hvm"
+kernel = "/usr/lib/xen/boot/hvmloader"
+boot = "c"
+pae = 1
+acpi = 1
+apic = 1
+localtime = 0
+on_poweroff = "destroy"
+on_reboot = "destroy"
+on_crash = "destroy"
+device_model = "/usr/lib/xen/bin/qemu-dm"
+sdl = 0
+vnc = 1
+vncunused = 1
+disk = [ "phy:/dev/sda8,hda,w", ",hdc:cdrom,r" ]
+vif = [ "mac=00:16:3e:0a:7b:39,bridge=xenbr0,type=ioemu" ]
+pci = [ "0001:0c:1b.2", "0000:01:13.0" ]
+parallel = "none"
+serial = "pty"
Index: tests/xmconfigdata/test-pci-devs.xml
===================================================================
RCS file: tests/xmconfigdata/test-pci-devs.xml
diff -N tests/xmconfigdata/test-pci-devs.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/xmconfigdata/test-pci-devs.xml	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1,56 @@
+<domain type='xen'>
+  <name>test</name>
+  <uuid>cc2315e7-d26a-307a-438c-6d188ec4c09c</uuid>
+  <memory>391168</memory>
+  <currentMemory>358400</currentMemory>
+  <vcpu>1</vcpu>
+  <os>
+    <type arch='i686' machine='xenfv'>hvm</type>
+    <loader>/usr/lib/xen/boot/hvmloader</loader>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <pae/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/lib/xen/bin/qemu-dm</emulator>
+    <disk type='block' device='disk'>
+      <driver name='phy'/>
+      <source dev='/dev/sda8'/>
+      <target dev='hda' bus='ide'/>
+    </disk>
+    <disk type='block' device='cdrom'>
+      <driver name='phy'/>
+      <target dev='hdc' bus='ide'/>
+      <readonly/>
+    </disk>
+    <interface type='bridge'>
+      <mac address='00:16:3e:0a:7b:39'/>
+      <source bridge='xenbr0'/>
+    </interface>
+    <serial type='pty'>
+      <target port='0'/>
+    </serial>
+    <console type='pty'>
+      <target port='0'/>
+    </console>
+    <input type='mouse' bus='ps2'/>
+    <graphics type='vnc' port='-1' autoport='yes'/>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/>
+      </source>
+    </hostdev>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/>
+      </source>
+    </hostdev>
+  </devices>
+</domain>
Index: tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr
===================================================================
RCS file: tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr
diff -N tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1 @@
+(vm (name 'pvtest')(memory 420)(maxmem 420)(vcpus 2)(uuid '596a5d21-71f4-8fb2-e068-e2386a5c413e')(on_poweroff 'destroy')(on_reboot 'destroy')(on_crash 'destroy')(image (linux (kernel '/var/lib/xen/vmlinuz.2Dn2YT')(ramdisk '/var/lib/xen/initrd.img.0u-Vhq')(args ' method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os  ')))(device (vbd (dev 'xvda')(uname 'phy:/dev/MainVG/GuestLV')(mode 'w')))(device (pci (dev (domain 0x0001)(bus 0x0c)(slot 0x1b)(func 0x2))(dev (domain 0x0000)(bus 0x01)(slot 0x13)(func 0x0)))))
\ No newline at end of file
Index: tests/xml2sexprdata/xml2sexpr-pci-devs.xml
===================================================================
RCS file: tests/xml2sexprdata/xml2sexpr-pci-devs.xml
diff -N tests/xml2sexprdata/xml2sexpr-pci-devs.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/xml2sexprdata/xml2sexpr-pci-devs.xml	16 Mar 2009 11:31:29 -0000
@@ -0,0 +1,33 @@
+<domain type='xen' id='15'>
+  <name>pvtest</name>
+  <uuid>596a5d2171f48fb2e068e2386a5c413e</uuid>
+  <os>
+    <type>linux</type>
+    <kernel>/var/lib/xen/vmlinuz.2Dn2YT</kernel>
+    <initrd>/var/lib/xen/initrd.img.0u-Vhq</initrd>
+    <cmdline> method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os  </cmdline>
+  </os>
+  <memory>430080</memory>
+  <vcpu>2</vcpu>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <disk type='block' device='disk'>
+      <source dev='/dev/MainVG/GuestLV'/>
+      <target dev='xvda'/>
+    </disk>
+    <console tty='/dev/pts/4'/>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/>
+      </source>
+    </hostdev>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <source>
+        <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/>
+      </source>
+    </hostdev>
+  </devices>
+</domain>
+


-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list