On Fri, Jul 25, 2008 at 04:17:30PM -0400, Guido Günther wrote:
> attached is some basic support for host device passthrough. It enables
> you to passthrough usb devices in qemu/kvm via:
On top of the hostdev passthrough (but it's actually totally
independent) I added usb massstorage backed by a file. Very handy for
installer testing where the preseed data is on the USB stick and the
CD/DVD is the installation medium.
At the moment I'm using a dummy target "usbdisk" so we don't have to
check for target == NULL in that many places. Once qemu handles it we
can fill in bus and device address for unplugging. To add a file as usb
massstorage to the guest you can use:

<disk type='file' device='disk'>
     <source file='/foo/bar/usbmass.img'/>
     <target bus='usb'/>
</disk>

Does this make sense?
 -- Guido
---
 src/domain_conf.c |   40 ++++++++++++++++++++++++++++------------
 src/domain_conf.h |    1 +
 src/qemu_conf.c   |   23 +++++++++++++++++++++--
 src/qemu_driver.c |   41 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 91 insertions(+), 14 deletions(-)

diff --git a/src/domain_conf.c b/src/domain_conf.c
index d36caeb..74ceecc 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -84,7 +84,8 @@ VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST,
               "fdc",
               "scsi",
               "virtio",
-              "xen")
+              "xen",
+              "usb")
 
 VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST,
               "user",
@@ -540,6 +541,14 @@ virDomainDiskDefParseXML(virConnectPtr conn,
         def->device = VIR_DOMAIN_DISK_DEVICE_DISK;
     }
 
+    if (bus) {
+        if ((def->bus = virDomainDiskBusTypeFromString(bus)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk bus type '%s'"), bus);
+            goto error;
+        }
+    }
+
     /* Only CDROM and Floppy devices are allowed missing source path
      * to indicate no media present */
     if (source == NULL &&
@@ -550,10 +559,16 @@ virDomainDiskDefParseXML(virConnectPtr conn,
         goto error;
     }
 
+    /* only USB devices are allowed missing target path since the hypervisor
+     * can assign bus and device number */
     if (target == NULL) {
-        virDomainReportError(conn, VIR_ERR_NO_TARGET,
-                             source ? "%s" : NULL, source);
-        goto error;
+        if (def->bus == VIR_DOMAIN_DISK_BUS_USB) {
+	    target = strdup("usbdisk");
+	} else {
+            virDomainReportError(conn, VIR_ERR_NO_TARGET,
+                                 source ? "%s" : NULL, source);
+            goto error;
+        }
     }
 
     if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
@@ -571,19 +586,14 @@ virDomainDiskDefParseXML(virConnectPtr conn,
         !STRPREFIX((const char *)target, "hd") &&
         !STRPREFIX((const char *)target, "sd") &&
         !STRPREFIX((const char *)target, "vd") &&
-        !STRPREFIX((const char *)target, "xvd")) {
+        !STRPREFIX((const char *)target, "xvd") &&
+        !STREQ((const char*)target, "usbdisk")) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("Invalid harddisk device name: %s"), target);
         goto error;
     }
 
-    if (bus) {
-        if ((def->bus = virDomainDiskBusTypeFromString(bus)) < 0) {
-            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                 _("unknown disk bus type '%s'"), bus);
-            goto error;
-        }
-    } else {
+    if (!bus) {
         if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
             def->bus = VIR_DOMAIN_DISK_BUS_FDC;
         } else {
@@ -612,6 +622,12 @@ virDomainDiskDefParseXML(virConnectPtr conn,
                              _("Invalid bus type '%s' for disk"), bus);
         goto error;
     }
+    if (def->device != VIR_DOMAIN_DISK_DEVICE_DISK &&
+        def->bus == VIR_DOMAIN_DISK_BUS_USB) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Invalid bus type '%s' for usb disk"), bus);
+        goto error;
+    }
 
     def->src = source;
     source = NULL;
