[libvirt] [PATCH 00/23] Improve device support for LXC

This series makes some major improvements to the device support for LXC - USB, block and char device passthrough - Hotplug/unplug support for disk, nic, usb, block and char devices

From: "Daniel P. Berrange" <berrange@redhat.com> Rename virDomainHostdevPartsParse to virDomainHostdevDefParseSubsys to reflect the fact that it only deals with hostdevs uing the traditional mode=subsystem, and not mode=capabilities Rename virDomainHostSourceFormat to virDomainHostdevDefFormatSubsys for the same reason. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/conf/domain_conf.c | 101 ++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 814859a..ed31431 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2949,31 +2949,16 @@ out: } static int -virDomainHostdevPartsParse(xmlNodePtr node, - xmlXPathContextPtr ctxt, - const char *mode, - const char *type, - virDomainHostdevDefPtr def, - unsigned int flags) +virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, + xmlXPathContextPtr ctxt, + const char *type, + virDomainHostdevDefPtr def, + unsigned int flags) { xmlNodePtr sourcenode; char *managed = NULL; int ret = -1; - /* @mode is passed in separately from the caller, since an - * 'intelligent hostdev' has no place for 'mode' in the XML (it is - * always 'subsys'). - */ - if (mode) { - if ((def->mode=virDomainHostdevModeTypeFromString(mode)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown hostdev mode '%s'"), mode); - goto error; - } - } else { - def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - } - /* @managed can be read from the xml document - it is always an * attribute of the toplevel element, no matter what type of * element that might be (pure hostdev, or higher level device @@ -4773,8 +4758,9 @@ virDomainActualNetDefParseXML(xmlNodePtr node, virReportOOMError(); goto error; } - if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype, - hostdev, flags) < 0) { + hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype, + hostdev, flags) < 0) { goto error; } } @@ -5150,8 +5136,9 @@ virDomainNetDefParseXML(virCapsPtr caps, virReportOOMError(); goto error; } - if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype, - hostdev, flags) < 0) { + hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype, + hostdev, flags) < 0) { goto error; } break; @@ -7320,9 +7307,27 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, if (!(def = virDomainHostdevDefAlloc())) goto error; - /* parse managed/mode/type, and the <source> element */ - if (virDomainHostdevPartsParse(node, ctxt, mode, type, def, flags) < 0) + if (mode) { + if ((def->mode = virDomainHostdevModeTypeFromString(mode)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown hostdev mode '%s'"), mode); + goto error; + } + } else { + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + } + + switch (def->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + /* parse managed/mode/type, and the <source> element */ + if (virDomainHostdevDefParseXMLSubsys(node, ctxt, type, def, flags) < 0) + goto error; + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected hostdev mode %d"), def->mode); goto error; + } if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { if (virDomainDeviceInfoParseXML(node, bootMap, def->info, @@ -12337,10 +12342,10 @@ virDomainFSDefFormat(virBufferPtr buf, } static int -virDomainHostdevSourceFormat(virBufferPtr buf, - virDomainHostdevDefPtr def, - unsigned int flags, - bool includeTypeInAddr) +virDomainHostdevDefFormatSubsys(virBufferPtr buf, + virDomainHostdevDefPtr def, + unsigned int flags, + bool includeTypeInAddr) { virBufferAddLit(buf, "<source"); if (def->startupPolicy) { @@ -12458,8 +12463,8 @@ virDomainActualNetDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: - if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def, - flags, true) < 0) { + if (virDomainHostdevDefFormatSubsys(buf, &def->data.hostdev.def, + flags, true) < 0) { return -1; } break; @@ -12565,8 +12570,8 @@ virDomainNetDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: - if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def, - flags, true) < 0) { + if (virDomainHostdevDefFormatSubsys(buf, &def->data.hostdev.def, + flags, true) < 0) { return -1; } break; @@ -13461,19 +13466,25 @@ virDomainHostdevDefFormat(virBufferPtr buf, const char *mode = virDomainHostdevModeTypeToString(def->mode); const char *type; - if (!mode || def->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + if (!mode) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected hostdev mode %d"), def->mode); return -1; } - type = virDomainHostdevSubsysTypeToString(def->source.subsys.type); - if (!type || - (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && - def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)) { + switch (def->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + type = virDomainHostdevSubsysTypeToString(def->source.subsys.type); + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), + def->source.subsys.type); + return -1; + } + break; + default: virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected hostdev type %d"), - def->source.subsys.type); + _("unexpected hostdev mode %d"), def->mode); return -1; } @@ -13481,8 +13492,12 @@ virDomainHostdevDefFormat(virBufferPtr buf, mode, type, def->managed ? "yes" : "no"); virBufferAdjustIndent(buf, 6); - if (virDomainHostdevSourceFormat(buf, def, flags, false) < 0) - return -1; + switch (def->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + if (virDomainHostdevDefFormatSubsys(buf, def, flags, false) < 0) + return -1; + break; + } virBufferAdjustIndent(buf, -6); if (virDomainDeviceInfoFormat(buf, def->info, -- 1.8.0.1

On 11/30/2012 01:26 PM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Rename virDomainHostdevPartsParse to virDomainHostdevDefParseSubsys to reflect the fact that it only deals with hostdevs uing the traditional mode=subsystem, and not mode=capabilities
Rename virDomainHostSourceFormat to virDomainHostdevDefFormatSubsys for the same reason.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/conf/domain_conf.c | 101 ++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 43 deletions(-)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Move the code for matching hostdev instances out of virDomainHostdevFind and into virDomainHostdevMatch method, which in turn calls out to other helper methods depending on the type of hostdev. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/conf/domain_conf.c | 108 +++++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ed31431..dd0e841 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7830,6 +7830,69 @@ virDomainHostdevRemove(virDomainDefPtr def, size_t i) return hostdev; } + +static int +virDomainHostdevMatchSubsysUSB(virDomainHostdevDefPtr a, + virDomainHostdevDefPtr b) +{ + if (b->source.subsys.u.usb.bus && b->source.subsys.u.usb.device) { + /* specified by bus location on host */ + if (a->source.subsys.u.usb.bus == b->source.subsys.u.usb.bus && + a->source.subsys.u.usb.device == b->source.subsys.u.usb.device) + return 1; + } else { + /* specified by product & vendor id */ + if (a->source.subsys.u.usb.product == b->source.subsys.u.usb.product && + a->source.subsys.u.usb.vendor == b->source.subsys.u.usb.vendor) + return 1; + } + return 0; +} + +static int +virDomainHostdevMatchSubsysPCI(virDomainHostdevDefPtr a, + virDomainHostdevDefPtr b) +{ + if (a->source.subsys.u.pci.domain == b->source.subsys.u.pci.domain && + a->source.subsys.u.pci.bus == b->source.subsys.u.pci.bus && + a->source.subsys.u.pci.slot == b->source.subsys.u.pci.slot && + a->source.subsys.u.pci.function == b->source.subsys.u.pci.function) + return 1; + return 0; +} + + +static int +virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a, + virDomainHostdevDefPtr b) +{ + if (a->source.subsys.type != b->source.subsys.type) + return 0; + + switch (a->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + return virDomainHostdevMatchSubsysPCI(a, b); + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return virDomainHostdevMatchSubsysUSB(a, b); + } + return 0; +} + + +static int +virDomainHostdevMatch(virDomainHostdevDefPtr a, + virDomainHostdevDefPtr b) +{ + if (a->mode != b->mode) + return 0; + + switch (a->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virDomainHostdevMatchSubsys(a, b); + } + return 0; +} + /* Find an entry in hostdevs that matches the source spec in * @match. return pointer to the entry in @found (if found is * non-NULL). Returns index (within hostdevs) of matched entry, or -1 @@ -7841,58 +7904,17 @@ virDomainHostdevFind(virDomainDefPtr def, virDomainHostdevDefPtr *found) { virDomainHostdevDefPtr local_found; - virDomainHostdevSubsysPtr m_subsys = &match->source.subsys; int i; if (!found) found = &local_found; *found = NULL; - /* There is no code that uses _MODE_CAPABILITIES, and nothing to - * compare if it did, so don't allow it. - */ - if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return -1; - for (i = 0 ; i < def->nhostdevs ; i++) { - virDomainHostdevDefPtr compare = def->hostdevs[i]; - virDomainHostdevSubsysPtr c_subsys = &compare->source.subsys; - - if (compare->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - c_subsys->type != m_subsys->type) { - continue; - } - - switch (m_subsys->type) - { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - if (c_subsys->u.pci.domain == m_subsys->u.pci.domain && - c_subsys->u.pci.bus == m_subsys->u.pci.bus && - c_subsys->u.pci.slot == m_subsys->u.pci.slot && - c_subsys->u.pci.function == m_subsys->u.pci.function) { - *found = compare; - } - break; - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - if (m_subsys->u.usb.bus && m_subsys->u.usb.device) { - /* specified by bus location on host */ - if (c_subsys->u.usb.bus == m_subsys->u.usb.bus && - c_subsys->u.usb.device == m_subsys->u.usb.device) { - *found = compare; - } - } else { - /* specified by product & vendor id */ - if (c_subsys->u.usb.product == m_subsys->u.usb.product && - c_subsys->u.usb.vendor == m_subsys->u.usb.vendor) { - *found = compare; - } - } - break; - default: + if (virDomainHostdevMatch(match, def->hostdevs[i])) { + *found = def->hostdevs[i]; break; } - if (*found) - break; } return *found ? i : -1; } -- 1.8.0.1

On 11/30/2012 01:26 PM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Move the code for matching hostdev instances out of virDomainHostdevFind and into virDomainHostdevMatch method, which in turn calls out to other helper methods depending on the type of hostdev.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/conf/domain_conf.c | 108 +++++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 43 deletions(-)
Looks like a reasonable refactor. It adds lines, but will make future tweaks to a single hostdev type easier and less nested. ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> The <hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assinging arbitrary nodes in /dev, the new 'capabilities' mode is to be used. The first capability support is 'storage', which is for assignment of block devices. Functionally this is really pretty similar to the <disk> support. The only difference is the device node name is identical in both host and container namespaces. <hostdev mode='capabilities' type='storage'> <source> <block>/dev/sdf1</block> </source> </hostdev> The second capability support is 'misc', which is for assignment of character devices. There is no existing parallel to this. Again the device node is the same inside & outside the container. <hostdev mode='capabilities' type='misc'> <source> <char>/dev/sdf1</char> </source> </hostdev> The reason for keeping the char & storage devices separate in the domain XML, is to mirror the split in the node device XML. NB the node device XML does not yet report character devices, but that's another new patch to come Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- docs/schemas/domaincommon.rng | 128 +++++++++++++++++-------- src/conf/domain_audit.c | 80 +++++++++++----- src/conf/domain_conf.c | 175 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 7 files changed, 375 insertions(+), 75 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 0e85739..072e5cc 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2812,63 +2812,109 @@ </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> - </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> + <ref name="alias"/> </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> + </choice> + </define> + + + <define name="hostdevsubsyspci"> + <attribute name="type"> + <value>pci</value> + </attribute> + <element name="source"> + <optional> + <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="rom"/> + <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="usbproduct"> <element name="vendor"> <attribute name="id"> diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 939d213..e61eabe 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -260,42 +260,72 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev, 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; + + 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 cndoing 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); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index dd0e841..d97bbc8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -530,12 +530,16 @@ VIR_ENUM_IMPL(virDomainGraphicsSpiceClipboardCopypaste, VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", - "capabilities") + "capabilities"); VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST, "usb", "pci") +VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, + "storage", + "misc") + VIR_ENUM_IMPL(virDomainPciRombarMode, VIR_DOMAIN_PCI_ROMBAR_LAST, "default", @@ -1440,6 +1444,17 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) */ 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) @@ -3026,6 +3041,71 @@ error: 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, @@ -7323,6 +7403,11 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, 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); @@ -7880,6 +7965,41 @@ virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a, 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) { @@ -7889,6 +8009,8 @@ virDomainHostdevMatch(virDomainHostdevDefPtr a, 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; } @@ -12437,6 +12559,35 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf, } 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, unsigned int flags) @@ -13504,14 +13655,28 @@ virDomainHostdevDefFormat(virBufferPtr buf, 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) { @@ -13519,6 +13684,10 @@ virDomainHostdevDefFormat(virBufferPtr buf, 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); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4ab15e9..f5f8918 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -383,6 +383,29 @@ struct _virDomainHostdevSubsys { } 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 */ @@ -392,12 +415,7 @@ struct _virDomainHostdevDef { 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 */ @@ -2242,6 +2260,7 @@ VIR_ENUM_DECL(virDomainWatchdogAction) 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) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2573b8a..75c9488 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -405,6 +405,7 @@ virDomainGraphicsSpiceZlibCompressionTypeToString; virDomainGraphicsTypeFromString; virDomainGraphicsTypeToString; virDomainHasDiskMirror; +virDomainHostdevCapsTypeToString; virDomainHostdevDefAlloc; virDomainHostdevDefClear; virDomainHostdevDefFree; diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml new file mode 100644 index 0000000..d98efb8 --- /dev/null +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -0,0 +1,34 @@ +<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='char'> + <source> + <char>/dev/tty0</char> + </source> + </hostdev> + </devices> +</domain> diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c index 6a87939..8694ca2 100644 --- a/tests/lxcxml2xmltest.c +++ b/tests/lxcxml2xmltest.c @@ -121,6 +121,7 @@ mymain(void) setenv("PATH", "/bin", 1); DO_TEST("systemd"); + DO_TEST("hostdev"); virCapabilitiesFree(caps); -- 1.8.0.1

On 11/30/2012 01:26 PM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The <hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assinging
s/assinging/assigning/
arbitrary nodes in /dev, the new 'capabilities' mode is to be used.
The first capability support is 'storage', which is for assignment of block devices. Functionally this is really pretty similar to the <disk> support. The only difference is the device node name is identical in both host and container namespaces.
<hostdev mode='capabilities' type='storage'> <source> <block>/dev/sdf1</block> </source> </hostdev>
The second capability support is 'misc', which is for assignment of character devices. There is no existing parallel to this. Again the device node is the same inside & outside the container.
<hostdev mode='capabilities' type='misc'> <source> <char>/dev/sdf1</char> </source> </hostdev>
The reason for keeping the char & storage devices separate in the domain XML, is to mirror the split in the node device XML. NB the node device XML does not yet report character devices, but that's another new patch to come
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- docs/schemas/domaincommon.rng | 128 +++++++++++++++++--------
Missing documentation in an appropriate docs/*.html.in. I haven't checked if you add it later in the series, or completely forgot.
src/conf/domain_audit.c | 80 +++++++++++----- src/conf/domain_conf.c | 175 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 7 files changed, 375 insertions(+), 75 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml
But at least you added tests.
+ + <define name="hostdevcaps"> + <attribute name="mode"> + <value>capabilities</value> + </attribute> + <choice> + <group> + <ref name="hostdevcapsstorage"/> + </group> + </choice> + </define>
+ <define name="hostdevcapsstorage"> + <attribute name="type"> + <value>storage</value> + </attribute> + <element name="source"> + <element name="block"> + <ref name="absFilePath"/> + </element> + </element>
I see where you added <hostdev mode='capabilities' type='storage'> but not where you added <hostdev mode='capabilities' type='misc'>. Did you mean to have a <choice>storage|misc</choice> for the type attribute in hostdevcapsstorage?
+ + 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; + + default: + VIR_WARN("Unexpected hostdev type while encoding audit message: %d", + hostdev->source.caps.type);
Are you also missing 'misc' handling in this section of C code? I see places where it appears, but it seems incomplete (maybe I missed something, though).
- if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) { - VIR_WARN("OOM while encoding audit message"); + default: + VIR_WARN("Unexpected hostdev mode while cndoing audit message: %d",
s/cndoing/encoding/
+++ b/src/conf/domain_conf.c @@ -530,12 +530,16 @@ VIR_ENUM_IMPL(virDomainGraphicsSpiceClipboardCopypaste,
VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", - "capabilities") + "capabilities");
Why the added ';'...
VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST, "usb", "pci")
...especially when it's already optional?
@@ -1440,6 +1444,17 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) */ 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; + }
For example, here you do handle both types. -- -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The<hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assinging arbitrary nodes in /dev, the new 'capabilities' mode is to be used.
The first capability support is 'storage', which is for assignment of block devices. Functionally this is really pretty similar to the<disk> support. The only difference is the device node name is identical in both host and container namespaces.
<hostdev mode='capabilities' type='storage'> <source> <block>/dev/sdf1</block> </source> </hostdev>
The second capability support is 'misc', which is for assignment of character devices. There is no existing parallel to this. Again the device node is the same inside& outside the container.
<hostdev mode='capabilities' type='misc'> <source> <char>/dev/sdf1</char> </source> </hostdev>
The reason for keeping the char& storage devices separate in the domain XML, is to mirror the split in the node device XML. NB the node device XML does not yet report character devices, but that's another new patch to come
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- docs/schemas/domaincommon.rng | 128 +++++++++++++++++-------- src/conf/domain_audit.c | 80 +++++++++++----- src/conf/domain_conf.c | 175 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 7 files changed, 375 insertions(+), 75 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 0e85739..072e5cc 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2812,63 +2812,109 @@ </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> -</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> +<ref name="alias"/> </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> +</choice> +</define> + + +<define name="hostdevsubsyspci"> +<attribute name="type"> +<value>pci</value> +</attribute> +<element name="source"> +<optional> +<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="rom"/> +<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="usbproduct"> <element name="vendor"> <attribute name="id"> diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 939d213..e61eabe 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -260,42 +260,72 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev, 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; + + 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 cndoing 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); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index dd0e841..d97bbc8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -530,12 +530,16 @@ VIR_ENUM_IMPL(virDomainGraphicsSpiceClipboardCopypaste,
VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", - "capabilities") + "capabilities");
VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST, "usb", "pci")
+VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, + "storage", + "misc") + VIR_ENUM_IMPL(virDomainPciRombarMode, VIR_DOMAIN_PCI_ROMBAR_LAST, "default", @@ -1440,6 +1444,17 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) */ 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) @@ -3026,6 +3041,71 @@ error: 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).
Per the @type is passed in from caller...
+ */ + 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; + }
But this indicates a parsing error.

