[libvirt] [v4 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches

Scott Feldman scofeldm at cisco.com
Tue May 25 00:22:15 UTC 2010


From: Scott Feldman <scofeldm at cisco.com>

This patch builds on the work recently posted by Stefan Berger.  It builds
on top of Stefan's three posted patches:

        [PATCH v9] vepa: parsing for 802.1Qb{g|h} XML
        [RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
        [PATCH v3] Add host UUID (to libvirt capabilities)

Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.

Changes from v3 to v4:
  - move from Stafan's 802.1Qb{g|h} XML v8 to v9
  - move hostuuid and vf index calcs to inside doPortProfileOp8021Qbh

Changes from v2 to v3:
  - remove debug fprintfs
  - use virGetHostUUID (thanks Stefan!)
  - fix compile issue when latest if_link.h isn't available
  - change poll timeout to 10s, at 1/8 intervals
     - if polling times out, log msg and return -ETIMEDOUT

Changes from v1 to v2:
  - Add Stefan's code for getPortProfileStatus
  - Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
     - if status indicates error, abort openMacvtapTap
     - if status indicates success, exit polling
     - if status is "in-progress" after 2 secs of polling, exit
       polling loop silently, without error

My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC.  I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML.  VM-to-VM,
and VM-to-external work as expected.  VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts.  I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour.  For example, adjusting a QoS setting like rate limit.  All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.

I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index.  We'll need to complete this by discovering the PF given the VF
linkdev.  Once we have the PF, we'll also have the VF index.  All this info-
mation is available from sysfs.

Signed-off-by: Scott Feldman <scofeldm at cisco.com>
---
 configure.ac           |   16 +
 src/qemu/qemu_conf.c   |    2 
 src/qemu/qemu_driver.c |    4 
 src/util/macvtap.c     |  778 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/util/macvtap.h     |    1 
 5 files changed, 785 insertions(+), 16 deletions(-)


diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
 fi
 AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
 
+AC_TRY_COMPILE([ #include <sys/socket.h>
+                 #include <linux/rtnetlink.h> ],
+                 [ int x = IFLA_PORT_MAX; ],
+                 [ with_virtualport=yes ],
+                 [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+    val=1
+else
+    val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
 
 dnl netlink library
 
 LIBNL_CFLAGS=""
 LIBNL_LIBS=""
 
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
     PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
     ], [
         AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
 AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
 AC_MSG_NOTICE([   netcf: $with_netcf])
 AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Storage Drivers])
 AC_MSG_NOTICE([])
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 111fa6e..95d4c1a 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1505,7 +1505,7 @@ qemudPhysIfaceConnect(virConnectPtr conn,
             if (err) {
                 close(rc);
                 rc = -1;
-                delMacvtap(net->ifname,
+                delMacvtap(net->ifname, net->data.direct.linkdev,
                            &net->data.direct.virtPortProfile);
             }
         }
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f02bf3b..f1a0d0e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3679,7 +3679,7 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver,
     for (i = 0; i < def->nnets; i++) {
         virDomainNetDefPtr net = def->nets[i];
         if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
-            delMacvtap(net->ifname,
+            delMacvtap(net->ifname, net->data.direct.linkdev,
                        &net->data.direct.virtPortProfile);
     }
 #endif
@@ -8369,7 +8369,7 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
 
 #if WITH_MACVTAP
     if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
-        delMacvtap(detach->ifname,
+        delMacvtap(detach->ifname, detach->data.direct.linkdev,
                    &detach->data.direct.virtPortProfile);
 #endif
 
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 5cbd02b..d5a08d9 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
 
 #include <config.h>
 
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
 
 # include <stdio.h>
 # include <errno.h>
@@ -41,6 +41,8 @@
 # include <linux/rtnetlink.h>
 # include <linux/if_tun.h>
 
+# include <netlink/msg.h>
+
 # include "util.h"
 # include "memory.h"
 # include "logging.h"
@@ -48,6 +50,7 @@
 # include "interface.h"
 # include "conf/domain_conf.h"
 # include "virterror_internal.h"
+# include "uuid.h"
 
 # define VIR_FROM_THIS VIR_FROM_NET
 
@@ -58,15 +61,23 @@
 # define MACVTAP_NAME_PREFIX	"macvtap"
 # define MACVTAP_NAME_PATTERN	"macvtap%d"
 
+# define MICROSEC_PER_SEC       (1000 * 1000)
+
 
 static int associatePortProfileId(const char *macvtap_ifname,
+                                  const char *linkdev,
                                   const virVirtualPortProfileDefPtr virtPort,
-                                  int vf,
                                   const unsigned char *vmuuid);
 
 static int disassociatePortProfileId(const char *macvtap_ifname,
+                                     const char *linkdev,
                                      const virVirtualPortProfileDefPtr virtPort);
 
+enum virVirtualPortOp {
+    ASSOCIATE = 0x1,
+    DISASSOCIATE = 0x2,
+};
+
 
 static int nlOpen(void)
 {
@@ -159,6 +170,156 @@ err_exit:
 }
 
 
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ *      on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ *            to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+                  char **respbuf, int *respbuflen, long to_usecs)
+{
+    int rc = 0;
+    struct sockaddr_nl nladdr = {
+            .nl_family = AF_NETLINK,
+            .nl_pid    = getpid(),
+            .nl_groups = nl_groups,
+    };
+    int rcvChunkSize = 1024; // expecting less than that
+    int rcvoffset = 0;
+    ssize_t nbytes;
+    int n;
+    struct timeval tv = {
+        .tv_sec  = to_usecs / MICROSEC_PER_SEC,
+        .tv_usec = to_usecs % MICROSEC_PER_SEC,
+    };
+    fd_set rfds;
+    bool gotvalid = false;
+    int fd = nlOpen();
+    static uint32_t seq = 0x1234;
+    uint32_t myseq = seq++;
+    uint32_t mypid = getpid();
+
+    if (fd < 0)
+        return -1;
+
+    nlmsg->nlmsg_pid = mypid;
+    nlmsg->nlmsg_seq = myseq;
+    nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+    nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+                    (struct sockaddr *)&nladdr, sizeof(nladdr));
+    if (nbytes < 0) {
+        virReportSystemError(errno,
+                             "%s", _("cannot send to netlink socket"));
+        rc = -1;
+        goto err_exit;
+    }
+
+    while (!gotvalid) {
+        rcvoffset = 0;
+        while (1) {
+            socklen_t addrlen = sizeof(nladdr);
+
+            if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+                virReportOOMError();
+                rc = -1;
+                goto err_exit;
+            }
+
+            FD_ZERO(&rfds);
+            FD_SET(fd, &rfds);
+
+            n = select(fd + 1, &rfds, NULL, NULL, &tv);
+            if (n == 0) {
+                rc = -1;
+                goto err_exit;
+            }
+
+            nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+                              (struct sockaddr *)&nladdr, &addrlen);
+            if (nbytes < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                    continue;
+                virReportSystemError(errno, "%s",
+                                     _("error receiving from netlink socket"));
+                rc = -1;
+                goto err_exit;
+            }
+            rcvoffset += nbytes;
+            break;
+        }
+        *respbuflen = rcvoffset;
+
+        /* check message for error */
+        if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+            struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+            struct nlmsgerr *err;
+
+            if (resp->nlmsg_pid != mypid ||
+                resp->nlmsg_seq != myseq)
+                continue;
+
+            /* skip reflected message */
+            if (resp->nlmsg_type & 0x10)
+                continue;
+
+            switch (resp->nlmsg_type) {
+               case NLMSG_ERROR:
+                  err = (struct nlmsgerr *)NLMSG_DATA(resp);
+                  if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+                      if (-err->error != EOPNOTSUPP) {
+                          /* assuming error msg from daemon */
+                          gotvalid = true;
+                          break;
+                      }
+                  }
+                  /* whatever this is, skip it */
+                  VIR_FREE(*respbuf);
+                  *respbuf = NULL;
+                  *respbuflen = 0;
+                  break;
+
+               case NLMSG_DONE:
+                  gotvalid = true;
+                  break;
+
+               default:
+                  VIR_FREE(*respbuf);
+                  *respbuf = NULL;
+                  *respbuflen = 0;
+                  break;
+            }
+        }
+    }
+
+err_exit:
+    if (rc == -1) {
+        VIR_FREE(*respbuf);
+        *respbuf = NULL;
+        *respbuflen = 0;
+    }
+
+    nlClose(fd);
+    return rc;
+}
+
+#endif
+
 static struct rtattr *
 rtattrCreate(char *buffer, int bufsize, int type,
              const void *data, int datalen)
