[ovs-dev] [PATCH v3] dpif-netdev: Call cpuid for x86 isa availability.

Van Haaren, Harry harry.van.haaren at intel.com
Tue Nov 30 16:53:07 UTC 2021


General comments;
Thanks for reworking, indeed removing dependencies for CPU ISA checking is a good
improvement, and allows CPU ISA optimized implementations to run more often.

> -----Original Message-----
> From: David Marchand <david.marchand at redhat.com>
> Sent: Tuesday, November 30, 2021 2:51 PM
> To: dev at openvswitch.org
> Cc: i.maximets at ovn.org; Stokes, Ian <ian.stokes at intel.com>; Amber, Kumar
> <kumar.amber at intel.com>; Van Haaren, Harry <harry.van.haaren at intel.com>
> Subject: [PATCH v3] dpif-netdev: Call cpuid for x86 isa availability.
> 
> DPIF AVX512 optimisations currently rely on DPDK availability while
> they can be used without DPDK.
> Besides, checking for availability of some isa only has to be done once
> and won't change while a OVS process runs.
> 
> Resolve isa availability in constructors by using a simplified query
> based on cpuid API that comes from the compiler.

Using constructors instead of an init() time call is interesting, but may not be what we
always want. For "vswitchd" it is a useful startup feature, however other binaries/tools
such as "ovs-vsctl" or "ovs-appctl" do not require CPUID-based ISA detection at all.
As per this patch, every launch of "ovs-vsctl" (or other tooling/binaries) will cause the
constructors to run.

I would like to add some VLOG_* info/logging to the CPU ISA detection, it may be useful
to understand the system if in future debug of CPU ISA implementations is required.
(This is how the constructor-running was identified, lots of printf() at tooling startup!)

> Note: this also fixes the check on BMI2 availability: DPDK had a bug
> for this isa, see https://git.dpdk.org/dpdk/commit/?id=aae3037ab1e0.

Yes, this has been resolved in DPDK (thanks to your linked patch), and will be consumed
into the next OVS 2.17 release.

> Suggested-by: Ilya Maximets <i.maximets at ovn.org>
> Signed-off-by: David Marchand <david.marchand at redhat.com>

Overall having OVS use its own CPUID checks is fine with me, I think some improvements
are possible with regards to debug/logging, and potentially revisit the constructor-function
vs cpu_isa_init() call, suggested improvements to implementation inline below.

Regards, -Harry


