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:

  <interface type='hostdev' managed='yes'>
    <source>
      <address type='pci' domain='0' bus='0' slot='4' function='3'/>
    </source>
    <mac address='00:11:22:33:44:55'/>
    <address type='pci' domain='0' bus='0' slot='7' function='0'/>
  </interface>

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. <source> syntax is identical to that
for plain <hostdev> devices, except that the <address> element should
have "type='usb'" added if bus/device are specified:

  <interface type='hostdev'>
    <source>
      <address type='usb' bus='0' device='4'/>
    </source>
    <mac address='00:11:22:33:44:55'/>
  </interface>

If the vendor/product form of usb specification is used, type='usb'
is implied:

  <interface type='hostdev'>
    <source>
      <vendor id='0x0012'/>
      <product id='0x24dd'/>
    </source>
    <mac address='00:11:22:33:44:55'/>
  </interface>

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 <interface
type='hostdev'> - just use a standard <hostdev> entry in that case
(and also for single-port PCI adapters).
This commit is contained in:
Laine Stump 2012-02-15 12:37:15 -05:00
parent 93870c4ef7
commit 3b1c191fe7
10 changed files with 292 additions and 15 deletions

View File

@ -2303,6 +2303,55 @@
...
</pre>
<h5><a name="elementsNICSHostdev">PCI Passthrough</a></h5>
<p>
A PCI network device (specified by the &lt;source&gt; 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 &lt;hostdev&gt; device definition and
<span class="since">Since 0.9.11</span>
</p>
<p>
Note that this "intelligent passthrough" of network devices is
very similar to the functionality of a standard &lt;hostdev&gt;
device, the difference being that this method allows specifying
a MAC address and &lt;virtualport&gt; 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 &lt;hostdev&gt; to assign the device to the
guest instead of &lt;interface type='hostdev'/&gt;.
</p>
<pre>
...
&lt;devices&gt;
&lt;interface type='hostdev'&gt;
&lt;source&gt;
&lt;address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/&gt;
&lt;/source&gt;
&lt;mac address='52:54:00:6d:90:02'&gt;
&lt;virtualport type='802.1Qbh'&gt;
&lt;parameters profileid='finance'/&gt;
&lt;/virtualport&gt;
&lt;/interface&gt;
&lt;/devices&gt;
...</pre>
<h5><a name="elementsNICSMulticast">Multicast tunnel</a></h5>
<p>

View File

@ -1416,6 +1416,56 @@
</optional>
</interleave>
</group>
<group>
<attribute name="type">
<value>hostdev</value>
</attribute>
<optional>
<attribute name="managed">
<choice>
<value>yes</value>
<value>no</value>
</choice>
</attribute>
</optional>
<interleave>
<element name="source">
<choice>
<group>
<ref name="usbproduct"/>
<optional>
<ref name="usbaddress"/>
</optional>
</group>
<element name="address">
<choice>
<group>
<attribute name="type">
<value>pci</value>
</attribute>
<ref name="pciaddress"/>
</group>
<group>
<attribute name="type">
<value>usb</value>
</attribute>
<attribute name="bus">
<ref name="usbAddr"/>
</attribute>
<attribute name="device">
<ref name="usbPort"/>
</attribute>
</group>
</choice>
</element>
</choice>
</element>
<optional>
<ref name="virtualPortProfile"/>
</optional>
<ref name="interface-options"/>
</interleave>
</group>
</choice>
</element>
</define>

View File

@ -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 <actual> 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, " <actual type='%s'", type);
if ((def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) &&
def->data.hostdev.def.managed) {
virBufferAddLit(buf, " managed='yes'");
}
virBufferAsprintf(buf, " <actual type='%s'>\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, " <interface type='%s'>\n", type);
virBufferAsprintf(buf, " <interface type='%s'", type);
if ((def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) &&
def->data.hostdev.def.managed) {
virBufferAddLit(buf, " managed='yes'");
}
virBufferAddLit(buf, ">\n");
virBufferAsprintf(buf,
" <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\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;
}

View File

@ -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

View File

@ -384,6 +384,7 @@ virDomainNetGetActualBandwidth;
virDomainNetGetActualBridgeName;
virDomainNetGetActualDirectDev;
virDomainNetGetActualDirectMode;
virDomainNetGetActualHostdev;
virDomainNetGetActualType;
virDomainNetGetActualVirtPortProfile;
virDomainNetIndexByMac;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,35 @@
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<memory>219136</memory>
<currentMemory>219136</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu</emulator>
<disk type='block' device='disk'>
<source dev='/dev/HostVG/QEMUGuest1'/>
<target dev='hda' bus='ide'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='usb' index='0'/>
<controller type='ide' index='0'/>
<interface type='hostdev' managed='yes'>
<mac address='00:11:22:33:44:55'/>
<source>
<address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/>
</source>
<virtualport type='802.1Qbg'>
<parameters managerid='11' typeid='1193047' typeidversion='2' instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/>
</virtualport>
</interface>
<memballoon model='virtio'/>
</devices>
</domain>

View File

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