diff --git a/src/domain_conf.h b/src/domain_conf.h
index 1aa5c39..a9c237e 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -72,6 +72,7 @@ enum virDomainDiskBus {
     VIR_DOMAIN_DISK_BUS_SCSI,
     VIR_DOMAIN_DISK_BUS_VIRTIO,
     VIR_DOMAIN_DISK_BUS_XEN,
+    VIR_DOMAIN_DISK_BUS_USB,
 
     VIR_DOMAIN_DISK_BUS_LAST
 };
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index 7678ac5..868b3dc 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -55,7 +55,8 @@ VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST,
               "floppy",
               "scsi",
               "virtio",
-              "xen")
+              "xen",
+              "usb")
 
 
 #define qemudLog(level, msg...) fprintf(stderr, msg)
@@ -772,6 +773,13 @@ int qemudBuildCommandLine(virConnectPtr conn,
             goto no_memory;                                             \
     } while (0)
 
+#define ADD_USBDISK(thisarg)                                            \
+    do {                                                                \
+        ADD_ARG_LIT("-usbdevice");                                      \
+        if ((asprintf(&qargv[qargc++], "disk:%s", thisarg)) == -1)      \
+            goto no_memory;                                             \
+    } while (0)
+
     snprintf(memory, sizeof(memory), "%lu", vm->def->memory/1024);
     snprintf(vcpus, sizeof(vcpus), "%lu", vm->def->vcpus);
 
@@ -883,6 +891,12 @@ int qemudBuildCommandLine(virConnectPtr conn,
             int idx = virDiskNameToIndex(disk->dst);
             const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
 
+            if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
+	        ADD_USBDISK(disk->src);
+		disk = disk->next;
+		continue;
+	    }
+
             if (idx < 0) {
                 qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                                  _("unsupported disk type '%s'"), disk->dst);
@@ -924,6 +938,12 @@ int qemudBuildCommandLine(virConnectPtr conn,
             char dev[NAME_MAX];
             char file[PATH_MAX];
 
+            if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
+	        ADD_USBDISK(disk->src);
+		disk = disk->next;
+		continue;
+	    }
+
             if (STREQ(disk->dst, "hdc") &&
                 disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
                 if (disk->src) {
@@ -947,7 +967,6 @@ int qemudBuildCommandLine(virConnectPtr conn,
 
             ADD_ARG_LIT(dev);
             ADD_ARG_LIT(file);
-
             disk = disk->next;
         }
     }
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 3381d10..73b6da4 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -2979,6 +2979,44 @@ static int qemudDomainAttachCdromDevice(virDomainPtr dom,
     return 0;
 }
 
+static int qemudDomainAttachUsbMassstorageDevice(virDomainPtr dom, virDomainDeviceDefPtr dev)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
+    int ret;
+    char *cmd, *reply;
+
+    ret = asprintf(&cmd, "usb_add disk:%s", dev->data.disk->src);
+
+    if (ret == -1) {
+    	qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "%s", _("out of memory"));
+        return ret;
+    }
+
+    if (qemudMonitorCommand(driver, vm, cmd, &reply) < 0) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "%s", _("cannot attach usb device"));
+        VIR_FREE(cmd);
+        return -1;
+    }
+
+    DEBUG ("attach_usb reply: %s", reply);
+    /* If the command failed qemu prints:
+     * Could not add ... */
+    if (strstr(reply, "Could not add ")) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s",
+                          _("adding usb device failed"));
+        VIR_FREE(reply);
+        VIR_FREE(cmd);
+        return -1;
+    }
+    VIR_FREE(reply);
+    VIR_FREE(cmd);
+    return 0;
+}
+
 static int qemudDomainAttachHostDevice(virDomainPtr dom, virDomainDeviceDefPtr dev)
 {
     struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
@@ -3051,6 +3089,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
     if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
         dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
 		ret = qemudDomainAttachCdromDevice(dom, dev);
+    } else if (dev->data.disk->device == VIR_DOMAIN_DEVICE_DISK &&
+        dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
+		ret = qemudDomainAttachUsbMassstorageDevice(dom, dev);
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOST &&
         dev->data.hostdev->type == VIR_DOMAIN_HOSTDEV_TYPE_USB) {
 		ret = qemudDomainAttachHostDevice(dom, dev);
-- 
1.5.6.3

--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to