On Fri, 2010-05-21 at 16:46 -0700, Chris Wright wrote:
* Stefan Berger (stefanb(a)linux.vnet.ibm.com) wrote:
> Below is David Alan's original patch with lots of changes.
>
> In particular, it now parses the following two XML descriptions, one
> for 802.1Qbg and 802.1Qbh and stored the data internally. The actual
> triggering of the switch setup protocol has not been implemented
> here but the relevant code to do that should go into the functions
> associatePortProfileId() and disassociatePortProfileId().
For future work, have you thought about preassociate on migration?
You mean first to do a preassociate and then after migration has
succeeded a (full) associate. I had thought about it. I believe the
current code would need to know what the actual cause was for it being
called. We currently handle it I believe correctly for
- 'direct' type of interface creation and destruction
- resume after a suspend
Now if we wanted to pre-associate on migration on the destination host,
we would need to know that the function was called due to a migration
operation in order not to call the associate function but rather the
pre-associate one. The I believe upon failure of migration the
disassociate would still be call-able on the target host, but in
addition we would need to call the associate function after the
migration was successful. We're not doing this. I suppose also that
nothing would be allowed to go wrong once one switches from
pre-associate to associate in such a scenario since the migration
succeeded and a failure to associate couldn't be handled gracefully
anymore (by failing migration).
[...]
>
> Some of the changes from V1 to V2:
> - parser and XML generator have been separated into their own
> functions so they can be re-used elsewhere (passthrough case
> for example)
> - Adapted XML parser and generator support the above shown type
> (802.1Qbg, 802.1Qbh).
> - Adapted schema to above XML
> - Adapted test XML to above XML
> - Passing through the VM's UUID which seems to be necessary for
> 802.1Qbh -- sorry no host UUID
> - adding virtual function ID to association function, in case it's
> necessary to use (for SR-IOV)
>
> Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
This is looking like pretty complete infrastructure to me. Have you
been able generate test (dis)associate messages through this framework w/
the vsi patches? Pending that (I can see you are able to at least
generate debug messages showing the thing work), and some minor nits
below.
I have been able to communicate with a self-hacked dummy server for
802.1Qbg that receives a netlink message and responds with a netlink
message via unicast to libvirt. With that server I am currently sending
the response immediately to the RTM_SETLINK message, which I believe
isn't what the kernel is doing. Rather it should send it back upon
RTM_GETLINK. If that server is missing or indicates failure to start, I
am timing out and fail the start of the VM. The disassociate part is in
the path of the tear down code and is being invoked as well. I haven't
tested much with that for 802.1Qbg.
Acked-by: Chris Wright <chrisw(a)redhat.com>
> >From a945107f047c7cd71f9c1b74fd74c47d8cdc3670 Mon Sep 17 00:00:00 2001
> From: David Allan <dallan(a)redhat.com>
> Date: Fri, 12 Mar 2010 13:25:04 -0500
> Subject: [PATCH 1/1] POC of port profile id support
>
> * Modified schema per DanPB's feedback
> * Added test for modified schema
> ---
> docs/schemas/domain.rng | 69 ++++++++++++++
> src/conf/domain_conf.c | 155 +++++++++++++++++++++++++++++++++
> src/conf/domain_conf.h | 35 +++++++
> src/qemu/qemu_conf.c | 18 +--
> src/qemu/qemu_conf.h | 5 -
> src/qemu/qemu_driver.c | 17 +--
> src/util/macvtap.c | 151 +++++++++++++++++++++++++++-----
> src/util/macvtap.h | 10 +-
> tests/domainschemadata/portprofile.xml | 36 +++++++
> 9 files changed, 446 insertions(+), 50 deletions(-)
> create mode 100644 tests/domainschemadata/portprofile.xml
>
> Index: libvirt-acl/src/conf/domain_conf.c
> ===================================================================
> --- libvirt-acl.orig/src/conf/domain_conf.c
> +++ libvirt-acl/src/conf/domain_conf.c
> @@ -242,6 +242,11 @@ VIR_ENUM_IMPL(virDomainNetdevMacvtap, VI
> "private",
> "bridge")
>
> +VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST,
> + "none",
> + "802.1Qbg",
> + "802.1Qbh")
> +
> VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
> "utc",
> "localtime",
> @@ -1807,6 +1812,145 @@ cleanup:
> }
>
>
> +static void
static int so we can capture an error?
Ok.
> +virVirtualPortProfileDefParseXML(xmlNodePtr node,
> + virVirtualPortProfileDefPtr virtPort)
> +{
> + char *virtPortType;
> + char *virtPortManagerID = NULL;
> + char *virtPortTypeID = NULL;
> + char *virtPortTypeIDVersion = NULL;
> + char *virtPortInstanceID = NULL;
> + char *virtPortProfileID = NULL;
> + xmlNodePtr cur = node->children;
int ret = -1;
> +
> + virtPortType = virXMLPropString(node, "type");
> + if (!virtPortType)
> + return;
> +
> + while (cur != NULL) {
> + if (xmlStrEqual(cur->name, BAD_CAST "parameters")) {
> +
> + virtPortManagerID = virXMLPropString(cur, "managerid");
> + virtPortTypeID = virXMLPropString(cur, "typeid");
> + virtPortTypeIDVersion = virXMLPropString(cur,
"typeidversion");
> + virtPortInstanceID = virXMLPropString(cur, "instanceid");
> + virtPortProfileID = virXMLPropString(cur, "profileid");
> +
> + break;
s/break/goto out/
> + }
> +
> + cur = cur->next;
> + }
> +
> + virtPort->virtPortType = VIR_VIRTUALPORT_NONE;
> +
> + switch (virVirtualPortTypeFromString(virtPortType)) {
> +
> + case VIR_VIRTUALPORT_8021QBG:
> + if (virtPortManagerID != NULL && virtPortTypeID != NULL
&&
> + virtPortTypeIDVersion != NULL) {
> + unsigned int val;
> +
> + if ((virStrToLong_ui(virtPortManagerID, NULL, 10, &val) &&
> + virStrToLong_ui(virtPortManagerID, NULL, 16, &val) ) ||
Why not a baseless strtol (0 instead of 10 and 16)? And I think this
should give some virDomainReportError() so that we can figure out what's
happening when the xml is incorrect.
Done.
Ditto for parsing the whole thing...
> + val > 0xff)
> + break;
s/break/goto out/
Ok.
> +
> + virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
> +
> + if ((virStrToLong_ui(virtPortTypeID, NULL, 10, &val) &&
> + virStrToLong_ui(virtPortTypeID, NULL, 16, &val) ) ||
> + val > 0xffffff)
> + break;
> +
> + virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
> +
> + if ((virStrToLong_ui(virtPortTypeIDVersion, NULL, 10, &val)
&&
> + virStrToLong_ui(virtPortTypeIDVersion, NULL, 16, &val) ) ||
> + val > 0xff)
> + break;
> +
> + virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
> +
> + if (virtPortInstanceID != NULL) {
> + if (virUUIDParse(virtPortInstanceID,
virtPort->u.virtPort8021Qbg.instanceID))
> + break;
> + } else {
> + if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID))
> + break;
> + }
> +
> + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
> + }
> + break;
> +
> + case VIR_VIRTUALPORT_8021QBH:
> + if (virtPortProfileID != NULL) {
> + if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
> + virtPortProfileID) != NULL)
> + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
> + }
> + break;
> +
> +
> + default:
> + case VIR_VIRTUALPORT_NONE:
> + case VIR_VIRTUALPORT_TYPE_LAST:
> + break;
error cases, no? goto out
Ok. However, I'd still allow the omission of <virtualport/> so I can
start a macvtap without having to have a (future) switch.
> + }
> +
ret = 0;
out:
> + VIR_FREE(virtPortManagerID);
> + VIR_FREE(virtPortTypeID);
> + VIR_FREE(virtPortTypeIDVersion);
> + VIR_FREE(virtPortInstanceID);
> + VIR_FREE(virtPortProfileID);
> + VIR_FREE(virtPortType);
return ret;
Ok.
> +}
> +
> +
> +static void
> +virVirtualPortProfileFormat(virBufferPtr buf, virVirtualPortProfileDefPtr
virtPort,
> + const char *indent)
> +{
> + char uuidstr[VIR_UUID_STRING_BUFLEN];
> +
> + if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE)
> + return;
> +
> + virBufferVSprintf(buf, "%s<virtualport type='%s'>\n",
> + indent,
virVirtualPortTypeToString(virtPort->virtPortType));
> +
> + switch (virtPort->virtPortType) {
> + case VIR_VIRTUALPORT_NONE:
> + case VIR_VIRTUALPORT_TYPE_LAST:
> + break;
> +
> + case VIR_VIRTUALPORT_8021QBG:
> + virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID,
> + uuidstr);
> + virBufferVSprintf(buf,
> + "%s <parameters managerid='%d'
typeid='%d' "
> + "typeidversion='%d'
instanceid='%s'/>\n",
> + indent,
> + virtPort->u.virtPort8021Qbg.managerID,
> + virtPort->u.virtPort8021Qbg.typeID,
> + virtPort->u.virtPort8021Qbg.typeIDVersion,
> + uuidstr);
> + break;
> +
> + case VIR_VIRTUALPORT_8021QBH:
> + virBufferVSprintf(buf,
> + "%s <parameters
profileid='%s'/>\n",
> + indent,
> + virtPort->u.virtPort8021Qbh.profileID);
> + break;
> + }
> +
> + virBufferVSprintf(buf, "%s</virtualport>\n", indent);
> +}
> +
> +
> /* Parse the XML definition for a network interface
> * @param node XML nodeset to parse for net definition
> * @return 0 on success, -1 on failure
> @@ -1832,6 +1976,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
> char *devaddr = NULL;
> char *mode = NULL;
> virNWFilterHashTablePtr filterparams = NULL;
> + virVirtualPortProfileDef virtPort;
> + bool virtPortParsed = false;
>
> if (VIR_ALLOC(def) < 0) {
> virReportOOMError();
> @@ -1873,6 +2019,11 @@ virDomainNetDefParseXML(virCapsPtr caps,
> xmlStrEqual(cur->name, BAD_CAST "source")) {
> dev = virXMLPropString(cur, "dev");
> mode = virXMLPropString(cur, "mode");
> + } else if ((virtPortParsed == false) &&
> + (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
> + xmlStrEqual(cur->name, BAD_CAST "virtualport"))
{
> + virVirtualPortProfileDefParseXML(cur, &virtPort);
> + virtPortParsed = true;
> } else if ((network == NULL) &&
> ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
> (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
> @@ -2048,6 +2199,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
> } else
> def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
>
> + def->data.direct.virtPortProfile = virtPort;
> +
> def->data.direct.linkdev = dev;
> dev = NULL;
>
> @@ -5140,6 +5293,8 @@ virDomainNetDefFormat(virBufferPtr buf,
> virBufferVSprintf(buf, " mode='%s'",
> virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
> virBufferAddLit(buf, "/>\n");
> + virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile,
> + " ");
> break;
>
> case VIR_DOMAIN_NET_TYPE_USER:
> Index: libvirt-acl/src/conf/domain_conf.h
> ===================================================================
> --- libvirt-acl.orig/src/conf/domain_conf.h
> +++ libvirt-acl/src/conf/domain_conf.h
> @@ -259,6 +259,39 @@ enum virDomainNetdevMacvtapType {
> };
>
>
> +enum virVirtualPortType {
> + VIR_VIRTUALPORT_NONE,
> + VIR_VIRTUALPORT_8021QBG,
> + VIR_VIRTUALPORT_8021QBH,
> +
> + VIR_VIRTUALPORT_TYPE_LAST,
> +};
> +
> +# ifdef IFLA_VF_PORT_PROFILE_MAX
> +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX
> +# else
> +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40
> +# endif
> +
> +/* profile data for macvtap (VEPA) */
> +typedef struct _virVirtualPortProfileDef virVirtualPortProfileDef;
> +typedef virVirtualPortProfileDef *virVirtualPortProfileDefPtr;
> +struct _virVirtualPortProfileDef {
> + enum virVirtualPortType virtPortType;
> + union {
> + struct {
> + uint8_t managerID;
> + uint32_t typeID; // 24 bit valid
> + uint8_t typeIDVersion;
> + unsigned char instanceID[VIR_UUID_BUFLEN];
> + } virtPort8021Qbg;
> + struct {
> + char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX];
> + } virtPort8021Qbh;
> + } u;
> +};
> +
> +
> /* Stores the virtual network interface configuration */
> typedef struct _virDomainNetDef virDomainNetDef;
> typedef virDomainNetDef *virDomainNetDefPtr;
> @@ -290,6 +323,7 @@ struct _virDomainNetDef {
> struct {
> char *linkdev;
> int mode;
> + virVirtualPortProfileDef virtPortProfile;
> } direct;
> } data;
> char *ifname;
> @@ -1089,6 +1123,7 @@ VIR_ENUM_DECL(virDomainSeclabel)
> VIR_ENUM_DECL(virDomainClockOffset)
>
> VIR_ENUM_DECL(virDomainNetdevMacvtap)
> +VIR_ENUM_DECL(virVirtualPort)
>
> VIR_ENUM_DECL(virDomainTimerName)
> VIR_ENUM_DECL(virDomainTimerTrack)
> Index: libvirt-acl/src/util/macvtap.c
> ===================================================================
> --- libvirt-acl.orig/src/util/macvtap.c
> +++ libvirt-acl/src/util/macvtap.c
> @@ -43,6 +43,7 @@
>
> # include "util.h"
> # include "memory.h"
> +# include "logging.h"
> # include "macvtap.h"
> # include "interface.h"
> # include "conf/domain_conf.h"
> @@ -57,6 +58,16 @@
> # define MACVTAP_NAME_PREFIX "macvtap"
> # define MACVTAP_NAME_PATTERN "macvtap%d"
>
> +
> +static int associatePortProfileId(const char *macvtap_ifname,
> + const virVirtualPortProfileDefPtr virtPort,
> + int vf,
> + const unsigned char *vmuuid);
> +
> +static int disassociatePortProfileId(const char *macvtap_ifname,
> + const virVirtualPortProfileDefPtr virtPort);
> +
> +
> static int nlOpen(void)
> {
> int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
> @@ -567,39 +578,38 @@ configMacvtapTap(int tapfd, int vnet_hdr
>
> return 0;
> }
> -
> -
> /**
> * openMacvtapTap:
> * Create an instance of a macvtap device and open its tap character
> * device.
> * @tgifname: Interface name that the macvtap is supposed to have. May
> * be NULL if this function is supposed to choose a name
> - * @macaddress: The MAC address for the macvtap device
> - * @linkdev: The interface name of the NIC to connect to the external bridge
> - * @mode_str: String describing the mode. Valid are 'bridge',
'vepa' and
> - * 'private'.
> + * @net: pointer to the virDomainNetDef object describing the direct
> + * type if an interface
> * @res_ifname: Pointer to a string pointer where the actual name of the
> * interface will be stored into if everything succeeded. It is up
> * to the caller to free the string.
> + * @vnet_hdr: Whether to enable IFF_VNET_HDR on the interface
> + * @vmuuid: The (raw) UUID of the VM
> *
> * Returns file descriptor of the tap device in case of success,
> * negative value otherwise with error reported.
> *
> + * Open a macvtap device and trigger the switch setup protocol
> + * if valid port profile parameters were provided.
> */
> int
> openMacvtapTap(const char *tgifname,
> - const unsigned char *macaddress,
> - const char *linkdev,
> - int mode,
> + virDomainNetDefPtr net,
Any reason to keep tgifname now that you're passing full virDomainNetDefPtr?
True. Next would be to hanlde the **res_ifname in this function, but
that I'd leave to a separate patch.
> char **res_ifname,
> - int vnet_hdr)
> + int vnet_hdr,
> + const unsigned char *vmuuid)
> {
> const char *type = "macvtap";
> int c, rc;
> char ifname[IFNAMSIZ];
> int retries, do_retry = 0;
> - uint32_t macvtapMode = macvtapModeFromInt(mode);
> + uint32_t macvtapMode = macvtapModeFromInt(net->data.direct.mode);
> const char *cr_ifname;
> int ifindex;
>
> @@ -616,7 +626,7 @@ openMacvtapTap(const char *tgifname,
> return -1;
> }
> cr_ifname = tgifname;
> - rc = link_add(type, macaddress, 6, tgifname, linkdev,
> + rc = link_add(type, net->mac, 6, tgifname, net->data.direct.linkdev,
> macvtapMode, &do_retry);
> if (rc)
> return -1;
> @@ -626,7 +636,8 @@ create_name:
> for (c = 0; c < 8192; c++) {
> snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
> if (ifaceGetIndex(false, ifname, &ifindex) == ENODEV) {
> - rc = link_add(type, macaddress, 6, ifname, linkdev,
> + rc = link_add(type, net->mac, 6, ifname,
> + net->data.direct.linkdev,
> macvtapMode, &do_retry);
> if (rc == 0)
> break;
> @@ -639,6 +650,14 @@ create_name:
> cr_ifname = ifname;
> }
>
> + if (associatePortProfileId(cr_ifname,
> + &net->data.direct.virtPortProfile,
> + -1,
> + vmuuid) != 0) {
> + rc = -1;
> + goto link_del_exit;
> + }
> +
> rc = ifaceUp(cr_ifname);
> if (rc != 0) {
> virReportSystemError(errno,
> @@ -647,7 +666,7 @@ create_name:
> "MAC address"),
> cr_ifname);
> rc = -1;
> - goto link_del_exit;
> + goto disassociate_exit;
> }
>
> rc = openTap(cr_ifname, 10);
> @@ -656,14 +675,18 @@ create_name:
> if (configMacvtapTap(rc, vnet_hdr) < 0) {
> close(rc);
> rc = -1;
> - goto link_del_exit;
> + goto disassociate_exit;
> }
> *res_ifname = strdup(cr_ifname);
> } else
> - goto link_del_exit;
> + goto disassociate_exit;
>
> return rc;
>
> +disassociate_exit:
> + disassociatePortProfileId(cr_ifname,
> + &net->data.direct.virtPortProfile);
> +
> link_del_exit:
> link_del(cr_ifname);
>
> @@ -673,14 +696,102 @@ link_del_exit:
>
> /**
> * delMacvtap:
> - * @ifname : The name of the macvtap interface
> + * @net: pointer to virDomainNetDef object
> *
> - * Delete an interface given its name.
> + * Delete an interface given its name. Disassociate
> + * it with the switch if port profile parameters
> + * were provided.
> */
> void
> -delMacvtap(const char *ifname)
> +delMacvtap(virDomainNetDefPtr net)
> {
> - link_del(ifname);
> + if (net->ifname) {
> + disassociatePortProfileId(net->ifname,
> + &net->data.direct.virtPortProfile);
> + link_del(net->ifname);
> + }
> }
>
> #endif
> +
> +
> +/**
> + * associatePortProfile
> + *
> + * @macvtap_ifname: The name of the macvtap device
> + * @virtPort: pointer to the object holding port profile parameters
> + * @vf: virtual function number, -1 if to be ignored
> + * @vmuuid : the UUID of the virtual machine
> + *
> + * Associate a port on a swtich with a profile. This function
> + * may notify a kernel driver or an external daemon to run
> + * the setup protocol. If profile parameters were not supplied
> + * by the user, then this function returns without doing
> + * anything.
> + *
> + * Returns 0 in case of success, != 0 otherwise with error
> + * having been reported.
> + */
> +static int
> +associatePortProfileId(const char *macvtap_ifname,
> + const virVirtualPortProfileDefPtr virtPort,
> + int vf,
> + const unsigned char *vmuuid)
> +{
> + int rc = 0;
> + VIR_DEBUG("Associating port profile '%p' on link device
'%s'",
> + virtPort, macvtap_ifname);
> + (void)vf;
> + (void)vmuuid;
> +
> + switch (virtPort->virtPortType) {
> + case VIR_VIRTUALPORT_NONE:
> + case VIR_VIRTUALPORT_TYPE_LAST:
> + break;
If we capture these on parsing and error out, then these could be
invalid here.
They would not even appear here. Problem is just I cannot even test
anything with macvtap anymore without having a profile which then in
turn requires either a daemon or an enic kernel driver.
> +
> + case VIR_VIRTUALPORT_8021QBG:
> +
> + break;
> +
> + case VIR_VIRTUALPORT_8021QBH:
> +
> + break;
> + }
> +
> + return rc;
> +}
> +
> +
> +/**
> + * disassociatePortProfile
> + *
> + * @macvtap_ifname: The name of the macvtap device
> + * @virtPort: point to object holding port profile parameters
> + *
> + * Returns 0 in case of success, != 0 otherwise with error
> + * having been reported.
> + */
> +static int
> +disassociatePortProfileId(const char *macvtap_ifname,
> + const virVirtualPortProfileDefPtr virtPort)
> +{
> + int rc = 0;
> + VIR_DEBUG("Disassociating port profile id '%p' on link device
'%s' ",
> + virtPort, macvtap_ifname);
> +
> + switch (virtPort->virtPortType) {
> + case VIR_VIRTUALPORT_NONE:
> + case VIR_VIRTUALPORT_TYPE_LAST:
> + break;
If we capture these on parsing and error out, then these could be
invalid here.
If I do that then please send me hardware :-)
I'll post v8 after some testing - while I still can :-)
Stefan