On Fri, Dec 14, 2012 at 06:37:41PM +0800, Osier Yang wrote:
On 2012年12月01日 04:26, Daniel P. Berrange wrote:
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 939d213..e61eabe 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -260,42 +260,72 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev, void virDomainHostdevDefFree(virDomainHostdevDefPtr def) @@ -3026,6 +3041,71 @@ error: 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).
Per the @type is passed in from caller...
Correct, but the caller gets that value from the XML which may result in NULL being passed
+ */ + 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; + }
But this indicates a parsing error.
So this check is correct. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> The <hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assigning arbitrary nodes in /dev, the new 'capabilities' mode is to be used. The first capability support is 'storage', which is for assignment of block devices. Functionally this is really pretty similar to the <disk> support. The only difference is the device node name is identical in both host and container namespaces. <hostdev mode='capabilities' type='storage'> <source> <block>/dev/sdf1</block> </source> </hostdev> The second capability support is 'misc', which is for assignment of character devices. There is no existing parallel to this. Again the device node is the same inside & outside the container. <hostdev mode='capabilities' type='misc'> <source> <char>/dev/input/event3</char> </source> </hostdev> The reason for keeping the char & storage devices separate in the domain XML, is to mirror the split in the node device XML. NB the node device XML does not yet report character devices, but that's another new patch to come Changes since v1: - Add missing docs - Add missing schema change Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- docs/formatdomain.html.in | 55 ++++++++++- docs/schemas/domaincommon.rng | 139 +++++++++++++++++++--------- src/conf/domain_audit.c | 92 ++++++++++++++----- src/conf/domain_conf.c | 173 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 8 files changed, 448 insertions(+), 78 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 292e032..d8fe35d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2123,13 +2123,15 @@ </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> @@ -2247,6 +2249,51 @@ more details on the address element. </dl> + + <h5><a href="elementsHostDevSubsys">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/characgter 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> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cdc5115..5e4f59b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2834,63 +2834,120 @@ </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> - </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> + <ref name="alias"/> </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> + </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"> diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 939d213..97b3aa4 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -260,42 +260,84 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev, 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); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 19af058..6a7646e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -536,6 +536,10 @@ VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST, "usb", "pci") +VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, + "storage", + "misc") + VIR_ENUM_IMPL(virDomainPciRombarMode, VIR_DOMAIN_PCI_ROMBAR_LAST, "default", @@ -1442,6 +1446,17 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) */ 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) @@ -3028,6 +3043,71 @@ error: 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, @@ -7384,6 +7464,11 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, 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); @@ -7941,6 +8026,41 @@ virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a, 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) { @@ -7950,6 +8070,8 @@ virDomainHostdevMatch(virDomainHostdevDefPtr a, 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; } @@ -12517,6 +12639,35 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf, } 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, unsigned int flags) @@ -13588,14 +13739,28 @@ virDomainHostdevDefFormat(virBufferPtr buf, 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) { @@ -13603,6 +13768,10 @@ virDomainHostdevDefFormat(virBufferPtr buf, 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); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index bc9ef88..5062e07 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -383,6 +383,29 @@ struct _virDomainHostdevSubsys { } 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 */ @@ -392,12 +415,7 @@ struct _virDomainHostdevDef { 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 */ @@ -2246,6 +2264,7 @@ VIR_ENUM_DECL(virDomainWatchdogAction) 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) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9288ad3..4b1a677 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -412,6 +412,7 @@ virDomainGraphicsSpiceZlibCompressionTypeToString; virDomainGraphicsTypeFromString; virDomainGraphicsTypeToString; virDomainHasDiskMirror; +virDomainHostdevCapsTypeToString; virDomainHostdevDefAlloc; virDomainHostdevDefClear; virDomainHostdevDefFree; diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml new file mode 100644 index 0000000..d98efb8 --- /dev/null +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -0,0 +1,34 @@ +<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='char'> + <source> + <char>/dev/tty0</char> + </source> + </hostdev> + </devices> +</domain> diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c index 6a87939..8694ca2 100644 --- a/tests/lxcxml2xmltest.c +++ b/tests/lxcxml2xmltest.c @@ -121,6 +121,7 @@ mymain(void) setenv("PATH", "/bin", 1); DO_TEST("systemd"); + DO_TEST("hostdev"); virCapabilitiesFree(caps); -- 1.7.11.7

