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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list