@@ -204,6 +365,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
 }
 
 
+# if WITH_MACVTAP
+
 static int
 link_add(const char *type,
          const unsigned char *macaddress, int macaddrsize,
@@ -655,8 +818,8 @@ create_name:
     }
 
     if (associatePortProfileId(cr_ifname,
+                               linkdev,
                                virtPortProfile,
-                               -1,
                                vmuuid) != 0) {
         rc = -1;
         goto link_del_exit;
@@ -689,6 +852,7 @@ create_name:
 
 disassociate_exit:
     disassociatePortProfileId(cr_ifname,
+                              linkdev,
                               virtPortProfile);
 
 link_del_exit:
@@ -701,6 +865,7 @@ link_del_exit:
 /**
  * delMacvtap:
  * @ifname : The name of the macvtap interface
+ * @linkdev: The interface name of the NIC to connect to the external bridge
  * @virtPortProfile: pointer to object holding the virtual port profile data
  *
  * Delete an interface given its name. Disassociate
@@ -709,24 +874,603 @@ link_del_exit:
  */
 void
 delMacvtap(const char *ifname,
+           const char *linkdev,
            virVirtualPortProfileDefPtr virtPortProfile)
 {
     if (ifname) {
         disassociatePortProfileId(ifname,
+                                  linkdev,
                                   virtPortProfile);
         link_del(ifname);
     }
 }
 
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+  [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+  [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+  [IFLA_PORT_RESPONSE]      = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, struct nlattr **tb, char **recvbuf)
+{
+    int rc = 0;
+    char nlmsgbuf[256] = { 0, };
+    struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+    struct nlmsgerr *err;
+    struct ifinfomsg i = {
+        .ifi_family = AF_UNSPEC,
+        .ifi_index  = ifindex
+    };
+    int recvbuflen;
+
+    *recvbuf = NULL;
+
+    nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+        goto buffer_too_small;
+
+    if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+        return -1;
+
+    if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+        goto malformed_resp;
 
+    resp = (struct nlmsghdr *)*recvbuf;
+
+    switch (resp->nlmsg_type) {
+    case NLMSG_ERROR:
+        err = (struct nlmsgerr *)NLMSG_DATA(resp);
+        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+            goto malformed_resp;
+
+        switch (-err->error) {
+        case 0:
+        break;
+
+        default:
+            virReportSystemError(-err->error,
+                                 _("error dumping %d interface"),
+                                 ifindex);
+            rc = -1;
+        }
+    break;
+
+    case GENL_ID_CTRL:
+    case NLMSG_DONE:
+        if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+                        tb, IFLA_MAX, ifla_policy)) {
+            goto malformed_resp;
+        }
+    break;
+
+    default:
+        goto malformed_resp;
+    }
+
+    if (rc != 0)
+        VIR_FREE(*recvbuf);
+
+    return rc;
+
+malformed_resp:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("malformed netlink response message"));
+    VIR_FREE(*recvbuf);
+    return -1;
+
+buffer_too_small:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("internal buffer is too small"));
+    return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+    int rc = 1;
+    const char *msg = NULL;
+    struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+                  *tb3[IFLA_PORT_MAX+1];
+
+    if (vf == PORT_SELF_VF) {
+        if (tb[IFLA_PORT_SELF]) {
+            if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+                                 ifla_port_policy)) {
+                msg = _("error parsing nested IFLA_VF_PORT part");
+                goto err_exit;
+            }
+        }
+    } else {
+        if (tb[IFLA_VF_PORTS]) {
+            if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+                                 ifla_vf_ports_policy)) {
+                msg = _("error parsing nested IFLA_VF_PORTS part");
+                goto err_exit;
+            }
+            if (tb2[IFLA_VF_PORT]) {
+                if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+                                     ifla_port_policy)) {
+                    msg = _("error parsing nested IFLA_VF_PORT part");
+                    goto err_exit;
+                }
+            }
+        }
+    }
+
+    if (tb3[IFLA_PORT_RESPONSE]) {
+        *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+         rc = 0;
+    } else {
+         msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+         goto err_exit;
+    }
+
+err_exit:
+    if (msg)
+        macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+    return rc;
+}
+
+
+static int
+doPortProfileOpSetLink(bool multicast,
+                       int ifindex,
+                       const char *profileId,
+                       struct ifla_port_vsi *portVsi,
+                       const unsigned char *instanceId,
+                       const unsigned char *hostUUID,
+                       int32_t vf,
+                       uint8_t op)
+{
+    int rc = 0;
+    char nlmsgbuf[256];
+    struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+    struct nlmsgerr *err;
+    char rtattbuf[64];
+    struct rtattr *rta, *vfports, *vfport;
+    struct ifinfomsg ifinfo = {
+        .ifi_family = AF_UNSPEC,
+        .ifi_index  = ifindex,
+    };
+    char *recvbuf = NULL;
+    int recvbuflen = 0;
+
+    memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+    nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+        goto buffer_too_small;
+
+    if (vf == PORT_SELF_VF) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+    } else {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+                                 rtattbuf, rta->rta_len)))
+            goto buffer_too_small;
+
+        /* beging nesting vfports */
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+    }
+
+    if (!rta)
+        goto buffer_too_small;
+
+    if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+        goto buffer_too_small;
+
+    if (profileId) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+                           profileId, strlen(profileId) + 1);
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+            goto buffer_too_small;
+    }
+
+    if (portVsi) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+                           portVsi, sizeof(*portVsi));
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+            goto buffer_too_small;
+    }
+
+    if (instanceId) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+                           instanceId, VIR_UUID_BUFLEN);
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+            goto buffer_too_small;
+    }
+
+    if (hostUUID) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+                           hostUUID, VIR_UUID_BUFLEN);
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+            goto buffer_too_small;
+    }
+
+    if (vf != PORT_SELF_VF) {
+        rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+                           &vf, sizeof(vf));
+        if (!rta)
+            goto buffer_too_small;
+
+        if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+            goto buffer_too_small;
+    }
+
+    rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+                       &op, sizeof(op));
+    if (!rta)
+        goto buffer_too_small;
+
+    if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+        goto buffer_too_small;
+
+    /* end nesting of vport */
+    vfport->rta_len  = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+    if (vf != PORT_SELF_VF) {
+        /* end nesting of vfports */
+        vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+    }
+
+    if (!multicast) {
+        if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+            return -1;
+    } else {
+        if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+                              5 * MICROSEC_PER_SEC) < 0)
+            return -1;
+    }
+
+    if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+        goto malformed_resp;
+
+    resp = (struct nlmsghdr *)recvbuf;
+
+    switch (resp->nlmsg_type) {
+    case NLMSG_ERROR:
+        err = (struct nlmsgerr *)NLMSG_DATA(resp);
+        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+            goto malformed_resp;
+
+        switch (-err->error) {
+        case 0:
+        break;
+
+        default:
+            virReportSystemError(-err->error,
+                _("error during virtual port configuration of ifindex %d"),
+                ifindex);
+            rc = -1;
+        }
+    break;
+
+    case NLMSG_DONE:
+    break;
+
+    default:
+        goto malformed_resp;
+    }
+
+    VIR_FREE(recvbuf);
+
+    return rc;
+
+malformed_resp:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("malformed netlink response message"));
+    VIR_FREE(recvbuf);
+    return -1;
+
+buffer_too_small:
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("internal buffer is too small"));
+    return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+                      int ifindex,
+                      const char *profileId,
+                      struct ifla_port_vsi *portVsi,
+                      const unsigned char *instanceId,
+                      const unsigned char *hostUUID,
+                      int32_t vf,
+                      uint8_t op)
+{
+    int rc;
+    char *recvbuf = NULL;
+    struct nlattr *tb[IFLA_MAX + 1];
+    int repeats = 80;
+    uint16_t status = 0;
+
+    rc = doPortProfileOpSetLink(multicast,
+                                ifindex,
+                                profileId,
+                                portVsi,
+                                instanceId,
+                                hostUUID,
+                                vf,
+                                op);
+
+    if (rc != 0) {
+        macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("sending of PortProfileRequest failed.\n"));
+        return rc;
+    }
+
+    if (!multicast) {
+        while (--repeats) {
+            rc = link_dump(ifindex, tb, &recvbuf);
+            if (rc)
+                goto err_exit;
+            rc = getPortProfileStatus(tb, vf, &status);
+            if (rc == 0) {
+                if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
+                    status == PORT_VDP_RESPONSE_SUCCESS) {
+                    break;
+                } else if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+                    // keep trying...
+                } else {
+                    virReportSystemError(EINVAL,
+                        _("error %d during port-profile setlink on ifindex %d"),
+                        status, ifindex);
+                    rc = 1;
+                    break;
+                }
+            }
+            usleep(125000);
+
+            VIR_FREE(recvbuf);
+        }
+    }
+
+    if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+        macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("port-profile setlink timed out"));
+        rc = -ETIMEDOUT;
+    }
+
+err_exit:
+    VIR_FREE(recvbuf);
+
+    return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+                       const virVirtualPortProfileDefPtr virtPort,
+                       enum virVirtualPortOp virtPortOp)
+{
+    int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+    (void)ifname;
+    (void)virtPort;
+    (void)vf;
+    (void)virtPortOp;
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("Kernel VF Port support was missing at compile time."));
+    rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+    int op = PORT_REQUEST_ASSOCIATE;
+    struct ifla_port_vsi portVsi = {
+        .vsi_mgr_id       = virtPort->u.virtPort8021Qbg.managerID,
+        .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+    };
+    bool multicast = true;
+    int ifindex;
+
+    if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+        rc = 1;
+        goto err_exit;
+    }
+
+    portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+    portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+    portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+    switch (virtPortOp) {
+    case ASSOCIATE:
+        op = PORT_REQUEST_ASSOCIATE;
+        break;
+    case DISASSOCIATE:
+        op = PORT_REQUEST_DISASSOCIATE;
+        break;
+    default:
+        macvtapError(VIR_ERR_INTERNAL_ERROR,
+                     _("operation type %d not supported"), op);
+        rc = 1;
+        goto err_exit;
+    }
+
+    rc = doPortProfileOpCommon(multicast, ifindex,
+                               NULL,
+                               &portVsi,
+                               virtPort->u.virtPort8021Qbg.instanceID,
+                               NULL,
+                               PORT_SELF_VF,
+                               op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+    return rc;
+}
+
+
+static int
+getPhysfn(const char *linkdev,
+          int32_t *vf,
+          char **physfndev)
+{
+    int rc = 0;
+
+# ifndef IFLA_VF_PORT_MAX
+
+    (void)linkdev;
+    (void)vf;
+    (void)physfndev;
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("Kernel VF Port support was missing at compile time."));
+    rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+    bool virtfn = false;
+
+    if (virtfn) {
+
+        // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+        // XXX: and set linkdev = PF device
+        // XXX: need to use get_physical_function_linux() or
+        // XXX: something like that to get PF
+        // XXX: device and figure out VF index
+
+        rc = 1;
+
+    } else {
+
+        /* Not SR-IOV VF: physfndev is linkdev and VF index
+         * refers to linkdev self
+         */
+
+        *vf = PORT_SELF_VF;
+        *physfndev = (char *)linkdev;
+    }
+
+# endif /* IFLA_VF_PORT_MAX */
+
+    return rc;
+}
+
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+                       const virVirtualPortProfileDefPtr virtPort,
+                       const unsigned char *vm_uuid,
+                       enum virVirtualPortOp virtPortOp)
+{
+    int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+    (void)ifname;
+    (void)virtPort;
+    (void)vm_uuid;
+    (void)virtPortOp;
+    macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+                 _("Kernel VF Port support was missing at compile time."));
+    rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+    char *physfndev;
+    unsigned char hostuuid[VIR_UUID_BUFLEN];
+    int32_t vf;
+    int op = PORT_REQUEST_ASSOCIATE;
+    bool multicast = false;
+    int ifindex;
+
+    rc = virGetHostUUID(hostuuid);
+    if (rc)
+        goto err_exit;
+
+    rc = getPhysfn(ifname, &vf, &physfndev);
+    if (rc)
+        goto err_exit;
+
+    if (ifaceGetIndex(true, physfndev, &ifindex) != 0) {
+        rc = 1;
+        goto err_exit;
+    }
+
+    switch (virtPortOp) {
+    case ASSOCIATE:
+        op = PORT_REQUEST_ASSOCIATE;
+        break;
+    case DISASSOCIATE:
+        op = PORT_REQUEST_DISASSOCIATE;
+        break;
+    default:
+        macvtapError(VIR_ERR_INTERNAL_ERROR,
+                     _("operation type %d not supported"), op);
+        rc = 1;
+        goto err_exit;
+    }
+
+    rc = doPortProfileOpCommon(multicast, ifindex,
+                               virtPort->u.virtPort8021Qbh.profileID,
+                               NULL,
+                               vm_uuid,
+                               hostuuid,
+                               vf,
+                               op);
+
+    switch (virtPortOp) {
+    case ASSOCIATE:
+        ifaceUp(ifname);
+        break;
+    case DISASSOCIATE:
+        ifaceDown(ifname);
+        break;
+    }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+    return rc;
+}
 
 /**
  * associatePortProfile
  *
  * @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
  * @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
@@ -740,15 +1484,14 @@ delMacvtap(const char *ifname,
  */
 static int
 associatePortProfileId(const char *macvtap_ifname,
+                       const char *linkdev,
                        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:
@@ -756,11 +1499,14 @@ associatePortProfileId(const char *macvtap_ifname,
         break;
 
     case VIR_VIRTUALPORT_8021QBG:
-
+        rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+                                    ASSOCIATE);
         break;
 
     case VIR_VIRTUALPORT_8021QBH:
-
+        rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+                                    vmuuid,
+                                    ASSOCIATE);
         break;
     }
 