The <hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assigning arbitrary nodes in /dev, the new 'capabilities' mode is to be used.
Changes since v1:
- Add missing docs - Add missing schema change
Yep, that was what I complained about in v1.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- docs/formatdomain.html.in | 55 ++++++++++- docs/schemas/domaincommon.rng | 139 +++++++++++++++++++--------- src/conf/domain_audit.c | 92 ++++++++++++++----- src/conf/domain_conf.c | 173 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 8 files changed, 448 insertions(+), 78 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 292e032..d8fe35d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2123,13 +2123,15 @@ </dd> </dl>
- <h4><a name="elementsUSB">USB and PCI devices</a></h4>
You are deleting an anchor name; this will invalidate any URLs that were previously pointing to that name. Not necessarily the end of the world; such URLs will still be to the right page, just wrong area of the page.
+ <h4><a name="elementsHostDev">Host device assignment</a></h4> + + <h5><a href="elementsHostDevSubsys">USB / PCI devices</a></h5>
@@ -2247,6 +2249,51 @@ more details on the address element. </dl>
+ + <h5><a href="elementsHostDevSubsys">Block / character devices</a></h5>
Oops, you used the anchor name #elementsHostDevSubsys twice. How did that validate? I think you want this anchor to be #elementsHostDevCapabilities
+ + <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/characgter device passthrough
s/characgter/character/
<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> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cdc5115..5e4f59b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2834,63 +2834,120 @@ </zeroOrMore> </element> </define> + <define name="hostdev"> <element name="hostdev"> + <choice> + <group> + <ref name="hostdevsubsys"/> + </group> + <group> + <ref name="hostdevcaps"/> + </group> + </choice> <optional>
+ <define name="hostdevcaps"> + <attribute name="mode"> + <value>capabilities</value> + </attribute> + <choice> + <group> + <ref name="hostdevcapsstorage"/> + </group> + </choice>
No choice for hostdevcapsmisc yet...
+ + <define name="hostdevcapsmisc"> + <attribute name="type"> + <value>misc</value> + </attribute> + <element name="source"> + <element name="char"> + <ref name="absFilePath"/> + </element> + </element> + </define>
...which makes this <define> unused. I don't know if that was intentional, or just an accidental omission, but trust that you'll get it sorted out before the end of the series.
+++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -0,0 +1,34 @@
+ <hostdev mode='capabilities' type='storage'> + <source> + <block>/dev/sdf1</block> + </source> + </hostdev> + <hostdev mode='capabilities' type='char'>
How did this validate? Shouldn't it be type='misc'? ACK with those issues fixed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Mon, Dec 17, 2012 at 11:58:05AM -0500, Eric Blake wrote:
The <hostdev> device type has long had a redundant "mode" attribute, which has always been "subsys". This finally introduces a new mode "capabilities", which will be used by the LXC driver for device assignment. Since container based virtualization uses a single kernel, the idea of assigning physical PCI devices doesn't make sense. It is still reasonable to assign USB devices, but for assigning arbitrary nodes in /dev, the new 'capabilities' mode is to be used.
Changes since v1:
- Add missing docs - Add missing schema change
Yep, that was what I complained about in v1.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- docs/formatdomain.html.in | 55 ++++++++++- docs/schemas/domaincommon.rng | 139 +++++++++++++++++++--------- src/conf/domain_audit.c | 92 ++++++++++++++----- src/conf/domain_conf.c | 173 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 31 +++++-- src/libvirt_private.syms | 1 + tests/lxcxml2xmldata/lxc-hostdev.xml | 34 +++++++ tests/lxcxml2xmltest.c | 1 + 8 files changed, 448 insertions(+), 78 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-hostdev.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 292e032..d8fe35d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2123,13 +2123,15 @@ </dd> </dl>
- <h4><a name="elementsUSB">USB and PCI devices</a></h4>
You are deleting an anchor name; this will invalidate any URLs that were previously pointing to that name. Not necessarily the end of the world; such URLs will still be to the right page, just wrong area of the page.
+ <h4><a name="elementsHostDev">Host device assignment</a></h4> + + <h5><a href="elementsHostDevSubsys">USB / PCI devices</a></h5>
@@ -2247,6 +2249,51 @@ more details on the address element. </dl>
+ + <h5><a href="elementsHostDevSubsys">Block / character devices</a></h5>
Oops, you used the anchor name #elementsHostDevSubsys twice. How did that validate? I think you want this anchor to be #elementsHostDevCapabilities
+ + <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/characgter device passthrough
s/characgter/character/
<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> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cdc5115..5e4f59b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2834,63 +2834,120 @@ </zeroOrMore> </element> </define> + <define name="hostdev"> <element name="hostdev"> + <choice> + <group> + <ref name="hostdevsubsys"/> + </group> + <group> + <ref name="hostdevcaps"/> + </group> + </choice> <optional>
+ <define name="hostdevcaps"> + <attribute name="mode"> + <value>capabilities</value> + </attribute> + <choice> + <group> + <ref name="hostdevcapsstorage"/> + </group> + </choice>
No choice for hostdevcapsmisc yet...
+ + <define name="hostdevcapsmisc"> + <attribute name="type"> + <value>misc</value> + </attribute> + <element name="source"> + <element name="char"> + <ref name="absFilePath"/> + </element> + </element> + </define>
...which makes this <define> unused. I don't know if that was intentional, or just an accidental omission, but trust that you'll get it sorted out before the end of the series.
+++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -0,0 +1,34 @@
+ <hostdev mode='capabilities' type='storage'> + <source> + <block>/dev/sdf1</block> + </source> + </hostdev> + <hostdev mode='capabilities' type='char'>
How did this validate? Shouldn't it be type='misc'?
ACK with those issues fixed.
Indeed you are correct. Applying the following fixes diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index d8fe35d..8d9ab9e 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2250,7 +2250,7 @@ </dl> - <h5><a href="elementsHostDevSubsys">Block / character devices</a></h5> + <h5><a href="elementsHostDevCaps">Block / character devices</a></h5> <p> Block / character devices from the host can be passed through @@ -2282,7 +2282,7 @@ <dl> <dt><code>hostdev</code></dt> <dd>The <code>hostdev</code> element is the main container for describing - host devices. For block/characgter device passthrough <code>mode</code> is + 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> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 5e4f59b..0529d62 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2888,6 +2888,9 @@ <group> <ref name="hostdevcapsstorage"/> </group> + <group> + <ref name="hostdevcapsmisc"/> + </group> </choice> </define> diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml index d98efb8..02a87a7 100644 --- a/tests/lxcxml2xmldata/lxc-hostdev.xml +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -25,7 +25,7 @@ <block>/dev/sdf1</block> </source> </hostdev> - <hostdev mode='capabilities' type='char'> + <hostdev mode='capabilities' type='misc'> <source> <char>/dev/tty0</char> </source> -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> The code for creating veth/macvlan devices is part of the LXC process startup code. Refactor this a little and export the methods to the rest of the LXC driver. This allows them to be reused for NIC hotplug code Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_process.c | 102 +++++++++++++++++++++----------------------------- src/lxc/lxc_process.h | 8 ++++ 2 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 8b9d02f..72e1be3 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -293,14 +293,12 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, } -static int virLXCProcessSetupInterfaceBridged(virConnectPtr conn, - virDomainDefPtr vm, - virDomainNetDefPtr net, - const char *brname, - unsigned int *nveths, - char ***veths) +char *virLXCProcessSetupInterfaceBridged(virConnectPtr conn, + virDomainDefPtr vm, + virDomainNetDefPtr net, + const char *brname) { - int ret = -1; + char *ret = NULL; char *parentVeth; char *containerVeth = NULL; const virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(net); @@ -314,24 +312,17 @@ static int virLXCProcessSetupInterfaceBridged(virConnectPtr conn, if (net->ifname == NULL) net->ifname = parentVeth; - if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) { - virReportOOMError(); - VIR_FREE(containerVeth); - goto cleanup; - } - (*veths)[(*nveths)] = containerVeth; - (*nveths)++; - if (virNetDevSetMAC(containerVeth, &net->mac) < 0) goto cleanup; - if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) - ret = virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac, - vm->uuid, vport, virDomainNetGetActualVlan(net)); - else - ret = virNetDevBridgeAddPort(brname, parentVeth); - if (ret < 0) - goto cleanup; + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) { + if (virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac, + vm->uuid, vport, virDomainNetGetActualVlan(net)) < 0) + goto cleanup; + } else { + if (virNetDevBridgeAddPort(brname, parentVeth) < 0) + goto cleanup; + } if (virNetDevSetOnline(parentVeth, true) < 0) goto cleanup; @@ -348,20 +339,18 @@ static int virLXCProcessSetupInterfaceBridged(virConnectPtr conn, virDomainConfNWFilterInstantiate(conn, vm->uuid, net) < 0) goto cleanup; - ret = 0; + ret = containerVeth; cleanup: return ret; } -static int virLXCProcessSetupInterfaceDirect(virConnectPtr conn, - virDomainDefPtr def, - virDomainNetDefPtr net, - unsigned int *nveths, - char ***veths) +char *virLXCProcessSetupInterfaceDirect(virConnectPtr conn, + virDomainDefPtr def, + virDomainNetDefPtr net) { - int ret = -1; + char *ret = NULL; char *res_ifname = NULL; virLXCDriverPtr driver = conn->privateData; virNetDevBandwidthPtr bw; @@ -376,7 +365,7 @@ static int virLXCProcessSetupInterfaceDirect(virConnectPtr conn, if (bw) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unable to set network bandwidth on direct interfaces")); - return -1; + return NULL; } /* XXX how todo port profiles ? @@ -390,15 +379,9 @@ static int virLXCProcessSetupInterfaceDirect(virConnectPtr conn, if (prof) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unable to set port profile on direct interfaces")); - return -1; + return NULL; } - if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) { - virReportOOMError(); - return -1; - } - (*veths)[(*nveths)] = NULL; - if (virNetDevMacVLanCreateWithVPortProfile( net->ifname, &net->mac, virDomainNetGetActualDirectDev(net), @@ -411,10 +394,7 @@ static int virLXCProcessSetupInterfaceDirect(virConnectPtr conn, virDomainNetGetActualBandwidth(net)) < 0) goto cleanup; - (*veths)[(*nveths)] = res_ifname; - (*nveths)++; - - ret = 0; + ret = res_ifname; cleanup: return ret; @@ -436,13 +416,14 @@ cleanup: */ static int virLXCProcessSetupInterfaces(virConnectPtr conn, virDomainDefPtr def, - unsigned int *nveths, + size_t *nveths, char ***veths) { int ret = -1; size_t i; for (i = 0 ; i < def->nnets ; i++) { + char *veth = NULL; /* If appropriate, grab a physical device from the configured * network's pool of devices, or resolve bridge device name * to the one defined in the network definition. @@ -450,6 +431,11 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn, if (networkAllocateActualDevice(def->nets[i]) < 0) goto cleanup; + if (VIR_EXPAND_N(*veths, *nveths, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + switch (virDomainNetGetActualType(def->nets[i])) { case VIR_DOMAIN_NET_TYPE_NETWORK: { virNetworkPtr network; @@ -486,12 +472,10 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn, if (fail) goto cleanup; - if (virLXCProcessSetupInterfaceBridged(conn, - def, - def->nets[i], - brname, - nveths, - veths) < 0) { + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + def, + def->nets[i], + brname))) { VIR_FREE(brname); goto cleanup; } @@ -505,21 +489,17 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn, _("No bridge name specified")); goto cleanup; } - if (virLXCProcessSetupInterfaceBridged(conn, - def, - def->nets[i], - brname, - nveths, - veths) < 0) + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + def, + def->nets[i], + brname))) goto cleanup; } break; case VIR_DOMAIN_NET_TYPE_DIRECT: - if (virLXCProcessSetupInterfaceDirect(conn, - def, - def->nets[i], - nveths, - veths) < 0) + if (!(veth = virLXCProcessSetupInterfaceDirect(conn, + def, + def->nets[i]))) goto cleanup; break; @@ -537,6 +517,8 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn, )); goto cleanup; } + + (*veths)[(*nveths)-1] = veth; } ret = 0; @@ -918,7 +900,7 @@ int virLXCProcessStart(virConnectPtr conn, size_t i; char *logfile = NULL; int logfd = -1; - unsigned int nveths = 0; + size_t nveths = 0; char **veths = NULL; int handshakefds[2] = { -1, -1 }; off_t pos = -1; diff --git a/src/lxc/lxc_process.h b/src/lxc/lxc_process.h index bac2c6c..779cc5f 100644 --- a/src/lxc/lxc_process.h +++ b/src/lxc/lxc_process.h @@ -47,4 +47,12 @@ void virLXCProcessAutostartAll(virLXCDriverPtr driver); int virLXCProcessReconnectAll(virLXCDriverPtr driver, virDomainObjListPtr doms); +char *virLXCProcessSetupInterfaceBridged(virConnectPtr conn, + virDomainDefPtr vm, + virDomainNetDefPtr net, + const char *brname); +char *virLXCProcessSetupInterfaceDirect(virConnectPtr conn, + virDomainDefPtr def, + virDomainNetDefPtr net); + #endif /* __LXC_PROCESS_H__ */ -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The code for creating veth/macvlan devices is part of the LXC process startup code. Refactor this a little and export the methods to the rest of the LXC driver. This allows them to be reused for NIC hotplug code
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_process.c | 102 +++++++++++++++++++++----------------------------- src/lxc/lxc_process.h | 8 ++++ 2 files changed, 50 insertions(+), 60 deletions(-)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> The virSecurityManager{Set,Restore}AllLabel methods are invoked at domain startup/shutdown to relabel resources associated with a domain. This works fine with QEMU, but with LXC they are in fact both currently no-ops since LXC does not support disks, hostdevs, or kernel/initrd files. Worse, when LXC gains support for disks/hostdevs, they will do the wrong thing, since they run in host context, not container context. Thus this patch turns then into a formal no-op when used with LXC. The LXC controller will call out to specific security manager labelling APIs as required during startup. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/security/security_selinux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 5409e32..ddf3da3 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -61,6 +61,7 @@ struct _virSecuritySELinuxData { char *file_context; char *content_context; virHashTablePtr mcs; + bool skipAllLabel; }; struct _virSecuritySELinuxCallbackData { @@ -363,6 +364,8 @@ virSecuritySELinuxLXCInitialize(virSecurityManagerPtr mgr) virConfPtr selinux_conf; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); + data->skipAllLabel = true; + selinux_conf = virConfReadFile(selinux_lxc_contexts_path(), 0); if (!selinux_conf) { virReportSystemError(errno, @@ -438,6 +441,8 @@ virSecuritySELinuxQEMUInitialize(virSecurityManagerPtr mgr) char *ptr; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); + data->skipAllLabel = false; + if (virFileReadAll(selinux_virtual_domain_context_path(), MAX_CONTEXT, &(data->domain_context)) < 0) { virReportSystemError(errno, _("cannot read SELinux virtual domain context file '%s'"), @@ -1438,11 +1443,12 @@ virSecuritySELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def, static int -virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, +virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, int migrated ATTRIBUTE_UNUSED) { virSecurityLabelDefPtr secdef; + virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); int i; int rc = 0; @@ -1452,7 +1458,7 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN if (secdef == NULL) return -1; - if (secdef->norelabel) + if (secdef->norelabel || data->skipAllLabel) return 0; for (i = 0 ; i < def->nhostdevs ; i++) { @@ -1810,7 +1816,7 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, if (secdef == NULL) return -1; - if (secdef->norelabel) + if (secdef->norelabel || data->skipAllLabel) return 0; for (i = 0 ; i < def->ndisks ; i++) { -- 1.8.0.1

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The virSecurityManager{Set,Restore}AllLabel methods are invoked at domain startup/shutdown to relabel resources associated with a domain. This works fine with QEMU, but with LXC they are in fact both currently no-ops since LXC does not support disks, hostdevs, or kernel/initrd files. Worse, when LXC gains support for disks/hostdevs, they will do the wrong thing, since they run in host context, not container context. Thus this patch turns then into a formal no-op when used with LXC. The LXC controller will call out to specific security manager labelling APIs as required during startup.
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- src/security/security_selinux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 5409e32..ddf3da3 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -61,6 +61,7 @@ struct _virSecuritySELinuxData { char *file_context; char *content_context; virHashTablePtr mcs; + bool skipAllLabel; };
struct _virSecuritySELinuxCallbackData { @@ -363,6 +364,8 @@ virSecuritySELinuxLXCInitialize(virSecurityManagerPtr mgr) virConfPtr selinux_conf; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr);
+ data->skipAllLabel = true; + selinux_conf = virConfReadFile(selinux_lxc_contexts_path(), 0); if (!selinux_conf) { virReportSystemError(errno, @@ -438,6 +441,8 @@ virSecuritySELinuxQEMUInitialize(virSecurityManagerPtr mgr) char *ptr; virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr);
+ data->skipAllLabel = false; + if (virFileReadAll(selinux_virtual_domain_context_path(), MAX_CONTEXT,&(data->domain_context))< 0) { virReportSystemError(errno, _("cannot read SELinux virtual domain context file '%s'"), @@ -1438,11 +1443,12 @@ virSecuritySELinuxRestoreSecuritySmartcardCallback(virDomainDefPtr def,
static int -virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, +virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, int migrated ATTRIBUTE_UNUSED) { virSecurityLabelDefPtr secdef; + virSecuritySELinuxDataPtr data = virSecurityManagerGetPrivateData(mgr); int i; int rc = 0;
@@ -1452,7 +1458,7 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN if (secdef == NULL) return -1;
- if (secdef->norelabel) + if (secdef->norelabel || data->skipAllLabel) return 0;
for (i = 0 ; i< def->nhostdevs ; i++) { @@ -1810,7 +1816,7 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, if (secdef == NULL) return -1;
- if (secdef->norelabel) + if (secdef->norelabel || data->skipAllLabel) return 0;
for (i = 0 ; i< def->ndisks ; i++) {
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> When LXC labels USB devices during hotplug, it is running in host context, so it needs to pass in a vroot path to the container root. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/qemu/qemu_cgroup.c | 3 ++- src/qemu/qemu_hostdev.c | 11 +++++++---- src/qemu/qemu_hotplug.c | 11 ++++++----- src/security/security_apparmor.c | 10 ++++++---- src/security/security_dac.c | 20 +++++++++++++------- src/security/security_driver.h | 6 ++++-- src/security/security_manager.c | 10 ++++++---- src/security/security_manager.h | 6 ++++-- src/security/security_nop.c | 6 ++++-- src/security/security_selinux.c | 20 +++++++++++++------- src/security/security_stack.c | 16 ++++++++++++---- src/util/hostusb.c | 17 +++++++++++++---- src/util/hostusb.h | 6 +++++- 13 files changed, 95 insertions(+), 47 deletions(-) diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 30cd1d6..084d89d 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -290,7 +290,8 @@ int qemuSetupCgroup(virQEMUDriverPtr driver, continue; if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device)) == NULL) + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) goto cleanup; if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index ab0f173..6d706a6 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -179,7 +179,8 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, continue; usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device); + hostdev->source.subsys.u.usb.device, + NULL); if (!usb) { VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", hostdev->source.subsys.u.usb.bus, @@ -657,6 +658,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, if (vendor && bus) { rc = usbFindDevice(vendor, product, bus, device, + NULL, autoAddress ? false : mandatory, usb); if (rc < 0) { @@ -677,7 +679,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, if (vendor) { usbDeviceList *devs; - rc = usbFindDeviceByVendor(vendor, product, mandatory, &devs); + rc = usbFindDeviceByVendor(vendor, product, NULL, mandatory, &devs); if (rc < 0) return -1; @@ -717,7 +719,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bus, device); } } else if (!vendor && bus) { - if (usbFindDeviceByBus(bus, device, mandatory, usb) < 0) + if (usbFindDeviceByBus(bus, device, NULL, mandatory, usb) < 0) return -1; } @@ -936,7 +938,8 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, continue; usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device); + hostdev->source.subsys.u.usb.device, + NULL); if (!usb) { VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index cfeae68..36022e4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1105,7 +1105,8 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, } if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device)) == NULL) + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) goto error; data.vm = vm; @@ -1173,7 +1174,7 @@ int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, } if (virSecurityManagerSetHostdevLabel(driver->securityManager, - vm->def, hostdev) < 0) + vm->def, hostdev, NULL) < 0) goto cleanup; switch (hostdev->source.subsys.type) { @@ -1201,7 +1202,7 @@ int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, error: if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, - vm->def, hostdev) < 0) + vm->def, hostdev, NULL) < 0) VIR_WARN("Unable to restore host device labelling on hotplug fail"); cleanup: @@ -2337,7 +2338,7 @@ qemuDomainDetachHostUsbDevice(virQEMUDriverPtr driver, if (ret < 0) return -1; - usb = usbGetDevice(subsys->u.usb.bus, subsys->u.usb.device); + usb = usbGetDevice(subsys->u.usb.bus, subsys->u.usb.device, NULL); if (usb) { usbDeviceListDel(driver->activeUsbHostdevs, usb); usbFreeDevice(usb); @@ -2388,7 +2389,7 @@ int qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, if (!ret) { if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, - vm->def, detach) < 0) { + vm->def, detach, NULL) < 0) { VIR_WARN("Failed to restore host device labelling"); } virDomainHostdevRemove(vm->def, idx); diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index b0cdb65..f57b81f 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -742,8 +742,8 @@ AppArmorReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, static int AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev) - + virDomainHostdevDefPtr dev, + const char *vroot) { struct SDPDOP *ptr; int ret = -1; @@ -770,7 +770,8 @@ AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr, switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); + dev->source.subsys.u.usb.device, + vroot); if (!usb) goto done; @@ -808,7 +809,8 @@ done: static int AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) + virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED, + const char *vroot ATTRIBUTE_UNUSED) { const virSecurityLabelDefPtr secdef = diff --git a/src/security/security_dac.c b/src/security/security_dac.c index b07c132..2861725 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -474,7 +474,8 @@ virSecurityDACSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, static int virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { void *params[] = {mgr, def}; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); @@ -494,7 +495,8 @@ virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, return 0; usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); + dev->source.subsys.u.usb.device, + vroot); if (!usb) goto done; @@ -550,8 +552,9 @@ virSecurityDACRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, static int virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, - virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainHostdevDefPtr dev) + virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr dev, + const char *vroot) { virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); @@ -571,7 +574,8 @@ virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, return 0; usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); + dev->source.subsys.u.usb.device, + vroot); if (!usb) goto done; @@ -728,7 +732,8 @@ virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, for (i = 0 ; i < def->nhostdevs ; i++) { if (virSecurityDACRestoreSecurityHostdevLabel(mgr, def, - def->hostdevs[i]) < 0) + def->hostdevs[i], + NULL) < 0) rc = -1; } for (i = 0 ; i < def->ndisks ; i++) { @@ -793,7 +798,8 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, for (i = 0 ; i < def->nhostdevs ; i++) { if (virSecurityDACSetSecurityHostdevLabel(mgr, def, - def->hostdevs[i]) < 0) + def->hostdevs[i], + NULL) < 0) return -1; } diff --git a/src/security/security_driver.h b/src/security/security_driver.h index d49b401..d4ddb45 100644 --- a/src/security/security_driver.h +++ b/src/security/security_driver.h @@ -61,10 +61,12 @@ typedef int (*virSecurityDomainSetImageLabel) (virSecurityManagerPtr mgr, virDomainDiskDefPtr disk); typedef int (*virSecurityDomainRestoreHostdevLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev); + virDomainHostdevDefPtr dev, + const char *vroot); typedef int (*virSecurityDomainSetHostdevLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev); + virDomainHostdevDefPtr dev, + const char *vroot); typedef int (*virSecurityDomainSetSavedStateLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, const char *savefile); diff --git a/src/security/security_manager.c b/src/security/security_manager.c index 0ebd53b..567f86c 100644 --- a/src/security/security_manager.c +++ b/src/security/security_manager.c @@ -275,10 +275,11 @@ int virSecurityManagerSetImageLabel(virSecurityManagerPtr mgr, int virSecurityManagerRestoreHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { if (mgr->drv->domainRestoreSecurityHostdevLabel) - return mgr->drv->domainRestoreSecurityHostdevLabel(mgr, vm, dev); + return mgr->drv->domainRestoreSecurityHostdevLabel(mgr, vm, dev, vroot); virReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -286,10 +287,11 @@ int virSecurityManagerRestoreHostdevLabel(virSecurityManagerPtr mgr, int virSecurityManagerSetHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { if (mgr->drv->domainSetSecurityHostdevLabel) - return mgr->drv->domainSetSecurityHostdevLabel(mgr, vm, dev); + return mgr->drv->domainSetSecurityHostdevLabel(mgr, vm, dev, vroot); virReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; diff --git a/src/security/security_manager.h b/src/security/security_manager.h index 1fdaf8e..e49cce7 100644 --- a/src/security/security_manager.h +++ b/src/security/security_manager.h @@ -71,10 +71,12 @@ int virSecurityManagerSetImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk); int virSecurityManagerRestoreHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev); + virDomainHostdevDefPtr dev, + const char *vroot); int virSecurityManagerSetHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, - virDomainHostdevDefPtr dev); + virDomainHostdevDefPtr dev, + const char *vroot); int virSecurityManagerSetSavedStateLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, const char *savefile); diff --git a/src/security/security_nop.c b/src/security/security_nop.c index 5f3270a..7bc8bba 100644 --- a/src/security/security_nop.c +++ b/src/security/security_nop.c @@ -84,14 +84,16 @@ static int virSecurityDomainSetImageLabelNop(virSecurityManagerPtr mgr ATTRIBUTE static int virSecurityDomainRestoreHostdevLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr vm ATTRIBUTE_UNUSED, - virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) + virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED, + const char *vroot ATTRIBUTE_UNUSED) { return 0; } static int virSecurityDomainSetHostdevLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr vm ATTRIBUTE_UNUSED, - virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) + virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED, + const char *vroot ATTRIBUTE_UNUSED) { return 0; } diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index ddf3da3..9070ff9 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1121,7 +1121,8 @@ virSecuritySELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, static int virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { virSecurityLabelDefPtr secdef; @@ -1145,7 +1146,8 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN return 0; usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); + dev->source.subsys.u.usb.device, + vroot); if (!usb) goto done; @@ -1198,7 +1200,8 @@ virSecuritySELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, static int virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { virSecurityLabelDefPtr secdef; @@ -1222,7 +1225,8 @@ virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUT return 0; usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); + dev->source.subsys.u.usb.device, + vroot); if (!usb) goto done; @@ -1464,7 +1468,8 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr, for (i = 0 ; i < def->nhostdevs ; i++) { if (virSecuritySELinuxRestoreSecurityHostdevLabel(mgr, def, - def->hostdevs[i]) < 0) + def->hostdevs[i], + NULL) < 0) rc = -1; } for (i = 0 ; i < def->ndisks ; i++) { @@ -1834,8 +1839,9 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, for (i = 0 ; i < def->nhostdevs ; i++) { if (virSecuritySELinuxSetSecurityHostdevLabel(mgr, - def, - def->hostdevs[i]) < 0) + def, + def->hostdevs[i], + NULL) < 0) return -1; } diff --git a/src/security/security_stack.c b/src/security/security_stack.c index 1094cbe..51510e5 100644 --- a/src/security/security_stack.c +++ b/src/security/security_stack.c @@ -236,7 +236,8 @@ virSecurityStackRestoreSecurityImageLabel(virSecurityManagerPtr mgr, static int virSecurityStackSetSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); @@ -244,7 +245,10 @@ virSecurityStackSetSecurityHostdevLabel(virSecurityManagerPtr mgr, int rc = 0; for (; item; item = item->next) { - if (virSecurityManagerSetHostdevLabel(item->securityManager, vm, dev) < 0) + if (virSecurityManagerSetHostdevLabel(item->securityManager, + vm, + dev, + vroot) < 0) rc = -1; } @@ -255,14 +259,18 @@ virSecurityStackSetSecurityHostdevLabel(virSecurityManagerPtr mgr, static int virSecurityStackRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, - virDomainHostdevDefPtr dev) + virDomainHostdevDefPtr dev, + const char *vroot) { virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); virSecurityStackItemPtr item = priv->itemsHead; int rc = 0; for (; item; item = item->next) { - if (virSecurityManagerRestoreHostdevLabel(item->securityManager, vm, dev) < 0) + if (virSecurityManagerRestoreHostdevLabel(item->securityManager, + vm, + dev, + vroot) < 0) rc = -1; } diff --git a/src/util/hostusb.c b/src/util/hostusb.c index 81a9f5a..24f925b 100644 --- a/src/util/hostusb.c +++ b/src/util/hostusb.c @@ -101,6 +101,7 @@ usbDeviceSearch(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *vroot, unsigned int flags) { DIR *dir = NULL; @@ -160,7 +161,7 @@ usbDeviceSearch(unsigned int vendor, found = true; } - usb = usbGetDevice(found_bus, found_devno); + usb = usbGetDevice(found_bus, found_devno, vroot); if (!usb) goto cleanup; @@ -189,6 +190,7 @@ cleanup: int usbFindDeviceByVendor(unsigned int vendor, unsigned product, + const char *vroot, bool mandatory, usbDeviceList **devices) { @@ -196,6 +198,7 @@ usbFindDeviceByVendor(unsigned int vendor, int count; if (!(list = usbDeviceSearch(vendor, product, 0 , 0, + vroot, USB_DEVICE_FIND_BY_VENDOR))) return -1; @@ -226,12 +229,14 @@ usbFindDeviceByVendor(unsigned int vendor, int usbFindDeviceByBus(unsigned int bus, unsigned devno, + const char *vroot, bool mandatory, usbDevice **usb) { usbDeviceList *list; if (!(list = usbDeviceSearch(0, 0, bus, devno, + vroot, USB_DEVICE_FIND_BY_BUS))) return -1; @@ -265,13 +270,15 @@ usbFindDevice(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *vroot, bool mandatory, usbDevice **usb) { usbDeviceList *list; unsigned int flags = USB_DEVICE_FIND_BY_VENDOR|USB_DEVICE_FIND_BY_BUS; - if (!(list = usbDeviceSearch(vendor, product, bus, devno, flags))) + if (!(list = usbDeviceSearch(vendor, product, bus, devno, + vroot, flags))) return -1; if (list->count == 0) { @@ -301,7 +308,8 @@ usbFindDevice(unsigned int vendor, usbDevice * usbGetDevice(unsigned int bus, - unsigned int devno) + unsigned int devno, + const char *vroot) { usbDevice *dev; @@ -321,7 +329,8 @@ usbGetDevice(unsigned int bus, usbFreeDevice(dev); return NULL; } - if (virAsprintf(&dev->path, USB_DEVFS "%03d/%03d", + if (virAsprintf(&dev->path, "%s" USB_DEVFS "%03d/%03d", + vroot ? vroot : "", dev->bus, dev->dev) < 0) { virReportOOMError(); usbFreeDevice(dev); diff --git a/src/util/hostusb.h b/src/util/hostusb.h index 4f55fdc..aee1526 100644 --- a/src/util/hostusb.h +++ b/src/util/hostusb.h @@ -29,15 +29,18 @@ typedef struct _usbDevice usbDevice; typedef struct _usbDeviceList usbDeviceList; usbDevice *usbGetDevice(unsigned int bus, - unsigned int devno); + unsigned int devno, + const char *vroot); int usbFindDeviceByBus(unsigned int bus, unsigned int devno, + const char *vroot, bool mandatory, usbDevice **usb); int usbFindDeviceByVendor(unsigned int vendor, unsigned int product, + const char *vroot, bool mandatory, usbDeviceList **devices); @@ -45,6 +48,7 @@ int usbFindDevice(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *vroot, bool mandatory, usbDevice **usb); -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When LXC labels USB devices during hotplug, it is running in host context, so it needs to pass in a vroot path to the container root.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Prepare to support different types of hostdevs by refactoring the current SELinux security driver code Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/security/security_selinux.c | 89 +++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 9070ff9..ad13490 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1118,26 +1118,15 @@ virSecuritySELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); } + static int -virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainDefPtr def, - virDomainHostdevDefPtr dev, - const char *vroot) +virSecuritySELinuxSetSecurityHostdevSubsysLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) { - virSecurityLabelDefPtr secdef; int ret = -1; - secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - if (secdef == NULL) - return -1; - - if (secdef->norelabel) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb; @@ -1182,6 +1171,32 @@ done: static int +virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) + +{ + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->norelabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySELinuxSetSecurityHostdevSubsysLabel(def, dev, vroot); + + default: + return 0; + } +} + + +static int virSecuritySELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque ATTRIBUTE_UNUSED) @@ -1197,26 +1212,14 @@ virSecuritySELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, return virSecuritySELinuxRestoreSecurityFileLabel(file); } + static int -virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainDefPtr def, - virDomainHostdevDefPtr dev, - const char *vroot) +virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(virDomainHostdevDefPtr dev, + const char *vroot) { - virSecurityLabelDefPtr secdef; int ret = -1; - secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - if (secdef == NULL) - return -1; - - if (secdef->norelabel) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb; @@ -1262,6 +1265,32 @@ done: static int +virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) + +{ + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->norelabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(dev, vroot); + + default: + return 0; + } +} + + +static int virSecuritySELinuxSetSecurityChardevLabel(virDomainDefPtr def, virDomainChrDefPtr dev, virDomainChrSourceDefPtr dev_source) -- 1.8.0.1

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Prepare to support different types of hostdevs by refactoring the current SELinux security driver code
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- src/security/security_selinux.c | 89 +++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 30 deletions(-)
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 9070ff9..ad13490 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1118,26 +1118,15 @@ virSecuritySELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); }
+ static int -virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainDefPtr def, - virDomainHostdevDefPtr dev, - const char *vroot) +virSecuritySELinuxSetSecurityHostdevSubsysLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot)
{ - virSecurityLabelDefPtr secdef; int ret = -1;
- secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - if (secdef == NULL) - return -1; - - if (secdef->norelabel) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb; @@ -1182,6 +1171,32 @@ done:
static int +virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) + +{ + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->norelabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySELinuxSetSecurityHostdevSubsysLabel(def, dev, vroot); + + default: + return 0; + } +} + + +static int virSecuritySELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, const char *file, void *opaque ATTRIBUTE_UNUSED) @@ -1197,26 +1212,14 @@ virSecuritySELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, return virSecuritySELinuxRestoreSecurityFileLabel(file); }
+ static int -virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainDefPtr def, - virDomainHostdevDefPtr dev, - const char *vroot) +virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(virDomainHostdevDefPtr dev, + const char *vroot)
{ - virSecurityLabelDefPtr secdef; int ret = -1;
- secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - if (secdef == NULL) - return -1; - - if (secdef->norelabel) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb; @@ -1262,6 +1265,32 @@ done:
static int +virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) + +{ + virSecurityLabelDefPtr secdef; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + if (secdef->norelabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(dev, vroot); + + default: + return 0; + } +} + + +static int virSecuritySELinuxSetSecurityChardevLabel(virDomainDefPtr def, virDomainChrDefPtr dev, virDomainChrSourceDefPtr dev_source)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> The SELinux security driver needs to learn to label storage/misc hostdev devices for LXC Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/security/security_selinux.c | 118 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index ad13490..6f0cd4d 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1171,6 +1171,65 @@ done: static int +virSecuritySELinuxSetSecurityHostdevCapsLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + virSecurityLabelDefPtr secdef; + char *path; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.storage.block))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxSetFilecon(path, secdef->imagelabel); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.misc.chardev))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxSetFilecon(path, secdef->imagelabel); + VIR_FREE(path); + break; + } + + default: + ret = 0; + break; + } + + return ret; +} + + +static int virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, virDomainHostdevDefPtr dev, @@ -1190,6 +1249,9 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return virSecuritySELinuxSetSecurityHostdevSubsysLabel(def, dev, vroot); + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return virSecuritySELinuxSetSecurityHostdevCapsLabel(def, dev, vroot); + default: return 0; } @@ -1265,6 +1327,59 @@ done: static int +virSecuritySELinuxRestoreSecurityHostdevCapsLabel(virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + char *path; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.storage.block))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxRestoreSecurityFileLabel(path); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.misc.chardev))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxRestoreSecurityFileLabel(path); + VIR_FREE(path); + break; + } + + default: + ret = 0; + break; + } + + return ret; +} + + +static int virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, virDomainHostdevDefPtr dev, @@ -1284,6 +1399,9 @@ virSecuritySELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUT case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(dev, vroot); + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return virSecuritySELinuxRestoreSecurityHostdevCapsLabel(dev, vroot); + default: return 0; } -- 1.8.0.1

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The SELinux security driver needs to learn to label storage/misc hostdev devices for LXC
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- src/security/security_selinux.c | 118 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+)
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index ad13490..6f0cd4d 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1171,6 +1171,65 @@ done:
static int +virSecuritySELinuxSetSecurityHostdevCapsLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + virSecurityLabelDefPtr secdef; + char *path; + + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); + if (secdef == NULL) + return -1; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block)< 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.storage.block))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxSetFilecon(path, secdef->imagelabel); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev)< 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.misc.chardev))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxSetFilecon(path, secdef->imagelabel); + VIR_FREE(path); + break; + } + + default: + ret = 0; + break; + } + + return ret; +} + + +static int virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def, virDomainHostdevDefPtr dev, @@ -1190,6 +1249,9 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return virSecuritySELinuxSetSecurityHostdevSubsysLabel(def, dev, vroot);
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return virSecuritySELinuxSetSecurityHostdevCapsLabel(def, dev, vroot); + default: return 0; } @@ -1265,6 +1327,59 @@ done:
static int +virSecuritySELinuxRestoreSecurityHostdevCapsLabel(virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + char *path; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block)< 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.storage.block))) { + virReportOOMError(); + return -1; + } + } + ret = virSecuritySELinuxRestoreSecurityFileLabel(path); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev)< 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(dev->source.caps.u.misc.chardev))) { + virReportOOMError(); + return -1; + } + }
I think it's better helper to get the path to label, to avoid the duplciate codes in virSecuritySELinuxRestoreSecurityHostdevCapsLabel and virSecuritySELinuxSetSecurityHostdevCapsLabel. ACK otherwise.

