[libvirt] [PATCH] SRIOV NIC offload feature discovery

James Chapman james.p.chapman at intel.com
Mon Feb 16 10:18:32 UTC 2015


Adding functionality to libvirt that will allow it
query the ethtool interface for the availability
of certain NIC HW offload features
---
 src/conf/device_conf.h             |   6 ++
 src/conf/node_device_conf.c        |   7 ++
 src/conf/node_device_conf.h        |   2 +
 src/libvirt_private.syms           |   1 +
 src/node_device/node_device_udev.c |   4 ++
 src/util/virnetdev.c               | 134 +++++++++++++++++++++++++++++++++++++
 src/util/virnetdev.h               |  12 +++-
 7 files changed, 165 insertions(+), 1 deletion(-)

diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
index 7256cdc..091f2f0 100644
--- a/src/conf/device_conf.h
+++ b/src/conf/device_conf.h
@@ -62,6 +62,12 @@ struct _virInterfaceLink {
     unsigned int speed;      /* link speed in Mbits per second */
 };
 
+typedef struct _virDevFeature virDevFeature;
+typedef virDevFeature *virDevFeaturePtr;
+struct _virDevFeature {
+   char *name;             /* device feature */
+};
+
 int virDevicePCIAddressIsValid(virDevicePCIAddressPtr addr);
 
 int virDevicePCIAddressParseXML(xmlNodePtr node,
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index a728a00..7f4dbfe 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -437,6 +437,12 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
                 virBufferEscapeString(&buf, "<address>%s</address>\n",
                                   data->net.address);
             virInterfaceLinkFormat(&buf, &data->net.lnk);
+            if (data->net.features) {
+                for (i = 0; i < data->net.nfeatures; i++) {
+                    virBufferAsprintf(&buf, "<feature name='%s'/>\n",
+                            data->net.features[i].name);
+                }
+            }
             if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
                 const char *subtyp =
                     virNodeDevNetCapTypeToString(data->net.subtype);
@@ -1679,6 +1685,7 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
     case VIR_NODE_DEV_CAP_NET:
         VIR_FREE(data->net.ifname);
         VIR_FREE(data->net.address);
+        VIR_FREE(data->net.features);
         break;
     case VIR_NODE_DEV_CAP_SCSI_HOST:
         VIR_FREE(data->scsi_host.wwnn);
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index fd5d179..918523a 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -141,6 +141,8 @@ struct _virNodeDevCapsDef {
             char *ifname;
             virInterfaceLink lnk;
             virNodeDevNetCapType subtype;  /* LAST -> no subtype */
+            size_t nfeatures;
+            virDevFeaturePtr features;
         } net;
         struct {
             unsigned int host;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c07a561..1d165a9 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1659,6 +1659,7 @@ virNetDevAddRoute;
 virNetDevClearIPAddress;
 virNetDevDelMulti;
 virNetDevExists;
+virNetDevGetFeatures;
 virNetDevGetIndex;
 virNetDevGetIPv4Address;
 virNetDevGetLinkInfo;
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 03c7a0b..349733f 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -719,6 +719,10 @@ static int udevProcessNetworkInterface(struct udev_device *device,
     if (virNetDevGetLinkInfo(data->net.ifname, &data->net.lnk) < 0)
         goto out;
 
+    if (virNetDevGetFeatures(data->net.ifname, &data->net.features,
+                &data->net.nfeatures) < 0)
+        goto out;
+
     ret = 0;
 
  out:
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 2a0eb43..e513c85 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -2728,3 +2728,137 @@ int virNetDevGetRxFilter(const char *ifname,
     *filter = fil;
     return ret;
 }
+
+#if defined(SIOCETHTOOL) && defined(HAVE_STRUCT_IFREQ)
+
+/**
+ * _virNetDevFeatureAvailable
+ * This function checks for the availability of a network device feature
+ *
+ * @ifname: name of the interface
+ * @cmd: reference to an ethtool command structure
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+_virNetDevFeatureAvailable(const char *ifname, struct ethtool_value *cmd)
+{
+    int ret = -1;
+    int sock = -1;
+    virIfreq ifr;
+
+    sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        virReportSystemError(errno, "%s", _("Cannot open control socket"));
+        goto cleanup;
+    }
+
+    strcpy(ifr.ifr_name, ifname);
+    ifr.ifr_data = (void*) cmd;
+
+    if (ioctl(sock, SIOCETHTOOL, &ifr) != 0) {
+        /* Privileged command, no error */
+        if (errno == EPERM || errno == EINVAL) {
+            virReportSystemError(errno, "%s", _("ioctl"));
+            /* Some kernels dont support named feature, no error */
+        } else if (errno == EOPNOTSUPP) {
+            virReportSystemError(errno, "%s", _("Warning"));
+        } else {
+            virReportSystemError(errno, "%s", _("Error"));
+            goto cleanup;
+        }
+    }
+
+    ret = cmd->data > 0 ? 1: 0;
+ cleanup:
+    if (sock)
+        VIR_FORCE_CLOSE(sock);
+
+    return ret;
+}
+
+
+/**
+ * virNetDevGetFeatures:
+ * This function gets the nic offloads features available for ifname
+ *
+ * @ifname: name of the interface
+ * @features: network device feature structures
+ * @nfeatures: number of features available
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virNetDevGetFeatures(const char *ifname,
+                     virDevFeaturePtr *features,
+                     size_t *nfeatures)
+{
+    int ret = -1;
+    size_t i = -1;
+    size_t j = -1;
+    struct ethtool_value cmd;
+
+    struct elem{
+        const char *name;
+        const int cmd;
+    };
+    /* legacy ethtool getters */
+    struct elem cmds[] = {
+        {"rx",     ETHTOOL_GRXCSUM},
+        {"tx",     ETHTOOL_GTXCSUM },
+        {"sg",     ETHTOOL_GSG},
+        {"tso",    ETHTOOL_GTSO},
+        {"gso",    ETHTOOL_GGSO},
+        {"gro",    ETHTOOL_GGRO},
+    };
+    /* ethtool masks */
+    struct elem flags[] = {
+        {"lro",    ETH_FLAG_LRO},
+        {"rxvlan", ETH_FLAG_RXVLAN},
+        {"txvlan", ETH_FLAG_TXVLAN},
+        {"ntuple", ETH_FLAG_NTUPLE},
+        {"rxhash", ETH_FLAG_RXHASH},
+    };
+
+    for (i = 0; i < (sizeof(cmds)/sizeof(struct elem)); i++) {
+        cmd.cmd = cmds[i].cmd;
+        if (_virNetDevFeatureAvailable(ifname, &cmd)) {
+            if (VIR_EXPAND_N(*features, *nfeatures, 1) < 0)
+                goto cleanup;
+            if ((ret = VIR_STRDUP((*features)[i].name, cmds[i].name)) != 1)
+                goto cleanup;
+        }
+    }
+
+    cmd.cmd = ETHTOOL_GFLAGS;
+    for (j = 0; j < (sizeof(flags)/sizeof(struct elem)); j++) {
+        if (_virNetDevFeatureAvailable(ifname, &cmd)) {
+            if (cmd.data & (flags[j].cmd)) {
+                if (VIR_EXPAND_N(*features, *nfeatures, 1) < 0)
+                    goto cleanup;
+                if ((ret = VIR_STRDUP((*features)[i++].name, flags[j].name)) != 1)
+                    goto cleanup;
+            }
+        }
+    }
+
+    ret = 0;
+ cleanup:
+
+    return ret;
+
+}
+#else
+int
+virNetDevGetFeatures(const char *ifname,
+                     virDevFeaturePtr *features,
+                     size_t *nfeatures)
+{
+    VIR_DEBUG("Getting network device features on %s is not implemented on this platform",
+              ifname);
+    *features = NULL;
+    *nfeatures = 0;
+
+    return 0;
+}
+#endif
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index de8b480..3847c68 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -31,6 +31,8 @@
 # include "virpci.h"
 # include "device_conf.h"
 
+# include <linux/ethtool.h>
+typedef struct ethtool_cmd virEthCmd;
 # ifdef HAVE_STRUCT_IFREQ
 typedef struct ifreq virIfreq;
 # else
@@ -182,9 +184,17 @@ int virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname,
                                     int *vf)
     ATTRIBUTE_NONNULL(1);
 
+int virNetDevGetFeatures(const char *ifname,
+                     virDevFeaturePtr *features,
+                     size_t *nfeatures)
+    ATTRIBUTE_NONNULL(1);
+
+int _virNetDevFeatureAvailable(const char *ifname, struct ethtool_value *cmd)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
 int virNetDevGetLinkInfo(const char *ifname,
                          virInterfaceLinkPtr lnk)
-    ATTRIBUTE_NONNULL(1);
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
 
 virNetDevRxFilterPtr virNetDevRxFilterNew(void)
    ATTRIBUTE_RETURN_CHECK;
-- 
2.1.0




More information about the libvir-list mailing list