[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
[libvirt] [PATCH/RFC]: hostdev passthrough support
- From: Guido Günther <agx sigxcpu org>
- To: libvir-list redhat com
- Subject: [libvirt] [PATCH/RFC]: hostdev passthrough support
- Date: Fri, 25 Jul 2008 16:17:30 -0400
Hi,
attached is some basic support for host device passthrough. It enables
you to passthrough usb devices in qemu/kvm via:
<devices>
<hostdev type='usb' vendor='0204' product='6025'/>
<hostdev type='usb' bus='001' device='007'/>
</devices>
I didn't implement unplug yet since this needs some modifications to
qemu/kvm to be able to identify the correct device to unplug.
Does this look reasonable?
-- Guido
---
src/domain_conf.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/domain_conf.h | 33 +++++++++++++++
src/qemu_conf.c | 26 ++++++++++++
src/qemu_driver.c | 110 +++++++++++++++++++++++++++++++++++++------------
4 files changed, 260 insertions(+), 28 deletions(-)
diff --git a/src/domain_conf.c b/src/domain_conf.c
index cd4a3da..d36caeb 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -125,6 +125,9 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
"sdl",
"vnc")
+VIR_ENUM_IMPL(virDomainHostdev, VIR_DOMAIN_HOSTDEV_TYPE_LAST,
+ "usb",
+ "pci")
static void virDomainReportError(virConnectPtr conn,
int code, const char *fmt, ...)
@@ -314,6 +317,15 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def)
VIR_FREE(def);
}
+void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
+{
+ if (!def)
+ return;
+
+ virDomainHostdevDefFree(def->next);
+ VIR_FREE(def);
+}
+
void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
{
if (!def)
@@ -332,6 +344,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
case VIR_DOMAIN_DEVICE_SOUND:
virDomainSoundDefFree(def->data.sound);
break;
+ case VIR_DOMAIN_DEVICE_HOST:
+ virDomainHostdevDefFree(def->data.hostdev);
+ break;
}
VIR_FREE(def);
@@ -350,7 +365,7 @@ void virDomainDefFree(virDomainDefPtr def)
virDomainChrDefFree(def->parallels);
virDomainChrDefFree(def->console);
virDomainSoundDefFree(def->sounds);
-
+ virDomainHostdevDefFree(def->hostdevs);
VIR_FREE(def->os.type);
VIR_FREE(def->os.arch);
@@ -1297,6 +1312,88 @@ error:
}
+static virDomainHostdevDefPtr
+virDomainHostdevDefParseXML(virConnectPtr conn,
+ const xmlNodePtr node) {
+
+ virDomainHostdevDefPtr def;
+ char *type, *vendor;
+
+ if (VIR_ALLOC(def) < 0) {
+ virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ type = virXMLPropString(node, "type");
+ if (type) {
+ if ((def->type = virDomainHostdevTypeFromString(type)) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown host device type '%s'"), type);
+ goto error;
+ } else {
+ type = VIR_DOMAIN_HOSTDEV_TYPE_USB;
+ }
+ }
+ vendor = virXMLPropString(node, "vendor");
+ if (vendor) {
+ char *product;
+
+ def->usb.byModel = 1;
+ if (virStrToLong_ui(vendor, NULL, 16, &def->usb.vendor) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse vendor %s"), vendor);
+ VIR_FREE(vendor);
+ goto error;
+ }
+ VIR_FREE(vendor);
+
+ product = virXMLPropString(node, "product");
+ if (product) {
+ if (virStrToLong_ui(product, NULL, 16, &def->usb.product) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse product %s"), product);
+ VIR_FREE(product);
+ goto error;
+ }
+ VIR_FREE(product);
+ }
+ } else {
+ char *bus, *device;
+
+ def->usb.byModel = 0;
+ bus = virXMLPropString(node, "bus");
+ if (bus) {
+ if (virStrToLong_ui(bus, NULL, 16, &def->usb.bus) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse bus %s"), bus);
+ VIR_FREE(bus);
+ goto error;
+ }
+ VIR_FREE(bus);
+ }
+
+ device = virXMLPropString(node, "device");
+ if (device) {
+ if (virStrToLong_ui(device, NULL, 16, &def->usb.device) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse device %s"), device);
+ VIR_FREE(device);
+ goto error;
+ }
+ VIR_FREE(device);
+ }
+ }
+
+cleanup:
+ /* NOP */
+ return def;
+
+error:
+ virDomainHostdevDefFree(def);
+ def = NULL;
+ goto cleanup;
+}
+
+
static int virDomainLifecycleParseXML(virConnectPtr conn,
xmlXPathContextPtr ctxt,
const char *xpath,
@@ -1363,6 +1460,10 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
dev->type = VIR_DOMAIN_DEVICE_SOUND;
if (!(dev->data.sound = virDomainSoundDefParseXML(conn, node)))
goto error;
+ } else if (xmlStrEqual(node->name, BAD_CAST "hostdev")) {
+ dev->type = VIR_DOMAIN_DEVICE_HOST;
+ if (!(dev->data.hostdev = virDomainHostdevDefParseXML(conn, node)))
+ goto error;
} else {
virDomainReportError(conn, VIR_ERR_XML_ERROR,
"%s", _("unknown device type"));
@@ -1829,6 +1930,22 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
}
VIR_FREE(nodes);
+ /* analysis of the host devices */
+ if ((n = virXPathNodeSet("./devices/hostdev", ctxt, &nodes)) < 0) {
+ virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot extract host devices"));
+ goto error;
+ }
+ for (i = 0 ; i < n ; i++) {
+ virDomainHostdevDefPtr hostdev = virDomainHostdevDefParseXML(conn, nodes[i]);
+ if (!hostdev)
+ goto error;
+
+ hostdev->next = def->hostdevs;
+ def->hostdevs = hostdev;
+ }
+ VIR_FREE(nodes);
+
return def;
error:
diff --git a/src/domain_conf.h b/src/domain_conf.h
index b438bc8..1aa5c39 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -257,7 +257,35 @@ struct _virDomainGraphicsDef {
} data;
};
+enum virDomainHostdevType {
+ VIR_DOMAIN_HOSTDEV_TYPE_USB,
+ VIR_DOMAIN_HOSTDEV_TYPE_PCI,
+ VIR_DOMAIN_HOSTDEV_TYPE_LAST
+};
+
+typedef struct _virDomainHostdevDef virDomainHostdevDef;
+typedef virDomainHostdevDef *virDomainHostdevDefPtr;
+struct _virDomainHostdevDef {
+ int type;
+ union {
+ struct {
+ int byModel;
+ union {
+ unsigned vendor;
+ unsigned bus;
+ };
+ union {
+ unsigned product;
+ unsigned device;
+ };
+ } usb;
+ struct {
+ /* TBD */
+ } pci;
+ };
+ virDomainHostdevDefPtr next;
+};
/* Flags for the 'type' field in next struct */
enum virDomainDeviceType {
@@ -265,6 +293,7 @@ enum virDomainDeviceType {
VIR_DOMAIN_DEVICE_NET,
VIR_DOMAIN_DEVICE_INPUT,
VIR_DOMAIN_DEVICE_SOUND,
+ VIR_DOMAIN_DEVICE_HOST,
};
typedef struct _virDomainDeviceDef virDomainDeviceDef;
@@ -276,6 +305,7 @@ struct _virDomainDeviceDef {
virDomainNetDefPtr net;
virDomainInputDefPtr input;
virDomainSoundDefPtr sound;
+ virDomainHostdevDefPtr hostdev;
} data;
};
@@ -360,6 +390,7 @@ struct _virDomainDef {
virDomainNetDefPtr nets;
virDomainInputDefPtr inputs;
virDomainSoundDefPtr sounds;
+ virDomainHostdevDefPtr hostdevs;
virDomainChrDefPtr serials;
virDomainChrDefPtr parallels;
virDomainChrDefPtr console;
@@ -414,6 +445,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def);
void virDomainNetDefFree(virDomainNetDefPtr def);
void virDomainChrDefFree(virDomainChrDefPtr def);
void virDomainSoundDefFree(virDomainSoundDefPtr def);
+void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
void virDomainDefFree(virDomainDefPtr vm);
void virDomainObjFree(virDomainObjPtr vm);
@@ -484,6 +516,7 @@ VIR_ENUM_DECL(virDomainDiskBus)
VIR_ENUM_DECL(virDomainNet)
VIR_ENUM_DECL(virDomainChr)
VIR_ENUM_DECL(virDomainSoundModel)
+VIR_ENUM_DECL(virDomainHostdev)
VIR_ENUM_DECL(virDomainInput)
VIR_ENUM_DECL(virDomainInputBus)
VIR_ENUM_DECL(virDomainGraphics)
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index 9cd8c1e..7678ac5 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -723,6 +723,7 @@ int qemudBuildCommandLine(virConnectPtr conn,
virDomainNetDefPtr net = vm->def->nets;
virDomainInputDefPtr input = vm->def->inputs;
virDomainSoundDefPtr sound = vm->def->sounds;
+ virDomainHostdevDefPtr hostdev = vm->def->hostdevs;
virDomainChrDefPtr serial = vm->def->serials;
virDomainChrDefPtr parallel = vm->def->parallels;
struct utsname ut;
@@ -1154,6 +1155,31 @@ int qemudBuildCommandLine(virConnectPtr conn,
ADD_ARG(modstr);
}
+ /* Add host passthrough hardware */
+ while (hostdev) {
+ int ret;
+ char* usbdev;
+
+ if (hostdev->type == VIR_DOMAIN_HOSTDEV_TYPE_USB) {
+ if(hostdev->usb.vendor) {
+ ret = asprintf(&usbdev, "host:%.4x:%.4x",
+ hostdev->usb.vendor, hostdev->usb.product);
+
+ } else {
+ ret = asprintf(&usbdev, "host:%.3x.%.3x",
+ hostdev->usb.bus, hostdev->usb.device);
+ }
+ if (ret < 0) {
+ usbdev = NULL;
+ goto error;
+ }
+ ADD_ARG_LIT("-usbdevice");
+ ADD_ARG_LIT(usbdev);
+ VIR_FREE(usbdev);
+ }
+ hostdev = hostdev->next;
+ }
+
if (migrateFrom) {
ADD_ARG_LIT("-incoming");
ADD_ARG_LIT(migrateFrom);
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index d0c8184..81bde4e 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -2941,12 +2941,84 @@ static int qemudDomainChangeCDROM(virDomainPtr dom,
return 0;
}
+static int qemudDomainAttachCdromDevice(virDomainPtr dom,
+ virDomainDeviceDefPtr dev)
+{
+ struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
+ virDomainDiskDefPtr disk;
+
+ disk = vm->def->disks;
+ while (disk) {
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
+ STREQ(disk->dst, dev->data.disk->dst))
+ break;
+ disk = disk->next;
+ }
+
+ if (!disk) {
+ qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+ "%s", _("CDROM not attached, cannot change media"));
+ return -1;
+ }
+
+ if (qemudDomainChangeCDROM(dom, vm, disk, dev->data.disk) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int qemudDomainAttachHostDevice(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;
+
+ if (dev->data.hostdev->usb.byModel) {
+ ret = asprintf(&cmd, "usb_add host:%.4x:%.4x",
+ dev->data.hostdev->usb.vendor,
+ dev->data.hostdev->usb.product);
+ } else {
+ ret = asprintf(&cmd, "usb_add host:%.3x.%.3x",
+ dev->data.hostdev->usb.bus,
+ dev->data.hostdev->usb.device);
+ }
+ if (ret == -1) {
+ qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+ "%s", _("out of memory"));
+ return -1;
+ }
+
+ 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 qemudDomainAttachDevice(virDomainPtr dom,
const char *xml) {
struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
virDomainDeviceDefPtr dev;
- virDomainDiskDefPtr disk;
+ int ret = 0;
if (!vm) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
@@ -2965,36 +3037,20 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
return -1;
}
- if (dev->type != VIR_DOMAIN_DEVICE_DISK ||
- dev->data.disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) {
- qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
- "%s", _("only CDROM disk devices can be attached"));
- VIR_FREE(dev);
- return -1;
- }
-
- disk = vm->def->disks;
- while (disk) {
- if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
- STREQ(disk->dst, dev->data.disk->dst))
- break;
- disk = disk->next;
- }
-
- if (!disk) {
+ if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
+ dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
+ ret = qemudDomainAttachCdromDevice(dom, dev);
+ } else if (dev->type == VIR_DOMAIN_DEVICE_HOST &&
+ dev->data.hostdev->type == VIR_DOMAIN_HOSTDEV_TYPE_USB) {
+ ret = qemudDomainAttachHostDevice(dom, dev);
+ } else {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
- "%s", _("CDROM not attached, cannot change media"));
- VIR_FREE(dev);
- return -1;
- }
-
- if (qemudDomainChangeCDROM(dom, vm, disk, dev->data.disk) < 0) {
- VIR_FREE(dev);
- return -1;
+ "%s", _("this devicetype cannnot be attached"));
+ ret = -1;
}
VIR_FREE(dev);
- return 0;
+ return ret;
}
static int qemudDomainGetAutostart(virDomainPtr dom,
--
1.5.6.3
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]