From: "Daniel P. Berrange" <berrange@redhat.com> Currently LXC guests can be given arbitrary pre-mounted filesystems, however, for some usecases it is more appropriate to provide block devices which the container can mount itself. This first impl only allows for <disk type='block'>, in other words exposing a host disk device to a container. Since LXC does not have device namespace virtualization, we are cheating a little bit. If the XML specifies /dev/sdc4 to be given to the container as /dev/sda1, when we do the mknod /dev/sda1 in the container's /dev, we actually use the major:minor number of /dev/sdc4, not /dev/sda1. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_cgroup.c | 18 +++++++++ src/lxc/lxc_container.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 767ef26..0636869 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -332,6 +332,24 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, } } + for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK) + continue; + + rc = virCgroupAllowDevicePath(cgroup, + def->disks[i]->src, + (def->disks[i]->readonly ? + VIR_CGROUP_DEVICE_READ : + VIR_CGROUP_DEVICE_RW) | + VIR_CGROUP_DEVICE_MKNOD); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to allow device %s for domain %s"), + def->disks[i]->src, def->name); + goto cleanup; + } + } + for (i = 0 ; i < def->nfss ; i++) { if (def->fss[i]->type != VIR_DOMAIN_FS_TYPE_BLOCK) continue; diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 3014564..be26de9 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -1211,6 +1211,106 @@ static int lxcContainerMountAllFS(virDomainDefPtr vmDef, } +static int lxcContainerSetupDisk(virDomainDefPtr vmDef, + virDomainDiskDefPtr def, + const char *dstprefix, + virSecurityManagerPtr securityDriver) +{ + char *src = NULL; + char *dst = NULL; + int ret = -1; + struct stat sb; + mode_t mode; + char *tmpsrc = def->src; + + if (def->type != VIR_DOMAIN_DISK_TYPE_BLOCK) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't setup disk for non-block device")); + goto cleanup; + } + if (def->src == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't setup disk without media")); + goto cleanup; + } + + if (virAsprintf(&src, "%s/%s", dstprefix, def->src) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "/dev/%s", def->dst) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), def->src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Disk source %s must be a character/block device"), + def->src); + goto cleanup; + } + + mode = 0700; + if (S_ISCHR(sb.st_mode)) + mode |= S_IFCHR; + else + mode |= S_IFBLK; + + /* Yes, the device name we're creating may not + * actually correspond to the major:minor number + * we're using, but we've no other option at this + * time. Just have to hope that containerized apps + * don't get upset that the major:minor is different + * to that normally implied by the device name + */ + VIR_DEBUG("Creating dev %s (%d,%d) from %s", + dst, major(sb.st_rdev), minor(sb.st_rdev), src); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + /* Labelling normally operates on src, but we need + * to actally label the dst here, so hack the config */ + def->src = dst; + if (virSecurityManagerSetImageLabel(securityDriver, vmDef, def) < 0) + goto cleanup; + + ret = 0; + +cleanup: + def->src = tmpsrc; + VIR_FREE(src); + VIR_FREE(dst); + return ret; +} + +static int lxcContainerSetupAllDisks(virDomainDefPtr vmDef, + const char *dstprefix, + virSecurityManagerPtr securityDriver) +{ + size_t i; + VIR_DEBUG("Setting up disks %s", dstprefix); + + for (i = 0 ; i < vmDef->ndisks ; i++) { + if (lxcContainerSetupDisk(vmDef, vmDef->disks[i], + dstprefix, securityDriver) < 0) + return -1; + } + + VIR_DEBUG("Setup all disks"); + return 0; +} + + static int lxcContainerGetSubtree(const char *prefix, char ***mountsret, size_t *nmountsret) @@ -1606,6 +1706,10 @@ static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef, if (lxcContainerMountAllFS(vmDef, "/.oldroot", true, sec_mount_options) < 0) goto cleanup; + /* Sets up any extra disks from guest config */ + if (lxcContainerSetupAllDisks(vmDef, "/.oldroot", securityDriver) < 0) + goto cleanup; + /* Gets rid of all remaining mounts from host OS, including /.oldroot itself */ if (lxcContainerUnmountSubtree("/.oldroot", true) < 0) goto cleanup; -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Currently LXC guests can be given arbitrary pre-mounted filesystems, however, for some usecases it is more appropriate to provide block devices which the container can mount itself. This first impl only allows for <disk type='block'>, in other words exposing a host disk device to a container. Since LXC does not have device namespace virtualization, we are cheating a little bit. If the XML specifies /dev/sdc4 to be given to the container as /dev/sda1, when we do the mknod /dev/sda1 in the container's /dev, we actually use the major:minor number of /dev/sdc4, not /dev/sda1.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_cgroup.c | 18 +++++++++ src/lxc/lxc_container.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+)
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 767ef26..0636869 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -332,6 +332,24 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, } }
+ for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK) + continue;
You return error when type != VIR_DOMAIN_DISK_TYPE_BLOCK in lxcContainerSetupDisk, So I think you can return error immediately here too. Just this little advice. ACK.

