From 3157c572e12c7b8c2c385a34bd1eaf0fd1d72137 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 9 Feb 2023 02:09:48 +0100 Subject: [PATCH] btrfs-progs: add run-time CPU feature detection Add support for run-time detection of CPU features on x86_64 to allow selection of accelerated implementations of hash algorithms. When possible use the compiler builtin (works on gcc and clang). The SHA extensions can't be detected by __builtin_cpu_supports and the __cpuid/__cpuidex macros are not consistently provided in all supported gcc and clang versions. Copy the __cpuidex and call it manually for the SHA extensions. Complete list https://en.wikipedia.org/wiki/CPUID . Signed-off-by: David Sterba --- Makefile | 1 + common/cpu-utils.c | 114 +++++++++++++++++++++++++++++++++++++++++++++ common/cpu-utils.h | 58 +++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 common/cpu-utils.c create mode 100644 common/cpu-utils.h diff --git a/Makefile b/Makefile index 26bd8f4c..2b979416 100644 --- a/Makefile +++ b/Makefile @@ -172,6 +172,7 @@ objects = \ kernel-shared/uuid-tree.o \ kernel-shared/volumes.o \ kernel-shared/zoned.o \ + common/cpu-utils.o \ common/device-scan.o \ common/device-utils.o \ common/extent-cache.o \ diff --git a/common/cpu-utils.c b/common/cpu-utils.c new file mode 100644 index 00000000..f7dda74f --- /dev/null +++ b/common/cpu-utils.c @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include +#include +#include "common/cpu-utils.h" + +unsigned long __cpu_flags = 0; +unsigned long __cpu_flags_orig = 0; + +#ifdef __x86_64__ +/* + * Do manual cpuid as cpuid.h is not available for all versions. + * + * Plain cpuid(level) is __cpuidex(level, 0), otherwise look for the leaf/subleaf + * modes at https://en.wikipedia.org/wiki/CPUID or compilers' cpuid.h. + */ + +#ifndef __cpuidex +#define __cpuidex(leaf, subleaf, a, b, c, d) \ + __asm__ __volatile__ ("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (leaf), "2" (subleaf)) +#endif + +#else + +#define __cpuidex(leaf, subleaf, a, b, c, d) \ + do { \ + a = 0; \ + b = 0; \ + c = 0; \ + d = 0; \ + } while(0) +#endif + +#define FLAG(name) if (__cpu_flags & CPU_FLAG_ ## name) printf(" " #name) +void cpu_print_flags(void) { + printf("CPU flags: 0x%lx\n", __cpu_flags); + printf("CPU features:"); + FLAG(SSE2); + FLAG(SSSE3); + FLAG(SSE41); + FLAG(SSE42); + FLAG(SHA); + FLAG(AVX); + FLAG(AVX2); + putchar(10); +} +#undef FLAG + +#ifdef __x86_64__ + +void cpu_detect_flags(void) +{ + unsigned int a, b, c, d; + + __builtin_cpu_init(); + if (__builtin_cpu_supports("sse2")) + __cpu_flags |= CPU_FLAG_SSE2; + if (__builtin_cpu_supports("ssse3")) + __cpu_flags |= CPU_FLAG_SSSE3; + if (__builtin_cpu_supports("sse4.1")) + __cpu_flags |= CPU_FLAG_SSE41; + if (__builtin_cpu_supports("sse4.2")) + __cpu_flags |= CPU_FLAG_SSE42; + if (__builtin_cpu_supports("avx")) + __cpu_flags |= CPU_FLAG_AVX; + if (__builtin_cpu_supports("avx2")) + __cpu_flags |= CPU_FLAG_AVX2; + + /* Flags unsupported by builtins */ + __cpuidex(7, 0, a, b, c, d); + if (b & (1UL << 29)) + __cpu_flags |= CPU_FLAG_SHA; + + __cpu_flags_orig = __cpu_flags; +} + +void cpu_set_level(unsigned long topbit) +{ + if (topbit) + __cpu_flags &= (topbit << 1) - 1; + else + __cpu_flags = 0; +} + +void cpu_reset_level(void) +{ + __cpu_flags = __cpu_flags_orig; +} + +#else + +void cpu_detect_flags() { } + +void cpu_set_level(unsigned long topbit) { } + +void cpu_reset_level(void) { } + +#endif diff --git a/common/cpu-utils.h b/common/cpu-utils.h new file mode 100644 index 00000000..d5f492f0 --- /dev/null +++ b/common/cpu-utils.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +/* + * Detect CPU feature bits at runtime (x86_64 only) + */ + +#ifndef __CPU_UTILS_H__ +#define __CPU_UTILS_H__ + +#include + +#define ENUM_CPU_BIT(name) \ + __ ## name ## _BIT, \ + name = (1U << __ ## name ## _BIT), \ + __ ## name ## _SEQ = __ ## name ## _BIT + +enum cpu_feature { + ENUM_CPU_BIT(CPU_FLAG_SSE2), + ENUM_CPU_BIT(CPU_FLAG_SSSE3), + ENUM_CPU_BIT(CPU_FLAG_SSE41), + ENUM_CPU_BIT(CPU_FLAG_SSE42), + ENUM_CPU_BIT(CPU_FLAG_SHA), + ENUM_CPU_BIT(CPU_FLAG_AVX), + ENUM_CPU_BIT(CPU_FLAG_AVX2), +}; + +#undef ENUM_CPU_BIT + +/* Private but in public header to allow inlining */ +extern unsigned long __cpu_flags; +extern unsigned long __cpu_flags_orig; + +/* Public API */ +void cpu_detect_flags(void); +void cpu_set_level(unsigned long topbit); +void cpu_reset_level(void); +void cpu_print_flags(void); + +static inline bool cpu_has_feature(enum cpu_feature f) +{ + return __cpu_flags & f; +} + +#endif