Add "redirdev" redirection device

- create a new "redirdev" element for this purpose
This commit is contained in:
Marc-André Lureau 2011-09-02 23:09:14 +08:00 committed by Daniel Veillard
parent fdd14a9d05
commit 162efa1a7c
16 changed files with 494 additions and 18 deletions

View File

@ -1387,6 +1387,45 @@
not used by qemu.</dd>
</dl>
<h4><a name="elementsRedir">Redirected devices</a></h4>
<p>
USB device redirection through a character device is
supported <span class="since">since after 0.9.5 (KVM
only)</span>:
</p>
<pre>
...
&lt;devices&gt;
&lt;redirdev bus='usb' type='tcp'&gt;
&lt;source mode='connect' host='localhost' service='4000'/&gt;
&lt;/redirdev&gt;
&lt;/devices&gt;
...</pre>
<dl>
<dt><code>redirdev</code></dt>
<dd>The <code>redirdev</code> element is the main container for
describing redirected devices. <code>bus</code> must be "usb"
for a USB device.
An additional attribute <code>type</code> is required,
matching one of the
supported <a href="#elementsConsole">serial device</a> types,
to describe the host side of the
tunnel; <code>type='tcp'</code>
or <code>type='spicevmc'</code> (which uses the usbredir
channel of a <a href="#elementsGraphics">SPICE graphics
device</a>) are typical. Further sub-elements, such
as <code>&lt;source&gt;</code>, may be required according to
the given type, although a <code>&lt;target&gt;</code>
sub-element is not required (since the consumer of the
character device is the hypervisor itself, rather than a
device visible in the guest).</dd>
</dl>
<h4><a name="elementsSmartcard">Smartcard devices</a></h4>
<p>

View File

@ -1724,21 +1724,25 @@
</element>
</define>
<define name="qemucdevSrcTypeChoice">
<choice>
<value>dev</value>
<value>file</value>
<value>pipe</value>
<value>unix</value>
<value>tcp</value>
<value>udp</value>
<value>null</value>
<value>stdio</value>
<value>vc</value>
<value>pty</value>
<value>spicevmc</value>
</choice>
</define>
<define name="qemucdevSrcType">
<attribute name="type">
<choice>
<value>dev</value>
<value>file</value>
<value>pipe</value>
<value>unix</value>
<value>tcp</value>
<value>udp</value>
<value>null</value>
<value>stdio</value>
<value>vc</value>
<value>pty</value>
<value>spicevmc</value>
</choice>
<ref name="qemucdevSrcTypeChoice"/>
</attribute>
</define>
<define name="qemucdevSrcDef">
@ -1975,6 +1979,19 @@
</optional>
</element>
</define>
<define name="redirdev">
<element name="redirdev">
<attribute name="bus">
<choice>
<value>usb</value>
</choice>
</attribute>
<attribute name="type">
<ref name="qemucdevSrcTypeChoice"/>
</attribute>
<ref name="qemucdevSrcDef"/>
</element>
</define>
<define name="hostdev">
<element name="hostdev">
<optional>
@ -2139,6 +2156,7 @@
<ref name="channel"/>
<ref name="smartcard"/>
<ref name="hub"/>
<ref name="redirdev"/>
</choice>
</zeroOrMore>
<optional>

View File

@ -308,6 +308,66 @@ cleanup:
}
/**
* virDomainAuditRedirdev:
* @vm: domain making a change in pass-through host device
* @redirdev: device being attached or removed
* @reason: one of "start", "attach", or "detach"
* @success: true if the device passthrough operation succeeded
*
* Log an audit message about an attempted device passthrough change.
*/
void
virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
const char *reason, bool success)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *vmname;
char *address;
char *device;
const char *virt;
virUUIDFormat(vm->def->uuid, uuidstr);
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
VIR_WARN("OOM while encoding audit message");
return;
}
if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
virt = "?";
}
switch (redirdev->bus) {
case VIR_DOMAIN_REDIRDEV_BUS_USB:
if (virAsprintf(&address, "USB redirdev") < 0) {
VIR_WARN("OOM while encoding audit message");
goto cleanup;
}
default:
VIR_WARN("Unexpected redirdev bus while encoding audit message: %d",
redirdev->bus);
goto cleanup;
}
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
VIR_WARN("OOM while encoding audit message");
goto cleanup;
}
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
"virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s",
virt, reason, vmname, uuidstr,
virDomainRedirdevBusTypeToString(redirdev->bus),
device);
cleanup:
VIR_FREE(vmname);
VIR_FREE(device);
VIR_FREE(address);
}
/**
* virDomainAuditCgroup:
* @vm: domain making the cgroups ACL change
@ -538,6 +598,11 @@ virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success)
virDomainAuditHostdev(vm, hostdev, "start", true);
}
for (i = 0 ; i < vm->def->nredirdevs ; i++) {
virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i];
virDomainAuditRedirdev(vm, redirdev, "start", true);
}
virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true);
virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true);

View File

@ -101,5 +101,10 @@ void virDomainAuditVcpu(virDomainObjPtr vm,
void virDomainAuditSecurityLabel(virDomainObjPtr vm,
bool success)
ATTRIBUTE_NONNULL(1);
void virDomainAuditRedirdev(virDomainObjPtr vm,
virDomainRedirdevDefPtr def,
const char *reason,
bool success)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
#endif /* __VIR_DOMAIN_AUDIT_H__ */