From: "Daniel P. Berrange" <berrange@redhat.com> This adds support for host device passthrough with the LXC driver. Since there is only a single kernel image, it doesn't make sense to pass through PCI devices, but USB devices are fine. For the latter we merely need to make the /dev/bus/usb/NNN/MMM character device exist in the container's /dev Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/Makefile.am | 1 + src/lxc/lxc_cgroup.c | 64 ++++++++ src/lxc/lxc_cgroup.h | 12 ++ src/lxc/lxc_conf.h | 3 + src/lxc/lxc_container.c | 124 ++++++++++++++- src/lxc/lxc_driver.c | 6 + src/lxc/lxc_hostdev.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/lxc_hostdev.h | 43 ++++++ src/lxc/lxc_process.c | 11 ++ 9 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 src/lxc/lxc_hostdev.c create mode 100644 src/lxc/lxc_hostdev.h diff --git a/src/Makefile.am b/src/Makefile.am index 627dbb5..c3459a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -411,6 +411,7 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_container.c lxc/lxc_container.h \ lxc/lxc_cgroup.c lxc/lxc_cgroup.h \ lxc/lxc_domain.c lxc/lxc_domain.h \ + lxc/lxc_hostdev.c lxc/lxc_hostdev.h \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 0636869..14c840a 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -291,6 +291,49 @@ struct _virLXCCgroupDevicePolicy { }; +int +virLXCSetupHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path '%s' for USB device", path); + rc = virCgroupAllowDevicePath(cgroup, path, + VIR_CGROUP_DEVICE_RW); + if (rc < 0) { + virReportSystemError(-rc, + _("Unable to allow device %s"), + path); + return -1; + } + + return 0; +} + + +int +virLXCTeardownHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path '%s' for USB device", path); + rc = virCgroupDenyDevicePath(cgroup, path, + VIR_CGROUP_DEVICE_RW); + if (rc < 0) { + virReportSystemError(-rc, + _("Unable to deny device %s"), + path); + return -1; + } + + return 0; +} + static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, virCgroupPtr cgroup) @@ -367,6 +410,27 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, } } + for (i = 0; i < def->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + usbDevice *usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) + goto cleanup; + + if (usbDeviceFileIterate(usb, virLXCSetupHostUsbDeviceCgroup, + cgroup) < 0) + goto cleanup; + } + rc = virCgroupAllowDeviceMajor(cgroup, 'c', LXC_DEV_MAJ_PTY, VIR_CGROUP_DEVICE_RWM); if (rc != 0) { diff --git a/src/lxc/lxc_cgroup.h b/src/lxc/lxc_cgroup.h index 6961943..c1d4551 100644 --- a/src/lxc/lxc_cgroup.h +++ b/src/lxc/lxc_cgroup.h @@ -24,7 +24,19 @@ # include "domain_conf.h" # include "lxc_fuse.h" +# include "hostusb.h" int virLXCCgroupSetup(virDomainDefPtr def); int virLXCCgroupGetMeminfo(virLXCMeminfoPtr meminfo); + +int +virLXCSetupHostUsbDeviceCgroup(usbDevice *dev, + const char *path, + void *opaque); + +int +virLXCTeardownHostUsbDeviceCgroup(usbDevice *dev, + const char *path, + void *opaque); + #endif /* __VIR_LXC_CGROUP_H__ */ diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h index 4ae0c5e..a6872ea 100644 --- a/src/lxc/lxc_conf.h +++ b/src/lxc/lxc_conf.h @@ -35,6 +35,7 @@ # include "cgroup.h" # include "security/security_manager.h" # include "configmake.h" +# include "hostusb.h" # define LXC_DRIVER_NAME "LXC" @@ -60,6 +61,8 @@ struct _virLXCDriver { int log_libvirtd; int have_netns; + usbDeviceList *activeUsbHostdevs; + virDomainEventStatePtr domainEventState; char *securityDriverName; diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index be26de9..0a407bf 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -1311,6 +1311,124 @@ static int lxcContainerSetupAllDisks(virDomainDefPtr vmDef, } +static int lxcContainerSetupHostdevSubsysUSB(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED) +{ + int ret = -1; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; + + if (virAsprintf(&dstdir, "/dev/bus/usb/%03d", + def->source.subsys.u.usb.bus) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dstfile, "%s/%03d", + dstdir, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&src, "%s/%s", dstprefix, dstfile) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("USB source %s was not a character device"), + src); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + if (virFileMakePath(dstdir) < 0) { + virReportSystemError(errno, + _("Unable to create %s"), dstdir); + goto cleanup; + } + + VIR_DEBUG("Creating dev %s (%d,%d)", + dstfile, major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dstfile, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dstfile); + goto cleanup; + } + + if (virSecurityManagerSetHostdevLabel(securityDriver, vmDef, def, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(src); + VIR_FREE(dstfile); + VIR_FREE(dstdir); + return ret; +} + + +static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef, + virDomainHostdevDefPtr def, + const char *dstprefix, + virSecurityManagerPtr securityDriver) +{ + switch (def->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return lxcContainerSetupHostdevSubsysUSB(vmDef, def, dstprefix, securityDriver); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevSubsysTypeToString(def->source.subsys.type)); + return -1; + } +} + + +static int lxcContainerSetupAllHostdevs(virDomainDefPtr vmDef, + const char *dstprefix, + virSecurityManagerPtr securityDriver) +{ + size_t i; + VIR_DEBUG("Setting up hostdevs %s", dstprefix); + + for (i = 0 ; i < vmDef->nhostdevs ; i++) { + virDomainHostdevDefPtr def = vmDef->hostdevs[i]; + switch (def->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + if (lxcContainerSetupHostdevSubsys(vmDef, def, dstprefix, securityDriver) < 0) + return -1; + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevModeTypeToString(def->mode)); + return -1; + } + } + + VIR_DEBUG("Setup all hostdevs"); + return 0; +} + + static int lxcContainerGetSubtree(const char *prefix, char ***mountsret, size_t *nmountsret) @@ -1710,7 +1828,11 @@ static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef, if (lxcContainerSetupAllDisks(vmDef, "/.oldroot", securityDriver) < 0) goto cleanup; - /* Gets rid of all remaining mounts from host OS, including /.oldroot itself */ + /* Sets up any extra host devices from guest config */ + if (lxcContainerSetupAllHostdevs(vmDef, "/.oldroot", securityDriver) < 0) + goto cleanup; + + /* Gets rid of all remaining mounts from host OS, including /.oldroot itself */ if (lxcContainerUnmountSubtree("/.oldroot", true) < 0) goto cleanup; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 2e9cfe4..1c6ec8c 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -42,6 +42,7 @@ #include "lxc_container.h" #include "lxc_domain.h" #include "lxc_driver.h" +#include "lxc_hostdev.h" #include "lxc_process.h" #include "memory.h" #include "util.h" @@ -1463,6 +1464,9 @@ static int lxcStartup(int privileged) if ((lxc_driver->caps = lxcCapsInit(lxc_driver)) == NULL) goto cleanup; + if ((lxc_driver->activeUsbHostdevs = usbDeviceListNew()) == NULL) + goto cleanup; + virLXCDomainSetPrivateDataHooks(lxc_driver->caps); if (virLXCProcessAutoDestroyInit(lxc_driver) < 0) @@ -1548,6 +1552,8 @@ static int lxcShutdown(void) virDomainObjListDeinit(&lxc_driver->domains); virDomainEventStateFree(lxc_driver->domainEventState); + usbDeviceListFree(lxc_driver->activeUsbHostdevs); + virLXCProcessAutoDestroyShutdown(lxc_driver); virCapabilitiesFree(lxc_driver->caps); diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c new file mode 100644 index 0000000..c063aa0 --- /dev/null +++ b/src/lxc/lxc_hostdev.c @@ -0,0 +1,390 @@ +/* + * virLXC_hostdev.c: VIRLXC hostdev management + * + * Copyright (C) 2006-2007, 2009-2012 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "lxc_hostdev.h" +#include "logging.h" +#include "virterror_internal.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_LXC + +int +virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, + virDomainDefPtr def) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + + if (!def->nhostdevs) + return 0; + + for (i = 0; i < def->nhostdevs; i++) { + usbDevice *usb = NULL; + hostdev = def->hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + def->name); + continue; + } + + usbDeviceSetUsedBy(usb, def->name); + + if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { + usbFreeDevice(usb); + return -1; + } + } + + return 0; +} + + +int +virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, + const char *name, + usbDeviceList *list) +{ + size_t i, j; + unsigned int count; + usbDevice *tmp; + + count = usbDeviceListCount(list); + + for (i = 0; i < count; i++) { + usbDevice *usb = usbDeviceListGet(list, i); + if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) { + const char *other_name = usbDeviceGetUsedBy(tmp); + + if (other_name) + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is in use by domain %s"), + usbDeviceGetName(tmp), other_name); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is already in use"), + usbDeviceGetName(tmp)); + goto error; + } + + usbDeviceSetUsedBy(usb, name); + VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", + usbDeviceGetBus(usb), usbDeviceGetDevno(usb), name); + /* + * The caller is responsible to steal these usb devices + * from the usbDeviceList that passed in on success, + * perform rollback on failure. + */ + if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) + goto error; + } + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = usbDeviceListGet(list, i); + usbDeviceListSteal(driver->activeUsbHostdevs, tmp); + } + return -1; +} + +int +virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, + bool mandatory, + usbDevice **usb) +{ + unsigned vendor = hostdev->source.subsys.u.usb.vendor; + unsigned product = hostdev->source.subsys.u.usb.product; + unsigned bus = hostdev->source.subsys.u.usb.bus; + unsigned device = hostdev->source.subsys.u.usb.device; + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; + int rc; + + *usb = NULL; + + if (vendor && bus) { + rc = usbFindDevice(vendor, product, bus, device, + NULL, + autoAddress ? false : mandatory, + usb); + if (rc < 0) { + return -1; + } else if (!autoAddress) { + goto out; + } else { + VIR_INFO("USB device %x:%x could not be found at previous" + " address (bus:%u device:%u)", + vendor, product, bus, device); + } + } + + /* When vendor is specified, its USB address is either unspecified or the + * device could not be found at the USB device where it had been + * automatically found before. + */ + if (vendor) { + usbDeviceList *devs; + + rc = usbFindDeviceByVendor(vendor, product, + NULL, + mandatory, &devs); + if (rc < 0) + return -1; + + if (rc == 1) { + *usb = usbDeviceListGet(devs, 0); + usbDeviceListSteal(devs, *usb); + } + usbDeviceListFree(devs); + + if (rc == 0) { + goto out; + } else if (rc > 1) { + if (autoAddress) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x were found," + " but none of them is at bus:%u device:%u"), + vendor, product, bus, device); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use <address> to specify one"), + vendor, product); + } + return -1; + } + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(*usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(*usb); + hostdev->source.subsys.u.usb.autoAddress = true; + + if (autoAddress) { + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + " from bus:%u device:%u)", + vendor, product, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + bus, device); + } + } else if (!vendor && bus) { + if (usbFindDeviceByBus(bus, device, + NULL, + mandatory, usb) < 0) + return -1; + } + +out: + if (!*usb) + hostdev->missing = 1; + return 0; +} + +static int +virLXCPrepareHostUSBDevices(virLXCDriverPtr driver, + virDomainDefPtr def) +{ + size_t i; + int ret = -1; + usbDeviceList *list; + usbDevice *tmp; + virDomainHostdevDefPtr *hostdevs = def->hostdevs; + int nhostdevs = def->nhostdevs; + + /* To prevent situation where USB device is assigned to two domains + * we need to keep a list of currently assigned USB devices. + * This is done in several loops which cannot be joined into one big + * loop. See virLXCPrepareHostdevPCIDevices() + */ + if (!(list = usbDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list + */ + for (i = 0 ; i < nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + bool required = true; + usbDevice *usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL) + required = false; + + if (virLXCFindHostdevUSBDevice(hostdev, required, &usb) < 0) + goto cleanup; + + if (usb && usbDeviceListAdd(list, usb) < 0) { + usbFreeDevice(usb); + goto cleanup; + } + } + + /* Mark devices in temporary list as used by @name + * and add them do driver list. However, if something goes + * wrong, perform rollback. + */ + if (virLXCPrepareHostdevUSBDevices(driver, def->name, list) < 0) + goto cleanup; + + /* Loop 2: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * in cleanup label. + */ + while (usbDeviceListCount(list) > 0) { + tmp = usbDeviceListGet(list, 0); + usbDeviceListSteal(list, tmp); + } + + ret = 0; + +cleanup: + usbDeviceListFree(list); + return ret; +} + + +int virLXCPrepareHostDevices(virLXCDriverPtr driver, + virDomainDefPtr def) +{ + size_t i; + + if (!def->nhostdevs) + return 0; + + /* Sanity check for supported configurations only */ + for (i = 0 ; i < def->nhostdevs ; i++) { + virDomainHostdevDefPtr dev = def->hostdevs[i]; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + switch (dev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported hostdev type %s"), + virDomainHostdevSubsysTypeToString(dev->source.subsys.type)); + break; + } + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported hostdev mode %s"), + virDomainHostdevModeTypeToString(dev->mode)); + break; + } + } + + if (virLXCPrepareHostUSBDevices(driver, def) < 0) + return -1; + + return 0; +} + + +static void +virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + usbDevice *usb, *tmp; + const char *used_by = NULL; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + name); + continue; + } + + /* Delete only those USB devices which belongs + * to domain @name because virLXCProcessStart() might + * have failed because USB device is already taken. + * Therefore we want to steal only those devices from + * the list which were taken by @name */ + + tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb); + usbFreeDevice(usb); + + if (!tmp) { + VIR_WARN("Unable to find device %03d.%03d " + "in list of active USB devices", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + continue; + } + + used_by = usbDeviceGetUsedBy(tmp); + if (STREQ_NULLABLE(used_by, name)) { + VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + name); + + usbDeviceListDel(driver->activeUsbHostdevs, tmp); + } + } +} + +void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return; + + virLXCDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs, + def->nhostdevs); +} diff --git a/src/lxc/lxc_hostdev.h b/src/lxc/lxc_hostdev.h new file mode 100644 index 0000000..dd98112 --- /dev/null +++ b/src/lxc/lxc_hostdev.h @@ -0,0 +1,43 @@ +/* + * virLXC_hostdev.h: VIRLXC hostdev management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __LXC_HOSTDEV_H__ +# define __LXC_HOSTDEV_H__ + +# include "lxc_conf.h" +# include "domain_conf.h" + +int virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, + virDomainDefPtr def); +int virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, + bool mandatory, + usbDevice **usb); +int virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, + const char *name, + usbDeviceList *list); +int virLXCPrepareHostDevices(virLXCDriverPtr driver, + virDomainDefPtr def); +void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, + virDomainDefPtr def); + +#endif /* __LXC_HOSTDEV_H__ */ diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 72e1be3..607e826 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -44,6 +44,7 @@ #include "logging.h" #include "command.h" #include "hooks.h" +#include "lxc_hostdev.h" #define VIR_FROM_THIS VIR_FROM_LXC @@ -252,6 +253,8 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, vm->pid = -1; vm->def->id = -1; + virLXCDomainReAttachHostDevices(driver, vm->def); + for (i = 0 ; i < vm->def->nnets ; i++) { virDomainNetDefPtr iface = vm->def->nets[i]; vport = virDomainNetGetActualVirtPortProfile(iface); @@ -973,6 +976,11 @@ int virLXCProcessStart(virConnectPtr conn, goto cleanup; } + /* Must be run before security labelling */ + VIR_DEBUG("Preparing host devices"); + if (virLXCPrepareHostDevices(driver, vm->def) < 0) + goto cleanup; + /* Here we open all the PTYs we need on the host OS side. * The LXC controller will open the guest OS side PTYs * and forward I/O between them. @@ -1298,6 +1306,9 @@ virLXCProcessReconnectDomain(void *payload, const void *name ATTRIBUTE_UNUSED, v if (!(priv->monitor = virLXCProcessConnectMonitor(driver, vm))) goto error; + if (virLXCUpdateActiveUsbHostdevs(driver, vm->def) < 0) + goto error; + if (virSecurityManagerReserveLabel(driver->securityManager, vm->def, vm->pid) < 0) goto error; -- 1.8.0.1

on 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This adds support for host device passthrough with the LXC driver. Since there is only a single kernel image, it doesn't make sense to pass through PCI devices, but USB devices are fine. For the latter we merely need to make the /dev/bus/usb/NNN/MMM character device exist in the container's /dev
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/Makefile.am | 1 + src/lxc/lxc_cgroup.c | 64 ++++++++ src/lxc/lxc_cgroup.h | 12 ++ src/lxc/lxc_conf.h | 3 + src/lxc/lxc_container.c | 124 ++++++++++++++- src/lxc/lxc_driver.c | 6 + src/lxc/lxc_hostdev.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/lxc_hostdev.h | 43 ++++++ src/lxc/lxc_process.c | 11 ++ 9 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 src/lxc/lxc_hostdev.c create mode 100644 src/lxc/lxc_hostdev.h
diff --git a/src/Makefile.am b/src/Makefile.am index 627dbb5..c3459a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -411,6 +411,7 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_container.c lxc/lxc_container.h \ lxc/lxc_cgroup.c lxc/lxc_cgroup.h \ lxc/lxc_domain.c lxc/lxc_domain.h \ + lxc/lxc_hostdev.c lxc/lxc_hostdev.h \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 0636869..14c840a 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -291,6 +291,49 @@ struct _virLXCCgroupDevicePolicy { };
+int +virLXCSetupHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path '%s' for USB device", path); + rc = virCgroupAllowDevicePath(cgroup, path, + VIR_CGROUP_DEVICE_RW); + if (rc < 0) {
Maybe it's better to give some warning message when rc == 1,it means the path is not a device.
+ virReportSystemError(-rc, + _("Unable to allow device %s"), + path); + return -1; + } + + return 0; +} + + +int +virLXCTeardownHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path '%s' for USB device", path); + rc = virCgroupDenyDevicePath(cgroup, path, + VIR_CGROUP_DEVICE_RW); + if (rc < 0) { + virReportSystemError(-rc, + _("Unable to deny device %s"), + path); + return -1; + } + + return 0; +} +
[...]
+static int lxcContainerSetupHostdevSubsysUSB(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED)
ATTRIBUTE_UNUSED should be removed.
+{ + int ret = -1; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; +
[...]
+ +int +virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, + const char *name, + usbDeviceList *list) +{ + size_t i, j; + unsigned int count; + usbDevice *tmp; + + count = usbDeviceListCount(list); + + for (i = 0; i < count; i++) { + usbDevice *usb = usbDeviceListGet(list, i); + if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) { + const char *other_name = usbDeviceGetUsedBy(tmp); + + if (other_name) + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is in use by domain %s"), + usbDeviceGetName(tmp), other_name); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is already in use"), + usbDeviceGetName(tmp)); + goto error; + } + + usbDeviceSetUsedBy(usb, name); + VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", + usbDeviceGetBus(usb), usbDeviceGetDevno(usb), name); + /* + * The caller is responsible to steal these usb devices + * from the usbDeviceList that passed in on success, + * perform rollback on failure. + */ + if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) + goto error; + } + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = usbDeviceListGet(list, i);
j?
+ usbDeviceListSteal(driver->activeUsbHostdevs, tmp); + } + return -1; +}
this patch looks good to me. ACK

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This adds support for host device passthrough with the LXC driver. Since there is only a single kernel image, it doesn't make sense to pass through PCI devices, but USB devices are fine. For the latter we merely need to make the /dev/bus/usb/NNN/MMM character device exist in the container's /dev
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
[...]
+static int lxcContainerSetupHostdevSubsysUSB(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED) +{ + int ret = -1; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; + + if (virAsprintf(&dstdir, "/dev/bus/usb/%03d", + def->source.subsys.u.usb.bus) < 0) {
better to use USB_DEVFS instead of /dev/bus/usb/

On Fri, Dec 14, 2012 at 03:31:39PM +0800, Gao feng wrote:
On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This adds support for host device passthrough with the LXC driver. Since there is only a single kernel image, it doesn't make sense to pass through PCI devices, but USB devices are fine. For the latter we merely need to make the /dev/bus/usb/NNN/MMM character device exist in the container's /dev
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
[...]
+static int lxcContainerSetupHostdevSubsysUSB(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED) +{ + int ret = -1; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; + + if (virAsprintf(&dstdir, "/dev/bus/usb/%03d", + def->source.subsys.u.usb.bus) < 0) {
better to use USB_DEVFS instead of /dev/bus/usb/
Yep, will change that Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> This extends support for host device passthrough with LXC to cover storage devices. In this case all we need todo is a mknod in the container's /dev and whitelist the device in cgroups Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_cgroup.c | 46 ++++++++++++++++++---------- src/lxc/lxc_container.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 15 deletions(-) diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 14c840a..0c3d5dd 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -414,21 +414,37 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, virDomainHostdevDefPtr hostdev = def->hostdevs[i]; usbDevice *usb; - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - if (hostdev->missing) - continue; - - if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - NULL)) == NULL) - goto cleanup; - - if (usbDeviceFileIterate(usb, virLXCSetupHostUsbDeviceCgroup, - cgroup) < 0) - goto cleanup; + switch (hostdev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) + goto cleanup; + + if (usbDeviceFileIterate(usb, virLXCSetupHostUsbDeviceCgroup, + cgroup) < 0) + goto cleanup; + break; + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + switch (hostdev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + if (virCgroupAllowDevicePath(cgroup, + hostdev->source.caps.u.storage.block, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) < 0) + goto cleanup; + break; + default: + break; + } + default: + break; + } } rc = virCgroupAllowDeviceMajor(cgroup, 'c', LXC_DEV_MAJ_PTY, diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 0a407bf..19c5702 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -1384,6 +1384,64 @@ cleanup: } +static int lxcContainerSetupHostdevCapsStorage(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED) +{ + char *src = NULL; + int ret = -1; + struct stat sb; + mode_t mode; + + if (def->source.caps.u.storage.block == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage host block path")); + goto cleanup; + } + + if (virAsprintf(&src, "%s/%s", dstprefix, def->source.caps.u.storage.block) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + src); + goto cleanup; + } + + if (!S_ISBLK(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Storage source %s must be a block device"), + def->source.caps.u.storage.block); + goto cleanup; + } + + mode = 0700 | S_IFBLK; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.storage.block, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(def->source.caps.u.storage.block, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + def->source.caps.u.storage.block); + goto cleanup; + } + + if (virSecurityManagerSetHostdevLabel(securityDriver, vmDef, def, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(src); + return ret; +} + + static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef, virDomainHostdevDefPtr def, const char *dstprefix, @@ -1402,6 +1460,24 @@ static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef, } +static int lxcContainerSetupHostdevCaps(virDomainDefPtr vmDef, + virDomainHostdevDefPtr def, + const char *dstprefix, + virSecurityManagerPtr securityDriver) +{ + switch (def->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + return lxcContainerSetupHostdevCapsStorage(vmDef, def, dstprefix, securityDriver); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevCapsTypeToString(def->source.subsys.type)); + return -1; + } +} + + static int lxcContainerSetupAllHostdevs(virDomainDefPtr vmDef, const char *dstprefix, virSecurityManagerPtr securityDriver) @@ -1416,6 +1492,10 @@ static int lxcContainerSetupAllHostdevs(virDomainDefPtr vmDef, if (lxcContainerSetupHostdevSubsys(vmDef, def, dstprefix, securityDriver) < 0) return -1; break; + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + if (lxcContainerSetupHostdevCaps(vmDef, def, dstprefix, securityDriver) < 0) + return -1; + break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device mode %s"), -- 1.8.0.1

From: "Daniel P. Berrange" <berrange@redhat.com> This extends support for host device passthrough with LXC to cover misc devices. In this case all we need todo is a mknod in the container's /dev and whitelist the device in cgroups Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_cgroup.c | 7 ++++++ src/lxc/lxc_container.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 0c3d5dd..4fe23c1 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -439,6 +439,13 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, VIR_CGROUP_DEVICE_MKNOD) < 0) goto cleanup; break; + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + if (virCgroupAllowDevicePath(cgroup, + hostdev->source.caps.u.misc.chardev, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) < 0) + goto cleanup; + break; default: break; } diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 19c5702..25a3ab5 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -1442,6 +1442,64 @@ cleanup: } +static int lxcContainerSetupHostdevCapsMisc(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED) +{ + char *src = NULL; + int ret = -1; + struct stat sb; + mode_t mode; + + if (def->source.caps.u.misc.chardev == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage host block path")); + goto cleanup; + } + + if (virAsprintf(&src, "%s/%s", dstprefix, def->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Storage source %s must be a character device"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.misc.chardev, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(def->source.caps.u.misc.chardev, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (virSecurityManagerSetHostdevLabel(securityDriver, vmDef, def, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(src); + return ret; +} + + static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef, virDomainHostdevDefPtr def, const char *dstprefix, @@ -1469,6 +1527,9 @@ static int lxcContainerSetupHostdevCaps(virDomainDefPtr vmDef, case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: return lxcContainerSetupHostdevCapsStorage(vmDef, def, dstprefix, securityDriver); + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + return lxcContainerSetupHostdevCapsMisc(vmDef, def, dstprefix, securityDriver); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device mode %s"), -- 1.8.0.1

on 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This extends support for host device passthrough with LXC to cover misc devices. In this case all we need todo is a mknod in the container's /dev and whitelist the device in cgroups
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_cgroup.c | 7 ++++++ src/lxc/lxc_container.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+)
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 0c3d5dd..4fe23c1 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -439,6 +439,13 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, VIR_CGROUP_DEVICE_MKNOD) < 0) goto cleanup; break; + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + if (virCgroupAllowDevicePath(cgroup, + hostdev->source.caps.u.misc.chardev, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) < 0) + goto cleanup; + break; default: break; } diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 19c5702..25a3ab5 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -1442,6 +1442,64 @@ cleanup: }
+static int lxcContainerSetupHostdevCapsMisc(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr def ATTRIBUTE_UNUSED, + const char *dstprefix ATTRIBUTE_UNUSED, + virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED)
ATTRIBUTE_UNUSED should be removed.
+{ + char *src = NULL; + int ret = -1; + struct stat sb; + mode_t mode; + + if (def->source.caps.u.misc.chardev == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage host block path"));
we need proper error message.
+ goto cleanup; + } + + if (virAsprintf(&src, "%s/%s", dstprefix, def->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Storage source %s must be a character device"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.misc.chardev, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(def->source.caps.u.misc.chardev, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (virSecurityManagerSetHostdevLabel(securityDriver, vmDef, def, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(src); + return ret; +} + + static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef, virDomainHostdevDefPtr def, const char *dstprefix, @@ -1469,6 +1527,9 @@ static int lxcContainerSetupHostdevCaps(virDomainDefPtr vmDef, case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: return lxcContainerSetupHostdevCapsStorage(vmDef, def, dstprefix, securityDriver);
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + return lxcContainerSetupHostdevCapsMisc(vmDef, def, dstprefix, securityDriver); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device mode %s"),
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> This wires up the LXC driver to support the domain device attach/ detach/update APIs, following the same code design as used in the QEMU driver. No actual changes are possible with this commit, it is only providing the framework Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 1c6ec8c..38d5d87 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2768,6 +2768,291 @@ lxcListAllDomains(virConnectPtr conn, return ret; } + +static int +lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent attach of device is not supported")); + break; + } + + return ret; +} + + +static int +lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent update of device is not supported")); + break; + } + + return ret; +} + + +static int +lxcDomainDetachDeviceConfig(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent detach of device is not supported")); + break; + } + + return ret; +} + + +static int +lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be attached"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + return ret; +} + + +static int +lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be detached"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + return ret; +} + + +/* Actions for lxcDomainModifyDeviceFlags */ +enum { + LXC_DEVICE_ATTACH, + LXC_DEVICE_UPDATE, + LXC_DEVICE_DETACH, +}; + + +static int +lxcDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, + unsigned int flags, int action) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainDefPtr vmdef = NULL; + virDomainDeviceDefPtr dev = NULL, dev_copy = NULL; + int ret = -1; + unsigned int affect; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG | + (action == LXC_DEVICE_UPDATE ? + VIR_DOMAIN_DEVICE_MODIFY_FORCE : 0), -1); + + affect = flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG); + + lxcDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + if (affect == VIR_DOMAIN_AFFECT_CURRENT) + flags |= VIR_DOMAIN_AFFECT_LIVE; + } else { + if (affect == VIR_DOMAIN_AFFECT_CURRENT) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + /* check consistency between flags and the vm state */ + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot do live update a device on " + "inactive domain")); + goto cleanup; + } + } + + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && !vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot modify device on transient domain")); + goto cleanup; + } + + dev = dev_copy = virDomainDeviceDefParse(driver->caps, vm->def, xml, + VIR_DOMAIN_XML_INACTIVE); + if (dev == NULL) + goto cleanup; + + if (flags & VIR_DOMAIN_AFFECT_CONFIG && + flags & VIR_DOMAIN_AFFECT_LIVE) { + /* If we are affecting both CONFIG and LIVE + * create a deep copy of device as adding + * to CONFIG takes one instance. + */ + dev_copy = virDomainDeviceDefCopy(driver->caps, vm->def, dev); + if (!dev_copy) + goto cleanup; + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + if (virDomainDefCompatibleDevice(vm->def, dev) < 0) + goto cleanup; + + /* Make a copy for updated domain. */ + vmdef = virDomainObjCopyPersistentDef(driver->caps, vm); + if (!vmdef) + goto cleanup; + switch (action) { + case LXC_DEVICE_ATTACH: + ret = lxcDomainAttachDeviceConfig(vmdef, dev); + break; + case LXC_DEVICE_DETACH: + ret = lxcDomainDetachDeviceConfig(vmdef, dev); + break; + case LXC_DEVICE_UPDATE: + ret = lxcDomainUpdateDeviceConfig(vmdef, dev); + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + break; + } + + if (ret == -1) + goto cleanup; + } + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + if (virDomainDefCompatibleDevice(vm->def, dev_copy) < 0) + goto cleanup; + + switch (action) { + case LXC_DEVICE_ATTACH: + ret = lxcDomainAttachDeviceLive(driver, vm, dev_copy); + break; + case LXC_DEVICE_DETACH: + ret = lxcDomainDetachDeviceLive(driver, vm, dev_copy); + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + ret = -1; + break; + } + + if (ret == -1) + goto cleanup; + /* + * update domain status forcibly because the domain status may be + * changed even if we failed to attach the device. For example, + * a new controller may be created. + */ + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) { + ret = -1; + goto cleanup; + } + } + + /* Finally, if no error until here, we can save config. */ + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + ret = virDomainSaveConfig(driver->configDir, vmdef); + if (!ret) { + virDomainObjAssignDef(vm, vmdef, false); + vmdef = NULL; + } + } + +cleanup: + virDomainDefFree(vmdef); + if (dev != dev_copy) + virDomainDeviceDefFree(dev_copy); + virDomainDeviceDefFree(dev); + if (vm) + virDomainObjUnlock(vm); + lxcDriverUnlock(driver); + return ret; +} + + +static int lxcDomainAttachDeviceFlags(virDomainPtr dom, + const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, + LXC_DEVICE_ATTACH); +} + + +static int lxcDomainAttachDevice(virDomainPtr dom, + const char *xml) +{ + return lxcDomainAttachDeviceFlags(dom, xml, + VIR_DOMAIN_AFFECT_LIVE); +} + + +static int lxcDomainUpdateDeviceFlags(virDomainPtr dom, + const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, + LXC_DEVICE_UPDATE); +} + + +static int lxcDomainDetachDeviceFlags(virDomainPtr dom, + const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, + LXC_DEVICE_DETACH); +} + + +static int lxcDomainDetachDevice(virDomainPtr dom, + const char *xml) +{ + return lxcDomainDetachDeviceFlags(dom, xml, + VIR_DOMAIN_AFFECT_LIVE); +} + /* Function Tables */ static virDriver lxcDriver = { .no = VIR_DRV_LXC, @@ -2809,6 +3094,11 @@ static virDriver lxcDriver = { .domainDefineXML = lxcDomainDefine, /* 0.4.2 */ .domainUndefine = lxcDomainUndefine, /* 0.4.2 */ .domainUndefineFlags = lxcDomainUndefineFlags, /* 0.9.4 */ + .domainAttachDevice = lxcDomainAttachDevice, /* 1.0.1 */ + .domainAttachDeviceFlags = lxcDomainAttachDeviceFlags, /* 1.0.1 */ + .domainDetachDevice = lxcDomainDetachDevice, /* 1.0.1 */ + .domainDetachDeviceFlags = lxcDomainDetachDeviceFlags, /* 1.0.1 */ + .domainUpdateDeviceFlags = lxcDomainUpdateDeviceFlags, /* 1.0.1 */ .domainGetAutostart = lxcDomainGetAutostart, /* 0.7.0 */ .domainSetAutostart = lxcDomainSetAutostart, /* 0.7.0 */ .domainGetSchedulerType = lxcGetSchedulerType, /* 0.5.0 */ -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This wires up the LXC driver to support the domain device attach/ detach/update APIs, following the same code design as used in the QEMU driver. No actual changes are possible with this commit, it is only providing the framework
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach/update device APIs to support changing of network interfaces in the persistent config file Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 38d5d87..ecd7cb8 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2770,52 +2770,115 @@ lxcListAllDomains(virConnectPtr conn, static int -lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net; switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + if (virDomainNetInsert(vmdef, net) < 0) { + virReportOOMError(); + goto cleanup; + } + dev->data.net = NULL; + ret = 0; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); break; } +cleanup: return ret; } static int -lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net; + int pos; + char mac[VIR_MAC_STRING_BUFLEN]; switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + pos = virDomainNetFindIdx(vmdef, net); + if (pos == -2) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + } else if (pos < 0) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + } + + virDomainNetDefFree(vmdef->nets[pos]); + + vmdef->nets[pos] = net; + dev->data.net = NULL; + ret = 0; + + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent update of device is not supported")); break; } +cleanup: return ret; } static int -lxcDomainDetachDeviceConfig(virDomainDefPtr vmDef ATTRIBUTE_UNUSED, +lxcDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net; + int idx; + char mac[VIR_MAC_STRING_BUFLEN]; switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + idx = virDomainNetFindIdx(vmdef, net); + if (idx == -2) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("multiple devices matching mac address %s found"), + virMacAddrFormat(&net->mac, mac)); + goto cleanup; + } else if (idx < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("no matching network device was found")); + goto cleanup; + } + /* this is guaranteed to succeed */ + virDomainNetDefFree(virDomainNetRemove(vmdef, idx)); + ret = 0; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); break; } +cleanup: return ret; } -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach/update device APIs to support changing of network interfaces in the persistent config file
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 38d5d87..ecd7cb8 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2770,52 +2770,115 @@ lxcListAllDomains(virConnectPtr conn,
static int -lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net;
switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + if (virDomainNetInsert(vmdef, net) < 0) { + virReportOOMError(); + goto cleanup; + } + dev->data.net = NULL; + ret = 0; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); break; }
+cleanup: return ret; }
static int -lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net; + int pos; + char mac[VIR_MAC_STRING_BUFLEN];
switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + pos = virDomainNetFindIdx(vmdef, net); + if (pos == -2) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + } else if (pos < 0) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + }
Need some proper err msg,just as the err msg in lxcDomainDetachDeviceConfig Ack

