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");