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