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 #if defined(CONFIG_KERNEL_MODE_NEON) 136 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 137 "kernel_mode_neon", CONFIG_KERNEL_MODE_NEON); 138 #endif /* CONFIG_KERNEL_MODE_NEON */ 139 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 140 "neon", zfs_neon_available()); 141 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 142 "sha256", zfs_sha256_available()); 143 #if defined(__aarch64__) 144 /* 145 * This technically can exist on 32b ARM but we don't 146 * define hooks to check for it and I didn't want to 147 * learn enough ARM ASM to add one. 148 */ 149 off += SIMD_STAT_PRINT(simd_stat_kstat_payload, 150 "sha512", zfs_sha512_available()); 151 #endif /* __aarch64__ */ 152 #endif /* __arm__ */ 153 /* We want to short-circuit this on unsupported platforms. */ 154 off += 1; 155 } 156 157 kmem_scnprintf(buf, MIN(off, size), "%s", simd_stat_kstat_payload); 158 #endif /* __linux__ */ 159 return (0); 160 } 161 #endif /* _KERNEL */ 162 163 void 164 simd_stat_init(void) 165 { 166 static boolean_t simd_stat_initialized = B_FALSE; 167 168 if (!simd_stat_initialized) { 169 #if defined(_KERNEL) 170 /* Install kstats for all implementations */ 171 simd_stat_kstat = kstat_create("zfs", 0, "simd", "misc", 172 KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); 173 174 175 if (simd_stat_kstat != NULL) { 176 simd_stat_kstat->ks_data = (void*)(uintptr_t)1; 177 simd_stat_kstat->ks_ndata = 1; 178 simd_stat_kstat->ks_flags |= KSTAT_FLAG_NO_HEADERS; 179 kstat_set_raw_ops(simd_stat_kstat, 180 NULL, 181 simd_stat_kstat_data, 182 NULL); 183 kstat_install(simd_stat_kstat); 184 } 185 #endif /* _KERNEL */ 186 } 187 /* Finish initialization */ 188 simd_stat_initialized = B_TRUE; 189 } 190 191 void 192 simd_stat_fini(void) 193 { 194 #if defined(_KERNEL) 195 if (simd_stat_kstat != NULL) { 196 kstat_delete(simd_stat_kstat); 197 simd_stat_kstat = NULL; 198 } 199 #endif 200 } 201 202 #ifdef _KERNEL 203 EXPORT_SYMBOL(simd_stat_init); 204 EXPORT_SYMBOL(simd_stat_fini); 205 #endif 206