Switchdev VF representor interface name on host is queried based on Bus:Device:Function information of pci SR-IOV device in Domain's 'hostdev' structure and subsequently verifying the required net sysfs directory and file entries of VF representor according to switchdev model. --- v3 includes changes based on v2's[1] feedback and suggestions. Fixes warnings reported by syntax-check. [1] https://www.redhat.com/archives/libvir-list/2018-February/msg00562.html po/POTFILES.in | 1 + src/libvirt_private.syms | 7 + src/util/Makefile.inc.am | 2 + src/util/virhostdev.c | 2 +- src/util/virhostdev.h | 8 + src/util/virnetdevhostdev.c | 374 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virnetdevhostdev.h | 35 +++++ 7 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 src/util/virnetdevhostdev.c create mode 100644 src/util/virnetdevhostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index d84859a4e..8cd6b86e8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -234,6 +234,7 @@ src/util/virmdev.c src/util/virnetdev.c src/util/virnetdevbandwidth.c src/util/virnetdevbridge.c +src/util/virnetdevhostdev.c src/util/virnetdevip.c src/util/virnetdevmacvlan.c src/util/virnetdevmidonet.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f6897915c..fad235206 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1923,6 +1923,7 @@ virHostCPUStatsAssign; virHostdevFindUSBDevice; virHostdevIsSCSIDevice; virHostdevManagerGetDefault; +virHostdevNetDevice; virHostdevPCINodeDeviceDetach; virHostdevPCINodeDeviceReAttach; virHostdevPCINodeDeviceReset; @@ -2306,6 +2307,12 @@ virNetDevBridgeSetSTPDelay; virNetDevBridgeSetVlanFiltering; +# util/virnetdevhostdev.h +virNetdevHostdevCheckVFRIfName; +virNetdevHostdevGetVFRIfName; +virNetdevHostdevVFRIfStats; + + # util/virnetdevip.h virNetDevIPAddrAdd; virNetDevIPAddrDel; diff --git a/src/util/Makefile.inc.am b/src/util/Makefile.inc.am index a3c3b711f..31fe11c68 100644 --- a/src/util/Makefile.inc.am +++ b/src/util/Makefile.inc.am @@ -104,6 +104,8 @@ UTIL_SOURCES = \ util/virnetdevbandwidth.h \ util/virnetdevbridge.c \ util/virnetdevbridge.h \ + util/virnetdevhostdev.c \ + util/virnetdevhostdev.h \ util/virnetdevip.c \ util/virnetdevip.h \ util/virnetdevmacvlan.c \ diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index a12224c58..4f7b46a04 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -306,7 +306,7 @@ virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) } -static int +int virHostdevNetDevice(virDomainHostdevDefPtr hostdev, int pfNetDevIdx, char **linkdev, diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h index 54e1c66be..735220add 100644 --- a/src/util/virhostdev.h +++ b/src/util/virhostdev.h @@ -60,6 +60,14 @@ struct _virHostdevManager { }; virHostdevManagerPtr virHostdevManagerGetDefault(void); + +int +virHostdevNetDevice(virDomainHostdevDefPtr hostdev, + int pfNetDevIdx, + char **linkdev, + int *vf) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4); + int virHostdevPreparePCIDevices(virHostdevManagerPtr hostdev_mgr, const char *drv_name, diff --git a/src/util/virnetdevhostdev.c b/src/util/virnetdevhostdev.c new file mode 100644 index 000000000..19f95bfdd --- /dev/null +++ b/src/util/virnetdevhostdev.c @@ -0,0 +1,374 @@ +/* + * virnetdevhostdev.c: utilities to get/verify Switchdev VF Representor + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <dirent.h> + +#include "virhostdev.h" +#include "virnetdev.h" +#include "virnetdevhostdev.h" +#include "viralloc.h" +#include "virstring.h" +#include "virfile.h" +#include "virerror.h" +#include "virlog.h" +#include "c-ctype.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.netdevhostdev"); + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +#define IFSWITCHIDSIZ 20 + +#ifdef __linux__ +/** + * virNetdevHostdevNetSysfsPath + * + * @pf_name: netdev name of the physical function (PF) + * @vf: virtual function (VF) number for the device of interest + * @vf_ifname: name of the VF representor interface + * + * Finds the VF representor name of VF# @vf of SRIOV PF @pfname, + * and puts it in @vf_ifname. The caller must free @vf_ifname + * when it's finished with it + * + * Returns 0 on success, -1 on failure + */ +static int +virNetdevHostdevNetSysfsPath(char *pf_name, + int vf, + char **vf_ifname) +{ + size_t i; + char *pf_switch_id = NULL; + char *pf_switch_id_file = NULL; + char *pf_subsystem_device_file = NULL; + char *pf_subsystem_device_switch_id = NULL; + char *pf_subsystem_device_port_name_file = NULL; + char *pf_subsystem_dir = NULL; + char *vf_rep_ifname = NULL; + char *vf_num_str = NULL; + DIR *dirp = NULL; + struct dirent *dp; + int ret = -1; + + if (virAsprintf(&pf_switch_id_file, SYSFS_NET_DIR "%s/phys_switch_id", + pf_name) < 0) + goto cleanup; + + if (virAsprintf(&pf_subsystem_dir, SYSFS_NET_DIR "%s/subsystem", + pf_name) < 0) + goto cleanup; + + /* a failure to read just means the driver doesn't support + * phys_switch_id, so ignoring the error from + * virFileReadAllQuiet(). + */ + if (virFileReadAllQuiet(pf_switch_id_file, IFSWITCHIDSIZ, + &pf_switch_id) <= 0) { + goto cleanup; + } + + if (virDirOpen(&dirp, pf_subsystem_dir) < 0) + goto cleanup; + + /* Iterate over the PFs subsystem devices to find entry with matching + * switch_id with that of PF. + */ + while (virDirRead(dirp, &dp, pf_subsystem_dir) > 0) { + if (STREQ(dp->d_name, pf_name)) + continue; + + VIR_FREE(pf_subsystem_device_file); + if (virAsprintf(&pf_subsystem_device_file, "%s/%s/phys_switch_id", + pf_subsystem_dir, dp->d_name) < 0) + goto cleanup; + + /* a failure to read just means the driver doesn't support the entry + * being probed for current device in subsystem dir, so ignoring the + * error in the following calls to virFileReadAllQuiet() and continue + * the loop to find device which supports this and is a match. + */ + VIR_FREE(pf_subsystem_device_switch_id); + if (virFileReadAllQuiet(pf_subsystem_device_file, IFSWITCHIDSIZ, + &pf_subsystem_device_switch_id) > 0) { + if (STRNEQ(pf_switch_id, pf_subsystem_device_switch_id)) + continue; + } + + if (virAsprintf(&pf_subsystem_device_port_name_file, + "%s/%s/phys_port_name", pf_subsystem_dir, + dp->d_name) < 0) + goto cleanup; + + VIR_FREE(vf_rep_ifname); + vf_rep_ifname = NULL; + + if (virFileReadAllQuiet + (pf_subsystem_device_port_name_file, IFNAMSIZ, + &vf_rep_ifname) <= 0) + continue; + + if (virAsprintf(&vf_num_str, "%d", vf) < 0) + goto cleanup; + + /* phys_port_name may contain just VF number or string in format + * as pf'X'vf'Y' or vf'Y', where X and Y are PF and VF numbers. + * As at this point, we are already with correct PF, just need + * to verify VF number now. + */ + + /* vf_rep_ifname read from file may contain new line,replace with '\0' + for string comparison below */ + i = strlen(vf_rep_ifname); + if (c_isspace(vf_rep_ifname[i-1])) { + vf_rep_ifname[i-1] = '\0'; + i -= 1; + } + + while (c_isdigit(vf_rep_ifname[i-1])) + i -= 1; + + if ((ret = STREQ((vf_rep_ifname + i), vf_num_str))) { + if (VIR_STRDUP(*vf_ifname, dp->d_name) < 0) + goto cleanup; + ret = 0; + break; + } + } + + cleanup: + VIR_DIR_CLOSE(dirp); + VIR_FREE(pf_switch_id); + VIR_FREE(pf_switch_id_file); + VIR_FREE(pf_subsystem_dir); + VIR_FREE(pf_subsystem_device_file); + VIR_FREE(pf_subsystem_device_switch_id); + VIR_FREE(pf_subsystem_device_port_name_file); + VIR_FREE(vf_num_str); + VIR_FREE(vf_rep_ifname); + return ret; +} + + +/** + * virNetdevHostdevGetVFRepIFName + * + * @hostdev: host device to check + * + * Returns VF string with VF representor name upon success else NULL + */ +char * +virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev) +{ + char *linkdev = NULL; + char *ifname = NULL; + char *vf_ifname = NULL; + int vf = -1; + + if (virHostdevNetDevice(hostdev, -1, &linkdev, &vf) < 0) + goto cleanup; + + if (virNetdevHostdevNetSysfsPath(linkdev, vf, &vf_ifname)) + goto cleanup; + + ignore_value(VIR_STRDUP(ifname, vf_ifname)); + + cleanup: + VIR_FREE(linkdev); + VIR_FREE(vf_ifname); + return ifname; +} + + +/** + * virNetdevHostdevCheckVFRepIFName + * + * @hostdev: host device to check + * @ifname : VF representor name to verify + * + * Returns true on success, false on failure + */ +bool +virNetdevHostdevCheckVFRIfName(virDomainHostdevDefPtr hostdev, + const char *ifname) +{ + char *linkdev = NULL; + char *vf_ifname = NULL; + int vf = -1; + bool ret = false; + + if (virHostdevNetDevice(hostdev, -1, &linkdev, &vf) < 0) + goto cleanup; + + if (virNetdevHostdevNetSysfsPath(linkdev, vf, &vf_ifname)) + goto cleanup; + + if (STREQ(ifname, vf_ifname)) + ret = true; + + cleanup: + VIR_FREE(linkdev); + VIR_FREE(vf_ifname); + return ret; +} + + +/*-------------------- interface stats --------------------*/ +/* Copy of virNetDevTapInterfaceStats for linux */ +/** + * virNetdevHostdevVFRepInterfaceStats: + * @ifname: interface + * @stats: where to store statistics + * @swapped: whether to swap RX/TX fields + * + * Fetch RX/TX statistics for given named interface (@ifname) and + * store them at @stats. The returned statistics are always from + * domain POV. Because in some cases this means swapping RX/TX in + * the stats and in others this means no swapping (consider TAP + * vs macvtap) caller might choose if the returned stats should + * be @swapped or not. + * + * Returns 0 on success, -1 otherwise (with error reported). + */ +int +virNetdevHostdevVFRIfStats(const char *ifname, + virDomainInterfaceStatsPtr stats, + bool swapped) +{ + int ifname_len; + FILE *fp; + char line[256], *colon; + + fp = fopen("/proc/net/dev", "r"); + if (!fp) { + virReportSystemError(errno, "%s", + _("Could not open /proc/net/dev")); + return -1; + } + + ifname_len = strlen(ifname); + + while (fgets(line, sizeof(line), fp)) { + long long dummy; + long long rx_bytes; + long long rx_packets; + long long rx_errs; + long long rx_drop; + long long tx_bytes; + long long tx_packets; + long long tx_errs; + long long tx_drop; + + /* The line looks like: + * " eth0:..." + * Split it at the colon. + */ + colon = strchr(line, ':'); + if (!colon) continue; + *colon = '\0'; + if (colon-ifname_len >= line && + STREQ(colon-ifname_len, ifname)) { + if (sscanf(colon+1, + "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld", + &rx_bytes, &rx_packets, &rx_errs, &rx_drop, + &dummy, &dummy, &dummy, &dummy, + &tx_bytes, &tx_packets, &tx_errs, &tx_drop, + &dummy, &dummy, &dummy, &dummy) != 16) + continue; + + if (swapped) { + stats->rx_bytes = tx_bytes; + stats->rx_packets = tx_packets; + stats->rx_errs = tx_errs; + stats->rx_drop = tx_drop; + stats->tx_bytes = rx_bytes; + stats->tx_packets = rx_packets; + stats->tx_errs = rx_errs; + stats->tx_drop = rx_drop; + } else { + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_packets; + stats->rx_errs = rx_errs; + stats->rx_drop = rx_drop; + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_packets; + stats->tx_errs = tx_errs; + stats->tx_drop = tx_drop; + } + + VIR_FORCE_FCLOSE(fp); + return 0; + } + } + VIR_FORCE_FCLOSE(fp); + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("/proc/net/dev: Interface not found")); + return -1; +} +#else +int +virNetdevHostdevVFRIfStats(const char *ifname ATTRIBUTE_UNUSED, + virDomainInterfaceStatsPtr stats ATTRIBUTE_UNUSED, + bool swapped ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("interface stats not implemented on this platform")); + return -1; +} + + +static const char *unsupported = N_("not supported on non-linux platforms"); + + +static int +virNetdevHostdevNetSysfsPath(char *pf_name ATTRIBUTE_UNUSED, + int vf ATTRIBUTE_UNUSED, + char **vf_ifname ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return -1; +} + + +char * +virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev ATTRIBUTE_UNUSED, + char **ifname ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return NULL; +} + + +static bool +virNetdevHostdevCheckVFRIfName(virDomainHostdevDefPtr hostdev ATTRIBUTE_UNUSED, + const char *ifname ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return false; +} +#endif diff --git a/src/util/virnetdevhostdev.h b/src/util/virnetdevhostdev.h new file mode 100644 index 000000000..3ea1804ff --- /dev/null +++ b/src/util/virnetdevhostdev.h @@ -0,0 +1,35 @@ +/* + * virnetdevhostdev.h: utilities to get/verify Switchdev VF Representor + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __VIR_NETDEV_HOSTDEV_H__ +#define __VIR_NETDEV_HOSTDEV_H__ +#include "virnetdevtap.h" + +char * +virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +bool +virNetdevHostdevCheckVFRIfName(virDomainHostdevDefPtr hostdev, + const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetdevHostdevVFRIfStats(const char *ifname, + virDomainInterfaceStatsPtr stats, + bool swapped) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +#endif /* __VIR_NETDEV_HOSTDEV_H__ */ -- 2.13.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list