On Fri, Dec 14, 2012 at 04:56:50PM +0800, Gao feng wrote:
On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach/update device APIs to support changing of network interfaces in the persistent config file
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 38d5d87..ecd7cb8 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2770,52 +2770,115 @@ lxcListAllDomains(virConnectPtr conn,
static int -lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net;
switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + if (virDomainNetInsert(vmdef, net) < 0) { + virReportOOMError(); + goto cleanup; + } + dev->data.net = NULL; + ret = 0; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); break; }
+cleanup: return ret; }
static int -lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef ATTRIBUTE_UNUSED, +lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainNetDefPtr net; + int pos; + char mac[VIR_MAC_STRING_BUFLEN];
switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + net = dev->data.net; + pos = virDomainNetFindIdx(vmdef, net); + if (pos == -2) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + } else if (pos < 0) { + virMacAddrFormat(&net->mac, mac); + virReportError(VIR_ERR_OPERATION_FAILED, + _("couldn't find matching device " + "with mac address %s"), mac); + goto cleanup; + }
Need some proper err msg,just as the err msg in lxcDomainDetachDeviceConfig
Ok, will change this error message to match Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach/update device APIs to support changing of disks in the persistent config file Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index ecd7cb8..9a56643 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2774,9 +2774,26 @@ lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainDiskDefPtr disk; virDomainNetDefPtr net; switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + if (virDomainDiskIndexByName(vmdef, disk->dst, true) >= 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("target %s already exists."), disk->dst); + return -1; + } + if (virDomainDiskInsert(vmdef, disk)) { + virReportOOMError(); + return -1; + } + /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ + dev->data.disk = NULL; + ret = 0; + break; + case VIR_DOMAIN_DEVICE_NET: net = dev->data.net; if (virDomainNetInsert(vmdef, net) < 0) { @@ -2849,11 +2866,23 @@ lxcDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { int ret = -1; + virDomainDiskDefPtr disk, det_disk; virDomainNetDefPtr net; int idx; char mac[VIR_MAC_STRING_BUFLEN]; switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + if (!(det_disk = virDomainDiskRemoveByName(vmdef, disk->dst))) { + virReportError(VIR_ERR_INVALID_ARG, + _("no target device %s"), disk->dst); + return -1; + } + virDomainDiskDefFree(det_disk); + ret = 0; + break; + case VIR_DOMAIN_DEVICE_NET: net = dev->data.net; idx = virDomainNetFindIdx(vmdef, net); -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach/update device APIs to support changing of disks in the persistent config file
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach/update device APIs to support changing of hostdevs in the persistent config file Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 9a56643..daf32c7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2776,6 +2776,7 @@ lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, int ret = -1; virDomainDiskDefPtr disk; virDomainNetDefPtr net; + virDomainHostdevDefPtr hostdev; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -2804,6 +2805,21 @@ lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, ret = 0; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + hostdev = dev->data.hostdev; + if (virDomainHostdevFind(vmdef, hostdev, NULL) >= 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("device is already in the domain configuration")); + return -1; + } + if (virDomainHostdevInsert(vmdef, hostdev) < 0) { + virReportOOMError(); + return -1; + } + dev->data.hostdev = NULL; + ret = 0; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); @@ -2868,6 +2884,7 @@ lxcDomainDetachDeviceConfig(virDomainDefPtr vmdef, int ret = -1; virDomainDiskDefPtr disk, det_disk; virDomainNetDefPtr net; + virDomainHostdevDefPtr hostdev, det_hostdev; int idx; char mac[VIR_MAC_STRING_BUFLEN]; @@ -2901,6 +2918,19 @@ lxcDomainDetachDeviceConfig(virDomainDefPtr vmdef, ret = 0; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: { + hostdev = dev->data.hostdev; + if ((idx = virDomainHostdevFind(vmdef, hostdev, &det_hostdev)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("device not present in domain configuration")); + return -1; + } + virDomainHostdevRemove(vmdef, idx); + virDomainHostdevDefFree(det_hostdev); + ret = 0; + break; + } + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange Wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach/update device APIs to support changing of hostdevs in the persistent config file
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of disks. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index daf32c7..a2bb497 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -30,6 +30,7 @@ #include <string.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/un.h> #include <sys/poll.h> #include <unistd.h> @@ -2943,6 +2944,136 @@ cleanup: static int +lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainDiskDefPtr def = dev->data.disk; + virCgroupPtr group = NULL; + int ret = -1; + char *dst; + struct stat sb; + bool created = false; + mode_t mode = 0; + char *tmpsrc = def->src; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if (def->type != VIR_DOMAIN_DISK_TYPE_BLOCK) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't setup disk for non-block device")); + goto cleanup; + } + if (def->src == NULL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't setup disk without media")); + goto cleanup; + } + + if (virDomainDiskIndexByName(vm->def, def->dst, true) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target %s already exists"), def->dst); + goto cleanup; + } + + if (stat(def->src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), def->src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Disk source %s must be a character/block device"), + def->src); + goto cleanup; + } + + if (virAsprintf(&dst, "/proc/%llu/root/dev/%s", + (unsigned long long)priv->initpid, def->dst) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + mode = 0700; + if (S_ISCHR(sb.st_mode)) + mode |= S_IFCHR; + else + mode |= S_IFBLK; + + /* Yes, the device name we're creating may not + * actually correspond to the major:minor number + * we're using, but we've no other option at this + * time. Just have to hope that containerized apps + * don't get upset that the major:minor is different + * to that normally implied by the device name + */ + VIR_DEBUG("Creating dev %s (%d,%d) from %s", + dst, major(sb.st_rdev), minor(sb.st_rdev), def->src); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + created = true; + + /* Labelling normally operates on src, but we need + * to actally label the dst here, so hack the config */ + def->src = dst; + if (virSecurityManagerSetImageLabel(driver->securityManager, + vm->def, def) < 0) + goto cleanup; + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, def->src, + (def->readonly ? + VIR_CGROUP_DEVICE_READ : + VIR_CGROUP_DEVICE_RW) | + VIR_CGROUP_DEVICE_MKNOD) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->src, vm->def->name); + goto cleanup; + } + + virDomainDiskInsertPreAlloced(vm->def, def); + + ret = 0; + +cleanup: + def->src = tmpsrc; + virDomainAuditDisk(vm, NULL, def->src, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst); + return ret; +} + + +static int lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, virDomainObjPtr vm ATTRIBUTE_UNUSED, virDomainDeviceDefPtr dev) @@ -2950,6 +3081,12 @@ lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, int ret = -1; switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = lxcDomainAttachDeviceDiskLive(driver, vm, dev); + if (!ret) + dev->data.disk = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -2962,6 +3099,77 @@ lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, static int +lxcDomainDetachDeviceDiskLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainDiskDefPtr def = NULL; + virCgroupPtr group = NULL; + int i, ret = -1; + char *dst; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if ((i = virDomainDiskIndexByName(vm->def, + dev->data.disk->dst, + false)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("disk %s not found"), dev->data.disk->dst); + goto cleanup; + } + + def = vm->def->disks[i]; + + if (virAsprintf(&dst, "/proc/%llu/root/dev/%s", + (unsigned long long)priv->initpid, def->dst) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + VIR_DEBUG("Unlinking %s (backed by %s)", dst, def->src); + if (unlink(dst) < 0 && errno != ENOENT) { + virDomainAuditDisk(vm, def->src, NULL, "detach", false); + virReportSystemError(errno, + _("Unable to remove device %s"), dst); + goto cleanup; + } + virDomainAuditDisk(vm, def->src, NULL, "detach", true); + + if (virCgroupDenyDevicePath(group, def->src, VIR_CGROUP_DEVICE_RWM) != 0) + VIR_WARN("cannot deny device %s for domain %s", + def->src, vm->def->name); + + virDomainDiskRemove(vm->def, i); + virDomainDiskDefFree(def); + + ret = 0; + +cleanup: + VIR_FREE(dst); + if (group) + virCgroupFree(&group); + return ret; +} + + +static int lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, virDomainObjPtr vm ATTRIBUTE_UNUSED, virDomainDeviceDefPtr dev) @@ -2969,6 +3177,10 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, int ret = -1; switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = lxcDomainDetachDeviceDiskLive(driver, vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of disks.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+)
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of NICs. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 5 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index a2bb497..08ac70d 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3073,9 +3073,141 @@ cleanup: } +/* XXX conn required for network -> bridge resolution */ static int -lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +lxcDomainAttachDeviceNetLive(virConnectPtr conn, + virDomainObjPtr vm, + virDomainNetDefPtr net) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + int actualType; + char *veth = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + /* preallocate new slot for device */ + if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) { + virReportOOMError(); + return -1; + } + + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(net) < 0) + return -1; + + actualType = virDomainNetGetActualType(net); + + switch (actualType) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: { + const char *brname = virDomainNetGetActualBridgeName(net); + if (!brname) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No bridge name specified")); + goto cleanup; + } + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + vm->def, + net, + brname))) + goto cleanup; + } break; + case VIR_DOMAIN_NET_TYPE_NETWORK: { + virNetworkPtr network; + char *brname = NULL; + bool fail = false; + int active; + virErrorPtr errobj; + + if (!(network = virNetworkLookupByName(conn, + net->data.network.name))) + goto cleanup; + + active = virNetworkIsActive(network); + if (active != 1) { + fail = true; + if (active == 0) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Network '%s' is not active."), + net->data.network.name); + } + + if (!fail) { + brname = virNetworkGetBridgeName(network); + if (brname == NULL) + fail = true; + } + + /* Make sure any above failure is preserved */ + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); + + if (fail) + goto cleanup; + + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + vm->def, + net, + brname))) { + VIR_FREE(brname); + goto cleanup; + } + VIR_FREE(brname); + } break; + case VIR_DOMAIN_NET_TYPE_DIRECT: { + if (!(veth = virLXCProcessSetupInterfaceDirect(conn, + vm->def, + net))) + goto cleanup; + } break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Network device type is not supported")); + goto cleanup; + } + + if (virNetDevSetNamespace(veth, priv->initpid) < 0) { + virDomainAuditNet(vm, NULL, net, "attach", false); + goto cleanup; + } + + virDomainAuditNet(vm, NULL, net, "attach", true); + + ret = 0; + +cleanup: + if (!ret) { + vm->def->nets[vm->def->nnets++] = net; + } else if (veth) { + switch (actualType) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + ignore_value(virNetDevVethDelete(veth)); + break; + + case VIR_DOMAIN_NET_TYPE_DIRECT: + ignore_value(virNetDevMacVLanDelete(veth)); + break; + } + } + + return ret; +} + + +static int +lxcDomainAttachDeviceLive(virConnectPtr conn, + virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -3087,6 +3219,13 @@ lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, dev->data.disk = NULL; break; + case VIR_DOMAIN_DEVICE_NET: + ret = lxcDomainAttachDeviceNetLive(conn, vm, + dev->data.net); + if (!ret) + dev->data.net = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3170,8 +3309,74 @@ cleanup: static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +lxcDomainDetachDeviceNetLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + int detachidx, ret = -1; + virDomainNetDefPtr detach = NULL; + char mac[VIR_MAC_STRING_BUFLEN]; + virNetDevVPortProfilePtr vport = NULL; + + detachidx = virDomainNetFindIdx(vm->def, dev->data.net); + if (detachidx == -2) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("multiple devices matching mac address %s found"), + virMacAddrFormat(&dev->data.net->mac, mac)); + goto cleanup; + } else if (detachidx < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("network device %s not found"), + virMacAddrFormat(&dev->data.net->mac, mac)); + goto cleanup; + } + detach = vm->def->nets[detachidx]; + + switch (virDomainNetGetActualType(detach)) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + if (virNetDevVethDelete(detach->ifname) < 0) { + virDomainAuditNet(vm, detach, NULL, "detach", false); + goto cleanup; + } + break; + + /* It'd be nice to support this, but with macvlan + * once assigned to a container nothing exists on + * the host side. Further the container can change + * the mac address of NIC name, so we can't easily + * find out which guest NIC it maps to + case VIR_DOMAIN_NET_TYPE_DIRECT: + */ + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only bridged veth devices can be detached")); + goto cleanup; + } + + virDomainAuditNet(vm, detach, NULL, "detach", true); + + virDomainConfNWFilterTeardown(detach); + + vport = virDomainNetGetActualVirtPortProfile(detach); + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) + ignore_value(virNetDevOpenvswitchRemovePort( + virDomainNetGetActualBridgeName(detach), + detach->ifname)); + ret = 0; +cleanup: + if (!ret) { + networkReleaseActualDevice(detach); + virDomainNetRemove(vm->def, detachidx); + virDomainNetDefFree(detach); + } + return ret; +} + + +static int +lxcDomainDetachDeviceLive(virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -3181,6 +3386,10 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, ret = lxcDomainDetachDeviceDiskLive(driver, vm, dev); break; + case VIR_DOMAIN_DEVICE_NET: + ret = lxcDomainDetachDeviceNetLive(vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), @@ -3299,7 +3508,7 @@ lxcDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, switch (action) { case LXC_DEVICE_ATTACH: - ret = lxcDomainAttachDeviceLive(driver, vm, dev_copy); + ret = lxcDomainAttachDeviceLive(dom->conn, driver, vm, dev_copy); break; case LXC_DEVICE_DETACH: ret = lxcDomainDetachDeviceLive(driver, vm, dev_copy); -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of NICs.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 5 deletions(-)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index a2bb497..08ac70d 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3073,9 +3073,141 @@ cleanup: }
+/* XXX conn required for network -> bridge resolution */ static int -lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +lxcDomainAttachDeviceNetLive(virConnectPtr conn, + virDomainObjPtr vm, + virDomainNetDefPtr net) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + int actualType; + char *veth = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + /* preallocate new slot for device */ + if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) { + virReportOOMError(); + return -1; + } + + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(net) < 0) + return -1; + + actualType = virDomainNetGetActualType(net); + + switch (actualType) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: { + const char *brname = virDomainNetGetActualBridgeName(net); + if (!brname) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No bridge name specified")); + goto cleanup; + } + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + vm->def, + net, + brname))) + goto cleanup; + } break; + case VIR_DOMAIN_NET_TYPE_NETWORK: { + virNetworkPtr network; + char *brname = NULL; + bool fail = false; + int active; + virErrorPtr errobj; + + if (!(network = virNetworkLookupByName(conn, + net->data.network.name))) + goto cleanup; + + active = virNetworkIsActive(network); + if (active != 1) { + fail = true; + if (active == 0) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Network '%s' is not active."), + net->data.network.name); + } + + if (!fail) { + brname = virNetworkGetBridgeName(network); + if (brname == NULL) + fail = true; + } + + /* Make sure any above failure is preserved */ + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); + + if (fail) + goto cleanup; + + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + vm->def, + net, + brname))) { + VIR_FREE(brname); + goto cleanup; + } + VIR_FREE(brname); + } break; + case VIR_DOMAIN_NET_TYPE_DIRECT: { + if (!(veth = virLXCProcessSetupInterfaceDirect(conn, + vm->def, + net))) + goto cleanup; + } break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Network device type is not supported")); + goto cleanup; + } + + if (virNetDevSetNamespace(veth, priv->initpid) < 0) { + virDomainAuditNet(vm, NULL, net, "attach", false);
Maybe it's better to move failed audit to the cleanup path.
+ goto cleanup; + } + + virDomainAuditNet(vm, NULL, net, "attach", true); + + ret = 0; + +cleanup: + if (!ret) { + vm->def->nets[vm->def->nnets++] = net; + } else if (veth) { + switch (actualType) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + ignore_value(virNetDevVethDelete(veth)); + break; + + case VIR_DOMAIN_NET_TYPE_DIRECT: + ignore_value(virNetDevMacVLanDelete(veth)); + break; + } + } + + return ret; +} + + +static int +lxcDomainAttachDeviceLive(virConnectPtr conn, + virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -3087,6 +3219,13 @@ lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, dev->data.disk = NULL; break;
+ case VIR_DOMAIN_DEVICE_NET: + ret = lxcDomainAttachDeviceNetLive(conn, vm, + dev->data.net); + if (!ret) + dev->data.net = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3170,8 +3309,74 @@ cleanup:
static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +lxcDomainDetachDeviceNetLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + int detachidx, ret = -1; + virDomainNetDefPtr detach = NULL; + char mac[VIR_MAC_STRING_BUFLEN]; + virNetDevVPortProfilePtr vport = NULL; + + detachidx = virDomainNetFindIdx(vm->def, dev->data.net); + if (detachidx == -2) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("multiple devices matching mac address %s found"), + virMacAddrFormat(&dev->data.net->mac, mac)); + goto cleanup; + } else if (detachidx < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("network device %s not found"), + virMacAddrFormat(&dev->data.net->mac, mac)); + goto cleanup; + } + detach = vm->def->nets[detachidx]; + + switch (virDomainNetGetActualType(detach)) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + if (virNetDevVethDelete(detach->ifname) < 0) { + virDomainAuditNet(vm, detach, NULL, "detach", false); + goto cleanup; + } + break; + + /* It'd be nice to support this, but with macvlan + * once assigned to a container nothing exists on + * the host side. Further the container can change + * the mac address of NIC name, so we can't easily + * find out which guest NIC it maps to + case VIR_DOMAIN_NET_TYPE_DIRECT: + */ + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only bridged veth devices can be detached")); + goto cleanup; + } + + virDomainAuditNet(vm, detach, NULL, "detach", true); + + virDomainConfNWFilterTeardown(detach); + + vport = virDomainNetGetActualVirtPortProfile(detach); + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) + ignore_value(virNetDevOpenvswitchRemovePort( + virDomainNetGetActualBridgeName(detach), + detach->ifname)); + ret = 0; +cleanup: + if (!ret) { + networkReleaseActualDevice(detach); + virDomainNetRemove(vm->def, detachidx); + virDomainNetDefFree(detach); + }
else virDomainAuditNet(vm, detach, NULL, "detach", false); ACK

