/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2024 Google, Inc. All rights reserved. */ #include #include #include #ifdef _KERNEL #ifdef __linux__ #include #endif /* __linux__ */ kstat_t *simd_stat_kstat; #endif /* _KERNEL */ #ifdef _KERNEL /* Sometimes, we don't define these at all. */ #ifndef HAVE_KERNEL_FPU #define HAVE_KERNEL_FPU (0) #endif #ifndef HAVE_UNDERSCORE_KERNEL_FPU #define HAVE_UNDERSCORE_KERNEL_FPU (0) #endif #define SIMD_STAT_PRINT(s, feat, val) \ kmem_scnprintf(s + off, MAX(4095-off, 0), "%-16s\t%1d\n", feat, (val)) static int simd_stat_kstat_data(char *buf, size_t size, void *data) { (void) data; static char simd_stat_kstat_payload[4096] = {0}; static int off = 0; #ifdef __linux__ if (off == 0) { off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "kfpu_allowed", kfpu_allowed()); #if defined(__x86_64__) || defined(__i386__) off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "kfpu", HAVE_KERNEL_FPU); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "kfpu_internal", HAVE_KERNEL_FPU_INTERNAL); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "__kernel_fpu", HAVE_UNDERSCORE_KERNEL_FPU); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sse", zfs_sse_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sse2", zfs_sse2_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sse3", zfs_sse3_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "ssse3", zfs_ssse3_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sse41", zfs_sse4_1_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sse42", zfs_sse4_2_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx", zfs_avx_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx2", zfs_avx2_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512f", zfs_avx512f_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512cd", zfs_avx512cd_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512er", zfs_avx512er_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512pf", zfs_avx512pf_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512bw", zfs_avx512bw_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512dq", zfs_avx512dq_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512vl", zfs_avx512vl_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512ifma", zfs_avx512ifma_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "avx512vbmi", zfs_avx512vbmi_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "ymm", __ymm_enabled()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "zmm", __zmm_enabled()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "bmi1", zfs_bmi1_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "bmi2", zfs_bmi2_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "aes", zfs_aes_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "pclmulqdq", zfs_pclmulqdq_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "movbe", zfs_movbe_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "osxsave", boot_cpu_has(X86_FEATURE_OSXSAVE)); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "xsaves", static_cpu_has(X86_FEATURE_XSAVES)); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "xsaveopt", static_cpu_has(X86_FEATURE_XSAVEOPT)); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "xsave", static_cpu_has(X86_FEATURE_XSAVE)); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "fxsr", static_cpu_has(X86_FEATURE_FXSR)); #endif /* __x86__ */ #if defined(__arm__) || defined(__aarch64__) off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "kernel_neon", HAVE_KERNEL_NEON); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "kernel_mode_neon", CONFIG_KERNEL_MODE_NEON); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "neon", zfs_neon_available()); off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sha256", zfs_sha256_available()); #if defined(__aarch64__) /* * This technically can exist on 32b ARM but we don't * define hooks to check for it and I didn't want to * learn enough ARM ASM to add one. */ off += SIMD_STAT_PRINT(simd_stat_kstat_payload, "sha512", zfs_sha512_available()); #endif /* __aarch64__ */ #endif /* __arm__ */ /* We want to short-circuit this on unsupported platforms. */ off += 1; } kmem_scnprintf(buf, MIN(off, size), "%s", simd_stat_kstat_payload); #endif /* __linux__ */ return (0); } #endif /* _KERNEL */ void simd_stat_init(void) { static boolean_t simd_stat_initialized = B_FALSE; if (!simd_stat_initialized) { #if defined(_KERNEL) /* Install kstats for all implementations */ simd_stat_kstat = kstat_create("zfs", 0, "simd", "misc", KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); if (simd_stat_kstat != NULL) { simd_stat_kstat->ks_data = (void*)(uintptr_t)1; simd_stat_kstat->ks_ndata = 1; simd_stat_kstat->ks_flags |= KSTAT_FLAG_NO_HEADERS; kstat_set_raw_ops(simd_stat_kstat, NULL, simd_stat_kstat_data, NULL); kstat_install(simd_stat_kstat); } #endif /* _KERNEL */ } /* Finish initialization */ simd_stat_initialized = B_TRUE; } void simd_stat_fini(void) { #if defined(_KERNEL) if (simd_stat_kstat != NULL) { kstat_delete(simd_stat_kstat); simd_stat_kstat = NULL; } #endif } #ifdef _KERNEL EXPORT_SYMBOL(simd_stat_init); EXPORT_SYMBOL(simd_stat_fini); #endif