From 3b1c191fe753b628c15f42796477ef68927ffbce Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Wed, 15 Feb 2012 12:37:15 -0500 Subject: [PATCH] conf: parse/format type='hostdev' network interfaces This is the new interface type that sets up an SR-IOV PCI network device to be assigned to the guest with PCI passthrough after initializing some network device-specific things from the config (e.g. MAC address, virtualport profile parameters). Here is an example of the syntax:
This would assign the PCI card from bus 0 slot 4 function 3 on the host, to bus 0 slot 7 function 0 on the guest, but would first set the MAC address of the card to 00:11:22:33:44:55. NB: The parser and formatter don't care if the PCI card being specified is a standard single function network adapter, or a virtual function (VF) of an SR-IOV capable network adapter, but the upcoming code that implements the back end of this config will work *only* with SR-IOV VFs. This is because modifying the mac address of a standard network adapter prior to assigning it to a guest is pointless - part of the device reset that occurs during that process will reset the MAC address to the value programmed into the card's firmware. Although it's not supported by any of libvirt's hypervisor drivers, usb network hostdevs are also supported in the parser and formatter for completeness and consistency. syntax is identical to that for plain devices, except that the
element should have "type='usb'" added if bus/device are specified:
If the vendor/product form of usb specification is used, type='usb' is implied: Again, the upcoming patch to fill in the backend of this functionality will log an error and fail with "Unsupported Config" if you actually try to assign a USB network adapter to a guest using - just use a standard entry in that case (and also for single-port PCI adapters). --- docs/formatdomain.html.in | 49 ++++++ docs/schemas/domaincommon.rng | 50 ++++++ src/conf/domain_conf.c | 154 ++++++++++++++++-- src/conf/domain_conf.h | 10 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 1 + src/uml/uml_conf.c | 5 + src/xenxs/xen_sxpr.c | 1 + .../qemuxml2argv-net-hostdev.xml | 35 ++++ tests/qemuxml2xmltest.c | 1 + 10 files changed, 292 insertions(+), 15 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index a61895f9f9..6434ae5bc7 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2303,6 +2303,55 @@ ... + +
PCI Passthrough
+ +

+ A PCI network device (specified by the <source> element) + is directly assigned to the guest using generic device + passthrough, after first optionally setting the device's MAC + address to the configured value, and associating the device with + an 802.1Qgh capable switch using an optionally specified + %lt;virtualport%gt; element (see the examples of virtualport + given above for type='direct' network devices). Note that - due + to limitations in standard single-port PCI ethernet card driver + design - only SR-IOV (Single Root I/O Virtualization) virtual + function (VF) devices can be assigned in this manner; to assign + a standard single-port PCI or PCIe ethernet card to a guest, use + the traditional <hostdev> device definition and + Since 0.9.11 +

+ +

+ Note that this "intelligent passthrough" of network devices is + very similar to the functionality of a standard <hostdev> + device, the difference being that this method allows specifying + a MAC address and <virtualport> for the passed-through + device. If these capabilities are not required, if you have a + standard single-port PCI, PCIe, or USB network card that doesn't + support SR-IOV (and hence would anyway lose the configured MAC + address during reset after being assigned to the guest domain), + or if you are using a version of libvirt older than 0.9.11, you + should use standard <hostdev> to assign the device to the + guest instead of <interface type='hostdev'/>. +

+ +
+  ...
+  <devices>
+    <interface type='hostdev'>
+      <source>
+        <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
+      </source>
+      <mac address='52:54:00:6d:90:02'>
+      <virtualport type='802.1Qbh'>
+        <parameters profileid='finance'/>
+      </virtualport>
+    </interface>
+  </devices>
+  ...
+ +
Multicast tunnel

diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 3908733283..a905457a76 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1416,6 +1416,56 @@ + + + hostdev + + + + + yes + no + + + + + + + + + + + + + + + + + pci + + + + + + usb + + + + + + + + + + + + + + + + + + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 94ee634ca3..70e92242f7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -286,7 +286,8 @@ VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST, "network", "bridge", "internal", - "direct") + "direct", + "hostdev") VIR_ENUM_IMPL(virDomainNetBackend, VIR_DOMAIN_NET_BACKEND_TYPE_LAST, "default", @@ -971,6 +972,10 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def) VIR_FREE(def->data.direct.linkdev); VIR_FREE(def->data.direct.virtPortProfile); break; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + virDomainHostdevDefClear(&def->data.hostdev.def); + VIR_FREE(def->data.hostdev.virtPortProfile); + break; default: break; } @@ -1021,6 +1026,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->data.direct.virtPortProfile); break; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + virDomainHostdevDefClear(&def->data.hostdev.def); + VIR_FREE(def->data.hostdev.virtPortProfile); + break; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_LAST: break; @@ -4115,7 +4125,9 @@ cleanup: static int virDomainActualNetDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, - virDomainActualNetDefPtr *def) + virDomainNetDefPtr parent, + virDomainActualNetDefPtr *def, + unsigned int flags) { virDomainActualNetDefPtr actual = NULL; int ret = -1; @@ -4123,6 +4135,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, xmlNodePtr bandwidth_node = NULL; char *type = NULL; char *mode = NULL; + char *addrtype = NULL; if (VIR_ALLOC(actual) < 0) { virReportOOMError(); @@ -4144,6 +4157,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, } if (actual->type != VIR_DOMAIN_NET_TYPE_BRIDGE && actual->type != VIR_DOMAIN_NET_TYPE_DIRECT && + actual->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && actual->type != VIR_DOMAIN_NET_TYPE_NETWORK) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported type '%s' in interface's element"), @@ -4179,6 +4193,34 @@ virDomainActualNetDefParseXML(xmlNodePtr node, (!(actual->data.direct.virtPortProfile = virNetDevVPortProfileParse(virtPortNode)))) goto error; + } else if (actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + xmlNodePtr virtPortNode = virXPathNode("./virtualport", ctxt); + virDomainHostdevDefPtr hostdev = &actual->data.hostdev.def; + + hostdev->parent.type = VIR_DOMAIN_DEVICE_NET; + hostdev->parent.data.net = parent; + hostdev->info = &parent->info; + /* The helper function expects type to already be found and + * passed in as a string, since it is in a different place in + * NetDef vs HostdevDef. + */ + addrtype = virXPathString("string(./source/address/@type)", ctxt); + /* if not explicitly stated, source/vendor implies usb device */ + if ((!addrtype) && virXPathNode("./source/vendor", ctxt) && + ((addrtype = strdup("usb")) == NULL)) { + virReportOOMError(); + goto error; + } + if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype, + hostdev, flags) < 0) { + goto error; + } + + if (virtPortNode && + (!(actual->data.hostdev.virtPortProfile = + virNetDevVPortProfileParse(virtPortNode)))) { + goto error; + } } bandwidth_node = virXPathNode("./bandwidth", ctxt); @@ -4192,6 +4234,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, error: VIR_FREE(type); VIR_FREE(mode); + VIR_FREE(addrtype); virDomainActualNetDefFree(actual); ctxt->node = save_ctxt; @@ -4213,6 +4256,7 @@ virDomainNetDefParseXML(virCapsPtr caps, unsigned int flags) { virDomainNetDefPtr def; + virDomainHostdevDefPtr hostdev; xmlNodePtr cur; char *macaddr = NULL; char *type = NULL; @@ -4234,6 +4278,7 @@ virDomainNetDefParseXML(virCapsPtr caps, char *devaddr = NULL; char *mode = NULL; char *linkstate = NULL; + char *addrtype = NULL; virNWFilterHashTablePtr filterparams = NULL; virNetDevVPortProfilePtr virtPort = NULL; virDomainActualNetDefPtr actual = NULL; @@ -4286,7 +4331,8 @@ virDomainNetDefParseXML(virCapsPtr caps, } else if ((virtPort == NULL) && ((def->type == VIR_DOMAIN_NET_TYPE_DIRECT) || (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) || - (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE)) && + (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) || + (def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)) && xmlStrEqual(cur->name, BAD_CAST "virtualport")) { if (!(virtPort = virNetDevVPortProfileParse(cur))) goto error; @@ -4338,8 +4384,10 @@ virDomainNetDefParseXML(virCapsPtr caps, (flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) && (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) && xmlStrEqual(cur->name, BAD_CAST "actual")) { - if (virDomainActualNetDefParseXML(cur, ctxt, &actual) < 0) + if (virDomainActualNetDefParseXML(cur, ctxt, def, + &actual, flags) < 0) { goto error; + } } else if (xmlStrEqual(cur->name, BAD_CAST "bandwidth")) { if (!(def->bandwidth = virNetDevBandwidthParse(cur))) goto error; @@ -4494,6 +4542,30 @@ virDomainNetDefParseXML(virCapsPtr caps, break; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + hostdev = &def->data.hostdev.def; + hostdev->parent.type = VIR_DOMAIN_DEVICE_NET; + hostdev->parent.data.net = def; + hostdev->info = &def->info; + /* The helper function expects type to already be found and + * passed in as a string, since it is in a different place in + * NetDef vs HostdevDef. + */ + addrtype = virXPathString("string(./source/address/@type)", ctxt); + /* if not explicitly stated, source/vendor implies usb device */ + if ((!addrtype) && virXPathNode("./source/vendor", ctxt) && + ((addrtype = strdup("usb")) == NULL)) { + virReportOOMError(); + goto error; + } + if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype, + hostdev, flags) < 0) { + goto error; + } + def->data.hostdev.virtPortProfile = virtPort; + virtPort = NULL; + break; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_LAST: break; @@ -4629,6 +4701,7 @@ cleanup: VIR_FREE(devaddr); VIR_FREE(mode); VIR_FREE(linkstate); + VIR_FREE(addrtype); virNWFilterHashTableFree(filterparams); return def; @@ -10725,7 +10798,8 @@ virDomainHostdevSourceFormat(virBufferPtr buf, static int virDomainActualNetDefFormat(virBufferPtr buf, - virDomainActualNetDefPtr def) + virDomainActualNetDefPtr def, + unsigned int flags) { int ret = -1; const char *type; @@ -10741,14 +10815,12 @@ virDomainActualNetDefFormat(virBufferPtr buf, return ret; } - if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE && - def->type != VIR_DOMAIN_NET_TYPE_DIRECT && - def->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected net type %s"), type); - goto error; + virBufferAsprintf(buf, " type == VIR_DOMAIN_NET_TYPE_HOSTDEV) && + def->data.hostdev.def.managed) { + virBufferAddLit(buf, " managed='yes'"); } - virBufferAsprintf(buf, " \n", type); + virBufferAddLit(buf, ">\n"); switch (def->type) { case VIR_DOMAIN_NET_TYPE_BRIDGE: @@ -10781,8 +10853,26 @@ virDomainActualNetDefFormat(virBufferPtr buf, goto error; virBufferAdjustIndent(buf, -8); break; - default: + + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + virBufferAdjustIndent(buf, 8); + if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def, + flags, true) < 0) { + return -1; + } + if (virNetDevVPortProfileFormat(def->data.hostdev.virtPortProfile, + buf) < 0) { + return -1; + } + virBufferAdjustIndent(buf, -8); break; + + case VIR_DOMAIN_NET_TYPE_NETWORK: + break; + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected net type %s"), type); + goto error; } virBufferAdjustIndent(buf, 8); @@ -10810,7 +10900,12 @@ virDomainNetDefFormat(virBufferPtr buf, return -1; } - virBufferAsprintf(buf, " \n", type); + virBufferAsprintf(buf, " type == VIR_DOMAIN_NET_TYPE_HOSTDEV) && + def->data.hostdev.def.managed) { + virBufferAddLit(buf, " managed='yes'"); + } + virBufferAddLit(buf, ">\n"); virBufferAsprintf(buf, " \n", @@ -10829,7 +10924,7 @@ virDomainNetDefFormat(virBufferPtr buf, return -1; virBufferAdjustIndent(buf, -6); if ((flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) && - (virDomainActualNetDefFormat(buf, def->data.network.actual) < 0)) + (virDomainActualNetDefFormat(buf, def->data.network.actual, flags) < 0)) return -1; break; @@ -10883,6 +10978,19 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAdjustIndent(buf, -6); break; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + virBufferAdjustIndent(buf, 6); + if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def, + flags, true) < 0) { + return -1; + } + if (virNetDevVPortProfileFormat(def->data.hostdev.virtPortProfile, + buf) < 0) { + return -1; + } + virBufferAdjustIndent(buf, -6); + break; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_LAST: break; @@ -14136,6 +14244,18 @@ virDomainNetGetActualDirectMode(virDomainNetDefPtr iface) return iface->data.network.actual->data.direct.mode; } +virDomainHostdevDefPtr +virDomainNetGetActualHostdev(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) + return &iface->data.hostdev.def; + if ((iface->type == VIR_DOMAIN_NET_TYPE_NETWORK) && + (iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)) { + return &iface->data.network.actual->data.hostdev.def; + } + return NULL; +} + virNetDevVPortProfilePtr virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface) { @@ -14144,6 +14264,8 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface) return iface->data.direct.virtPortProfile; case VIR_DOMAIN_NET_TYPE_BRIDGE: return iface->data.bridge.virtPortProfile; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + return iface->data.hostdev.virtPortProfile; case VIR_DOMAIN_NET_TYPE_NETWORK: if (!iface->data.network.actual) return NULL; @@ -14152,6 +14274,8 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface) return iface->data.network.actual->data.direct.virtPortProfile; case VIR_DOMAIN_NET_TYPE_BRIDGE: return iface->data.network.actual->data.bridge.virtPortProfile; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + return iface->data.network.actual->data.hostdev.virtPortProfile; default: return NULL; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 14581e4b51..c3f12ad227 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -694,6 +694,7 @@ enum virDomainNetType { VIR_DOMAIN_NET_TYPE_BRIDGE, VIR_DOMAIN_NET_TYPE_INTERNAL, VIR_DOMAIN_NET_TYPE_DIRECT, + VIR_DOMAIN_NET_TYPE_HOSTDEV, VIR_DOMAIN_NET_TYPE_LAST, }; @@ -744,6 +745,10 @@ struct _virDomainActualNetDef { int mode; /* enum virMacvtapMode from util/macvtap.h */ virNetDevVPortProfilePtr virtPortProfile; } direct; + struct { + virDomainHostdevDef def; + virNetDevVPortProfilePtr virtPortProfile; + } hostdev; } data; virNetDevBandwidthPtr bandwidth; }; @@ -797,6 +802,10 @@ struct _virDomainNetDef { int mode; /* enum virMacvtapMode from util/macvtap.h */ virNetDevVPortProfilePtr virtPortProfile; } direct; + struct { + virDomainHostdevDef def; + virNetDevVPortProfilePtr virtPortProfile; + } hostdev; } data; struct { bool sndbuf_specified; @@ -1922,6 +1931,7 @@ int virDomainNetGetActualType(virDomainNetDefPtr iface); const char *virDomainNetGetActualBridgeName(virDomainNetDefPtr iface); const char *virDomainNetGetActualDirectDev(virDomainNetDefPtr iface); int virDomainNetGetActualDirectMode(virDomainNetDefPtr iface); +virDomainHostdevDefPtr virDomainNetGetActualHostdev(virDomainNetDefPtr iface); virNetDevVPortProfilePtr virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface); virNetDevBandwidthPtr diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 751d6d5df0..a6d741a6b1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -384,6 +384,7 @@ virDomainNetGetActualBandwidth; virDomainNetGetActualBridgeName; virDomainNetGetActualDirectDev; virDomainNetGetActualDirectMode; +virDomainNetGetActualHostdev; virDomainNetGetActualType; virDomainNetGetActualVirtPortProfile; virDomainNetIndexByMac; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index e0e2945ce7..c628aed927 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2720,6 +2720,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, case VIR_DOMAIN_NET_TYPE_BRIDGE: case VIR_DOMAIN_NET_TYPE_INTERNAL: case VIR_DOMAIN_NET_TYPE_DIRECT: + case VIR_DOMAIN_NET_TYPE_HOSTDEV: case VIR_DOMAIN_NET_TYPE_LAST: break; } diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 89fdd9fb4c..397d33224a 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -254,6 +254,11 @@ umlBuildCommandLineNet(virConnectPtr conn, _("direct networking type not supported")); goto error; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("hostdev networking type not supported")); + goto error; + case VIR_DOMAIN_NET_TYPE_LAST: break; } diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 8994cbc5da..e5df953fb3 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -1956,6 +1956,7 @@ xenFormatSxprNet(virConnectPtr conn, case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_INTERNAL: case VIR_DOMAIN_NET_TYPE_DIRECT: + case VIR_DOMAIN_NET_TYPE_HOSTDEV: case VIR_DOMAIN_NET_TYPE_LAST: break; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml new file mode 100644 index 0000000000..65cd55df60 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -0,0 +1,35 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu + + + +

+ + + + + + +
+ + + + + + + + diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 4a2e016eca..03c75f81d1 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -171,6 +171,7 @@ mymain(void) DO_TEST("net-eth"); DO_TEST("net-eth-ifname"); DO_TEST("net-virtio-network-portgroup"); + DO_TEST("net-hostdev"); DO_TEST("sound"); DO_TEST("net-bandwidth"); -- 2.47.2