From c9027d8f44f9f66a0aa98482258e88567fccb954 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Mon, 23 Feb 2015 15:38:29 +0000 Subject: [PATCH] SRIOV NIC offload feature discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding functionality to libvirt that will allow it query the ethtool interface for the availability of certain NIC HW offload features Here is an example of the feature XML definition: net_eth4_90_e2_ba_5e_a5_45 /sys/devices/pci0000:00/0000:00:03.0/0000:08:00.1/net/eth4 pci_0000_08_00_1 eth4
90:e2:ba:5e:a5:45
Signed-off-by: Ján Tomko --- docs/formatnode.html.in | 18 +++ docs/schemas/nodedev.rng | 14 ++ src/conf/device_conf.c | 14 ++ src/conf/device_conf.h | 17 +++ src/conf/node_device_conf.c | 41 +++++- src/conf/node_device_conf.h | 2 + src/libvirt_private.syms | 1 + src/node_device/node_device_udev.c | 3 + src/util/virnetdev.c | 128 ++++++++++++++++++ src/util/virnetdev.h | 6 + .../net_00_13_02_b9_f9_d3.xml | 9 ++ .../net_00_15_58_2f_e9_55.xml | 9 ++ 12 files changed, 261 insertions(+), 1 deletion(-) diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in index b820a34a16..ba9a0f8672 100644 --- a/docs/formatnode.html.in +++ b/docs/formatnode.html.in @@ -183,6 +183,24 @@ link. So far, the whole element is just for output, not setting. +
feature
+
If present, the hw offloads supported by this network + interface. Possible features are: +
+
rx
rx-checksumming
+
tx
tx-checksumming
+
sg
scatter-gather
+
tso
tcp-segmentation-offload
+
ufo
udp-fragmentation-offload
+
gso
generic-segmentation-offload
+
gro
generic-receive-offload
+
lro
large-receive-offload
+
rxvlan
rx-vlan-offload
+
txvlan
tx-vlan-offload
+
ntuple
ntuple-filters
+
rxhash
receive-hashing
+
+
capability
A network protocol exposed by the device, where the attribute type can be "80203" for IEEE diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 13c5402213..744dccdf5f 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -274,11 +274,25 @@ + + + + + + + + + + + [a-zA-Z\-_]+ + + + diff --git a/src/conf/device_conf.c b/src/conf/device_conf.c index 5ffe159b79..98808e2079 100644 --- a/src/conf/device_conf.c +++ b/src/conf/device_conf.c @@ -39,6 +39,20 @@ VIR_ENUM_IMPL(virInterfaceState, "down", "lowerlayerdown", "testing", "dormant", "up") +VIR_ENUM_IMPL(virNetDevFeature, + VIR_NET_DEV_FEAT_LAST, + "rx", + "tx", + "sg", + "tso", + "gso", + "gro", + "lro", + "rxvlan", + "txvlan", + "ntuple", + "rxhash") + int virDevicePCIAddressIsValid(virDevicePCIAddressPtr addr) { /* PCI bus has 32 slots and 8 functions per slot */ diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index 7256cdcf53..7ea90f6738 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -62,6 +62,23 @@ struct _virInterfaceLink { unsigned int speed; /* link speed in Mbits per second */ }; +typedef enum { + VIR_NET_DEV_FEAT_GRXCSUM, + VIR_NET_DEV_FEAT_GTXCSUM, + VIR_NET_DEV_FEAT_GSG, + VIR_NET_DEV_FEAT_GTSO, + VIR_NET_DEV_FEAT_GGSO, + VIR_NET_DEV_FEAT_GGRO, + VIR_NET_DEV_FEAT_LRO, + VIR_NET_DEV_FEAT_RXVLAN, + VIR_NET_DEV_FEAT_TXVLAN, + VIR_NET_DEV_FEAT_NTUPLE, + VIR_NET_DEV_FEAT_RXHASH, + VIR_NET_DEV_FEAT_LAST +} virNetDevFeature; + +VIR_ENUM_DECL(virNetDevFeature) + 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 a728a0081a..f9c9b6fa7a 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -437,6 +437,16 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def) virBufferEscapeString(&buf, "
%s
\n", data->net.address); virInterfaceLinkFormat(&buf, &data->net.lnk); + if (data->net.features) { + for (i = 0; i < VIR_NET_DEV_FEAT_LAST; i++) { + bool b; + ignore_value(virBitmapGetBit(data->net.features, i, &b)); + if (b) { + virBufferAsprintf(&buf, "\n", + virNetDevFeatureTypeToString(i)); + } + } + } if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) { const char *subtyp = virNodeDevNetCapTypeToString(data->net.subtype); @@ -927,8 +937,10 @@ virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt, union _virNodeDevCapData *data) { xmlNodePtr orignode, lnk; - int ret = -1; + size_t i = -1; + int ret = -1, n = -1; char *tmp; + xmlNodePtr *nodes = NULL; orignode = ctxt->node; ctxt->node = node; @@ -943,6 +955,31 @@ virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt, data->net.address = virXPathString("string(./address[1])", ctxt); + if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) + goto out; + + if (n > 0) { + if (!(data->net.features = virBitmapNew(VIR_NET_DEV_FEAT_LAST))) + goto out; + } + + for (i = 0; i < n; i++) { + int val; + if (!(tmp = virXMLPropString(nodes[i], "name"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing network device feature name")); + goto out; + } + + if ((val = virNetDevFeatureTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("unknown network device feature '%s'"), + tmp); + goto out; + } + ignore_value(virBitmapSetBit(data->net.features, val)); + } + data->net.subtype = VIR_NODE_DEV_CAP_NET_LAST; tmp = virXPathString("string(./capability/@type)", ctxt); @@ -1679,6 +1716,8 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps) case VIR_NODE_DEV_CAP_NET: VIR_FREE(data->net.ifname); VIR_FREE(data->net.address); + virBitmapFree(data->net.features); + data->net.features = NULL; 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 fd5d1799a2..38c6d45788 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -26,6 +26,7 @@ # define __VIR_NODE_DEVICE_CONF_H__ # include "internal.h" +# include "virbitmap.h" # include "virutil.h" # include "virthread.h" # include "virpci.h" @@ -141,6 +142,7 @@ struct _virNodeDevCapsDef { char *ifname; virInterfaceLink lnk; virNodeDevNetCapType subtype; /* LAST -> no subtype */ + virBitmapPtr features; /* enum virNetDevFeature */ } net; struct { unsigned int host; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 13e0931570..c810cf70d1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1669,6 +1669,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 03c7a0b7b1..8c39e5f0f4 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -719,6 +719,9 @@ 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) < 0) + goto out; + ret = 0; out: diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 2a0eb43b79..36e69a3688 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -2728,3 +2728,131 @@ 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. + */ +static 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) { + switch (errno) { + case EPERM: + VIR_DEBUG("ethtool ioctl: permission denied"); + break; + case EINVAL: + VIR_DEBUG("ethtool ioctl: invalid request"); + break; + case EOPNOTSUPP: + VIR_DEBUG("ethtool ioctl: request not supported"); + break; + default: + virReportSystemError(errno, "%s", _("ethtool ioctl 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, + virBitmapPtr *out) +{ + int ret = -1; + size_t i = -1; + size_t j = -1; + struct ethtool_value cmd = { 0 }; + + struct elem{ + const int cmd; + const virNetDevFeature feat; + }; + /* legacy ethtool getters */ + struct elem cmds[] = { + {ETHTOOL_GRXCSUM, VIR_NET_DEV_FEAT_GRXCSUM}, + {ETHTOOL_GTXCSUM, VIR_NET_DEV_FEAT_GTXCSUM}, + {ETHTOOL_GSG, VIR_NET_DEV_FEAT_GSG}, + {ETHTOOL_GTSO, VIR_NET_DEV_FEAT_GTSO}, + {ETHTOOL_GGSO, VIR_NET_DEV_FEAT_GGSO}, + {ETHTOOL_GGRO, VIR_NET_DEV_FEAT_GGRO}, + }; + /* ethtool masks */ + struct elem flags[] = { + {ETH_FLAG_LRO, VIR_NET_DEV_FEAT_LRO}, + {ETH_FLAG_RXVLAN, VIR_NET_DEV_FEAT_RXVLAN}, + {ETH_FLAG_TXVLAN, VIR_NET_DEV_FEAT_TXVLAN}, + {ETH_FLAG_NTUPLE, VIR_NET_DEV_FEAT_NTUPLE}, + {ETH_FLAG_RXHASH, VIR_NET_DEV_FEAT_RXHASH}, + }; + + if (!(*out = virBitmapNew(VIR_NET_DEV_FEAT_LAST))) + goto cleanup; + + for (i = 0; i < ARRAY_CARDINALITY(cmds); i++) { + cmd.cmd = cmds[i].cmd; + if (virNetDevFeatureAvailable(ifname, &cmd)) + ignore_value(virBitmapSetBit(*out, cmds[i].feat)); + } + + cmd.cmd = ETHTOOL_GFLAGS; + if (virNetDevFeatureAvailable(ifname, &cmd)) { + for (j = 0; j < ARRAY_CARDINALITY(flags); j++) { + if (cmd.data & flags[j].cmd) + ignore_value(virBitmapSetBit(*out, flags[j].feat)); + } + } + + ret = 0; + cleanup: + + return ret; + +} +#else +int +virNetDevGetFeatures(const char *ifname ATTRIBUTE_UNUSED, + virBitmapPtr *out ATTRIBUTE_UNUSED) +{ + VIR_DEBUG("Getting network device features on %s is not implemented on this platform", + ifname); + return 0; +} +#endif diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index de8b48014f..643479d8f6 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -25,12 +25,14 @@ # include +# include "virbitmap.h" # include "virsocketaddr.h" # include "virnetlink.h" # include "virmacaddr.h" # include "virpci.h" # include "device_conf.h" +# include # ifdef HAVE_STRUCT_IFREQ typedef struct ifreq virIfreq; # else @@ -182,6 +184,10 @@ int virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname, int *vf) ATTRIBUTE_NONNULL(1); +int virNetDevGetFeatures(const char *ifname, + virBitmapPtr *out) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + int virNetDevGetLinkInfo(const char *ifname, virInterfaceLinkPtr lnk) ATTRIBUTE_NONNULL(1); diff --git a/tests/nodedevschemadata/net_00_13_02_b9_f9_d3.xml b/tests/nodedevschemadata/net_00_13_02_b9_f9_d3.xml index 970ccca366..2a34fed371 100644 --- a/tests/nodedevschemadata/net_00_13_02_b9_f9_d3.xml +++ b/tests/nodedevschemadata/net_00_13_02_b9_f9_d3.xml @@ -4,6 +4,15 @@ eth0
00:13:02:b9:f9:d3
+ + + + + + + + + diff --git a/tests/nodedevschemadata/net_00_15_58_2f_e9_55.xml b/tests/nodedevschemadata/net_00_15_58_2f_e9_55.xml index 741c959137..81d398cc7c 100644 --- a/tests/nodedevschemadata/net_00_15_58_2f_e9_55.xml +++ b/tests/nodedevschemadata/net_00_15_58_2f_e9_55.xml @@ -4,6 +4,15 @@ eth1
00:15:58:2f:e9:55
+ + + + + + + + +