1 #include <stdbool.h>
2 #include <stddef.h>
3
4 #include <cpuinfo.h>
5 #include <cpuinfo/internal-api.h>
6 #include <cpuinfo/log.h>
7
8 #ifdef __linux__
9 #include <linux/api.h>
10
11 #include <sys/syscall.h>
12 #include <unistd.h>
13 #if !defined(__NR_getcpu)
14 #include <asm-generic/unistd.h>
15 #endif
16 #endif
17
18 bool cpuinfo_is_initialized = false;
19
20 struct cpuinfo_processor* cpuinfo_processors = NULL;
21 struct cpuinfo_core* cpuinfo_cores = NULL;
22 struct cpuinfo_cluster* cpuinfo_clusters = NULL;
23 struct cpuinfo_package* cpuinfo_packages = NULL;
24 struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = {NULL};
25
26 uint32_t cpuinfo_processors_count = 0;
27 uint32_t cpuinfo_cores_count = 0;
28 uint32_t cpuinfo_clusters_count = 0;
29 uint32_t cpuinfo_packages_count = 0;
30 uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = {0};
31 uint32_t cpuinfo_max_cache_size = 0;
32
33 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
34 struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL;
35 uint32_t cpuinfo_uarchs_count = 0;
36 #else
37 struct cpuinfo_uarch_info cpuinfo_global_uarch = {cpuinfo_uarch_unknown};
38 #endif
39
40 #ifdef __linux__
41 uint32_t cpuinfo_linux_cpu_max = 0;
42 const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL;
43 const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL;
44 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
45 const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL;
46 #endif
47 #endif
48
cpuinfo_get_processors(void)49 const struct cpuinfo_processor* cpuinfo_get_processors(void) {
50 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
51 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors");
52 }
53 return cpuinfo_processors;
54 }
55
cpuinfo_get_cores(void)56 const struct cpuinfo_core* cpuinfo_get_cores(void) {
57 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
58 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
59 }
60 return cpuinfo_cores;
61 }
62
cpuinfo_get_clusters(void)63 const struct cpuinfo_cluster* cpuinfo_get_clusters(void) {
64 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
65 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters");
66 }
67 return cpuinfo_clusters;
68 }
69
cpuinfo_get_packages(void)70 const struct cpuinfo_package* cpuinfo_get_packages(void) {
71 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
72 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages");
73 }
74 return cpuinfo_packages;
75 }
76
cpuinfo_get_uarchs()77 const struct cpuinfo_uarch_info* cpuinfo_get_uarchs() {
78 if (!cpuinfo_is_initialized) {
79 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs");
80 }
81 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
82 return cpuinfo_uarchs;
83 #else
84 return &cpuinfo_global_uarch;
85 #endif
86 }
87
cpuinfo_get_processor(uint32_t index)88 const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) {
89 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
90 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor");
91 }
92 if CPUINFO_UNLIKELY (index >= cpuinfo_processors_count) {
93 return NULL;
94 }
95 return &cpuinfo_processors[index];
96 }
97
cpuinfo_get_core(uint32_t index)98 const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) {
99 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
100 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
101 }
102 if CPUINFO_UNLIKELY (index >= cpuinfo_cores_count) {
103 return NULL;
104 }
105 return &cpuinfo_cores[index];
106 }
107
cpuinfo_get_cluster(uint32_t index)108 const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) {
109 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
110 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster");
111 }
112 if CPUINFO_UNLIKELY (index >= cpuinfo_clusters_count) {
113 return NULL;
114 }
115 return &cpuinfo_clusters[index];
116 }
117
cpuinfo_get_package(uint32_t index)118 const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) {
119 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
120 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package");
121 }
122 if CPUINFO_UNLIKELY (index >= cpuinfo_packages_count) {
123 return NULL;
124 }
125 return &cpuinfo_packages[index];
126 }
127
cpuinfo_get_uarch(uint32_t index)128 const struct cpuinfo_uarch_info* cpuinfo_get_uarch(uint32_t index) {
129 if (!cpuinfo_is_initialized) {
130 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch");
131 }
132 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
133 if CPUINFO_UNLIKELY (index >= cpuinfo_uarchs_count) {
134 return NULL;
135 }
136 return &cpuinfo_uarchs[index];
137 #else
138 if CPUINFO_UNLIKELY (index != 0) {
139 return NULL;
140 }
141 return &cpuinfo_global_uarch;
142 #endif
143 }
144
cpuinfo_get_processors_count(void)145 uint32_t cpuinfo_get_processors_count(void) {
146 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
147 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count");
148 }
149 return cpuinfo_processors_count;
150 }
151
cpuinfo_get_cores_count(void)152 uint32_t cpuinfo_get_cores_count(void) {
153 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
154 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count");
155 }
156 return cpuinfo_cores_count;
157 }
158
cpuinfo_get_clusters_count(void)159 uint32_t cpuinfo_get_clusters_count(void) {
160 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
161 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count");
162 }
163 return cpuinfo_clusters_count;
164 }
165
cpuinfo_get_packages_count(void)166 uint32_t cpuinfo_get_packages_count(void) {
167 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
168 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count");
169 }
170 return cpuinfo_packages_count;
171 }
172
cpuinfo_get_uarchs_count(void)173 uint32_t cpuinfo_get_uarchs_count(void) {
174 if (!cpuinfo_is_initialized) {
175 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count");
176 }
177 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
178 return cpuinfo_uarchs_count;
179 #else
180 return 1;
181 #endif
182 }
183
cpuinfo_get_l1i_caches(void)184 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) {
185 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
186 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches");
187 }
188 return cpuinfo_cache[cpuinfo_cache_level_1i];
189 }
190
cpuinfo_get_l1d_caches(void)191 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) {
192 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
193 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches");
194 }
195 return cpuinfo_cache[cpuinfo_cache_level_1d];
196 }
197
cpuinfo_get_l2_caches(void)198 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) {
199 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
200 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches");
201 }
202 return cpuinfo_cache[cpuinfo_cache_level_2];
203 }
204
cpuinfo_get_l3_caches(void)205 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) {
206 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
207 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches");
208 }
209 return cpuinfo_cache[cpuinfo_cache_level_3];
210 }
211
cpuinfo_get_l4_caches(void)212 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) {
213 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
214 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches");
215 }
216 return cpuinfo_cache[cpuinfo_cache_level_4];
217 }
218
cpuinfo_get_l1i_cache(uint32_t index)219 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) {
220 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
221 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache");
222 }
223 if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) {
224 return NULL;
225 }
226 return &cpuinfo_cache[cpuinfo_cache_level_1i][index];
227 }
228
cpuinfo_get_l1d_cache(uint32_t index)229 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) {
230 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
231 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache");
232 }
233 if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) {
234 return NULL;
235 }
236 return &cpuinfo_cache[cpuinfo_cache_level_1d][index];
237 }
238
cpuinfo_get_l2_cache(uint32_t index)239 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) {
240 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
241 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache");
242 }
243 if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) {
244 return NULL;
245 }
246 return &cpuinfo_cache[cpuinfo_cache_level_2][index];
247 }
248
cpuinfo_get_l3_cache(uint32_t index)249 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) {
250 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
251 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache");
252 }
253 if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) {
254 return NULL;
255 }
256 return &cpuinfo_cache[cpuinfo_cache_level_3][index];
257 }
258
cpuinfo_get_l4_cache(uint32_t index)259 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) {
260 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
261 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache");
262 }
263 if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) {
264 return NULL;
265 }
266 return &cpuinfo_cache[cpuinfo_cache_level_4][index];
267 }
268
cpuinfo_get_l1i_caches_count(void)269 uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) {
270 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
271 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count");
272 }
273 return cpuinfo_cache_count[cpuinfo_cache_level_1i];
274 }
275
cpuinfo_get_l1d_caches_count(void)276 uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) {
277 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
278 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count");
279 }
280 return cpuinfo_cache_count[cpuinfo_cache_level_1d];
281 }
282
cpuinfo_get_l2_caches_count(void)283 uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) {
284 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
285 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count");
286 }
287 return cpuinfo_cache_count[cpuinfo_cache_level_2];
288 }
289
cpuinfo_get_l3_caches_count(void)290 uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) {
291 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
292 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count");
293 }
294 return cpuinfo_cache_count[cpuinfo_cache_level_3];
295 }
296
cpuinfo_get_l4_caches_count(void)297 uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) {
298 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
299 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count");
300 }
301 return cpuinfo_cache_count[cpuinfo_cache_level_4];
302 }
303
cpuinfo_get_max_cache_size(void)304 uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) {
305 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
306 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size");
307 }
308 return cpuinfo_max_cache_size;
309 }
310
cpuinfo_get_current_processor(void)311 const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) {
312 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
313 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor");
314 }
315 #ifdef __linux__
316 /* Initializing this variable silences a MemorySanitizer error. */
317 unsigned cpu = 0;
318 if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
319 return 0;
320 }
321 if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
322 return 0;
323 }
324 return cpuinfo_linux_cpu_to_processor_map[cpu];
325 #else
326 return NULL;
327 #endif
328 }
329
cpuinfo_get_current_core(void)330 const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) {
331 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
332 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core");
333 }
334 #ifdef __linux__
335 /* Initializing this variable silences a MemorySanitizer error. */
336 unsigned cpu = 0;
337 if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
338 return 0;
339 }
340 if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
341 return 0;
342 }
343 return cpuinfo_linux_cpu_to_core_map[cpu];
344 #else
345 return NULL;
346 #endif
347 }
348
cpuinfo_get_current_uarch_index(void)349 uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) {
350 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
351 cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index");
352 }
353 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
354 #ifdef __linux__
355 if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
356 /* Special case: avoid syscall on systems with only a single
357 * type of cores
358 */
359 return 0;
360 }
361
362 /* General case */
363 /* Initializing this variable silences a MemorySanitizer error. */
364 unsigned cpu = 0;
365 if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
366 return 0;
367 }
368 if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
369 return 0;
370 }
371 return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
372 #else
373 /* Fallback: pretend to be on the big core. */
374 return 0;
375 #endif
376 #else
377 /* Only ARM/ARM64/RISCV processors may include cores of different types
378 * in the same package. */
379 return 0;
380 #endif
381 }
382
cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index)383 uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) {
384 if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
385 cpuinfo_log_fatal(
386 "cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default");
387 }
388 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
389 #ifdef __linux__
390 if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
391 /* Special case: avoid syscall on systems with only a single
392 * type of cores
393 */
394 return 0;
395 }
396
397 /* General case */
398 /* Initializing this variable silences a MemorySanitizer error. */
399 unsigned cpu = 0;
400 if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
401 return default_uarch_index;
402 }
403 if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
404 return default_uarch_index;
405 }
406 return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
407 #else
408 /* Fallback: no API to query current core, use default uarch index. */
409 return default_uarch_index;
410 #endif
411 #else
412 /* Only ARM/ARM64/RISCV processors may include cores of different types
413 * in the same package. */
414 return 0;
415 #endif
416 }
417