Switchdev VF Representor interface name on host is derived based on BDF of pci SR-IOV device in 'hostdev' and querying required net sysfs entries on host. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 5 + src/util/virhostdev.c | 10 ++ src/util/virhostdev.h | 6 + src/util/virnetdevhostdev.c | 284 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virnetdevhostdev.h | 33 +++++ 7 files changed, 340 insertions(+) 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 285955469..73ce73397 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -237,6 +237,7 @@ src/util/virnetdevmacvlan.c src/util/virnetdevmidonet.c src/util/virnetdevopenvswitch.c src/util/virnetdevtap.c +src/util/virnetdevhostdev.c src/util/virnetdevveth.c src/util/virnetdevvportprofile.c src/util/virnetlink.c diff --git a/src/Makefile.am b/src/Makefile.am index db68e01db..0f3c3f1bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,6 +148,7 @@ UTIL_SOURCES = \ util/virnetdev.h util/virnetdev.c \ util/virnetdevbandwidth.h util/virnetdevbandwidth.c \ util/virnetdevbridge.h util/virnetdevbridge.c \ + util/virnetdevhostdev.h util/virnetdevhostdev.c \ util/virnetdevip.h util/virnetdevip.c \ util/virnetdevmacvlan.c util/virnetdevmacvlan.h \ util/virnetdevmidonet.h util/virnetdevmidonet.c \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3b14d7d15..d9bc8ad72 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2288,6 +2288,11 @@ virNetDevBridgeSetSTPDelay; virNetDevBridgeSetVlanFiltering; +# util/virnetdevhostdev.h +virNetdevHostdevCheckVFRepIFName; +virNetdevHostdevGetVFRepIFName; + + # util/virnetdevip.h virNetDevIPAddrAdd; virNetDevIPAddrDel; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index a12224c58..7a90779f0 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -350,6 +350,16 @@ virHostdevNetDevice(virDomainHostdevDefPtr hostdev, return ret; } +/* Non static wrapper for virHostdevNetDevice to use outside filescope */ +int +virHostdevNetDeviceWrapper(virDomainHostdevDefPtr hostdev, + int pfNetDevIdx, + char **linkdev, + int *vf) +{ + return virHostdevNetDevice(hostdev, pfNetDevIdx, linkdev, vf); +} + static bool virHostdevIsPCINetDevice(virDomainHostdevDefPtr hostdev) diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h index 54e1c66be..7dc860a85 100644 --- a/src/util/virhostdev.h +++ b/src/util/virhostdev.h @@ -202,5 +202,11 @@ int virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr mgr, int virHostdevPCINodeDeviceReset(virHostdevManagerPtr mgr, virPCIDevicePtr pci) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int +virHostdevNetDeviceWrapper(virDomainHostdevDefPtr hostdev, + int pfNetDevIdx, + char **linkdev, + int *vf) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4); #endif /* __VIR_HOSTDEV_H__ */ diff --git a/src/util/virnetdevhostdev.c b/src/util/virnetdevhostdev.c new file mode 100644 index 000000000..ecf4b7d8f --- /dev/null +++ b/src/util/virnetdevhostdev.c @@ -0,0 +1,284 @@ +/* + * 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 "virnetdevhostdev.h" +#include "viralloc.h" +#include "virstring.h" +#include "virfile.h" +#include "virerror.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.netdevhostdev"); + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +#define IFSWITCHIDSIZ 20 + +#ifndef SYSFS_NET_DIR +#define SYSFS_NET_DIR "/sys/class/net/" +#endif + +#if 0 +static int +virNetdevHostdevPCISysfsPath(virDomainHostdevDefPtr hostdev, + char **sysfs_path) +{ + virPCIDeviceAddress config_address; + + config_address.domain = hostdev->source.subsys.u.pci.addr.domain; + config_address.bus = hostdev->source.subsys.u.pci.addr.bus; + config_address.slot = hostdev->source.subsys.u.pci.addr.slot; + config_address.function = hostdev->source.subsys.u.pci.addr.function; + + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); +} + + +static int +virNetdevHostdevNetDevice(virDomainHostdevDefPtr hostdev, + int pfNetDevIdx, + char **linkdev, + int *vf) +{ + int ret = -1; + char *sysfs_path = NULL; + + if (virNetdevHostdevPCISysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + if (virPCIIsVirtualFunction(sysfs_path) == 1) { + if (virPCIGetVirtualFunctionInfo(sysfs_path, pfNetDevIdx, + linkdev, vf) < 0) + goto cleanup; + } else { + /* In practice this should never happen, since we currently + * only support assigning SRIOV VFs via <interface + * type='hostdev'>, and it is only those devices that should + * end up calling this function. + */ + if (virPCIGetNetName(sysfs_path, 0, NULL, linkdev) < 0) + goto cleanup; + + if (!linkdev) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The device at %s has no network device name"), + sysfs_path); + goto cleanup; + } + + *vf = -1; + } + + ret = 0; + + cleanup: + VIR_FREE(sysfs_path); + + return ret; +} +#endif + + +/** + * virNetdevHostdevNetSysfsPath + * + * @pf_name: netdev name of the physical function (PF) + * @vf: virtual function (VF) number for the device of interest + * @vf_representor: name of the VF representor interface + * + * Finds the VF representor name of VF# @vf of SRIOV PF @pfname, and + * puts it in @vf_representor. The caller must free @vf_representor + * when it's finished with it + * + * Returns 0 on success, -1 on failure + */ +static int +virNetdevHostdevNetSysfsPath(char *pf_name, int vf, char **vf_representor) +{ + 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_representor_name = NULL; + char *vf_num_str = NULL; + char *vf_suffix = 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; + + 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; + + if (virAsprintf(&pf_subsystem_device_file, "%s/%s/phys_switch_id", + pf_subsystem_dir, dp->d_name) < 0) + goto cleanup; + + if (virFileReadAllQuiet(pf_subsystem_device_file, IFSWITCHIDSIZ, + &pf_subsystem_device_switch_id) > 0) { + if (!STREQ(pf_switch_id, pf_subsystem_device_switch_id)) { + VIR_FREE(pf_subsystem_device_file); + VIR_FREE(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; + + if (virFileReadAllQuiet + (pf_subsystem_device_port_name_file, IFNAMSIZ, + &vf_representor_name) <= 0) { + VIR_FREE(pf_subsystem_device_file); + VIR_FREE(pf_subsystem_device_switch_id); + VIR_FREE(pf_subsystem_device_port_name_file); + continue; + } + + if (virAsprintf(&vf_num_str, "%d", vf) < 0) + goto cleanup; + + /* phys_port_name may contain just VF number or string with + * 'vf' or 'VF' followed by VF number at the end. + */ + if (!(vf_suffix = strcasestr(vf_representor_name, "vf"))) + vf_suffix = vf_representor_name; + + if (strstr(vf_suffix, vf_num_str)) { + if (virAsprintf(vf_representor, "%s", 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_representor_name); + return ret; +} + + +/** + * virNetdevHostdevGetVFRepIFName + * + * @hostdev: host device to check + * @ifname : Contains VF representor name upon successful return. + * + * Returns 0 on success, -1 on failure + */ +int +virNetdevHostdevGetVFRepIFName(virDomainHostdevDefPtr hostdev, + char **ifname) +{ + char *linkdev = NULL; + char *vf_representor = NULL; + int vf = -1; + int ret = -1; + + if (virHostdevNetDeviceWrapper(hostdev, -1, &linkdev, &vf) < 0) + goto cleanup; + + if (virNetdevHostdevNetSysfsPath(linkdev, vf, &vf_representor)) + goto cleanup; + + if (VIR_STRDUP(*ifname, vf_representor) > 0) + ret = 0; + + cleanup: + VIR_FREE(linkdev); + VIR_FREE(vf_representor); + return ret; +} + + +/** + * virNetdevHostdevCheckVFRepIFName + * + * @hostdev: host device to check + * @ifname : VF representor name to verify + * + * Returns 0 on success, -1 on failure + */ +int +virNetdevHostdevCheckVFRepIFName(virDomainHostdevDefPtr hostdev, + const char *ifname) +{ + char *linkdev = NULL; + char *vf_representor = NULL; + int vf = -1; + int ret = -1; + + if (virHostdevNetDeviceWrapper(hostdev, -1, &linkdev, &vf) < 0) + goto cleanup; + + if (virNetdevHostdevNetSysfsPath(linkdev, vf, &vf_representor)) + goto cleanup; + + if (STREQ(ifname, vf_representor)) + ret = 0; + + cleanup: + VIR_FREE(linkdev); + VIR_FREE(vf_representor); + return ret; +} diff --git a/src/util/virnetdevhostdev.h b/src/util/virnetdevhostdev.h new file mode 100644 index 000000000..9fb5c5069 --- /dev/null +++ b/src/util/virnetdevhostdev.h @@ -0,0 +1,33 @@ +/* + * 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" + +int +virNetdevHostdevGetVFRepIFName(virDomainHostdevDefPtr hostdev, + char **ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int +virNetdevHostdevCheckVFRepIFName(virDomainHostdevDefPtr hostdev, + const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +#define virNetdevHostdevVFRepInterfaceStats virNetDevTapInterfaceStats +#endif /* __VIR_NETDEV_HOSTDEV_H__ */ -- 2.13.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list