On Fri, Dec 14, 2012 at 08:06:49PM +0800, Gao feng wrote:
On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com> + case VIR_DOMAIN_NET_TYPE_NETWORK: { + virNetworkPtr network; + char *brname = NULL; + bool fail = false; + int active; + virErrorPtr errobj; + + if (!(network = virNetworkLookupByName(conn, + net->data.network.name))) + goto cleanup; + + active = virNetworkIsActive(network); + if (active != 1) { + fail = true; + if (active == 0) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Network '%s' is not active."), + net->data.network.name); + } + + if (!fail) { + brname = virNetworkGetBridgeName(network); + if (brname == NULL) + fail = true; + } + + /* Make sure any above failure is preserved */ + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); + + if (fail) + goto cleanup; + + if (!(veth = virLXCProcessSetupInterfaceBridged(conn, + vm->def, + net, + brname))) { + VIR_FREE(brname); + goto cleanup; + } + VIR_FREE(brname); + } break; + case VIR_DOMAIN_NET_TYPE_DIRECT: { + if (!(veth = virLXCProcessSetupInterfaceDirect(conn, + vm->def, + net))) + goto cleanup; + } break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Network device type is not supported")); + goto cleanup; + } + + if (virNetDevSetNamespace(veth, priv->initpid) < 0) { + virDomainAuditNet(vm, NULL, net, "attach", false);
Maybe it's better to move failed audit to the cleanup path.
I only wanted the audit message to be logged in the cases where we've actually tried to give the device to the container. Basically if the set namespace fails only. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of USB host devices. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 08ac70d..50050cf 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -39,6 +39,7 @@ #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" +#include "lxc_cgroup.h" #include "lxc_conf.h" #include "lxc_container.h" #include "lxc_domain.h" @@ -3205,6 +3206,195 @@ cleanup: static int +lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + int ret = -1; + char *vroot = NULL; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; + bool created = false; + usbDeviceList *list = NULL; + usbDevice *usb = NULL; + virCgroupPtr group = NULL; + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dstdir, "%s/dev/bus/usb/%03d", + vroot, + def->source.subsys.u.usb.bus) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dstfile, "%s/%03d", + dstdir, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&src, "/dev/bus/usb/%03d/%03d", + def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!(usb = usbGetDevice(def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device, vroot))) + goto cleanup; + + if (!(list = usbDeviceListNew())) + goto cleanup; + + if (usbDeviceListAdd(list, usb) < 0) { + usbFreeDevice(usb); + usb = NULL; + goto cleanup; + } + + if (virLXCPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) { + usb = NULL; + goto cleanup; + } + + usbDeviceListSteal(list, usb); + + if (stat(src, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), src); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("USB source %s was not a character device"), + src); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + if (virFileMakePath(dstdir) < 0) { + virReportSystemError(errno, + _("Unable to create %s"), dstdir); + goto cleanup; + } + + VIR_DEBUG("Creating dev %s (%d,%d)", + dstfile, major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dstfile, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dstfile); + goto cleanup; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (usbDeviceFileIterate(usb, + virLXCSetupHostUsbDeviceCgroup, + group) < 0) + goto cleanup; + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (ret < 0 && created) + unlink(dstfile); + + usbDeviceListFree(list); + if (ret < 0 && usb) + usbDeviceListSteal(driver->activeUsbHostdevs, usb); + + virCgroupFree(&group); + VIR_FREE(src); + VIR_FREE(dstfile); + VIR_FREE(dstdir); + VIR_FREE(vroot); + return ret; +} + + +static int +lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return lxcDomainAttachDeviceHostdevSubsysUSBLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevSubsysTypeToString(dev->data.hostdev->source.subsys.type)); + return -1; + } +} + + +static int +lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + return -1; + } + + switch (dev->data.hostdev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return lxcDomainAttachDeviceHostdevSubsysLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevModeTypeToString(dev->data.hostdev->mode)); + return -1; + } +} + + +static int lxcDomainAttachDeviceLive(virConnectPtr conn, virLXCDriverPtr driver, virDomainObjPtr vm, @@ -3226,6 +3416,12 @@ lxcDomainAttachDeviceLive(virConnectPtr conn, dev->data.net = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = lxcDomainAttachDeviceHostdevLive(driver, vm, dev); + if (!ret) + dev->data.disk = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3375,6 +3571,138 @@ cleanup: static int +lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int idx, ret = -1; + char *dst = NULL; + char *vroot = NULL; + usbDevice *usb = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + goto cleanup; + } + + + if ((idx = virDomainHostdevFind(vm->def, + dev->data.hostdev, + &def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("usb device not found")); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/dev/bus/usb/%03d/%03d", + vroot, + def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (!(usb = usbGetDevice(def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device, vroot))) + goto cleanup; + + VIR_DEBUG("Unlinking %s", dst); + if (unlink(dst) < 0 && errno != ENOENT) { + virDomainAuditHostdev(vm, def, "detach", false); + virReportSystemError(errno, + _("Unable to remove device %s"), dst); + goto cleanup; + } + virDomainAuditHostdev(vm, def, "detach", true); + + if (usbDeviceFileIterate(usb, + virLXCTeardownHostUsbDeviceCgroup, + group) < 0) + VIR_WARN("cannot deny device %s for domain %s", + dst, vm->def->name); + + usbDeviceListDel(driver->activeUsbHostdevs, usb); + + virDomainHostdevRemove(vm->def, idx); + virDomainHostdevDefFree(def); + + ret = 0; + +cleanup: + usbFreeDevice(usb); + VIR_FREE(dst); + VIR_FREE(vroot); + virCgroupFree(&group); + return ret; +} + +static int +lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return lxcDomainDetachDeviceHostdevUSBLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevSubsysTypeToString(dev->data.hostdev->source.subsys.type)); + return -1; + } +} + + +static int +lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + return -1; + } + + switch (dev->data.hostdev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevModeTypeToString(dev->data.hostdev->mode)); + return -1; + } +} + + +static int lxcDomainDetachDeviceLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3390,6 +3718,10 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, ret = lxcDomainDetachDeviceNetLive(vm, dev); break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of USB host devices.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+)
[...]
static int +lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int idx, ret = -1; + char *dst = NULL; + char *vroot = NULL; + usbDevice *usb = NULL; + + if (!priv->initpid) {
No need,already checked in lxcDomainDetachDeviceHostdevLive
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + goto cleanup; + } +
[...]
+static int +lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + return -1; + } +
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host storage devices. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 50050cf..27ee3d7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3224,6 +3224,12 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, usbDevice *usb = NULL; virCgroupPtr group = NULL; + if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host USB device already exists")); + return -1; + } + if (virAsprintf(&vroot, "/proc/%llu/root", (unsigned long long)priv->initpid) < 0) { virReportOOMError(); @@ -3351,6 +3357,123 @@ cleanup: static int +lxcDomainAttachDeviceHostdevStorageLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + virCgroupPtr group = NULL; + int ret = -1; + char *dst; + char *vroot; + struct stat sb; + bool created = false; + mode_t mode = 0; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if (!def->source.caps.u.storage.block) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage block path")); + goto cleanup; + } + + if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host device already exists")); + return -1; + } + + if (stat(def->source.caps.u.storage.block, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + def->source.caps.u.storage.block); + goto cleanup; + } + + if (!S_ISBLK(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Hostdev source %s must be a block device"), + def->source.caps.u.storage.block); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/%s", + vroot, + def->source.caps.u.storage.block) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + mode = 0700 | S_IFBLK; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.storage.block, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, def->source.caps.u.storage.block, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->source.caps.u.storage.block, vm->def->name); + goto cleanup; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst); + return ret; +} + + +static int lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3369,6 +3492,24 @@ lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver, static int +lxcDomainAttachDeviceHostdevCapsLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + return lxcDomainAttachDeviceHostdevStorageLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevCapsTypeToString(dev->data.hostdev->source.caps.type)); + return -1; + } +} + + +static int lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3385,6 +3526,9 @@ lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver, case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return lxcDomainAttachDeviceHostdevSubsysLive(driver, vm, dev); + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return lxcDomainAttachDeviceHostdevCapsLive(driver, vm, dev); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device mode %s"), @@ -3658,6 +3802,78 @@ cleanup: return ret; } + +static int +lxcDomainDetachDeviceHostdevStorageLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int i, ret = -1; + char *dst = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if ((i = virDomainHostdevFind(vm->def, + dev->data.hostdev, + &def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("hostdev %s not found"), + dev->data.hostdev->source.caps.u.storage.block); + goto cleanup; + } + + if (virAsprintf(&dst, "/proc/%llu/root/%s", + (unsigned long long)priv->initpid, + def->source.caps.u.storage.block) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + VIR_DEBUG("Unlinking %s", dst); + if (unlink(dst) < 0 && errno != ENOENT) { + virDomainAuditHostdev(vm, def, "detach", false); + virReportSystemError(errno, + _("Unable to remove device %s"), dst); + goto cleanup; + } + virDomainAuditHostdev(vm, def, "detach", true); + + if (virCgroupDenyDevicePath(group, def->source.caps.u.storage.block, VIR_CGROUP_DEVICE_RWM) != 0) + VIR_WARN("cannot deny device %s for domain %s", + def->source.caps.u.storage.block, vm->def->name); + + virDomainHostdevRemove(vm->def, i); + virDomainHostdevDefFree(def); + + ret = 0; + +cleanup: + VIR_FREE(dst); + if (group) + virCgroupFree(&group); + return ret; +} + + static int lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, virDomainObjPtr vm, @@ -3677,6 +3893,24 @@ lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, static int +lxcDomainDetachDeviceHostdevCapsLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + return lxcDomainDetachDeviceHostdevStorageLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevCapsTypeToString(dev->data.hostdev->source.caps.type)); + return -1; + } +} + + +static int lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3693,6 +3927,9 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev); + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return lxcDomainDetachDeviceHostdevCapsLive(driver, vm, dev); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device mode %s"), -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host storage devices.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 50050cf..27ee3d7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3224,6 +3224,12 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, usbDevice *usb = NULL; virCgroupPtr group = NULL;
+ if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host USB device already exists")); + return -1; + } +
It shouldn't be in patch [19/23]?
if (virAsprintf(&vroot, "/proc/%llu/root", (unsigned long long)priv->initpid) < 0) { virReportOOMError(); @@ -3351,6 +3357,123 @@ cleanup:
static int +lxcDomainAttachDeviceHostdevStorageLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + virCgroupPtr group = NULL; + int ret = -1; + char *dst; + char *vroot; + struct stat sb; + bool created = false; + mode_t mode = 0; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if (!def->source.caps.u.storage.block) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage block path")); + goto cleanup; + } + + if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host device already exists")); + return -1; + } + + if (stat(def->source.caps.u.storage.block, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + def->source.caps.u.storage.block); + goto cleanup; + } + + if (!S_ISBLK(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Hostdev source %s must be a block device"), + def->source.caps.u.storage.block); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/%s", + vroot, + def->source.caps.u.storage.block) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + mode = 0700 | S_IFBLK; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.storage.block, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, def->source.caps.u.storage.block, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->source.caps.u.storage.block, vm->def->name); + goto cleanup; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst);
dst and vroot should be freed. And I wonder vm->def->hostdevs shouldn't be restored when attaching failed?
+ return ret; +} + +
[...]
+ +static int +lxcDomainDetachDeviceHostdevStorageLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int i, ret = -1; + char *dst = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known"));
Need proper error msg here. ACK