View File

@ -127,7 +127,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
"watchdog",
"controller",
"graphics",
"hub")
"hub",
"redirdev")
VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
"none",
@ -441,6 +442,9 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_CRASHED+1,
VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST,
"usb")
VIR_ENUM_IMPL(virDomainRedirdevBus, VIR_DOMAIN_REDIRDEV_BUS_LAST,
"usb")
#define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1)
VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
"unknown")
@ -1013,6 +1017,17 @@ void virDomainHubDefFree(virDomainHubDefPtr def)
VIR_FREE(def);
}
void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def)
{
if (!def)
return;
virDomainChrSourceDefClear(&def->source.chr);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
{
if (!def)
@ -1052,6 +1067,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
case VIR_DOMAIN_DEVICE_HUB:
virDomainHubDefFree(def->data.hub);
break;
case VIR_DOMAIN_DEVICE_REDIRDEV:
virDomainRedirdevDefFree(def->data.redirdev);
break;
}
VIR_FREE(def);
@ -5344,7 +5362,6 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
virBitmapPtr bootMap,
unsigned int flags)
{
xmlNodePtr cur;
virDomainHostdevDefPtr def;
char *mode, *type = NULL, *managed = NULL;
@ -5391,8 +5408,8 @@ virDomainHostdevDefParseXML(const xmlNodePtr 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(cur, def) < 0)
goto error;
if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0)
goto error;
}
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
@ -5445,6 +5462,68 @@ error:
}
static virDomainRedirdevDefPtr
virDomainRedirdevDefParseXML(const xmlNodePtr node,
unsigned int flags)
{
xmlNodePtr cur;
virDomainRedirdevDefPtr def;
char *bus, *type = NULL;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
return NULL;
}
bus = virXMLPropString(node, "bus");
if (bus) {
if ((def->bus = virDomainRedirdevBusTypeFromString(bus)) < 0) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown redirdev bus '%s'"), bus);
goto error;
}
} else {
def->bus = VIR_DOMAIN_REDIRDEV_BUS_USB;
}
type = virXMLPropString(node, "type");
if (type) {
if ((def->source.chr.type = virDomainChrTypeFromString(type)) < 0) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown redirdev character device type '%s'"), type);
goto error;
}
} else {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing type in redirdev"));
goto error;
}
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "source")) {
int remaining;
remaining = virDomainChrSourceDefParseXML(&def->source.chr, cur, flags);
if (remaining != 0)
goto error;
}
}
cur = cur->next;
}
cleanup:
VIR_FREE(bus);
VIR_FREE(type);
return def;
error:
virDomainRedirdevDefFree(def);
def = NULL;
goto cleanup;
}
static int virDomainLifecycleParseXML(xmlXPathContextPtr ctxt,
const char *xpath,
int *val,
@ -5650,6 +5729,10 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
dev->type = VIR_DOMAIN_DEVICE_HUB;
if (!(dev->data.hub = virDomainHubDefParseXML(node, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "redirdev")) {
dev->type = VIR_DOMAIN_DEVICE_REDIRDEV;
if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(node, flags)))
goto error;
} else {
virDomainReportError(VIR_ERR_XML_ERROR,
"%s", _("unknown device type"));
@ -7069,6 +7152,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
}
VIR_FREE(nodes);
/* analysis of the redirected devices */
if ((n = virXPathNodeSet("./devices/redirdev", ctxt, &nodes)) < 0) {
goto error;
}
if (n && VIR_ALLOC_N(def->redirdevs, n) < 0)
goto no_memory;
for (i = 0 ; i < n ; i++) {
virDomainRedirdevDefPtr redirdev = virDomainRedirdevDefParseXML(nodes[i],
flags);
if (!redirdev)
goto error;
def->redirdevs[def->nredirdevs++] = redirdev;
}
VIR_FREE(nodes);
/* analysis of security label */
if (virSecurityLabelDefParseXML(def, ctxt, flags) == -1)
goto error;
@ -10158,6 +10257,22 @@ virDomainHostdevDefFormat(virBufferPtr buf,
return 0;
}
static int
virDomainRedirdevDefFormat(virBufferPtr buf,
virDomainRedirdevDefPtr def,
unsigned int flags)
{
const char *bus;
bus = virDomainRedirdevBusTypeToString(def->bus);
virBufferAsprintf(buf, " <redirdev bus='%s'", bus);
if (virDomainChrSourceDefFormat(buf, &def->source.chr, false, flags) < 0)
return -1;
virBufferAddLit(buf, " </redirdev>\n");
return 0;
}
static int
virDomainHubDefFormat(virBufferPtr buf,
@ -10592,6 +10707,10 @@ virDomainDefFormatInternal(virDomainDefPtr def,
if (virDomainHostdevDefFormat(&buf, def->hostdevs[n], flags) < 0)
goto cleanup;
for (n = 0 ; n < def->nredirdevs ; n++)
if (virDomainRedirdevDefFormat(&buf, def->redirdevs[n], flags) < 0)
goto cleanup;
for (n = 0 ; n < def->nhubs ; n++)
if (virDomainHubDefFormat(&buf, def->hubs[n], flags) < 0)
goto cleanup;

View File

@ -939,6 +939,23 @@ struct _virDomainHostdevDef {
virDomainDeviceInfo info; /* Guest address */
};
enum virDomainRedirdevBus {
VIR_DOMAIN_REDIRDEV_BUS_USB,
VIR_DOMAIN_REDIRDEV_BUS_LAST
};
typedef struct _virDomainRedirdevDef virDomainRedirdevDef;
typedef virDomainRedirdevDef *virDomainRedirdevDefPtr;
struct _virDomainRedirdevDef {
int bus; /* enum virDomainRedirdevBus */
union {
virDomainChrSourceDef chr;
} source;
virDomainDeviceInfo info; /* Guest address */
};
enum {
VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO,
@ -979,6 +996,7 @@ enum virDomainDeviceType {
VIR_DOMAIN_DEVICE_CONTROLLER,
VIR_DOMAIN_DEVICE_GRAPHICS,
VIR_DOMAIN_DEVICE_HUB,
VIR_DOMAIN_DEVICE_REDIRDEV,
VIR_DOMAIN_DEVICE_LAST,
};
@ -1000,6 +1018,7 @@ struct _virDomainDeviceDef {
virDomainWatchdogDefPtr watchdog;
virDomainGraphicsDefPtr graphics;
virDomainHubDefPtr hub;
virDomainRedirdevDefPtr redirdev;
} data;
};
@ -1312,6 +1331,9 @@ struct _virDomainDef {
int nhostdevs;
virDomainHostdevDefPtr *hostdevs;
int nredirdevs;
virDomainRedirdevDefPtr *redirdevs;
int nsmartcards;
virDomainSmartcardDefPtr *smartcards;
@ -1479,6 +1501,7 @@ void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def);
void virDomainVideoDefFree(virDomainVideoDefPtr def);
void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
void virDomainHubDefFree(virDomainHubDefPtr def);
void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def);
void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
int type);
@ -1760,6 +1783,7 @@ VIR_ENUM_DECL(virDomainVideo)
VIR_ENUM_DECL(virDomainHostdevMode)
VIR_ENUM_DECL(virDomainHostdevSubsys)
VIR_ENUM_DECL(virDomainHub)
VIR_ENUM_DECL(virDomainRedirdevBus)
VIR_ENUM_DECL(virDomainInput)
VIR_ENUM_DECL(virDomainInputBus)
VIR_ENUM_DECL(virDomainGraphics)

