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=utf8 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 b820a34a1..ba9a0f867 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 13c540221..744dccdf5 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 5ffe159b7..98808e207 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 7256cdcf5..7ea90f673 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 a728a0081..f9c9b6fa7 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 fd5d1799a..38c6d4578 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 13e093157..c810cf70d 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 03c7a0b7b..8c39e5f0f 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 2a0eb43b7..36e69a368 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 de8b48014..643479d8f 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 970ccca36..2a34fed37 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 741c95913..81d398cc7 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
+ + + + + + + + + -- 2.39.5