On Fri, Dec 14, 2012 at 07:44:33PM +0800, Gao feng wrote:
On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host storage devices.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 50050cf..27ee3d7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3224,6 +3224,12 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, usbDevice *usb = NULL; virCgroupPtr group = NULL;
+ if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host USB device already exists")); + return -1; + } +
It shouldn't be in patch [19/23]?
Yes, will move it.
+ + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst);
dst and vroot should be freed. And I wonder vm->def->hostdevs shouldn't be restored when attaching failed?
It is harmless to have the array be 1 element too large IMHO, so simpler to not try to re-alloc smaller. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host storage devices.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+)
[...]
static int +lxcDomainAttachDeviceHostdevStorageLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{
[...]
+ + if (virCgroupAllowDevicePath(group, def->source.caps.u.storage.block, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) != 0) {
VIR_CGROUP_DEVICE_RWM, prev patch has the same problem.
+ virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->source.caps.u.storage.block, vm->def->name); + goto cleanup; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst);
dst and vroot need be freed. ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host misc devices. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 27ee3d7..17445a7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3474,6 +3474,123 @@ cleanup: static int +lxcDomainAttachDeviceHostdevMiscLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + virCgroupPtr group = NULL; + int ret = -1; + char *dst; + char *vroot; + struct stat sb; + bool created = false; + mode_t mode = 0; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if (!def->source.caps.u.misc.chardev) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage block path")); + goto cleanup; + } + + if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host device already exists")); + return -1; + } + + if (stat(def->source.caps.u.misc.chardev, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Hostdev source %s must be a block device"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/%s", + vroot, + def->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.misc.chardev, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, def->source.caps.u.misc.chardev, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->source.caps.u.misc.chardev, vm->def->name); + goto cleanup; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst); + return ret; +} + + +static int lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3500,6 +3617,9 @@ lxcDomainAttachDeviceHostdevCapsLive(virLXCDriverPtr driver, case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: return lxcDomainAttachDeviceHostdevStorageLive(driver, vm, dev); + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + return lxcDomainAttachDeviceHostdevMiscLive(driver, vm, dev); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device type %s"), @@ -3875,6 +3995,77 @@ cleanup: static int +lxcDomainDetachDeviceHostdevMiscLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int i, ret = -1; + char *dst = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if ((i = virDomainHostdevFind(vm->def, + dev->data.hostdev, + &def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("hostdev %s not found"), + dev->data.hostdev->source.caps.u.misc.chardev); + goto cleanup; + } + + if (virAsprintf(&dst, "/proc/%llu/root/%s", + (unsigned long long)priv->initpid, + def->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + VIR_DEBUG("Unlinking %s", dst); + if (unlink(dst) < 0 && errno != ENOENT) { + virDomainAuditHostdev(vm, def, "detach", false); + virReportSystemError(errno, + _("Unable to remove device %s"), dst); + goto cleanup; + } + virDomainAuditHostdev(vm, def, "detach", true); + + if (virCgroupDenyDevicePath(group, def->source.caps.u.misc.chardev, VIR_CGROUP_DEVICE_RWM) != 0) + VIR_WARN("cannot deny device %s for domain %s", + def->source.caps.u.misc.chardev, vm->def->name); + + virDomainHostdevRemove(vm->def, i); + virDomainHostdevDefFree(def); + + ret = 0; + +cleanup: + VIR_FREE(dst); + if (group) + virCgroupFree(&group); + return ret; +} + + +static int lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3901,6 +4092,9 @@ lxcDomainDetachDeviceHostdevCapsLive(virLXCDriverPtr driver, case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: return lxcDomainDetachDeviceHostdevStorageLive(driver, vm, dev); + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + return lxcDomainDetachDeviceHostdevMiscLive(driver, vm, dev); + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported host device type %s"), -- 1.8.0.1

On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of host misc devices.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 27ee3d7..17445a7 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3474,6 +3474,123 @@ cleanup:
static int +lxcDomainAttachDeviceHostdevMiscLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + virCgroupPtr group = NULL; + int ret = -1; + char *dst; + char *vroot; + struct stat sb; + bool created = false; + mode_t mode = 0; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach disk until init PID is known")); + goto cleanup; + } + + if (!def->source.caps.u.misc.chardev) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Missing storage block path")); + goto cleanup; + } + + if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("host device already exists")); + return -1; + } + + if (stat(def->source.caps.u.misc.chardev, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access %s"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (!S_ISCHR(sb.st_mode)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Hostdev source %s must be a block device"), + def->source.caps.u.misc.chardev); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/%s", + vroot, + def->source.caps.u.misc.chardev) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + mode = 0700 | S_IFCHR; + + VIR_DEBUG("Creating dev %s (%d,%d)", + def->source.caps.u.misc.chardev, + major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(dst, mode, sb.st_rdev) < 0) { + virReportSystemError(errno, + _("Unable to create device %s"), + dst); + goto cleanup; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, def->source.caps.u.misc.chardev, + VIR_CGROUP_DEVICE_RW | + VIR_CGROUP_DEVICE_MKNOD) != 0) {
VIR_CGROUP_DEVICE_RWM
+ virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + def->source.caps.u.misc.chardev, vm->def->name); + goto cleanup; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (group) + virCgroupFree(&group); + if (dst && created && ret < 0) + unlink(dst); + return ret; +} +
dst and vroot need be freed. ACK

From: "Daniel P. Berrange" <berrange@redhat.com> The virDomainDefCompatibleDevice method checks if the domain has a USB controller for any USB devices. This is not required when using containers, so skip that check Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/conf/domain_conf.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d97bbc8..43336d7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14530,12 +14530,14 @@ int virDomainDefCompatibleDevice(virDomainDefPtr def, virDomainDeviceDefPtr dev) { - if (!virDomainDefHasUSB(def) && - virDomainDeviceIsUSB(dev)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Device configuration is not compatible: " - "Domain has no USB bus support")); - return -1; + if (virDomainDeviceIsUSB(dev)) { + if (STRNEQ(def->os.type, "exe") && + !virDomainDefHasUSB(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Device configuration is not compatible: " + "Domain has no USB bus support")); + return -1; + } } return 0; -- 1.8.0.1

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The virDomainDefCompatibleDevice method checks if the domain has a USB controller for any USB devices. This is not required when using containers, so skip that check
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- src/conf/domain_conf.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d97bbc8..43336d7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14530,12 +14530,14 @@ int virDomainDefCompatibleDevice(virDomainDefPtr def, virDomainDeviceDefPtr dev) { - if (!virDomainDefHasUSB(def)&& - virDomainDeviceIsUSB(dev)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Device configuration is not compatible: " - "Domain has no USB bus support")); - return -1; + if (virDomainDeviceIsUSB(dev)) { + if (STRNEQ(def->os.type, "exe")&& + !virDomainDefHasUSB(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Device configuration is not compatible: " + "Domain has no USB bus support")); + return -1; + } }
return 0;
ACK

From: "Daniel P. Berrange" <berrange@redhat.com> Common practice is to accept NULL for all "free" methods Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/util/hostusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/hostusb.c b/src/util/hostusb.c index 24f925b..79b04eb 100644 --- a/src/util/hostusb.c +++ b/src/util/hostusb.c @@ -355,6 +355,9 @@ usbGetDevice(unsigned int bus, void usbFreeDevice(usbDevice *dev) { + if (!dev) + return; + VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev); -- 1.8.0.1

On 2012年12月01日 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Common practice is to accept NULL for all "free" methods
Signed-off-by: Daniel P. Berrange<berrange@redhat.com> --- src/util/hostusb.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/util/hostusb.c b/src/util/hostusb.c index 24f925b..79b04eb 100644 --- a/src/util/hostusb.c +++ b/src/util/hostusb.c @@ -355,6 +355,9 @@ usbGetDevice(unsigned int bus, void usbFreeDevice(usbDevice *dev) { + if (!dev) + return; + VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev);
ACK

On Fri, Nov 30, 2012 at 08:26:14PM +0000, Daniel P. Berrange wrote:
This series makes some major improvements to the device support for LXC
- USB, block and char device passthrough - Hotplug/unplug support for disk, nic, usb, block and char devices
I'd like to have this series in the forthcoming release, so some reviews needed... Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

on 2012/12/11 01:19, Daniel P. Berrange wrote:
On Fri, Nov 30, 2012 at 08:26:14PM +0000, Daniel P. Berrange wrote:
This series makes some major improvements to the device support for LXC
- USB, block and char device passthrough - Hotplug/unplug support for disk, nic, usb, block and char devices
I'd like to have this series in the forthcoming release, so some reviews needed...
Since this patchset is so large,please give me some time to review and test them. I will do this job in this week. Thanks!
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Gao feng
-
Osier Yang