View File

@ -219,6 +219,7 @@ virDomainAuditHostdev;
virDomainAuditMemory;
virDomainAuditNet;
virDomainAuditNetDevice;
virDomainAuditRedirdev;
virDomainAuditSecurityLabel;
virDomainAuditStart;
virDomainAuditStop;
@ -375,6 +376,8 @@ virDomainObjSetState;
virDomainObjTaint;
virDomainObjUnlock;
virDomainObjUnref;
virDomainRedirdevBusTypeFromString;
virDomainRedirdevBusTypeToString;
virDomainRemoveInactive;
virDomainSaveConfig;
virDomainSaveStatus;

View File

@ -606,6 +606,33 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev
}
int
qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx)
{
if (idx == -1) {
int i;
idx = 0;
for (i = 0 ; i < def->nredirdevs ; i++) {
int thisidx;
if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for redirected device"));
return -1;
}
if (thisidx >= idx)
idx = thisidx + 1;
}
}
if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) {
virReportOOMError();
return -1;
}
return 0;
}
int
qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller)
{
@ -653,6 +680,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps)
if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0)
return -1;
}
for (i = 0; i < def->nredirdevs ; i++) {
if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0)
return -1;
}
for (i = 0; i < def->nvideos ; i++) {
if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0)
goto no_memory;
@ -2348,6 +2379,45 @@ qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev)
}
char *
qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev,
virBitmapPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Redirection bus %s is not supported by QEMU"),
virDomainRedirdevBusTypeToString(dev->bus));
goto error;
}
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("USB redirection is not supported "
"by this version of QEMU"));
goto error;
}
virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s",
dev->info.alias,
dev->info.alias);
if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferError(&buf)) {
virReportOOMError();
goto error;
}
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
virBitmapPtr qemuCaps)
@ -4839,6 +4909,32 @@ qemuBuildCommandLine(virConnectPtr conn,
virCommandAddArgList(cmd, "-watchdog-action", action, NULL);
}
/* Add redirected devices */
for (i = 0 ; i < def->nredirdevs ; i++) {
virDomainRedirdevDefPtr redirdev = def->redirdevs[i];
char *devstr;
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr,
redirdev->info.alias,
qemuCaps))) {
goto error;
}
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
goto error;
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildRedirdevDevStr(redirdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
/* Add host passthrough hardware */
for (i = 0 ; i < def->nhostdevs ; i++) {
virDomainHostdevDefPtr hostdev = def->hostdevs[i];

View File

@ -120,6 +120,7 @@ char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
virBitmapPtr qemuCaps);
char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virBitmapPtr qemuCaps);
char * qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev, virBitmapPtr qemuCaps);
int qemuNetworkIfaceConnect(virDomainDefPtr def,
@ -189,8 +190,9 @@ int qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr ad
int qemuDomainNetVLAN(virDomainNetDefPtr def);
int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx);
int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, virBitmapPtr qemuCaps);
int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx);
int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx);
int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller);
int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx);
int
qemuParseKeywords(const char *str,

View File

@ -4978,6 +4978,13 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
dev->data.hostdev = NULL;
break;
case VIR_DOMAIN_DEVICE_REDIRDEV:
ret = qemuDomainAttachRedirdevDevice(driver, vm,
dev->data.redirdev);
if (!ret)
dev->data.redirdev = NULL;
break;
default:
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be attached"),

View File

@ -911,6 +911,49 @@ error:
}
int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainRedirdevDefPtr redirdev)
{
int ret;
qemuDomainObjPrivatePtr priv = vm->privateData;
char *devstr = NULL;
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
if (qemuAssignDeviceRedirdevAlias(vm->def, redirdev, -1) < 0)
goto error;
if (!(devstr = qemuBuildRedirdevDevStr(redirdev, priv->qemuCaps)))
goto error;
}
if (VIR_REALLOC_N(vm->def->redirdevs, vm->def->nredirdevs+1) < 0) {
virReportOOMError();
goto error;
}
qemuDomainObjEnterMonitorWithDriver(driver, vm);
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE))
ret = qemuMonitorAddDevice(priv->mon, devstr);
else
goto error;
qemuDomainObjExitMonitorWithDriver(driver, vm);
virDomainAuditRedirdev(vm, redirdev, "attach", ret == 0);
if (ret < 0)
goto error;
vm->def->redirdevs[vm->def->nredirdevs++] = redirdev;
VIR_FREE(devstr);
return 0;
error:
VIR_FREE(devstr);
return -1;
}
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)