@@ -772,6 +1518,7 @@ associatePortProfileId(const char *macvtap_ifname,
  * disassociatePortProfile
  *
  * @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
  * @virtPort: point to object holding port profile parameters
  *
  * Returns 0 in case of success, != 0 otherwise with error
@@ -779,9 +1526,11 @@ associatePortProfileId(const char *macvtap_ifname,
  */
 static int
 disassociatePortProfileId(const char *macvtap_ifname,
+                          const char *linkdev,
                           const virVirtualPortProfileDefPtr virtPort)
 {
     int rc = 0;
+
     VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
               virtPort, macvtap_ifname);
 
@@ -791,13 +1540,18 @@ disassociatePortProfileId(const char *macvtap_ifname,
         break;
 
     case VIR_VIRTUALPORT_8021QBG:
-
+        rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort,
+                                    DISASSOCIATE);
         break;
 
     case VIR_VIRTUALPORT_8021QBH:
-
+        rc = doPortProfileOp8021Qbh(linkdev, virtPort,
+                                    NULL,
+                                    DISASSOCIATE);
         break;
     }
 
     return rc;
 }
+
+#endif
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index ae11c5c..35db31c 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -72,6 +72,7 @@ int openMacvtapTap(const char *ifname,
                    char **res_ifname);
 
 void delMacvtap(const char *ifname,
+                const char *linkdev,
                 virVirtualPortProfileDefPtr virtPortProfile);
 
 # endif /* WITH_MACVTAP */




More information about the libvir-list mailing list