Hi, attached is a second version. Changes are: * s/bus/subsystem/ * support hexadecimal and decimal attributes * introduce device and source elements. I decided to not drop vendor and product id into their own elements since the structure would then become very nested for no good reason. Some examples: <hostdev mode="subsys" type='usb'> <source> <device vendor="0x0204" product="0x6025"/> </source> </hostdev> <hostdev mode="subsys" type='usb'> <source> <address bus="001" device="003"/> </source> </hostdev> Support for <target> will be coming once I got around to adjust qemu/kvm. Cheers, -- Guido
>From 264b731042da15601d0840149557aceae5c7d96e Mon Sep 17 00:00:00 2001 From: Guido Guenther <agx@xxxxxxxxxxx> Date: Fri, 25 Jul 2008 15:18:16 -0400 Subject: [PATCH] host device passthrough --- src/domain_conf.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/domain_conf.h | 51 +++++++++++++ src/qemu_conf.c | 29 ++++++++ src/qemu_driver.c | 111 +++++++++++++++++++++------- 4 files changed, 371 insertions(+), 28 deletions(-) diff --git a/src/domain_conf.c b/src/domain_conf.c index 4998a7d..ab2ee9f 100644 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -131,6 +131,13 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "sdl", "vnc") +VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, + "subsys", + "capabilities") + +VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST, + "usb", + "pci") static void virDomainReportError(virConnectPtr conn, int code, const char *fmt, ...) @@ -332,6 +339,16 @@ void virDomainSoundDefFree(virDomainSoundDefPtr def) VIR_FREE(def); } +void virDomainHostdevDefFree(virDomainHostdevDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->target); + virDomainHostdevDefFree(def->next); + VIR_FREE(def); +} + void virDomainDeviceDefFree(virDomainDeviceDefPtr def) { if (!def) @@ -350,6 +367,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_SOUND: virDomainSoundDefFree(def->data.sound); break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + virDomainHostdevDefFree(def->data.hostdev); + break; } VIR_FREE(def); @@ -369,7 +389,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); @@ -1400,6 +1420,172 @@ error: goto cleanup; } +static int +virDomainHostdevSubsysUsbDefParseXML(virConnectPtr conn, + const xmlNodePtr node, + virDomainHostdevDefPtr def) { + + int ret = -1; + xmlNodePtr cur; + + def->source.subsys.usb.vendor = 0; + def->source.subsys.usb.product = 0; + def->source.subsys.usb.bus = 0; + def->source.subsys.usb.device = 0; + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "device")) { + char *vendor, *product; + + vendor = virXMLPropString(cur, "vendor"); + if (vendor) { + if (virStrToLong_ui(vendor, NULL, 0, + &def->source.subsys.usb.vendor) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse vendor %s"), vendor); + VIR_FREE(vendor); + goto out; + } + VIR_FREE(vendor); + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("usb device needs vendor")); + goto out; + } + + product = virXMLPropString(cur, "product"); + if (product) { + if (virStrToLong_ui(product, NULL, 0, + &def->source.subsys.usb.product) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse product %s"), product); + VIR_FREE(product); + goto out; + } + VIR_FREE(product); + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("usb device needs product")); + goto out; + } + + } else if (xmlStrEqual(cur->name, BAD_CAST "address")) { + char *bus, *device; + + bus = virXMLPropString(cur, "bus"); + if (bus) { + if (virStrToLong_ui(bus, NULL, 0, + &def->source.subsys.usb.bus) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse bus %s"), bus); + VIR_FREE(bus); + goto out; + } + VIR_FREE(bus); + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("usb address needs bus id")); + goto out; + } + + device = virXMLPropString(cur, "device"); + if (device) { + if (virStrToLong_ui(device, NULL, 0, + &def->source.subsys.usb.device) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse device %s"), + device); + VIR_FREE(device); + goto out; + } + VIR_FREE(device); + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("usb address needs device id")); + goto out; + } + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown usb source type '%s'"), cur->name); + goto out; + } + } + cur = cur->next; + } + ret = 0; +out: + return ret; +} + + +static virDomainHostdevDefPtr +virDomainHostdevDefParseXML(virConnectPtr conn, + const xmlNodePtr node) { + + xmlNodePtr cur; + virDomainHostdevDefPtr def; + char *mode, *type = NULL; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + def->target = NULL; + + mode = virXMLPropString(node, "mode"); + if (mode) { + if ((def->mode=virDomainHostdevModeTypeFromString(mode)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown hostdev mode '%s'"), mode); + goto error; + } + } else { + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + } + + type = virXMLPropString(node, "type"); + if (type) { + if ((def->source.subsys.type = virDomainHostdevSubsysTypeFromString(type)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown host device type '%s'"), type); + goto error; + } + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing type in hostdev")); + goto error; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "source")) { + if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + if (virDomainHostdevSubsysUsbDefParseXML(conn, cur, def) < 0) + goto error; + } + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("uknown node %s"), cur->name); + } + } + cur = cur->next; + } + +cleanup: + VIR_FREE(type); + VIR_FREE(mode); + return def; + +error: + virDomainHostdevDefFree(def); + def = NULL; + goto cleanup; +} + static int virDomainLifecycleParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, @@ -1471,6 +1657,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_HOSTDEV; + if (!(dev->data.hostdev = virDomainHostdevDefParseXML(conn, node))) + goto error; } else { virDomainReportError(conn, VIR_ERR_XML_ERROR, "%s", _("unknown device type")); @@ -1965,6 +2155,22 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } VIR_FREE(nodes); + /* analysis of the host devices */ + if ((n = virXPathNodeSet(conn, "./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 527dc71..8a9d1db 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -279,7 +279,52 @@ struct _virDomainGraphicsDef { } data; }; +enum virDomainHostdevMode { + VIR_DOMAIN_HOSTDEV_MODE_SUBSYS, + VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES, + VIR_DOMAIN_HOSTDEV_MODE_LAST, +}; + +enum virDomainHostdevSubsysType { + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB, + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI, + + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST +}; + +typedef struct _virDomainHostdevDef virDomainHostdevDef; +typedef virDomainHostdevDef *virDomainHostdevDefPtr; +struct _virDomainHostdevDef { + int mode; /* enum virDomainHostdevMode */ + union { + struct { + int type; /* enum virDomainHostdevBusType */ + union { + struct { + unsigned bus; + unsigned device; + + unsigned vendor; + unsigned product; + } usb; + struct { + unsigned domain; + unsigned bus; + unsigned slot; + unsigned function; + } pci; + }; + } subsys; + struct { + /* TBD: struct capabilities see: + * https://www.redhat.com/archives/libvir-list/2008-July/msg00429.html + */ + } caps; + } source; + char* target; + virDomainHostdevDefPtr next; +}; /* Flags for the 'type' field in next struct */ enum virDomainDeviceType { @@ -288,6 +333,7 @@ enum virDomainDeviceType { VIR_DOMAIN_DEVICE_NET, VIR_DOMAIN_DEVICE_INPUT, VIR_DOMAIN_DEVICE_SOUND, + VIR_DOMAIN_DEVICE_HOSTDEV, }; typedef struct _virDomainDeviceDef virDomainDeviceDef; @@ -300,6 +346,7 @@ struct _virDomainDeviceDef { virDomainNetDefPtr net; virDomainInputDefPtr input; virDomainSoundDefPtr sound; + virDomainHostdevDefPtr hostdev; } data; }; @@ -386,6 +433,7 @@ struct _virDomainDef { virDomainNetDefPtr nets; virDomainInputDefPtr inputs; virDomainSoundDefPtr sounds; + virDomainHostdevDefPtr hostdevs; virDomainChrDefPtr serials; virDomainChrDefPtr parallels; virDomainChrDefPtr console; @@ -441,6 +489,7 @@ void virDomainFSDefFree(virDomainFSDefPtr 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); @@ -515,6 +564,8 @@ VIR_ENUM_DECL(virDomainFS) VIR_ENUM_DECL(virDomainNet) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainSoundModel) +VIR_ENUM_DECL(virDomainHostdevMode) +VIR_ENUM_DECL(virDomainHostdevSubsys) 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 f5d12c7..73039c5 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; @@ -1152,6 +1153,34 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG(modstr); } + /* Add host passthrough hardware */ + while (hostdev) { + int ret; + char* usbdev; + + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + if(hostdev->source.subsys.usb.vendor) { + ret = asprintf(&usbdev, "host:%.4x:%.4x", + hostdev->source.subsys.usb.vendor, + hostdev->source.subsys.usb.product); + + } else { + ret = asprintf(&usbdev, "host:%.3d.%.3d", + hostdev->source.subsys.usb.bus, + hostdev->source.subsys.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 7fe3903..1aff53e 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -2951,12 +2951,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->source.subsys.usb.vendor) { + ret = asprintf(&cmd, "usb_add host:%.4x:%.4x", + dev->data.hostdev->source.subsys.usb.vendor, + dev->data.hostdev->source.subsys.usb.product); + } else { + ret = asprintf(&cmd, "usb_add host:%.3d.%.3d", + dev->data.hostdev->source.subsys.usb.bus, + dev->data.hostdev->source.subsys.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, @@ -2975,36 +3047,21 @@ 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_HOSTDEV && + dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_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
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list