View File

@ -53,6 +53,9 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainRedirdevDefPtr hostdev);
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);

View File

@ -0,0 +1,8 @@
LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c \
-device ich9-usb-ehci1,id=usb,bus=pci.0,multifunction=on,addr=0x4.0x7 \
-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \
-device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \
-device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \
-chardev socket,id=charredir0,host=localhost,port=4000 \
-device usb-redir,chardev=charredir0,id=redir0 \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0

View File

@ -0,0 +1,38 @@
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<memory>219136</memory>
<currentMemory>219200</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu</emulator>
<controller type='usb' index='0' model='ich9-ehci1'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci1'>
<master startport='0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci2'>
<master startport='2'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci3'>
<master startport='4'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
</controller>
<redirdev bus='usb' type='tcp'>
<source mode='connect' host='localhost' service='4000'/>
<protocol type='raw'/>
</redirdev>
<memballoon model='virtio'/>
</devices>
</domain>

View File

@ -507,6 +507,10 @@ mymain(void)
DO_TEST("usb-ports", false,
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB,
QEMU_CAPS_NODEFCONFIG);
DO_TEST("usb-redir", false,
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB,
QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_USB_REDIR);
DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);

View File

@ -189,6 +189,8 @@ mymain(void)
DO_TEST("lease");
DO_TEST("event_idx");
DO_TEST("usb-redir");
/* These tests generate different XML */
DO_TEST_DIFFERENT("balloon-device-auto");
DO_TEST_DIFFERENT("channel-virtio-auto");