> Changes since v2:
> - fixed compilation with AVX512,
> 
> Changes since v1:
> - fixed minor checkpatch issue,
> 
> ---
>  lib/automake.mk                        |  2 +
>  lib/cpu.c                              | 68 ++++++++++++++++++++++++++
>  lib/cpu.h                              | 34 +++++++++++++
>  lib/dpdk-stub.c                        |  9 ----
>  lib/dpdk.c                             | 52 --------------------
>  lib/dpdk.h                             |  1 -
>  lib/dpif-netdev-avx512.c               |  5 +-
>  lib/dpif-netdev-extract-avx512.c       | 14 +++---
>  lib/dpif-netdev-lookup-avx512-gather.c |  7 +--
>  9 files changed, 118 insertions(+), 74 deletions(-)
>  create mode 100644 lib/cpu.c
>  create mode 100644 lib/cpu.h
> 
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 46f869a336..5224e08564 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -38,6 +38,8 @@ lib_libopenvswitchavx512_la_CFLAGS = \
>  	-fPIC \
>  	$(AM_CFLAGS)
>  lib_libopenvswitchavx512_la_SOURCES = \
> +	lib/cpu.c \
> +	lib/cpu.h \
>  	lib/dpif-netdev-lookup-avx512-gather.c \
>  	lib/dpif-netdev-extract-avx512.c \
>  	lib/dpif-netdev-avx512.c
> diff --git a/lib/cpu.c b/lib/cpu.c
> new file mode 100644
> index 0000000000..ea1934d3ca
> --- /dev/null
> +++ b/lib/cpu.c
> @@ -0,0 +1,68 @@
> +/*
> + * Copyright (c) 2021, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "cpu.h"
> +#include "openvswitch/compiler.h"
> +
> +#ifdef __x86_64__
> +#include <cpuid.h>
> +#include <inttypes.h>
> +
> +enum x86_reg {
> +    EAX,
> +    EBX,
> +    ECX,
> +    EDX,
> +};
> +#define X86_LEAF_MASK 0x80000000
> +#define X86_EXT_FEATURES_LEAF 0x00000007
> +static bool x86_has_isa(uint32_t leaf, enum x86_reg reg, uint32_t bit)
> +{
> +    uint32_t maxleaf = __get_cpuid_max(leaf & X86_LEAF_MASK, NULL);
> +    uint32_t regs[4];
> +
> +    if (maxleaf < leaf) {
> +        return false;

This is a programming error, not a runtime error correct? We're asking for a
leaf that has not been supported in OVS. Presumably the programmer intended
to ask for a feature that OVS has support for. So a unique/identifiable error return
would be better than "false" which is currently ambiguous? (Currently the return
value "false" means both "not supported" and "programming error")

> +    }
> +    __cpuid_count(leaf, 0, regs[EAX], regs[EBX], regs[ECX], regs[EDX]);
> +    return (regs[reg] & ((uint32_t) 1 << bit)) != 0;
> +}
> +
> +static bool x86_isa[OVS_CPU_ISA_X86_LAST - OVS_CPU_ISA_X86_FIRST + 1];
> +#define X86_ISA(leaf, reg, bit, name) \
> +OVS_CONSTRUCTOR(cpu_isa_ ## name) { \
> +    x86_isa[name - OVS_CPU_ISA_X86_FIRST] = x86_has_isa(leaf, reg, bit); \
> +}

Instead of a constructor, can each "x86_has_isa" call just check if the init() function
has been executed already?

This avoids adding constructor functions, allows using VLOG, and defers doing CPU
ISA checking in tools (ovs-appctl etc) which do not use the actual result.
 

> +X86_ISA(X86_EXT_FEATURES_LEAF, EBX,  8, OVS_CPU_ISA_X86_BMI2)
> +X86_ISA(X86_EXT_FEATURES_LEAF, EBX, 16, OVS_CPU_ISA_X86_AVX512F)
> +X86_ISA(X86_EXT_FEATURES_LEAF, EBX, 30, OVS_CPU_ISA_X86_AVX512BW)
> +X86_ISA(X86_EXT_FEATURES_LEAF, ECX,  1, OVS_CPU_ISA_X86_AVX512VBMI)
> +X86_ISA(X86_EXT_FEATURES_LEAF, ECX, 14, OVS_CPU_ISA_X86_VPOPCNTDQ)
> +#endif

As noted above, a debug level VLOG would be helpful to identify the ISA detected
by OVS when starting up. To display this clearly to the user, some form of string-ified
name of the ISA would be helpful.  This is currently achieved by adding a human-readable
version of the ISA to the struct itself. A similar approach could work here? E.g.:

  X86_ISA(X86_EXT_FEATURES_LEAF, EBX,  8, OVS_CPU_ISA_X86_BMI2, "bmi2")

or refactoring to add " OVS_CPU_ISA_X86_" at the MACRO level, and then use a 
macro to STRINGIFY() the flag if we don't want strings at all..?

  X86_ISA(X86_EXT_FEATURES_LEAF, EBX,  8, BMI2)

> +
> +bool
> +cpu_has_isa(enum ovs_cpu_isa isa OVS_UNUSED)
> +{
> +#ifdef __x86_64__
> +    if (isa >= OVS_CPU_ISA_X86_FIRST &&
> +        isa <= OVS_CPU_ISA_X86_LAST) {
> +        return x86_isa[isa - OVS_CPU_ISA_X86_FIRST];
> +    }
>
> +#endif
> +    return false;
> +}
> diff --git a/lib/cpu.h b/lib/cpu.h
> new file mode 100644
> index 0000000000..92897bb71c
> --- /dev/null
> +++ b/lib/cpu.h
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright (c) 2021, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef CPU_H
> +#define CPU_H 1
> +
> +#include <stdbool.h>
> +
> +enum ovs_cpu_isa {
> +    OVS_CPU_ISA_X86_FIRST,
> +    OVS_CPU_ISA_X86_BMI2 = OVS_CPU_ISA_X86_FIRST,
> +    OVS_CPU_ISA_X86_AVX512F,
> +    OVS_CPU_ISA_X86_AVX512BW,
> +    OVS_CPU_ISA_X86_AVX512VBMI,
> +    OVS_CPU_ISA_X86_VPOPCNTDQ,
> +    OVS_CPU_ISA_X86_LAST = OVS_CPU_ISA_X86_VPOPCNTDQ,
> +};

The enum counting tricks are kinda difficult to read, but it works fine.

> +
> +bool cpu_has_isa(enum ovs_cpu_isa);
> +
> +#endif /* CPU_H */
> diff --git a/lib/dpdk-stub.c b/lib/dpdk-stub.c
> index fe24f9abdf..c332c217cb 100644
> --- a/lib/dpdk-stub.c
> +++ b/lib/dpdk-stub.c
> @@ -79,15 +79,6 @@ print_dpdk_version(void)
>  {
>  }
> 
> -bool
> -dpdk_get_cpu_has_isa(const char *arch OVS_UNUSED,
> -                     const char *feature OVS_UNUSED)
> -{
> -    VLOG_DBG_ONCE("DPDK not supported in this version of Open vSwitch, "
> -                  "cannot use CPU flag based optimizations");
> -    return false;
> -}

Yes makes sense to remove DPDK based impl if OVS is going to carry & maintain its own.

<snip removing rest of current implementation>



More information about the dev mailing list