xref: /freebsd/sys/contrib/openzfs/module/zcommon/simd_stat.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
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