[libvirt] [PATCH v4 3/6] util: Add helper APIs to get/verify VF Representor name

John Ferlan jferlan at redhat.com
Tue Jul 17 15:10:11 UTC 2018



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 at cavium.com>
> ---
>  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__ */
> 




More information about the libvir-list mailing list