1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2024 Google, Inc. All rights reserved. 23 */ 24 #include <sys/zfs_context.h> 25 #include <sys/kstat.h> 26 #include <sys/simd.h> 27 28 29 #ifdef _KERNEL 30 #ifdef __linux__ 31 #include <linux/simd.h> 32 #endif /* __linux__ */ 33 kstat_t *simd_stat_kstat; 34 #endif /* _KERNEL */ 35 36 #ifdef _KERNEL 37 /* Sometimes, we don't define these at all. */ 38 #ifndef HAVE_KERNEL_FPU 39 #define HAVE_KERNEL_FPU (0) 40 #endif 41 #ifndef HAVE_KERNEL_NEON 42 #define HAVE_KERNEL_NEON (0) 43 #endif 44 #ifndef HAVE_KERNEL_FPU_INTERNAL 45 #define HAVE_KERNEL_FPU_INTERNAL (0) 46 #endif 47 #ifndef HAVE_UNDERSCORE_KERNEL_FPU 48 #define HAVE_UNDERSCORE_KERNEL_FPU (0) 49 #endif 50 51 #define SIMD_STAT_PRINT(s, feat, val) \ 52 kmem_scnprintf(s + off, MAX(4095-off, 0), "%-16s\t%1d\n", feat, (val)) 53 54 static int 55 simd_stat_kstat_data(char *buf, size_t size, void *data) 56 { 57 (void) data; 58 59 static char simd_stat_kstat_payload[4096] = {0}; 60 static int off = 0; 61 #ifdef __linux__ 62 if (off == 0) { 63 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 64 "kfpu_allowed", kfpu_allowed()); 65 #if defined(__x86_64__) || defined(__i386__) 66 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 67 "kfpu", HAVE_KERNEL_FPU); 68 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 69 "kfpu_internal", HAVE_KERNEL_FPU_INTERNAL); 70 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 71 "__kernel_fpu", HAVE_UNDERSCORE_KERNEL_FPU); 72 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 73 "sse", zfs_sse_available()); 74 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 75 "sse2", zfs_sse2_available()); 76 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 77 "sse3", zfs_sse3_available()); 78 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 79 "ssse3", zfs_ssse3_available()); 80 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 81 "sse41", zfs_sse4_1_available()); 82 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 83 "sse42", zfs_sse4_2_available()); 84 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 85 "avx", zfs_avx_available()); 86 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 87 "avx2", zfs_avx2_available()); 88 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 89 "avx512f", zfs_avx512f_available()); 90 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 91 "avx512cd", zfs_avx512cd_available()); 92 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 93 "avx512er", zfs_avx512er_available()); 94 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 95 "avx512pf", zfs_avx512pf_available()); 96 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 97 "avx512bw", zfs_avx512bw_available()); 98 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 99 "avx512dq", zfs_avx512dq_available()); 100 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 101 "avx512vl", zfs_avx512vl_available()); 102 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 103 "avx512ifma", zfs_avx512ifma_available()); 104 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 105 "avx512vbmi", zfs_avx512vbmi_available()); 106 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 107 "ymm", __ymm_enabled()); 108 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 109 "zmm", __zmm_enabled()); 110 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 111 "bmi1", zfs_bmi1_available()); 112 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 113 "bmi2", zfs_bmi2_available()); 114 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 115 "aes", zfs_aes_available()); 116 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 117 "pclmulqdq", zfs_pclmulqdq_available()); 118 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 119 "movbe", zfs_movbe_available()); 120 121 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 122 "osxsave", boot_cpu_has(X86_FEATURE_OSXSAVE)); 123 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 124 "xsaves", static_cpu_has(X86_FEATURE_XSAVES)); 125 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 126 "xsaveopt", static_cpu_has(X86_FEATURE_XSAVEOPT)); 127 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 128 "xsave", static_cpu_has(X86_FEATURE_XSAVE)); 129 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 130 "fxsr", static_cpu_has(X86_FEATURE_FXSR)); 131 #endif /* __x86__ */ 132 #if defined(__arm__) || defined(__aarch64__) 133 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 134 "kernel_neon", HAVE_KERNEL_NEON); 135 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 136 "kernel_mode_neon", CONFIG_KERNEL_MODE_NEON); 137 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 138 "neon", zfs_neon_available()); 139 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 140 "sha256", zfs_sha256_available()); 141 #if defined(__aarch64__) 142 /* 143 * This technically can exist on 32b ARM but we don't 144 * define hooks to check for it and I didn't want to 145 * learn enough ARM ASM to add one. 146 */ 147 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 148 "sha512", zfs_sha512_available()); 149 #endif /* __aarch64__ */ 150 #endif /* __arm__ */ 151 /* We want to short-circuit this on unsupported platforms. */ 152 off += 1; 153 } 154 155 kmem_scnprintf(buf, MIN(off, size), "%s", simd_stat_kstat_payload); 156 #endif /* __linux__ */ 157 return (0); 158 } 159 #endif /* _KERNEL */ 160 161 void 162 simd_stat_init(void) 163 { 164 static boolean_t simd_stat_initialized = B_FALSE; 165 166 if (!simd_stat_initialized) { 167 #if defined(_KERNEL) 168 /* Install kstats for all implementations */ 169 simd_stat_kstat = kstat_create("zfs", 0, "simd", "misc", 170 KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); 171 172 173 if (simd_stat_kstat != NULL) { 174 simd_stat_kstat->ks_data = (void*)(uintptr_t)1; 175 simd_stat_kstat->ks_ndata = 1; 176 simd_stat_kstat->ks_flags |= KSTAT_FLAG_NO_HEADERS; 177 kstat_set_raw_ops(simd_stat_kstat, 178 NULL, 179 simd_stat_kstat_data, 180 NULL); 181 kstat_install(simd_stat_kstat); 182 } 183 #endif /* _KERNEL */ 184 } 185 /* Finish initialization */ 186 simd_stat_initialized = B_TRUE; 187 } 188 189 void 190 simd_stat_fini(void) 191 { 192 #if defined(_KERNEL) 193 if (simd_stat_kstat != NULL) { 194 kstat_delete(simd_stat_kstat); 195 simd_stat_kstat = NULL; 196 } 197 #endif 198 } 199 200 #ifdef _KERNEL 201 EXPORT_SYMBOL(simd_stat_init); 202 EXPORT_SYMBOL(simd_stat_fini); 203 #endif 204