This started out as a fix for a crash reported in IRC and on libvir-list: https://www.redhat.com/archives/libvir-list/2015-August/msg00162.html but as I examined the existing code I found so many small nits to pick in surrounding functions that I decided to fix them all in this patch. The most important fix here is that virNetDevGetFeatures was creating a struct ethtool_gfeatures object as a local on the stack and, although the struct is defined with 0 elements in its features array, we were telling the ethtool ioctl that we had made space for 2 elements. This led to a crash, as outlined in the email above. The fix for this is to allocate the memory for the ethtool_gfeatures object using VIR_ALLOC_VAR, adding on GFEATURES_SIZE elements of type ethtool_get_features_block Beyond that crash fixer, the following fixups were made to the hierarchy of functions between virNetDevGetFeatures() and virNetDevSendEthtoolIoctl(): * macros used to examine the gfeatures bits were renamed from FEATURE_* to GFEATURE_* virNetDevSentEthtoolIoctl(): * no need to initialize sock to -1, since it is always set at the top of the function. * remove VIR_DEBUG log (and subsequent *skipping* of error log!) when errno is EPERM, EINVAL or EOPNOTSUPP. If one of those errors were ever encountered, this would have been *very* problematic, as it would have led to one of those situations where virsh reports "an error was encountered but the cause is unknown" (or whatever the message is when we have an error but no log message). * always call VIR_FORCE_CLOSE(sock) since we know that sock is either a valid fd, or else -1 (which VIR_FORCE_CLOSE() will skip). virNetDevFeatureAvailable() * simplify it - no need for ret. * follow libvirt convention of checking for "bobLobLaw(lawblog) < 0" instead of "!bobLobLaw(lawblog)". virNetDevGFeatureAvailable() * eliminate this function, as it was ill-conceived (it really was only checking for one gfeature (TX_UDP_TNL), not *any* gfeature. virNetDevGetFeatures() * move all data tables (struct elem) out of the function so that they will be statics instead of taking up space on the stack. * remove pointless/incorrect initialization of i = -1. * remove unnecessary static initialization of struct ethtool_value cmd * g_cmd is now a pointer instead of automatic * use libvirt convention of checking return from virNetDevFeatureAvailable() < 0, instead of "!virNetDevFeatureAvailable()", and immediately return to caller with an error when virNetDevFeatureAvailable() returns an error (previously it was ignored). * remove superfluous size_t j, and just re-use i instead. * runtime allocation/free of proper size object for ethtoolfeatures as described above. * directly call virNetDevSentEthtoolIoctl() instead of now defunct virNetDevGFeatureAvailable(). --- V2: I had been looking for something like VIR_ALLOC_VAR() when writing the patch originally, but somehow missed it, and did an ugly hack with VIR_ALLOC_N and a union. In this version I clean that up and use VIR_ALLOC_VAR() instead. NB: This patch does *not* attempt to determine the proper number of elements for the gfeature array at runtime, as proposed in this patch: https://www.redhat.com/archives/libvir-list/2015-August/msg00263.html since that wasn't the cause of the crash. I'll leave it up to Moshe to repost that patch rebased around this one (or whatever ends up being pushed) if he thinks there is value to it. Also - as I mentioned in earlier mail in response to the crash, I noticed when looking into the gfeatures ethtool code that it looks to me like TX_UDP_TNL should actually be 26 rather than 25, but I may be missing something. Moshe - can you either confirm or deny that? Where did you get the value 25 from? src/util/virnetdev.c | 177 +++++++++++++++++++++++---------------------------- 1 file changed, 79 insertions(+), 98 deletions(-) diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 1e20789..05fbff5 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -91,10 +91,10 @@ VIR_LOG_INIT("util.netdev"); #if HAVE_DECL_ETHTOOL_GFEATURES # define TX_UDP_TNL 25 # define GFEATURES_SIZE 2 -# define FEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field) -# define FEATURE_FIELD_FLAG(index) (1U << (index) % 32U) -# define FEATURE_BIT_IS_SET(blocks, index, field) \ - (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index)) +# define GFEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field) +# define GFEATURE_FIELD_FLAG(index) (1U << (index) % 32U) +# define GFEATURE_BIT_IS_SET(blocks, index, field) \ + (GFEATURE_WORD(blocks, index, field) & GFEATURE_FIELD_FLAG(index)) #endif typedef enum { @@ -3032,11 +3032,10 @@ static int virNetDevSendEthtoolIoctl(const char *ifname, void *cmd) { int ret = -1; - int sock = -1; + int sock; virIfreq ifr; - sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (sock < 0) { + if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { virReportSystemError(errno, "%s", _("Cannot open control socket")); goto cleanup; } @@ -3044,27 +3043,11 @@ virNetDevSendEthtoolIoctl(const char *ifname, void *cmd) memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ifname); ifr.ifr_data = cmd; - ret = ioctl(sock, SIOCETHTOOL, &ifr); - if (ret != 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; - } - } + if ((ret = ioctl(sock, SIOCETHTOOL, &ifr)) < 0) + virReportSystemError(errno, "%s", _("ethtool ioctl error")); cleanup: - if (sock) - VIR_FORCE_CLOSE(sock); + VIR_FORCE_CLOSE(sock); return ret; } @@ -3081,35 +3064,50 @@ virNetDevSendEthtoolIoctl(const char *ifname, void *cmd) static int virNetDevFeatureAvailable(const char *ifname, struct ethtool_value *cmd) { - int ret = -1; - cmd = (void*)cmd; - if (!virNetDevSendEthtoolIoctl(ifname, cmd)) - ret = cmd->data > 0 ? 1 : 0; - return ret; + if (virNetDevSendEthtoolIoctl(ifname, cmd) < 0) + return -1; + return cmd->data > 0 ? 1 : 0; } -# if HAVE_DECL_ETHTOOL_GFEATURES -/** - * virNetDevGFeatureAvailable - * This function checks for the availability of a network device gfeature - * - * @ifname: name of the interface - * @cmd: reference to a gfeatures ethtool command structure - * - * Returns 0 if not found, 1 on success, and -1 on failure. - */ -static int -virNetDevGFeatureAvailable(const char *ifname, struct ethtool_gfeatures *cmd) -{ - int ret = -1; +/* static tables used by virNetDevGetFeatures() */ +struct elem { + const int cmd; + const virNetDevFeature feat; +}; - cmd = (void*)cmd; - if (!virNetDevSendEthtoolIoctl(ifname, cmd)) - ret = FEATURE_BIT_IS_SET(cmd->features, TX_UDP_TNL, active); - return ret; -} +/* 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}, +# if HAVE_DECL_ETHTOOL_GGSO + {ETHTOOL_GGSO, VIR_NET_DEV_FEAT_GGSO}, +# endif +# if HAVE_DECL_ETHTOOL_GGRO + {ETHTOOL_GGRO, VIR_NET_DEV_FEAT_GGRO}, +# endif +}; + +# if HAVE_DECL_ETHTOOL_GFLAGS +/* ethtool masks */ +struct elem flags[] = { +# if HAVE_DECL_ETH_FLAG_LRO + {ETH_FLAG_LRO, VIR_NET_DEV_FEAT_LRO}, +# endif +# if HAVE_DECL_ETH_FLAG_TXVLAN + {ETH_FLAG_RXVLAN, VIR_NET_DEV_FEAT_RXVLAN}, + {ETH_FLAG_TXVLAN, VIR_NET_DEV_FEAT_TXVLAN}, +# endif +# if HAVE_DECL_ETH_FLAG_NTUBLE + {ETH_FLAG_NTUPLE, VIR_NET_DEV_FEAT_NTUPLE}, +# endif +# if HAVE_DECL_ETH_FLAG_RXHASH + {ETH_FLAG_RXHASH, VIR_NET_DEV_FEAT_RXHASH}, +# endif +}; # endif @@ -3127,71 +3125,54 @@ int virNetDevGetFeatures(const char *ifname, virBitmapPtr *out) { - size_t i = -1; - struct ethtool_value cmd = { 0 }; + size_t i; + int ret; + struct ethtool_value cmd; # if HAVE_DECL_ETHTOOL_GFEATURES - struct ethtool_gfeatures g_cmd = { 0 }; + struct ethtool_gfeatures *g_cmd; # endif - 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}, -# if HAVE_DECL_ETHTOOL_GGSO - {ETHTOOL_GGSO, VIR_NET_DEV_FEAT_GGSO}, -# endif -# if HAVE_DECL_ETHTOOL_GGRO - {ETHTOOL_GGRO, VIR_NET_DEV_FEAT_GGRO}, -# endif - }; if (!(*out = virBitmapNew(VIR_NET_DEV_FEAT_LAST))) return -1; + /* first set of capabilities are one capability per + * command. cmd.data is set if the interface has that + * capability + */ for (i = 0; i < ARRAY_CARDINALITY(cmds); i++) { cmd.cmd = cmds[i].cmd; - if (virNetDevFeatureAvailable(ifname, &cmd)) + if ((ret = virNetDevFeatureAvailable(ifname, &cmd)) < 0) + return -1; + if (ret) ignore_value(virBitmapSetBit(*out, cmds[i].feat)); } # if HAVE_DECL_ETHTOOL_GFLAGS - size_t j = -1; - /* ethtool masks */ - struct elem flags[] = { -# if HAVE_DECL_ETH_FLAG_LRO - {ETH_FLAG_LRO, VIR_NET_DEV_FEAT_LRO}, -# endif -# if HAVE_DECL_ETH_FLAG_TXVLAN - {ETH_FLAG_RXVLAN, VIR_NET_DEV_FEAT_RXVLAN}, - {ETH_FLAG_TXVLAN, VIR_NET_DEV_FEAT_TXVLAN}, -# endif -# if HAVE_DECL_ETH_FLAG_NTUBLE - {ETH_FLAG_NTUPLE, VIR_NET_DEV_FEAT_NTUPLE}, -# endif -# if HAVE_DECL_ETH_FLAG_RXHASH - {ETH_FLAG_RXHASH, VIR_NET_DEV_FEAT_RXHASH}, -# endif - }; - + /* second set of capabilities are all stored as 1 bit each in + * cmd.data of the result of the single ETHTOOL_GFLAGS command + */ 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)); - } - } + if ((ret = virNetDevFeatureAvailable(ifname, &cmd)) < 0) + return -1; + for (i = 0; ret && i < ARRAY_CARDINALITY(flags); i++) + if (cmd.data & flags[i].cmd) + ignore_value(virBitmapSetBit(*out, flags[i].feat)); # endif # if HAVE_DECL_ETHTOOL_GFEATURES - g_cmd.cmd = ETHTOOL_GFEATURES; - g_cmd.size = GFEATURES_SIZE; - if (virNetDevGFeatureAvailable(ifname, &g_cmd)) + /* allocate an object with GFEATURES_SIZE elements in the features array */ + if (VIR_ALLOC_VAR(g_cmd, struct ethtool_get_features_block, GFEATURES_SIZE) < 0) + return -1; + + g_cmd->cmd = ETHTOOL_GFEATURES; + g_cmd->size = GFEATURES_SIZE; + if (virNetDevSendEthtoolIoctl(ifname, g_cmd) < 0) { + VIR_FREE(g_cmd); + return -1; + } + if (GFEATURE_BIT_IS_SET(g_cmd->features, TX_UDP_TNL, active)) ignore_value(virBitmapSetBit(*out, VIR_NET_DEV_FEAT_TXUDPTNL)); + VIR_FREE(g_cmd); # endif if (virNetDevRDMAFeature(ifname, out) < 0) -- 2.1.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list