</dd>
</dl>
- <h4><a name="elementsUSB">USB and PCI devices</a></h4>
+ <h4><a name="elementsHostDev">Host device assignment</a></h4>
+
+ <h5><a href="elementsHostDevSubsys">USB / PCI devices</a></h5>
<p>
USB and PCI devices attached to the host can be passed through
- to the guest using
- the <code>hostdev</code> element. <span class="since">since after
- 0.4.4 for USB and 0.6.0 for PCI (KVM only)</span>:
+ to the guest using the <code>hostdev</code> element.
+ <span class="since">since after 0.4.4 for USB and 0.6.0 for PCI
+ (KVM only)</span>:
</p>
<pre>
more details on the address element.
</dl>
+
+ <h5><a href="elementsHostDevCaps">Block / character devices</a></h5>
+
+ <p>
+ Block / character devices from the host can be passed through
+ to the guest using the <code>hostdev</code> element. This is
+ only possible with container based virtualization.
+ <span class="since">since after 1.0.1 for LXC</span>:
+ </p>
+
+ <pre>
+...
+<hostdev mode='capabilities' type='storage'>
+ <source>
+ <block>/dev/sdf1</block>
+ </source>
+</hostdev>
+...
+ </pre>
+
+ <pre>
+...
+<hostdev mode='capabilities' type='misc'>
+ <source>
+ <char>/dev/input/event3</char>
+ </source>
+</hostdev>
+...
+ </pre>
+
+ <dl>
+ <dt><code>hostdev</code></dt>
+ <dd>The <code>hostdev</code> element is the main container for describing
+ host devices. For block/character device passthrough <code>mode</code> is
+ always "capabilities" and <code>type</code> is "block" for a block
+ device and "char" for a character device.
+ </dd>
+ <dt><code>source</code></dt>
+ <dd>The source element describes the device as seen from the host.
+ For block devices, the path to the block device in the host
+ OS is provided in the nested "block" element, while for character
+ devices the "char" element is used
+ </dd>
+ </dl>
+
<h4><a name="elementsRedir">Redirected devices</a></h4>
<p>
</zeroOrMore>
</element>
</define>
+
<define name="hostdev">
<element name="hostdev">
+ <choice>
+ <group>
+ <ref name="hostdevsubsys"/>
+ </group>
+ <group>
+ <ref name="hostdevcaps"/>
+ </group>
+ </choice>
<optional>
- <attribute name="mode">
- <choice>
- <value>subsystem</value>
- <value>capabilities</value>
- </choice>
- </attribute>
+ <ref name="alias"/>
</optional>
- <attribute name="type">
- <choice>
- <value>usb</value>
- <value>pci</value>
- </choice>
- </attribute>
- <optional>
- <attribute name="managed">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <group>
- <element name="source">
- <optional>
- <ref name="startupPolicy"/>
- </optional>
- <choice>
- <group>
- <ref name="usbproduct"/>
- <optional>
- <ref name="usbaddress"/>
- </optional>
- </group>
- <ref name="usbaddress"/>
- <element name="address">
- <ref name="pciaddress"/>
- </element>
- </choice>
- </element>
- </group>
<optional>
<ref name="deviceBoot"/>
</optional>
<optional>
- <ref name="alias"/>
+ <ref name="rom"/>
</optional>
<optional>
<ref name="address"/>
</optional>
+ </element>
+ </define>
+
+ <define name="hostdevsubsys">
+ <optional>
+ <attribute name="mode">
+ <value>subsystem</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="managed">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <choice>
+ <ref name="hostdevsubsyspci"/>
+ <ref name="hostdevsubsysusb"/>
+ </choice>
+ </define>
+
+ <define name="hostdevcaps">
+ <attribute name="mode">
+ <value>capabilities</value>
+ </attribute>
+ <choice>
+ <group>
+ <ref name="hostdevcapsstorage"/>
+ </group>
+ <group>
+ <ref name="hostdevcapsmisc"/>
+ </group>
+ </choice>
+ </define>
+
+
+ <define name="hostdevsubsyspci">
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <element name="source">
<optional>
- <ref name="rom"/>
+ <ref name="startupPolicy"/>
+ </optional>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="hostdevsubsysusb">
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <element name="source">
+ <optional>
+ <ref name="startupPolicy"/>
</optional>
+ <choice>
+ <group>
+ <ref name="usbproduct"/>
+ <optional>
+ <ref name="usbaddress"/>
+ </optional>
+ </group>
+ <ref name="usbaddress"/>
+ </choice>
</element>
</define>
+
+ <define name="hostdevcapsstorage">
+ <attribute name="type">
+ <value>storage</value>
+ </attribute>
+ <element name="source">
+ <element name="block">
+ <ref name="absFilePath"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="hostdevcapsmisc">
+ <attribute name="type">
+ <value>misc</value>
+ </attribute>
+ <element name="source">
+ <element name="char">
+ <ref name="absFilePath"/>
+ </element>
+ </element>
+ </define>
+
<define name="usbproduct">
<element name="vendor">
<attribute name="id">
virt = "?";
}
- switch (hostdev->source.subsys.type) {
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
- if (virAsprintf(&address, "%.4x:%.2x:%.2x.%.1x",
- hostdev->source.subsys.u.pci.domain,
- hostdev->source.subsys.u.pci.bus,
- hostdev->source.subsys.u.pci.slot,
- hostdev->source.subsys.u.pci.function) < 0) {
+ switch (hostdev->mode) {
+ case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
+ switch (hostdev->source.subsys.type) {
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+ if (virAsprintf(&address, "%.4x:%.2x:%.2x.%.1x",
+ hostdev->source.subsys.u.pci.domain,
+ hostdev->source.subsys.u.pci.bus,
+ hostdev->source.subsys.u.pci.slot,
+ hostdev->source.subsys.u.pci.function) < 0) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+ break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+ if (virAsprintf(&address, "%.3d.%.3d",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device) < 0) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+ break;
+ default:
+ VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
+ hostdev->source.subsys.type);
+ 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,
+ virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type),
+ device);
break;
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
- if (virAsprintf(&address, "%.3d.%.3d",
- hostdev->source.subsys.u.usb.bus,
- hostdev->source.subsys.u.usb.device) < 0) {
- VIR_WARN("OOM while encoding audit message");
+
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ switch (hostdev->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ if (!(device = virAuditEncode("disk",
+ VIR_AUDIT_STR(hostdev->source.caps.u.storage.block)))) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+
+ VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
+ "virt=%s resrc=hostdev reason=%s %s uuid=%s %s",
+ virt, reason, vmname, uuidstr, device);
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
+ if (!(device = virAuditEncode("chardev",
+ VIR_AUDIT_STR(hostdev->source.caps.u.misc.chardev)))) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+
+ VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
+ "virt=%s resrc=hostdev reason=%s %s uuid=%s %s",
+ virt, reason, vmname, uuidstr, device);
+ break;
+
+ default:
+ VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
+ hostdev->source.caps.type);
goto cleanup;
}
break;
- default:
- VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
- hostdev->source.subsys.type);
- goto cleanup;
- }
- if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
- VIR_WARN("OOM while encoding audit message");
+ default:
+ VIR_WARN("Unexpected hostdev mode while encoding audit message: %d",
+ hostdev->mode);
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,
- virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type),
- device);
-
cleanup:
VIR_FREE(vmname);
VIR_FREE(device);
"usb",
"pci")
+VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST,
+ "storage",
+ "misc")
+
VIR_ENUM_IMPL(virDomainPciRombarMode,
VIR_DOMAIN_PCI_ROMBAR_LAST,
"default",
*/
if (def->parent.type == VIR_DOMAIN_DEVICE_NONE)
virDomainDeviceInfoFree(def->info);
+
+ if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
+ switch (def->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ VIR_FREE(def->source.caps.u.storage.block);
+ break;
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
+ VIR_FREE(def->source.caps.u.misc.chardev);
+ break;
+ }
+ }
}
void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
return ret;
}
+static int
+virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
+ xmlXPathContextPtr ctxt,
+ const char *type,
+ virDomainHostdevDefPtr def)
+{
+ xmlNodePtr sourcenode;
+ int ret = -1;
+
+ /* @type is passed in from the caller rather than read from the
+ * xml document, because it is specified in different places for
+ * different kinds of defs - it is an attribute of
+ * <source>/<address> for an intelligent hostdev (<interface>),
+ * but an attribute of the toplevel element for a standard
+ * <hostdev>. (the functions we're going to call expect address
+ * type to already be known).
+ */
+ if (type) {
+ if ((def->source.caps.type
+ = virDomainHostdevCapsTypeFromString(type)) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unknown host device source address type '%s'"),
+ type);
+ goto error;
+ }
+ } else {
+ virReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing source address type"));
+ goto error;
+ }
+
+ if (!(sourcenode = virXPathNode("./source", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing <source> element in hostdev device"));
+ goto error;
+ }
+
+ switch (def->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ if (!(def->source.caps.u.storage.block =
+ virXPathString("string(./source/block[1])", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing <block> element in hostdev storage device"));
+ goto error;
+ }
+ break;
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
+ if (!(def->source.caps.u.misc.chardev =
+ virXPathString("string(./source/char[1])", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing <char> element in hostdev character device"));
+ goto error;
+ }
+ break;
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("address type='%s' not supported in hostdev interfaces"),
+ virDomainHostdevCapsTypeToString(def->source.caps.type));
+ goto error;
+ }
+ ret = 0;
+error:
+ return ret;
+}
+
int
virDomainDiskFindControllerModel(virDomainDefPtr def,
virDomainDiskDefPtr disk,
if (virDomainHostdevDefParseXMLSubsys(node, ctxt, type, def, flags) < 0)
goto error;
break;
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ /* parse managed/mode/type, and the <source> element */
+ if (virDomainHostdevDefParseXMLCaps(node, ctxt, type, def) < 0)
+ goto error;
+ break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected hostdev mode %d"), def->mode);
}
+static int
+virDomainHostdevMatchCapsStorage(virDomainHostdevDefPtr a,
+ virDomainHostdevDefPtr b)
+{
+ return STREQ_NULLABLE(a->source.caps.u.storage.block,
+ b->source.caps.u.storage.block);
+}
+
+
+static int
+virDomainHostdevMatchCapsMisc(virDomainHostdevDefPtr a,
+ virDomainHostdevDefPtr b)
+{
+ return STREQ_NULLABLE(a->source.caps.u.misc.chardev,
+ b->source.caps.u.misc.chardev);
+}
+
+
+static int
+virDomainHostdevMatchCaps(virDomainHostdevDefPtr a,
+ virDomainHostdevDefPtr b)
+{
+ if (a->source.caps.type != b->source.caps.type)
+ return 0;
+
+ switch (a->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ return virDomainHostdevMatchCapsStorage(a, b);
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
+ return virDomainHostdevMatchCapsMisc(a, b);
+ }
+ return 0;
+}
+
+
static int
virDomainHostdevMatch(virDomainHostdevDefPtr a,
virDomainHostdevDefPtr b)
switch (a->mode) {
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
return virDomainHostdevMatchSubsys(a, b);
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ return virDomainHostdevMatchCaps(a, b);
}
return 0;
}
return 0;
}
+static int
+virDomainHostdevDefFormatCaps(virBufferPtr buf,
+ virDomainHostdevDefPtr def)
+{
+ virBufferAddLit(buf, "<source>\n");
+
+ virBufferAdjustIndent(buf, 2);
+ switch (def->source.caps.type)
+ {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ virBufferEscapeString(buf, "<block>%s</block>\n",
+ def->source.caps.u.storage.block);
+ break;
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
+ virBufferEscapeString(buf, "<char>%s</char>\n",
+ def->source.caps.u.misc.chardev);
+ break;
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected hostdev type %d"),
+ def->source.caps.type);
+ return -1;
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</source>\n");
+ return 0;
+}
+
static int
virDomainActualNetDefFormat(virBufferPtr buf,
virDomainActualNetDefPtr def,
return -1;
}
break;
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ type = virDomainHostdevCapsTypeToString(def->source.caps.type);
+ if (!type) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected hostdev type %d"),
+ def->source.caps.type);
+ return -1;
+ }
+ break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected hostdev mode %d"), def->mode);
return -1;
}
- virBufferAsprintf(buf, " <hostdev mode='%s' type='%s' managed='%s'>\n",
- mode, type, def->managed ? "yes" : "no");
+ virBufferAsprintf(buf, " <hostdev mode='%s' type='%s'",
+ mode, type);
+ if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ virBufferAsprintf(buf, " managed='%s'>\n",
+ def->managed ? "yes" : "no");
+ else
+ virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 6);
switch (def->mode) {
if (virDomainHostdevDefFormatSubsys(buf, def, flags, false) < 0)
return -1;
break;
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ if (virDomainHostdevDefFormatCaps(buf, def) < 0)
+ return -1;
+ break;
}
virBufferAdjustIndent(buf, -6);
} u;
};
+
+enum virDomainHostdevCapsType {
+ VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE,
+ VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC,
+
+ VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST
+};
+
+typedef struct _virDomainHostdevCaps virDomainHostdevCaps;
+typedef virDomainHostdevCaps *virDomainHostdevCapsPtr;
+struct _virDomainHostdevCaps {
+ int type; /* enum virDOmainHostdevCapsType */
+ union {
+ struct {
+ char *block;
+ } storage;
+ struct {
+ char *chardev;
+ } misc;
+ } u;
+};
+
+
/* basic device for direct passthrough */
struct _virDomainHostdevDef {
virDomainDeviceDef parent; /* higher level Def containing this */
unsigned int missing : 1;
union {
virDomainHostdevSubsys subsys;
- struct {
- /* TBD: struct capabilities see:
- * https://www.redhat.com/archives/libvir-list/2008-July/msg00429.html
- */
- int dummy;
- } caps;
+ virDomainHostdevCaps caps;
} source;
virDomainHostdevOrigStates origstates;
virDomainDeviceInfoPtr info; /* Guest address */
VIR_ENUM_DECL(virDomainVideo)
VIR_ENUM_DECL(virDomainHostdevMode)
VIR_ENUM_DECL(virDomainHostdevSubsys)
+VIR_ENUM_DECL(virDomainHostdevCaps)
VIR_ENUM_DECL(virDomainPciRombarMode)
VIR_ENUM_DECL(virDomainHub)
VIR_ENUM_DECL(virDomainRedirdevBus)
virDomainGraphicsTypeFromString;
virDomainGraphicsTypeToString;
virDomainHasDiskMirror;
+virDomainHostdevCapsTypeToString;
virDomainHostdevDefAlloc;
virDomainHostdevDefClear;
virDomainHostdevDefFree;
--- /dev/null
+<domain type='lxc'>
+ <name>demo</name>
+ <uuid>8369f1ac-7e46-e869-4ca5-759d51478066</uuid>
+ <memory unit='KiB'>500000</memory>
+ <currentMemory unit='KiB'>500000</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64'>exe</type>
+ <init>/bin/sh</init>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <filesystem type='mount' accessmode='passthrough'>
+ <source dir='/root/container'/>
+ <target dir='/'/>
+ </filesystem>
+ <console type='pty'>
+ <target type='lxc' port='0'/>
+ </console>
+ <hostdev mode='capabilities' type='storage'>
+ <source>
+ <block>/dev/sdf1</block>
+ </source>
+ </hostdev>
+ <hostdev mode='capabilities' type='misc'>
+ <source>
+ <char>/dev/tty0</char>
+ </source>
+ </hostdev>
+ </devices>
+</domain>
setenv("PATH", "/bin", 1);
DO_TEST("systemd");
+ DO_TEST("hostdev");
virCapabilitiesFree(caps);