On 06/28/2018 09:22 AM, Jai Singh Rana wrote: > Introduce new APIs virNetdevHostdevGetVFRIfName and > virNetdevHostdevCheckVFRIfName to fetch/verify VF Representor name > according to switch model for network devices in linux. > > Switchdev VF representor interface name on host is queried based on > Bus:Device:Function information of pci SR-IOV device in Domain's > hostdev def and subsequently verifying the required net sysfs > directory and file entries of VF representor. > > Signed-off-by: Jai Singh Rana <JaiSingh.Rana@xxxxxxxxxx> > --- > po/POTFILES | 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 | 300 ++++++++++++++++++++++++++++++++++++++++++++ > src/util/virnetdevhostdev.h | 34 +++++ > 7 files changed, 353 insertions(+), 1 deletion(-) > create mode 100644 src/util/virnetdevhostdev.c > create mode 100644 src/util/virnetdevhostdev.h > This will certainly be where things get a bit more "unknown" to me... Still I think this patch can be split in half - with one patch being just the promotion of virHostdevNetDevice to external and the second adding the new module. > diff --git a/po/POTFILES b/po/POTFILES > index be2874487c..3f8731f342 100644 > --- a/po/POTFILES > +++ b/po/POTFILES > @@ -235,6 +235,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 0ba8cd2a14..5aa8f7ed64 100644 > --- a/src/libvirt_private.syms > +++ b/src/libvirt_private.syms > @@ -1968,6 +1968,7 @@ virHostdevFindUSBDevice; > virHostdevIsMdevDevice; > virHostdevIsSCSIDevice; > virHostdevManagerGetDefault; > +virHostdevNetDevice; > virHostdevPCINodeDeviceDetach; > virHostdevPCINodeDeviceReAttach; > virHostdevPCINodeDeviceReset; > @@ -2356,6 +2357,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 a22265606c..0846a8e025 100644 > --- a/src/util/Makefile.inc.am > +++ b/src/util/Makefile.inc.am > @@ -108,6 +108,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 f4bd19df64..c6ee725860 100644 > --- a/src/util/virhostdev.c > +++ b/src/util/virhostdev.c > @@ -303,7 +303,7 @@ virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) > } > > Could you add a brief header here describing the inputs and what it returns. Perhaps of most interest is the values that @pfNetDevIdx can be (I see -1, 0, 1 being used). It also looks like @linkdev and @vf are returned as well as 0, -1 for success/failure. The @vf can be -1 and ret == 0 if the else condition is followed. Useful things to know for external consumers. > -static int > +int > virHostdevNetDevice(virDomainHostdevDefPtr hostdev, > int pfNetDevIdx, > char **linkdev, > diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h > index 8f77c00221..7412a20aa9 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); (3) had better be NONNULL too! > + > 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 0000000000..2d3e46c81f > --- /dev/null > +++ b/src/util/virnetdevhostdev.c > @@ -0,0 +1,300 @@ > +/* > + * virnetdevhostdev.c: utilities to get/verify Switchdev VF Representor Far be it for me to be the license expert, but there's no copyright year here. I would have at least expected something, such as: * Copyright (C) 2018 XXX where for XXX I've seen people add a company name or their own name - there's lots of examples to choose from. Not my exact area of expertise over the "right value" though. I would put Red Hat Inc. there, but then again, that's where I work. > + * > + * 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 1 on success, 0 for no switchdev support for @pfname device Typically we prefer -1 on failure, 0 on success Still you seem to be having a 3rd condition where 0 could be we failed and we don't care and 1 could be we succeeded. The -1 could be left as a fatal error since currently there'd be no way to determine the difference between OOM from virAsprintf or failure from virFileReadAllQuiet. In both cases the caller should ignore/reset the last error rather than this lower down API. > + */ > +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; > + int vf_rep_num; > + const char *vf_num_str; > + DIR *dirp = NULL; > + struct dirent *dp; > + int ret = 0; > + > + if (virAsprintf(&pf_switch_id_file, SYSFS_NET_DIR "%s/phys_switch_id", > + pf_name) < 0) > + goto cleanup; should be a fatal failure > + > + if (!virFileExists(pf_switch_id_file)) > + goto cleanup; could be a graceful failure > + > + /* If file exists, failure to read or if it's an empty file just means > + * that driver doesn't support phys_switch_id, therefore ignoring the error "that the driver" s/ignoring/ignore > + * from virFileReadAllQuiet(). > + */ > + if (virFileReadAllQuiet(pf_switch_id_file, IFSWITCHIDSIZ, > + &pf_switch_id) <= 0) > + goto cleanup; > + Seems like this could be a graceful failure > + if (virAsprintf(&pf_subsystem_dir, SYSFS_NET_DIR "%s/subsystem", > + pf_name) < 0) > + goto cleanup; Would be a fatal failure. I'll let you decide on the rest. > + > + 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; > + > + if (!virFileExists(pf_subsystem_device_file)) > + goto cleanup; > + > + /* If file exists, failure to read or if it's an empty file just means > + * the driver doesn't support the entry being probed for current > + * device in subsystem dir, therefore 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; > + } > + else > + continue; > + > + VIR_FREE(pf_subsystem_device_port_name_file); > + if (virAsprintf(&pf_subsystem_device_port_name_file, > + "%s/%s/phys_port_name", pf_subsystem_dir, > + dp->d_name) < 0) > + goto cleanup; > + > + if (!virFileExists(pf_subsystem_device_port_name_file)) > + goto cleanup; > + > + VIR_FREE(vf_rep_ifname); > + if (virFileReadAllQuiet > + (pf_subsystem_device_port_name_file, IFNAMSIZ, > + &vf_rep_ifname) <= 0) > + continue; > + > + /* 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 which is always at the end. > + */ > + > + /* 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; > + } > + > + /* Locating the start of numeric portion of VF in the string */ > + while (c_isdigit(vf_rep_ifname[i-1])) > + i -= 1; > + > + vf_num_str = vf_rep_ifname + i; > + vf_rep_num = virParseNumber(&vf_num_str); > + > + if (vf_rep_num == vf) { > + if (VIR_STRDUP(*vf_ifname, dp->d_name) < 0) > + goto cleanup; > + ret = 1; > + break; Could be a goto cleanup too... because... > + } > + } ...if we complete the virDirRead loop without any match is that a bad thing? > + > + 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_rep_ifname); > + return ret; > +} > + > + > +/** > + * virNetdevHostdevGetVFRepIFName ^^^ This .... > + * > + * @hostdev: host device to check > + * > + * Returns VF string with VF representor name upon success else NULL > + */ > +char * > +virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev) ... doesn't match this ^^^^ Asking from the point of not sure, but is "VFRIfName" redundant? That is could it just be "VFRName" or is there a delineation between a VFR Name and a VFR IFName that needs to be made. You may want to just call this "GetVFRInfo" leaving for the possibility of future expansion to more than just @ifname. Thus you pass the @ifname as a parameter and use 0 and -1 return values to indicate success or failure. > +{ > + 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)) { > + virResetLastError(); This is what I meant earlier - you have no way to determine if the failure is fatal or not and resetting an OOM may not acceptible from this caller. I think the caller of this API should make that determination (something I validate later on). > + goto cleanup; > + } > + > + ignore_value(VIR_STRDUP(ifname, vf_ifname)); Why not just return vf_ifname directly later? It's already a VIR_STRDUP'd value - especially since you VIR_FREE it below. > + > + 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) Same notes about the method name > +{ > + 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)) { > + virResetLastError(); Again, you really don't know the reason for failure here. This is a Check method with a couple of ways to fail. Even the previous call could have some sort of failure that isn't reset. So prehaps the Reset should be in cleanup in this case. > + goto cleanup; > + } > + > + if (STREQ(ifname, vf_ifname)) > + ret = true; ret = STREQ(ifname, vf_ifname); > + > + cleanup: > + VIR_FREE(linkdev); > + VIR_FREE(vf_ifname); > + return ret; > +} > + > + > +/** > + * virNetdevHostdevVFRepInterfaceStats: > + * @ifname: interface > + * @stats: where to store statistics > + * @swapped: whether to swap RX/TX fields > + * > + * Returns 0 on success, -1 otherwise (with error reported). > + */ > +int > +virNetdevHostdevVFRIfStats(const char *ifname, > + virDomainInterfaceStatsPtr stats, > + bool swapped) > +{ > + return virNetDevGetProcNetdevStats(ifname, stats, swapped); Not sure I ever see the need for this to be specific... You're not validating that ifname is a VFRIfName, so the GetProcNetdevStats could just be called directly instead when it's needed later on. If by chance there could be multiple ways to obtain stats for one of these I could see use for the wrapper, but that doesn't happen in this patch series, so the direct call is better since the assumption is that @ifname is a vf_ifname (from the eventual caller). > +} > +#else > +static const char *unsupported = N_("not supported on non-linux platforms"); > + > + If any of the above in the #ifdef __linux__ do change, don't forget to modify these as well! > +char * > +virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev ATTRIBUTE_UNUSED) > +{ > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); > + return NULL; > +} > + > + > +bool > +virNetdevHostdevCheckVFRIfName(virDomainHostdevDefPtr hostdev ATTRIBUTE_UNUSED, > + const char *ifname ATTRIBUTE_UNUSED) > +{ > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); > + return false; > +} > + > + > +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")); Why the change here? IDC, but consisitently speaking it's different. > + return -1; > +} > +#endif > diff --git a/src/util/virnetdevhostdev.h b/src/util/virnetdevhostdev.h > new file mode 100644 > index 0000000000..5fb846fad5 > --- /dev/null > +++ b/src/util/virnetdevhostdev.h > @@ -0,0 +1,34 @@ > +/* > + * virnetdevhostdev.h: utilities to get/verify Switchdev VF Representor > + * Same here w/ copyright year > + * > + * 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__ > + > +char * virNetdevHostdevGetVFRIfName(virDomainHostdevDefPtr hostdev) > + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; You can follow/copy the .c header here and the other two: char * virNetdevHostdevGetVFRIfName(...) John > + > +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__ */ > -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list